diff --git a/.gitignore b/.gitignore index 3afa4ac6..9c070a9e 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ SDL2.dll *.vcxproj.user */llvm-* */llvm_* +*/_llvm* */x64/* **/Debug/* **/Debug */* diff --git a/BeefBoot/BeefBoot.vcxproj b/BeefBoot/BeefBoot.vcxproj index cab7e38c..d317bbad 100644 --- a/BeefBoot/BeefBoot.vcxproj +++ b/BeefBoot/BeefBoot.vcxproj @@ -108,7 +108,7 @@ Level3 Disabled _DEBUG;_CONSOLE;%(PreprocessorDefinitions) - ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include + ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include true false MultiThreadedDebug @@ -152,7 +152,7 @@ true NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true - ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm\tools\clang\include + ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm\tools\clang\include MultiThreaded false true diff --git a/BeefBoot/CMakeLists.txt b/BeefBoot/CMakeLists.txt index 365fa336..ad29063b 100644 --- a/BeefBoot/CMakeLists.txt +++ b/BeefBoot/CMakeLists.txt @@ -119,7 +119,7 @@ add_executable(${PROJECT_NAME} ${SRC_FILES} ) -find_package(LLVM 18.1 CONFIG COMPONENTS) +find_package(LLVM 19.1 CONFIG COMPONENTS) if (LLVM_FOUND) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") @@ -128,7 +128,7 @@ if (LLVM_FOUND) include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) - set(TARGET_LIBS_OS "-lLLVM-18") + set(TARGET_LIBS_OS "-lLLVM-19") else() message(FATAL_ERROR "LLVM not found") endif() diff --git a/BeefBuild/BeefProj.toml b/BeefBuild/BeefProj.toml index 38960488..0fc316c1 100644 --- a/BeefBuild/BeefProj.toml +++ b/BeefBuild/BeefProj.toml @@ -7,7 +7,7 @@ StartupObject = "BeefBuild.Program" [Platform.Windows] Description = "BeefBuild" -FileVersion = "0.43.5" +FileVersion = "0.43.6" [Configs.Debug.Win32] TargetName = "" @@ -19,8 +19,8 @@ TargetName = "$(ProjectName)_d" OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib Rpcrt4.lib Ole32.lib" CLibType = "Dynamic" BeefLibType = "DynamicDebug" -DebugCommandArguments = "-proddir=c:\\proj\\BeefTest update BeefProj1" -DebugWorkingDirectory = "c:\\beef\\ide" +DebugCommandArguments = "-run" +DebugWorkingDirectory = "c:\\proj\\BeefTest" EnvironmentVars = ["_NO_DEBUG_HEAP=1"] PreprocessorMacros = ["DEBUG", "CLI"] diff --git a/BeefBuild/src/BuildApp.bf b/BeefBuild/src/BuildApp.bf index 3aaacb3e..e9ba6891 100644 --- a/BeefBuild/src/BuildApp.bf +++ b/BeefBuild/src/BuildApp.bf @@ -25,6 +25,7 @@ namespace BeefBuild public bool mWantsGenerate = false; public bool mHandledVerb; public String mRunArgs ~ delete _; + public String mStartingDirectory = new .() ~ delete _; MainVerbState mMainVerbState; /*void Test() @@ -57,6 +58,8 @@ namespace BeefBuild public override void Init() { + Directory.GetCurrentDirectory(mStartingDirectory); + GetVersionInfo(var exeTime); if (mVerbosity == .Default) @@ -388,9 +391,9 @@ namespace BeefBuild mFailed = true; } - protected override void CompileFailed() + protected override void CompileFailed(Stopwatch stopwatch) { - base.CompileFailed(); + base.CompileFailed(stopwatch); mFailed = true; } @@ -434,9 +437,6 @@ namespace BeefBuild { if ((mVerb == .Run) && (!mDidRun) && (!mFailed)) { - let curPath = scope String(); - Directory.GetCurrentDirectory(curPath); - let workspaceOptions = gApp.GetCurWorkspaceOptions(); let options = gApp.GetCurProjectOptions(mWorkspace.mStartupProject); let targetPaths = scope List(); @@ -445,7 +445,7 @@ namespace BeefBuild if (targetPaths.IsEmpty) return; - ExecutionQueueCmd executionCmd = QueueRun(targetPaths[0], mRunArgs ?? "", curPath); + ExecutionQueueCmd executionCmd = QueueRun(targetPaths[0], mRunArgs ?? "", mStartingDirectory); executionCmd.mRunFlags |= .NoRedirect; executionCmd.mIsTargetRun = true; mDidRun = true; diff --git a/BeefBuild/src/Program.bf b/BeefBuild/src/Program.bf index 610dac9d..5164047f 100644 --- a/BeefBuild/src/Program.bf +++ b/BeefBuild/src/Program.bf @@ -31,7 +31,7 @@ namespace BeefBuild //TestZip2(); String commandLine = scope String(); - commandLine.Join(" ", params args); + commandLine.Join(" ", args); BuildApp mApp = new BuildApp(); mApp.ParseCommandLine(commandLine); diff --git a/BeefLibs/Beefy2D/BeefProj.toml b/BeefLibs/Beefy2D/BeefProj.toml index 6a90b2be..f7bbe704 100644 --- a/BeefLibs/Beefy2D/BeefProj.toml +++ b/BeefLibs/Beefy2D/BeefProj.toml @@ -1,4 +1,5 @@ FileVersion = 1 +Dependencies = {corlib = "*", MiniZ = "*"} [Project] Name = "Beefy2D" @@ -50,3 +51,7 @@ OtherLinkFlags = "$(LinkFlags) \"$(ProjectDir)/dist/BeefySysLib64.lib\"" CLibType = "Static" BeefLibType = "Static" PostBuildCmds = ["CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.dll\")", "CopyToDependents(\"$(ProjectDir)/dist/BeefySysLib64.pdb\")"] + +[Configs.Release_NoLink.Win32] +OtherLinkFlags = "" +PreprocessorMacros = ["RELEASE", "BF32"] diff --git a/BeefLibs/Beefy2D/src/BFApp.bf b/BeefLibs/Beefy2D/src/BFApp.bf index 6c341123..d1518b0a 100644 --- a/BeefLibs/Beefy2D/src/BFApp.bf +++ b/BeefLibs/Beefy2D/src/BFApp.bf @@ -332,6 +332,22 @@ namespace Beefy } } + public Point? CursorScreenPos + { + get + { + for (var window in mWindows) + { + if (var widgetWindow = window as WidgetWindow) + { + if (widgetWindow.mHasMouseInside) + return .(widgetWindow.mClientMouseX + widgetWindow.mClientX, widgetWindow.mClientMouseY + widgetWindow.mClientY); + } + } + return null; + } + } + public static void Startup(String[] args, Action startupCallback) { /*string[] newArgs = new string[args.Length + 1]; diff --git a/BeefLibs/Beefy2D/src/BFWindow.bf b/BeefLibs/Beefy2D/src/BFWindow.bf index a4cdf220..f5cbe2a5 100644 --- a/BeefLibs/Beefy2D/src/BFWindow.bf +++ b/BeefLibs/Beefy2D/src/BFWindow.bf @@ -5,6 +5,7 @@ using System.Diagnostics; using Beefy.gfx; using Beefy.sys; using System.IO; +using Beefy.geom; #if MONOTOUCH using MonoTouch; @@ -109,7 +110,7 @@ namespace Beefy public virtual void PreDraw(Graphics g) { - g.PushDrawLayer(mDefaultDrawLayer); + g.PushDrawLayer(mDefaultDrawLayer); } public virtual void PostDraw(Graphics g) @@ -327,6 +328,9 @@ namespace Beefy static void Static_NativeDragDropFileDelegate(void* window, char8* filePath) { GetBFWindow(window).DragDropFile(StringView(filePath)); } #endif + public Rect ClientRect => .(mClientX, mClientY, mClientWidth, mClientHeight); + public Rect WindowRect => .(mX, mY, mWindowWidth, mWindowHeight); + public this() { } @@ -480,6 +484,11 @@ namespace Beefy BFWindow_Resize(mNativeWindow, (int32)x, (int32)y, (int32)width, (int32)height, (int32)showKind); } + public virtual void ResizeClient(int width, int height) + { + Resize(mX, mY, mWindowWidth + (width - mClientWidth), mWindowHeight + (height - mClientHeight)); + } + public void SetForeground() { BFWindow_SetForeground(mNativeWindow); diff --git a/BeefLibs/Beefy2D/src/Utils.bf b/BeefLibs/Beefy2D/src/Utils.bf index 8fd91952..21195eb6 100644 --- a/BeefLibs/Beefy2D/src/Utils.bf +++ b/BeefLibs/Beefy2D/src/Utils.bf @@ -6,6 +6,7 @@ using System.Threading; using System.IO; using System.Diagnostics; using System.Security.Cryptography; +using Beefy.gfx; namespace Beefy { @@ -21,8 +22,6 @@ namespace Beefy public static float Deg2Rad = Math.PI_f / 180.0f; - - public static int32 Rand() { return mRandom.NextI32(); @@ -33,6 +32,12 @@ namespace Beefy return (Rand() & 0xFFFFFF) / (float)0xFFFFFF; } + public static float RandFloatS() + { +#unwarn + return ((Rand() & 0x1FFFFFF) / (float)0xFFFFFF) - 1.0f; + } + public static float Interpolate(float left, float right, float pct) { return left + (right - left) * pct; @@ -472,5 +477,12 @@ namespace Beefy { val = (float)Math.Round(val * scale); } + + public static Image GetAnimFrame(Image[] images, float pct) + { + int frameNum = (int)(images.Count * (pct - 0.000001f)); + frameNum %= images.Count; + return images[frameNum]; + } } } diff --git a/BeefLibs/Beefy2D/src/geom/Plane.bf b/BeefLibs/Beefy2D/src/geom/Plane.bf index 8e52b3d7..70ab9a9b 100644 --- a/BeefLibs/Beefy2D/src/geom/Plane.bf +++ b/BeefLibs/Beefy2D/src/geom/Plane.bf @@ -283,7 +283,7 @@ namespace Beefy.geom public override void ToString(String str) { - str.AppendF($"{Normal:{Normal} D:{D}}"); + str.AppendF($"{{Normal:{Normal} D:{D}}}"); } #endregion diff --git a/BeefLibs/Beefy2D/src/geom/Rect.bf b/BeefLibs/Beefy2D/src/geom/Rect.bf index 32beb6ed..b14504f1 100644 --- a/BeefLibs/Beefy2D/src/geom/Rect.bf +++ b/BeefLibs/Beefy2D/src/geom/Rect.bf @@ -97,6 +97,14 @@ namespace Beefy.geom public T CenterX => mX + mWidth / 2; public T CenterY => mY + mHeight / 2; + public static Rect operator implicit (Rect value) + where T : operator implicit TOther + where TOther : operator TOther + TOther, operator TOther - TOther, operator TOther * TOther, operator TOther / TOther, operator -TOther, operator TOther / int8, IIsNaN, operator implicit int8 + where int : operator TOther <=> TOther + { + return .(value.mX, value.mY, value.mWidth, value.mHeight); + } + public this(T x = default, T y = default, T width = default, T height = default) { mX = x; @@ -236,6 +244,11 @@ namespace Beefy.geom mHeight *= scaleY; } + public void Scale(T scale) mut + { + Scale(scale, scale); + } + public void ScaleFrom(T scaleX, T scaleY, T centerX, T centerY) mut { Offset(-centerX, -centerY); @@ -249,6 +262,7 @@ namespace Beefy.geom } } + typealias RectI32 = Rect; typealias Rect = Rect; typealias RectD = Rect; } diff --git a/BeefLibs/Beefy2D/src/geom/Vector2.bf b/BeefLibs/Beefy2D/src/geom/Vector2.bf index 7db097fc..9f1c06da 100644 --- a/BeefLibs/Beefy2D/src/geom/Vector2.bf +++ b/BeefLibs/Beefy2D/src/geom/Vector2.bf @@ -46,6 +46,11 @@ namespace Beefy.geom public Vector2 Normalized => Vector2(mX, mY)..Normalize(); + public bool IsLengthLess(float check) + { + return mX * mX + mY * mY < check * check; + } + public static Vector2 Normalize(Vector2 vector) { Vector2 newVec; @@ -104,6 +109,11 @@ namespace Beefy.geom return Vector2((float)Math.Cos(angle) * length, (float)Math.Sin(angle) * length); } + public static Vector2 operator -(Vector2 vec1) + { + return Vector2(-vec1.mX, -vec1.mY); + } + public static Vector2 operator +(Vector2 vec1, Vector2 vec2) { return Vector2(vec1.mX + vec2.mX, vec1.mY + vec2.mY); @@ -123,5 +133,10 @@ namespace Beefy.geom { return Vector2(vec1.mX / factor, vec1.mY / factor); } + + public override void ToString(String strBuffer) + { + strBuffer.AppendF("{0:0.0#}, {1:0.0#}", mX, mY); + } } } diff --git a/BeefLibs/Beefy2D/src/geom/Vector3.bf b/BeefLibs/Beefy2D/src/geom/Vector3.bf index 4ee85123..0adfc7e7 100644 --- a/BeefLibs/Beefy2D/src/geom/Vector3.bf +++ b/BeefLibs/Beefy2D/src/geom/Vector3.bf @@ -101,6 +101,8 @@ namespace Beefy.geom } } + public Vector2 XY => .(mX, mY); + public this(float x, float y, float z) { mX = x; @@ -306,5 +308,7 @@ namespace Beefy.geom { str.AppendF("{0:0.0#}, {1:0.0#}, {2:0.0#}", mX, mY, mZ); } + + public static Vector3 operator implicit(Vector2 vec) => .(vec.mX, vec.mY, 0); } } diff --git a/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf b/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf index ea9d6448..c4de0423 100644 --- a/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf +++ b/BeefLibs/Beefy2D/src/gfx/DrawLayer.bf @@ -13,6 +13,8 @@ namespace Beefy.gfx #if !STUDIO_CLIENT public class DrawLayer { + public static List sDrawLayers = new .() ~ delete _; + [CallingConvention(.Stdcall), CLink] static extern void* DrawLayer_Create(void* window); @@ -33,10 +35,12 @@ namespace Beefy.gfx public this(BFWindow window) { mNativeDrawLayer = DrawLayer_Create((window != null) ? (window.mNativeWindow) : null); + sDrawLayers.Add(this); } public ~this() { + sDrawLayers.Remove(this); DrawLayer_Delete(mNativeDrawLayer); } diff --git a/BeefLibs/Beefy2D/src/gfx/Font.bf b/BeefLibs/Beefy2D/src/gfx/Font.bf index d5fe081c..005c00b2 100644 --- a/BeefLibs/Beefy2D/src/gfx/Font.bf +++ b/BeefLibs/Beefy2D/src/gfx/Font.bf @@ -6,6 +6,7 @@ using Beefy.geom; using Beefy.utils; using System.Diagnostics; using System.Threading; +using res; namespace Beefy.gfx { @@ -36,6 +37,104 @@ namespace Beefy.gfx public float mMaxWidth; } + public class FontEffect + { + public enum Kind + { + case None; + case Outline(float thickness, uint32 color); + } + + public class EffectCharData + { + public Font.CharData mSrcCharData; + public Font.CharData mEffectCharData ~ delete _; + } + + public class FontEntry + { + public Font mFont; + public ImageAtlas mAtlas = new ImageAtlas() ~ delete _; + public List mCharData = new .() ~ delete _; + } + + public Kind mKind; + public String mEffectOptions = new .() ~ delete _; + public Dictionary mFontEntries = new .() ~ DeleteDictionaryAndValues!(_); + public Dictionary mCharData = new .() ~ DeleteDictionaryAndValues!(_); + + public this(Kind kind) + { + mKind = kind; + switch (mKind) + { + case .Outline(let thickness, let color): + mEffectOptions..Clear().AppendF( + $""" + Effect=Stroke + Size={thickness} + Color=#{color:X} + """); + default: + } + } + + public ~this() + { + + } + + public Font.CharData Apply(Font font, Font.CharData charData) + { + EffectCharData effectCharData = null; + if (mCharData.TryAdd(charData, ?, var effectCharDataPtr)) + { + effectCharData = new EffectCharData(); + *effectCharDataPtr = effectCharData; + + effectCharData.mSrcCharData = charData; + effectCharData.mEffectCharData = new .(charData); + } + else + { + return (*effectCharDataPtr).mEffectCharData; + } + + FontEntry fontEntry = null; + if (mFontEntries.TryAdd(font, ?, var fontEntryPtr)) + { + fontEntry = new .(); + *fontEntryPtr = fontEntry; + } + else + fontEntry = *fontEntryPtr; + + int32 ofsX = 0; + int32 ofsY = 0; + if (mKind case .Outline(let thickness, ?)) + { + ofsX += (.)Math.Ceiling(thickness) + 2; + ofsY += (.)Math.Ceiling(thickness) + 2; + } + + effectCharData.mEffectCharData.mXOffset -= ofsX; + effectCharData.mEffectCharData.mYOffset -= ofsY; + var effectImage = fontEntry.mAtlas.Alloc(charData.mImageSegment.mSrcWidth + ofsX * 2, charData.mImageSegment.mSrcHeight + ofsY * 2); + effectCharData.mEffectCharData.mImageSegment = effectImage; + effectCharData.mSrcCharData.mImageSegment.ApplyEffect(effectImage, mEffectOptions); + + /*uint32 color = 0xFFFFFFFF; + effectImage.SetBits(0, 0, 1, 1, 1, &color);*/ + + return effectCharData.mEffectCharData; + } + + public void Prepare(Font font, StringView str) + { + + } + } + public class Font { [CallingConvention(.Stdcall), CLink] @@ -89,6 +188,24 @@ namespace Beefy.gfx public int32 mYOffset; public int32 mXAdvance; public bool mIsCombiningMark; + + public this() + { + + } + + public this(CharData copyFrom) + { + mImageSegment = copyFrom.mImageSegment; + mX = copyFrom.mX; + mY = copyFrom.mY; + mWidth = copyFrom.mWidth; + mHeight = copyFrom.mHeight; + mXOffset = copyFrom.mXOffset; + mYOffset = copyFrom.mYOffset; + mXAdvance = copyFrom.mXAdvance; + mIsCombiningMark = copyFrom.mIsCombiningMark; + } } public class Page @@ -139,6 +256,9 @@ namespace Beefy.gfx //BitmapFont mBMFont ~ delete _; public StringView mEllipsis = "..."; + List mFontEffectStack ~ delete _; + DisposeProxy mFontEffectDisposeProxy = new .(new () => { PopFontEffect(); }) ~ delete _; + public this() { } @@ -153,6 +273,19 @@ namespace Beefy.gfx FTFont_ClearCache(); } + public DisposeProxy PushFontEffect(FontEffect fontEffect) + { + if (mFontEffectStack == null) + mFontEffectStack = new .(); + mFontEffectStack.Add(fontEffect); + return mFontEffectDisposeProxy; + } + + public void PopFontEffect() + { + mFontEffectStack.PopBack(); + } + static void BuildFontNameCache() { #if BF_PLATFORM_WINDOWS @@ -383,6 +516,14 @@ namespace Beefy.gfx void GetFontPath(StringView fontName, String path) { + if (fontName.StartsWith('[')) + { + path.Set(fontName); + if (FilePackManager.TryMakeMemoryString(path)) + return; + path.Clear(); + } + if (fontName.Contains('.')) { Path.GetAbsolutePath(fontName, BFApp.sApp.mInstallDir, path); @@ -666,13 +807,23 @@ namespace Beefy.gfx mLowCharData[(int)checkChar] = charData; else mCharData[checkChar] = charData; - return charData; + break; } - + } + + if (charData == null) + { if (checkChar == (char32)'?') return null; return GetCharData((char32)'?'); } + + if (mFontEffectStack?.IsEmpty == false) + { + var fontEffect = mFontEffectStack.Back; + charData = fontEffect.Apply(this, charData); + } + return charData; } @@ -828,10 +979,10 @@ namespace Beefy.gfx } } - public void Draw(Graphics g, StringView theString, FontMetrics* fontMetrics = null) + public void Draw(Graphics g, float x, float y, StringView theString, FontMetrics* fontMetrics = null) { - float curX = 0; - float curY = 0; + float curX = x; + float curY = y; Matrix newMatrix = Matrix(); bool hasClipRect = g.mClipRect.HasValue; @@ -839,7 +990,10 @@ namespace Beefy.gfx uint32 color = g.mColor; - g.PushTextRenderState(); + bool usingTextRenderState = mFontEffectStack?.IsEmpty != false; + + if (usingTextRenderState) + g.PushTextRenderState(); float markTopOfs = 0; float markBotOfs = 0; @@ -869,6 +1023,8 @@ namespace Beefy.gfx } CharData charData = GetCharData(c); + if (charData == null) + continue; float drawX = curX + charData.mXOffset; float drawY = curY + charData.mYOffset; @@ -943,7 +1099,11 @@ namespace Beefy.gfx prevChar = c; } - g.PopRenderState(); + if (usingTextRenderState) + g.PopRenderState(); + + if (fontMetrics != null) + fontMetrics.mMaxX = Math.Max(fontMetrics.mMaxX, curX); } public float Draw(Graphics g, StringView theString, float x, float y, int32 justification = -1, float width = 0, FontOverflowMode stringEndMode = FontOverflowMode.Overflow, FontMetrics* fontMetrics = null) @@ -1060,14 +1220,11 @@ namespace Beefy.gfx if (g != null) { - using (g.PushTranslate(useX, useY)) - Draw(g, workingStr, fontMetrics); - } - else - { - if (fontMetrics != null) - fontMetrics.mMaxWidth = Math.Max(fontMetrics.mMaxWidth, GetWidth(workingStr)); + Draw(g, useX, useY, workingStr, fontMetrics); } + + if (fontMetrics != null) + fontMetrics.mMaxWidth = Math.Max(fontMetrics.mMaxWidth, GetWidth(workingStr)); drawHeight += GetLineSpacing(); if (fontMetrics != null) diff --git a/BeefLibs/Beefy2D/src/gfx/Graphics.bf b/BeefLibs/Beefy2D/src/gfx/Graphics.bf index 45293fb9..00d33739 100644 --- a/BeefLibs/Beefy2D/src/gfx/Graphics.bf +++ b/BeefLibs/Beefy2D/src/gfx/Graphics.bf @@ -547,6 +547,11 @@ namespace Beefy.gfx (int)mPixelSnapping, mSmoothing ? 1 : 0, mAdditive ? 1 : 0, (int)g.mColor);*/ } + public void DrawCentered(Image image, float x = 0, float y = 0) + { + Draw(image, x - image.mWidth / 2, y - image.mHeight / 2); + } + public void Draw(IDrawable drawable, Vector2 vec) => Draw(drawable, vec.mX, vec.mY); public void DrawButton(Image image, float x, float y, float width) @@ -924,6 +929,11 @@ namespace Beefy.gfx FillRect(x, y + height - thickness, width, thickness); } + public void OutlineRect(Rect rect) + { + OutlineRect(rect.mX, rect.mY, rect.mWidth, rect.mHeight); + } + public void FillRectGradient(float x, float y, float width, float height, Color colorTopLeft, Color colorTopRight, Color colorBotLeft, Color colorBotRight) { @@ -943,6 +953,39 @@ namespace Beefy.gfx Gfx_SetDrawVertex(5, m.tx + (m.a + m.c), m.ty + (m.b + m.d), 0, 0, 0, Color.Mult(mColor, colorBotRight)); } + public void FillRectGradientEx(float x, float y, float width, float height, + Color colorTopLeft, Color colorTopRight, Color colorBotLeft, Color colorBotRight) + { + Matrix m = Matrix.IdentityMatrix; + m.SetMultiplied(x, y, width, height, ref mMatrix); + + int32 r = ((int32)colorTopLeft.R + colorTopRight.R + colorBotLeft.R + colorBotRight.R) / 4; + int32 g = ((int32)colorTopLeft.G + colorTopRight.G + colorBotLeft.G + colorBotRight.G) / 4; + int32 b = ((int32)colorTopLeft.B + colorTopRight.B + colorBotLeft.B + colorBotRight.B) / 4; + int32 a = ((int32)colorTopLeft.A + colorTopRight.A + colorBotLeft.A + colorBotRight.A) / 4; + Color centerColor = .(r, g, b, a); + + //TODO: Multiply color + + Gfx_AllocTris(mWhiteDot.mNativeTextureSegment, 12); + + Gfx_SetDrawVertex(0, m.tx, m.ty, 0, 0, 0, Color.Mult(mColor, colorTopLeft)); + Gfx_SetDrawVertex(1, m.tx + m.a, m.ty + m.b, 0, 0, 0, Color.Mult(mColor, colorTopRight)); + Gfx_SetDrawVertex(2, m.tx + (m.a + m.c) * 0.5f, m.ty + (m.b + m.d) * 0.5f, 0, 0, 0, Color.Mult(mColor, centerColor)); + + Gfx_CopyDrawVertex(3, 1); // Top Right + Gfx_SetDrawVertex(4, m.tx + (m.a + m.c), m.ty + (m.b + m.d), 0, 0, 0, Color.Mult(mColor, colorBotRight)); + Gfx_CopyDrawVertex(5, 2); // Center + + Gfx_CopyDrawVertex(6, 2); // Center + Gfx_CopyDrawVertex(7, 4); // Bottom Right + Gfx_SetDrawVertex(8, m.tx + m.c, m.ty + m.d, 0, 0, 0, Color.Mult(mColor, colorBotLeft)); + + Gfx_CopyDrawVertex(9, 8); // Bottom Left + Gfx_CopyDrawVertex(10, 0); // Top Left + Gfx_CopyDrawVertex(11, 2); // Center + } + void DoDrawLine(float x0, float y0, float x1, float y1, float width) { Image img = GetBorderedWhiteSquare(Math.Max((int)width, 1)); diff --git a/BeefLibs/Beefy2D/src/gfx/Image.bf b/BeefLibs/Beefy2D/src/gfx/Image.bf index 5f737827..f5db7e87 100644 --- a/BeefLibs/Beefy2D/src/gfx/Image.bf +++ b/BeefLibs/Beefy2D/src/gfx/Image.bf @@ -3,6 +3,7 @@ using System.Collections; using System.Text; using Beefy.utils; using System.Diagnostics; +using res; #if STUDIO_CLIENT using Beefy.ipc; @@ -70,6 +71,9 @@ namespace Beefy.gfx [CallingConvention(.Stdcall), CLink] static extern int32 Gfx_Texture_GetHeight(void* textureSegment); + [CallingConvention(.Stdcall), CLink] + static extern void Gfx_ApplyEffect(void* destTextureSegment, void* srcTextureSegment, char8* options); + public this() { } @@ -94,11 +98,13 @@ namespace Beefy.gfx return CreateFromNativeTextureSegment(aNativeTextureSegment); } - public static Image LoadFromFile(String fileName, LoadFlags flags = .None) + public static Image LoadFromFile(StringView fileName, LoadFlags flags = .None) { scope AutoBeefPerf("Image.LoadFromFile"); - void* aNativeTextureSegment = Gfx_LoadTexture(fileName, (int32)flags); + var useFileName = scope String()..Append(fileName); + FilePackManager.TryMakeMemoryString(useFileName); + void* aNativeTextureSegment = Gfx_LoadTexture(useFileName, (int32)flags); if (aNativeTextureSegment == null) { if (flags.HasFlag(.FatalError)) @@ -186,6 +192,11 @@ namespace Beefy.gfx Gfx_ModifyTextureSegment(mNativeTextureSegment, srcImage.mNativeTextureSegment, (int32)srcX, (int32)srcY, (int32)srcWidth, (int32)srcHeight); } + public void ApplyEffect(Image destImage, StringView options) + { + Gfx_ApplyEffect(destImage.mNativeTextureSegment, mNativeTextureSegment, options.ToScopeCStr!()); + } + public void SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32* bits) { Gfx_Texture_SetBits(mNativeTextureSegment, (.)destX, (.)destY, (.)destWidth, (.)destHeight, (.)srcPitch, bits); diff --git a/BeefLibs/Beefy2D/src/gfx/ImageAtlas.bf b/BeefLibs/Beefy2D/src/gfx/ImageAtlas.bf new file mode 100644 index 00000000..f600f752 --- /dev/null +++ b/BeefLibs/Beefy2D/src/gfx/ImageAtlas.bf @@ -0,0 +1,66 @@ +using System.Collections; +using System; +namespace Beefy.gfx; + +class ImageAtlas +{ + public class Page + { + public Image mImage ~ delete _; + public int32 mCurX; + public int32 mCurY; + public int32 mMaxRowHeight; + } + + public List mPages = new .() ~ DeleteContainerAndItems!(_); + public bool mAllowMultiplePages = true; + + public int32 mImageWidth = 1024; + public int32 mImageHeight = 1024; + + public this() + { + + } + + public Image Alloc(int32 width, int32 height) + { + Page page = null; + if (!mPages.IsEmpty) + { + page = mPages.Back; + if (page.mCurX + (int)width > page.mImage.mSrcWidth) + { + // Move down to next row + page.mCurX = 0; + page.mCurY += page.mMaxRowHeight; + page.mMaxRowHeight = 0; + } + + if (page.mCurY + height > page.mImage.mSrcHeight) + { + // Doesn't fit + page = null; + } + } + + if (page == null) + { + page = new .(); + page.mImage = Image.CreateDynamic(mImageWidth, mImageHeight); + + uint32* colors = new uint32[mImageWidth*mImageHeight]*; + defer delete colors; + for (int i < mImageWidth*mImageHeight) + colors[i] = 0xFF000000 | (.)i; + + page.mImage.SetBits(0, 0, mImageWidth, mImageHeight, mImageWidth, colors); + mPages.Add(page); + } + + Image image = page.mImage.CreateImageSegment(page.mCurX, page.mCurY, width, height); + page.mCurX += width; + page.mMaxRowHeight = Math.Max(page.mMaxRowHeight, height); + return image; + } +} \ No newline at end of file diff --git a/BeefLibs/Beefy2D/src/gfx/Matrix.bf b/BeefLibs/Beefy2D/src/gfx/Matrix.bf index d48dfed6..a03e2d25 100644 --- a/BeefLibs/Beefy2D/src/gfx/Matrix.bf +++ b/BeefLibs/Beefy2D/src/gfx/Matrix.bf @@ -168,6 +168,11 @@ namespace Beefy.gfx return Point(tx + a * point.x + c * point.y, ty + b * point.x + d * point.y); } + public Vector2 Multiply(Vector2 point) + { + return Vector2(tx + a * point.mX + c * point.mY, ty + b * point.mX + d * point.mY); + } + public void Invert() mut { float _a = a; diff --git a/BeefLibs/Beefy2D/src/gfx/Shader.bf b/BeefLibs/Beefy2D/src/gfx/Shader.bf index 25e0de6a..334888b5 100644 --- a/BeefLibs/Beefy2D/src/gfx/Shader.bf +++ b/BeefLibs/Beefy2D/src/gfx/Shader.bf @@ -1,6 +1,7 @@ using System; using System.Collections; using System.Text; +using res; namespace Beefy.gfx { @@ -28,9 +29,22 @@ namespace Beefy.gfx [CallingConvention(.Stdcall), CLink] static extern void* Gfx_GetShaderParam(void* shader, String paramName); - public static Shader CreateFromFile(String fileName, VertexDefinition vertexDefinition) + public static Shader CreateFromFile(StringView fileName, VertexDefinition vertexDefinition) { - void* aNativeShader = Gfx_LoadShader(fileName, vertexDefinition.mNativeVertexDefinition); + var useFileName = scope String(fileName); + if (FilePackManager.TryMakeMemoryString(useFileName, ".fx_VS_vs_4_0")) + { + var useFileName2 = scope String(fileName); + if (FilePackManager.TryMakeMemoryString(useFileName2, ".fx_PS_ps_4_0")) + { + useFileName.Append("\n"); + useFileName.Append(useFileName2); + } + } + + FilePackManager.TryMakeMemoryString(useFileName, ".fx"); + + void* aNativeShader = Gfx_LoadShader(useFileName, vertexDefinition.mNativeVertexDefinition); if (aNativeShader == null) return null; diff --git a/BeefLibs/Beefy2D/src/res/FilePack.bf b/BeefLibs/Beefy2D/src/res/FilePack.bf new file mode 100644 index 00000000..1e48fcb7 --- /dev/null +++ b/BeefLibs/Beefy2D/src/res/FilePack.bf @@ -0,0 +1,592 @@ +#pragma warning disable 168 + +using System; +using System.IO; +using System.Collections; +using utils; + +namespace res; + +class FilePack +{ + public class FindFileData + { + public enum Flags + { + None = 0, + Files = 1, + Directories = 2, + } + + public virtual bool MoveNext() => false; + public virtual void GetFileName(String outStr) {} + public virtual void GetFilePath(String outStr) {} + + public virtual Platform.BfpTimeStamp GetWriteTime() => 0; + public virtual Platform.BfpTimeStamp GetCreatedTime() => 0; + public virtual Platform.BfpTimeStamp GetAccessTime() => 0; + public virtual Platform.BfpFileAttributes GetFileAttributes() => 0; + public virtual int64 GetFileSize() => 0; + } + + public class FileEntry + { + public virtual void GetFilePath(String outFileName) {} + public virtual void GetFileName(String outFileName) {} + public virtual Result> GetFileData() => .Err; + public virtual FindFileData CreateFindFileData(StringView wildcard, FindFileData.Flags flags) => null; + public virtual Result ExtractTo(StringView dest) => .Err; + } + + public virtual StringView FilePath => default; + + public virtual bool PathPackMatches(StringView path) + { + return false; + } + + public virtual Stream OpenFile(StringView file) + { + return null; + } + + public virtual FileEntry GetFileEntry(StringView path) + { + return null; + } +} + +#if BF_DEPENDS_MINIZ +class ZipFilePack : FilePack +{ + public class ZipFindFileData : FindFileData + { + public ZipFileEntry mZipFileEntry; + public int32 mIdx = -1; + public String mWildcard ~ delete _; + public Flags mFlags; + + public override bool MoveNext() + { + while (true) + { + mIdx++; + if ((mZipFileEntry.mFiles == null) || (mIdx >= mZipFileEntry.mFiles.Count)) + return false; + + var fileName = GetFileName(.. scope .()); + if (Path.WildcareCompare(fileName, mWildcard)) + break; + } + return true; + } + + public override void GetFilePath(String outFileName) + { + var fileEntry = mZipFileEntry.mFiles[mIdx]; + fileEntry.mZipEntry.GetFileName(outFileName); + } + + public override void GetFileName(String outFileName) + { + var fileEntry = mZipFileEntry.mFiles[mIdx]; + var filePath = fileEntry.mZipEntry.GetFileName(.. scope .()); + Path.GetFileName(filePath, outFileName); + } + + public override Platform.BfpTimeStamp GetWriteTime() + { + return (.)mZipFileEntry.mFiles[mIdx].mZipEntry.GetTimestamp(); + } + + public override Platform.BfpTimeStamp GetCreatedTime() + { + return GetWriteTime(); + } + + public override Platform.BfpTimeStamp GetAccessTime() + { + return GetWriteTime(); + } + + public override Platform.BfpFileAttributes GetFileAttributes() + { + return .Normal; + } + + public override int64 GetFileSize() + { + return mZipFileEntry.mFiles[mIdx].mZipEntry.GetUncompressedSize(); + } + } + + public class ZipFileEntry : FileEntry + { + public MiniZ.ZipFile.Entry mZipEntry ~ delete _; + public List mFiles ~ delete _; + + public override Result> GetFileData() + { + return mZipEntry.ExtractToMemory(); + } + + public override FindFileData CreateFindFileData(StringView wildcard, FindFileData.Flags flags) + { + var findFileData = new ZipFindFileData(); + findFileData.mZipFileEntry = this; + findFileData.mWildcard = new .(wildcard); + findFileData.mFlags = flags; + return findFileData; + } + + public override Result ExtractTo(StringView dest) + { + return mZipEntry.ExtractToFile(dest); + } + + public override void GetFilePath(String outFileName) + { + mZipEntry.GetFileName(outFileName); + } + + public override void GetFileName(String outFileName) + { + var filePath = mZipEntry.GetFileName(.. scope .()); + Path.GetFileName(filePath, outFileName); + } + } + + String mPath ~ delete _; + MiniZ.ZipFile mZipFile ~ delete _; + Dictionary mFileMap ~ DeleteDictionaryAndKeysAndValues!(_); + ZipFileEntry mRootFileEntry; + + public List RootFiles + { + get + { + FillFileMap(); + return mRootFileEntry.mFiles; + } + } + + public this() + { + + } + + public override StringView FilePath => mPath; + + public Result Init(StringView path) + { + mPath = new .(path); + mZipFile = new MiniZ.ZipFile(); + Try!(mZipFile.OpenMapped(path)); + return .Ok; + } + + public Result Init(StringView path, Range range) + { + mPath = new .(path); + mZipFile = new MiniZ.ZipFile(); + Try!(mZipFile.OpenMapped(path, range)); + return .Ok; + } + + public Result Init(StringView path, Span data) + { + mPath = new .(path); + mZipFile = new MiniZ.ZipFile(); + Try!(mZipFile.Open(data)); + return .Ok; + } + + void FillFileMap() + { + if (mFileMap != null) + return; + mRootFileEntry = new .(); + mRootFileEntry.mFiles = new .(); + mFileMap = new .(); + mFileMap[new .("/")] = mRootFileEntry; + + for (int i < mZipFile.GetNumFiles()) + { + var entry = new MiniZ.ZipFile.Entry(); + mZipFile.SelectEntry(i, entry); + var fileName = entry.GetFileName(.. new .()); + + ZipFileEntry zipFileEntry = new .(); + zipFileEntry.mZipEntry = entry; + mFileMap[fileName] = zipFileEntry; + + var checkFileName = StringView(fileName); + if (checkFileName.EndsWith('/')) + checkFileName.RemoveFromEnd(1); + var fileDir = Path.GetDirectoryPath(checkFileName, .. scope .()); + fileDir.Append("/"); + if (mFileMap.TryGet(fileDir, ?, var dirEntry)) + { + if (dirEntry.mFiles == null) + dirEntry.mFiles = new .(); + dirEntry.mFiles.Add(zipFileEntry); + } + } + } + + public override FileEntry GetFileEntry(StringView path) + { + var path; + FillFileMap(); + if (path.StartsWith('/')) + path.RemoveFromStart(1); + if (path.Contains('\\')) + { + var pathStr = scope:: String(path); + pathStr.Replace('\\', '/'); + path = pathStr; + } + if (mFileMap.TryGetAlt(path, ?, var entry)) + return entry; + return null; + } + + public override bool PathPackMatches(StringView path) + { + return Path.Equals(path, mPath); + } + + public override Stream OpenFile(StringView file) + { + return base.OpenFile(file); + } +} +#endif + +static class FilePackManager +{ + public static List sFilePacks = new .() ~ DeleteContainerAndItems!(_); + + public static void AddFilePack(FilePack filePack) + { + sFilePacks.Add(filePack); + } + + public static void RemoveFilePack(FilePack filePack) + { + sFilePacks.Remove(filePack); + } + + [StaticHook(typeof(Platform.Hook), typeof(FilePackManager.HookOverrides)), AlwaysInclude] + public static struct Hook + { + } + + static StableIndexedList sStreams = new .() ~ { DeleteContainerAndItems!(_); _ = null; } + static StableIndexedList sFindFileData = new .() ~ { DeleteContainerAndItems!(_); _ = null; } + + static Stream TryGetStream(Platform.BfpFile* file, out int idx) + { + idx = (int)(void*)file; + return sStreams?.SafeGet(idx); + } + + static FilePack.FindFileData TryGetFindFileData(Platform.BfpFindFileData* findFileData, out int idx) + { + idx = (int)(void*)findFileData; + return sFindFileData?.SafeGet(idx); + } + + public static struct HookOverrides + { + [StaticHook] + public static Platform.BfpFile* BfpFile_Create(char8* name, Platform.BfpFileCreateKind createKind, Platform.BfpFileCreateFlags createFlags, Platform.BfpFileAttributes createdFileAttrs, Platform.BfpFileResult* outResult) + { + if ((createKind == .OpenExisting) && (!createFlags.HasFlag(.Write))) + { + if (var memory = TryGetMemory(.(name))) + { + var memoryStream = new FixedMemoryStream(memory); + return (.)(void*)sStreams.Add(memoryStream); + } + } + + return Hook.sBfpFile_Create(name, createKind, createFlags, createdFileAttrs, outResult); + } + + [StaticHook] + public static int BfpFile_GetSystemHandle(Platform.BfpFile* file) + { + return Hook.sBfpFile_GetSystemHandle(file); + } + + [StaticHook] + public static void BfpFile_Release(Platform.BfpFile* file) + { + if (var stream = TryGetStream(file, var idx)) + { + sStreams.RemoveAt(idx); + delete stream; + return; + } + + Hook.sBfpFile_Release(file); + } + + [StaticHook] + public static int BfpFile_Write(Platform.BfpFile* file, void* buffer, int size, int timeoutMS, Platform.BfpFileResult* outResult) + { + return Hook.sBfpFile_Write(file, buffer, size, timeoutMS, outResult); + } + + [StaticHook] + public static int BfpFile_Read(Platform.BfpFile* file, void* buffer, int size, int timeoutMS, Platform.BfpFileResult* outResult) + { + if (var stream = TryGetStream(file, ?)) + { + switch (stream.TryRead(.((.)buffer, size))) + { + case .Ok(let val): + if (val != size) + { + if (outResult != null) + *outResult = .PartialData; + } + else if (outResult != null) + *outResult = .Ok; + return val; + case .Err(let err): + if (outResult != null) + *outResult = .UnknownError; + return 0; + } + } + + return Hook.sBfpFile_Read(file, buffer, size, timeoutMS, outResult); + } + + [StaticHook] + public static void BfpFile_Flush(Platform.BfpFile* file) + { + if (var stream = TryGetStream(file, ?)) + { + return; + } + + Hook.sBfpFile_Flush(file); + } + + [StaticHook] + public static int64 BfpFile_GetFileSize(Platform.BfpFile* file) + { + if (var stream = TryGetStream(file, ?)) + { + return stream.Length; + } + + return Hook.sBfpFile_GetFileSize(file); + } + + [StaticHook] + public static int64 BfpFile_Seek(Platform.BfpFile* file, int64 offset, Platform.BfpFileSeekKind seekKind) + { + if (var stream = TryGetStream(file, ?)) + { + stream.Seek(offset, (.)seekKind); + return stream.Position; + } + + return Hook.sBfpFile_Seek(file, offset, seekKind); + + } + + [StaticHook] + public static void BfpFile_Truncate(Platform.BfpFile* file, Platform.BfpFileResult* outResult) + { + Hook.sBfpFile_Truncate(file, outResult); + } + + [StaticHook] + public static Platform.BfpFindFileData* BfpFindFileData_FindFirstFile(char8* path, Platform.BfpFindFileFlags flags, Platform.BfpFileResult* outResult) + { + StringView pathStr = .(path); + + String findDir = scope .(); + Path.GetDirectoryPath(pathStr, findDir).IgnoreError(); + findDir.Append("/"); + String wildcard = scope .(); + Path.GetFileName(pathStr, wildcard); + + if (var packFile = FindFilePackEntry(findDir)) + { + var packFindFile = packFile.CreateFindFileData(wildcard, (.)flags); + if (!packFindFile.MoveNext()) + { + delete packFile; + if (outResult != null) + *outResult = .NoResults; + return null; + } + return (.)(void*)sFindFileData.Add(packFindFile); + } + + return Hook.sBfpFindFileData_FindFirstFile(path, flags, outResult); + } + + [StaticHook] + public static bool BfpFindFileData_FindNextFile(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.MoveNext(); + } + + return Hook.sBfpFindFileData_FindNextFile(findData); + } + + [StaticHook] + public static void BfpFindFileData_GetFileName(Platform.BfpFindFileData* findData, char8* outName, int32* inOutNameSize, Platform.BfpFileResult* outResult) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + var str = packFindFile.GetFileName(.. scope .()); + Platform.SetStrHelper(str, outName, inOutNameSize, (.)outResult); + return; + } + + Hook.sBfpFindFileData_GetFileName(findData, outName, inOutNameSize, outResult); + } + + [StaticHook] + public static Platform.BfpTimeStamp BfpFindFileData_GetTime_LastWrite(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetWriteTime(); + } + + return Hook.sBfpFindFileData_GetTime_LastWrite(findData); + } + + [StaticHook] + public static Platform.BfpTimeStamp BfpFindFileData_GetTime_Created(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetCreatedTime(); + } + + return Hook.sBfpFindFileData_GetTime_Created(findData); + } + + [StaticHook] + public static Platform.BfpTimeStamp BfpFindFileData_GetTime_Access(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetAccessTime(); + } + + return Hook.sBfpFindFileData_GetTime_Access(findData); + } + + [StaticHook] + public static Platform.BfpFileAttributes BfpFindFileData_GetFileAttributes(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetFileAttributes(); + } + + return Hook.sBfpFindFileData_GetFileAttributes(findData); + } + + [StaticHook] + public static int64 BfpFindFileData_GetFileSize(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, ?)) + { + return packFindFile.GetFileSize(); + } + + return Hook.sBfpFindFileData_GetFileSize(findData); + } + + [StaticHook] + public static void BfpFindFileData_Release(Platform.BfpFindFileData* findData) + { + if (var packFindFile = TryGetFindFileData(findData, var idx)) + { + sFindFileData.RemoveAt(idx); + delete packFindFile; + return; + } + + Hook.sBfpFindFileData_Release(findData); + } + } + + public static FilePack.FileEntry FindFilePackEntry(StringView path) + { + if (path.StartsWith('[')) + { + int packEndIdx = path.IndexOf(']'); + + if (packEndIdx != -1) + { + var packPath = path.Substring(1, packEndIdx - 1); + var filePath = path.Substring(packEndIdx + 1); + for (var filePack in sFilePacks) + { + if (filePack.PathPackMatches(packPath)) + { + return filePack.GetFileEntry(filePath); + } + } + } + } + return null; + } + + public static Result> TryGetMemory(StringView path) + { + var fileEntry = FindFilePackEntry(path); + if (fileEntry == null) + return .Err; + + return fileEntry.GetFileData(); + } + + public static bool TryMakeMemoryString(String path) + { + if (TryGetMemory(path) case .Ok(let val)) + { + String prevPath = scope .()..Append(path); + + var ext = Path.GetExtension(path, .. scope .()); + path.Set(scope $"@{(int)(void*)val.Ptr:X}:{val.Length}:{prevPath}"); + return true; + } + return false; + } + + public static bool TryMakeMemoryString(String path, params Span checkExts) + { + var ext = Path.GetExtension(path, .. scope .()); + if (!ext.IsEmpty) + return TryMakeMemoryString(path); + + for (var checkExt in checkExts) + { + var testPath = scope String(); + testPath.Append(path); + testPath.Append(checkExt); + if (TryMakeMemoryString(testPath)) + { + path.Set(testPath); + return true; + } + } + return false; + } +} \ No newline at end of file diff --git a/BeefLibs/Beefy2D/src/res/PEFile.bf b/BeefLibs/Beefy2D/src/res/PEFile.bf new file mode 100644 index 00000000..e09b0531 --- /dev/null +++ b/BeefLibs/Beefy2D/src/res/PEFile.bf @@ -0,0 +1,275 @@ +#pragma warning disable 168 + +using System; +using System.IO; + +namespace Beefy.Res; + + +static class PEFile +{ + public const uint16 PE_MACHINE_X86 = 0x14c; + public const uint16 PE_MACHINE_X64 = 0x8664; + + [CRepr] + public struct PEHeader + { + public uint16 e_magic; // Magic number + public uint16 e_cblp; // uint8s on last page of file + public uint16 e_cp; // Pages in file + public uint16 e_crlc; // Relocations + public uint16 e_cparhdr; // Size of header in paragraphs + public uint16 e_minalloc; // Minimum extra paragraphs needed + public uint16 e_maxalloc; // Maximum extra paragraphs needed + public uint16 e_ss; // Initial (relative) SS value + public uint16 e_sp; // Initial SP value + public uint16 e_csum; // Checksum + public uint16 e_ip; // Initial IP value + public uint16 e_cs; // Initial (relative) CS value + public uint16 e_lfarlc; // File address of relocation table + public uint16 e_ovno; // Overlay number + public uint16[4] e_res; // Reserved uint16s + public uint16 e_oemid; // OEM identifier (for e_oeminfo) + public uint16 e_oeminfo; // OEM information; e_oemid specific + public uint16[10] e_res2; // Reserved uint16s + public int32 e_lfanew; // File address of new exe header + }; + + + [CRepr] + public struct PEFileHeader + { + public uint16 mMachine; + public uint16 mNumberOfSections; + public uint32 mTimeDateStamp; + public uint32 mPointerToSymbolTable; + public uint32 mNumberOfSymbols; + public uint16 mSizeOfOptionalHeader; + public uint16 mCharacteristics; + }; + + [CRepr] + public struct PEImportObjectHeader + { + public uint16 mSig1; + public uint16 mSig2; + public uint16 mVersion; + public uint16 mMachine; + public uint32 mTimeDateStamp; + public uint32 mDataSize; + public uint16 mHint; + public uint16 mType; + }; + + [CRepr] + public struct PEDataDirectory + { + public uint32 mVirtualAddress; + public uint32 mSize; + }; + + [CRepr] + public struct PEOptionalHeader32 + { + // + // Standard fields. + // + + public uint16 mMagic; + public uint8 mMajorLinkerVersion; + public uint8 mMinorLinkerVersion; + public uint32 mSizeOfCode; + public uint32 mSizeOfInitializedData; + public uint32 mSizeOfUninitializedData; + public uint32 mAddressOfEntryPoint; + public uint32 mBaseOfCode; + public uint32 mBaseOfData; + + // + // NT additional fields. + // + + public uint32 mImageBase; + public uint32 mSectionAlignment; + public uint32 mFileAlignment; + public uint16 mMajorOperatingSystemVersion; + public uint16 mMinorOperatingSystemVersion; + public uint16 mMajorImageVersion; + public uint16 mMinorImageVersion; + public uint16 mMajorSubsystemVersion; + public uint16 mMinorSubsystemVersion; + public uint32 mReserved1; + public uint32 mSizeOfImage; + public uint32 mSizeOfHeaders; + public uint32 mCheckSum; + public uint16 mSubsystem; + public uint16 mDllCharacteristics; + public uint32 mSizeOfStackReserve; + public uint32 mSizeOfStackCommit; + public uint32 mSizeOfHeapReserve; + public uint32 mSizeOfHeapCommit; + public uint32 mLoaderFlags; + public uint32 mNumberOfRvaAndSizes; + public PEDataDirectory[16] mDataDirectory; + }; + + [CRepr] + public struct PEOptionalHeader64 + { + // + // Standard fields. + // + + public uint16 mMagic; + public uint8 mMajorLinkerVersion; + public uint8 mMinorLinkerVersion; + public uint32 mSizeOfCode; + public uint32 mSizeOfInitializedData; + public uint32 mSizeOfUninitializedData; + public uint32 mAddressOfEntryPoint; + public uint32 mBaseOfCode; + + // + // NT additional fields. + // + + public uint64 mImageBase; + public uint32 mSectionAlignment; + public uint32 mFileAlignment; + public uint16 mMajorOperatingSystemVersion; + public uint16 mMinorOperatingSystemVersion; + public uint16 mMajorImageVersion; + public uint16 mMinorImageVersion; + public uint16 mMajorSubsystemVersion; + public uint16 mMinorSubsystemVersion; + public uint32 mReserved1; + public uint32 mSizeOfImage; + public uint32 mSizeOfHeaders; + public uint32 mCheckSum; + public uint16 mSubsystem; + public uint16 mDllCharacteristics; + public uint64 mSizeOfStackReserve; + public uint64 mSizeOfStackCommit; + public uint64 mSizeOfHeapReserve; + public uint64 mSizeOfHeapCommit; + public uint32 mLoaderFlags; + public uint32 mNumberOfRvaAndSizes; + public PEDataDirectory[16] mDataDirectory; + }; + + [CRepr] + struct PE_NTHeaders32 + { + uint32 mSignature; + PEFileHeader mFileHeader; + PEOptionalHeader32 mOptionalHeader; + }; + + [CRepr] + public struct PE_NTHeaders64 + { + public uint32 mSignature; + public PEFileHeader mFileHeader; + public PEOptionalHeader64 mOptionalHeader; + }; + + const int IMAGE_SIZEOF_SHORT_NAME = 8; + + [CRepr] + public struct PESectionHeader + { + public char8[IMAGE_SIZEOF_SHORT_NAME] mName; + public uint32 mVirtualSize; + public uint32 mVirtualAddress; + public uint32 mSizeOfRawData; + public uint32 mPointerToRawData; + public uint32 mPointerToRelocations; + public uint32 mPointerToLineNumbers; + public uint16 mNumberOfRelocations; + public uint16 mNumberOfLineNumbers; + public uint32 mCharacteristics; + }; + + [CRepr] + struct COFFRelocation + { + uint32 mVirtualAddress; + uint32 mSymbolTableIndex; + uint16 mType; + }; + + [CRepr] + struct PE_SymInfo + { + [Union] + public struct Name + { + public char8[8] mName; + public int32[2] mNameOfs; + } + + public Name mName; + + /*union + { + char mName[8]; + int32 mNameOfs[2]; + };*/ + + public int32 mValue; + public uint16 mSectionNum; + public uint16 mType; + public int8 mStorageClass; + public int8 mNumOfAuxSymbols; + }; + + [CRepr] + struct PE_SymInfoAux + { + public uint32 mLength; + public uint16 mNumberOfRelocations; + public uint16 mNumberOfLinenumbers; + public uint32 mCheckSum; + public uint16 mNumber; + public uint8 mSelection; + public char8 mUnused; + public char8 mUnused2; + public char8 mUnused3; + }; + + public static Result LocateExtraFileInExecutable() + { +#if BF_PLATFORM_WINDOWS + var exePath = Environment.GetExecutableFilePath(.. scope .()); + var exeFileInfo = scope FileInfo(exePath); + + int fileLength = exeFileInfo.GetFileSize(); + + var moduleHandle = Windows.GetModuleHandleA(null); + uint8* modulePtr = (.)(void*)(int)moduleHandle; + PEFile.PEHeader* header = (.)modulePtr; + PEFile.PE_NTHeaders64* hdr64 = (.)(modulePtr + header.e_lfanew); + if (hdr64.mFileHeader.mMachine != PEFile.PE_MACHINE_X64) + return .Err; + + int exeEnd = 0; + for (int sectIdx < hdr64.mFileHeader.mNumberOfSections) + { + PEFile.PESectionHeader* sectHdrHead = (.)(modulePtr + header.e_lfanew + sizeof(PEFile.PE_NTHeaders64) + sectIdx * sizeof(PEFile.PESectionHeader)); + exeEnd = Math.Max(exeEnd, sectHdrHead.mPointerToRawData + sectHdrHead.mSizeOfRawData); + } + + if (hdr64.mOptionalHeader.mDataDirectory[4].mVirtualAddress != 0) + { + // This handles the case where our installer is signed + fileLength = hdr64.mOptionalHeader.mDataDirectory[4].mVirtualAddress; + } + + Range span = .(exeEnd, fileLength); + if (span.Length > 0) + return span; +#endif + return .Err; + } +} + diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf index 46e6e5ed..3f7b7e03 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkButton.bf @@ -14,7 +14,7 @@ namespace Beefy.theme.dark public float mLabelYOfs; [DesignEditable(DefaultEditString=true)] - public String Label + public StringView Label { get { diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf index bff67f10..e32d4856 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkCheckBox.bf @@ -121,7 +121,8 @@ namespace Beefy.theme.dark { g.SetFont(mFont); - DarkTheme.DrawUnderlined(g, mLabel, GS!(22), GS!(-1)); + using(g.PushColor(DarkTheme.COLOR_TEXT)) + DarkTheme.DrawUnderlined(g, mLabel, GS!(22), GS!(-1)); /*int underlinePos = mLabel.IndexOf('&'); if ((underlinePos != -1) && (underlinePos < mLabel.Length - 1)) diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf index ade3ea14..9d2a0e84 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkDialog.bf @@ -119,7 +119,8 @@ namespace Beefy.theme.dark g.SetFont(mFont); if (mText != null) - g.DrawString(mText, mTextInsets.mLeft, mTextInsets.mTop, FontAlign.Left, mWidth - mTextInsets.Horz, FontOverflowMode.Wrap); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + g.DrawString(mText, mTextInsets.mLeft, mTextInsets.mTop, FontAlign.Left, mWidth - mTextInsets.Horz, FontOverflowMode.Wrap); } } } diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf index b2d32e3e..f334ece6 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkEditWidget.bf @@ -50,6 +50,7 @@ namespace Beefy.theme.dark } public Font mFont; + public float mLineHeightScale = 1.0f; public uint32[] mTextColors = sDefaultColors; public uint32 mHiliteColor = 0xFF2f5c88; public uint32 mUnfocusedHiliteColor = 0x00000000; @@ -68,6 +69,8 @@ namespace Beefy.theme.dark protected static uint32[] sDefaultColors = new uint32[] ( Color.White ) ~ delete _; + public float LineHeight => Math.Max(Math.Round(mFont.GetLineSpacing() * mLineHeightScale), 1); + public this(EditWidgetContent refContent = null) : base(refContent) { //mTextInsets.Set(-3, 2, 0, 2); @@ -79,6 +82,8 @@ namespace Beefy.theme.dark mHeight = GS!(24); mHorzJumpSize = GS!(40); mFont = DarkTheme.sDarkTheme?.mSmallFont; + mTextColors[0] = DarkTheme.COLOR_TEXT; + mHiliteColor = DarkTheme.COLOR_TEXT_SELECTED; } protected override EditWidgetContent.Data CreateEditData() @@ -102,7 +107,7 @@ namespace Beefy.theme.dark mLineCoords.GrowUninitialized(mData.mLineStarts.Count); mLineCoordJumpTable.Clear(); - float fontHeight = mFont.GetLineSpacing(); + float fontHeight = LineHeight; int prevJumpIdx = -1; float jumpCoordSpacing = GetJumpCoordSpacing(); @@ -212,6 +217,13 @@ namespace Beefy.theme.dark return defaultVal; } + public float GetTextOffset() + { + float baseLineSpacing = mFont.GetLineSpacing(); + float lineSpacing = LineHeight; + return lineSpacing / 2.0f - baseLineSpacing / 2.0f; + } + public int FindUncollapsedLine(int line) { var line; @@ -507,7 +519,7 @@ namespace Beefy.theme.dark ((embed.mKind == .HideLine) && (!hideLine))) selStartX += GS!(4); - Rect rect = .(selStartX, mLineCoords[lineIdx] - GS!(1), embed.GetWidth(hideLine), mFont.GetLineSpacing() + GS!(3)); + Rect rect = .(selStartX, mLineCoords[lineIdx] - GS!(1) + GetTextOffset(), embed.GetWidth(hideLine), mFont.GetLineSpacing() + GS!(3)); if (rect.mY < 0) rect.mY = 0; return rect; @@ -524,7 +536,9 @@ namespace Beefy.theme.dark #unwarn int lineCount = GetLineCount(); - float lineSpacing = mFont.GetLineSpacing(); + float lineSpacing = LineHeight; + float fontLineSpacing = mFont.GetLineSpacing(); + float textYOffset = GetTextOffset(); float offsetY = mTextInsets.mTop; if (mHeight < lineSpacing) @@ -563,7 +577,7 @@ namespace Beefy.theme.dark { if (mHiliteCurrentLine && selStartIdx == selEndIdx) { - float thickness = 2 * (lineSpacing / 18); + float thickness = Math.Ceiling(2 * (Math.Floor(fontLineSpacing) / 18)); // This isn't quite the right value, but I'm not sure how to get this // to properly highlight the whole line without getting cut off - this works well for now. float totalLineWidth = mEditWidget.mScrollContentContainer.mWidth - thickness; @@ -594,18 +608,18 @@ namespace Beefy.theme.dark if (mCharWidth <= 2) { using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.75f)))) - g.FillRect(x, y, GS!(2), lineSpacing); + g.FillRect(x, y + textYOffset, GS!(2), fontLineSpacing); } else { using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness * 0.30f)))) - g.FillRect(x, y, mCharWidth, lineSpacing); + g.FillRect(x, y + textYOffset, mCharWidth, fontLineSpacing); } } else { using (g.PushColor(Color.Mult(cursorColor, Color.Get(brightness)))) - g.FillRect(x, y, Math.Max(1.0f, GS!(1)), lineSpacing); + g.FillRect(x, y + textYOffset, Math.Max(1.0f, GS!(1)), fontLineSpacing); } drewCursor = true; } @@ -699,7 +713,7 @@ namespace Beefy.theme.dark } float nextX = curX; - nextX = DrawText(g, sectionText, curX, curY, curTypeIdAndFlags); + nextX = DrawText(g, sectionText, curX, curY + textYOffset, curTypeIdAndFlags); DrawSectionFlagsOver(g, curX, curY, nextX - curX, flags); //int32 lineDrawStartColumn = lineDrawStart - lineStart; diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf index 556b611c..767c405d 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkListView.bf @@ -978,7 +978,7 @@ namespace Beefy.theme.dark public float mDragOffset = 0; public bool mShowColumnGrid; public bool mShowGridLines; - public Color mGridLinesColor = 0x0CFFFFFF; + public Color mGridLinesColor; public bool mShowHeader = true; public bool mEndInEllipsis; public bool mWordWrap; @@ -1002,6 +1002,7 @@ namespace Beefy.theme.dark { mFont = DarkTheme.sDarkTheme.mSmallFont; mBoldFont = DarkTheme.sDarkTheme.mSmallBoldFont; + mGridLinesColor = DarkTheme.COLOR_GRID; SetShowHeader(true); } @@ -1028,6 +1029,12 @@ namespace Beefy.theme.dark mScrollContentInsets.mBottom += GS!(2); base.InitScrollbars(wantHorz, wantVert); + + float scrollIncrement = this.mFont.GetLineSpacing(); + if (mHorzScrollbar != null) + mHorzScrollbar.mScrollIncrement = scrollIncrement; + if (mVertScrollbar != null) + mVertScrollbar.mScrollIncrement = scrollIncrement; } protected override ListViewItem CreateListViewItem() @@ -1087,7 +1094,7 @@ namespace Beefy.theme.dark if ((mShowColumnGrid) && (columnIdx < mColumns.Count - 1)) { - DrawColumnGridColumn(g, column.mWidth, DarkTheme.sUnitSize, mHeight - DarkTheme.sUnitSize - 1, 0xFF707070); + DrawColumnGridColumn(g, column.mWidth, DarkTheme.sUnitSize, mHeight - DarkTheme.sUnitSize - 1, DarkTheme.COLOR_GRID); } float sortArrowX = g.mFont.GetWidth(column.mLabel) + DarkTheme.sUnitSize/2; @@ -1146,7 +1153,7 @@ namespace Beefy.theme.dark if ((mShowColumnGrid) && (columnIdx < mColumns.Count - 1)) { - DrawColumnGridColumn(g, curX, GS!(4), mHeight - GS!(8), 0xFF888888); + DrawColumnGridColumn(g, curX, GS!(4), mHeight - GS!(8), DarkTheme.COLOR_GRID); } } } diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf index dea78e05..d0b826eb 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkMenu.bf @@ -32,7 +32,7 @@ namespace Beefy.theme.dark let darkMenuWidget = (DarkMenuWidget)mMenuWidget; g.SetFont(mMenuItem.mBold ? darkMenuWidget.mBoldFont : darkMenuWidget.mFont); - using (g.PushColor(mMenuItem.mDisabled ? 0xFFA8A8A8 : 0xFFFFFFFF)) + using (g.PushColor(mMenuItem.mDisabled ? DarkTheme.COLOR_TEXT_DISABLED : DarkTheme.COLOR_TEXT)) { StringView leftStr = mMenuItem.mLabel; StringView rightStr = default; @@ -43,12 +43,9 @@ namespace Beefy.theme.dark leftStr.RemoveToEnd(barIdx); } - using (g.PushColor(DarkTheme.COLOR_TEXT)) - { - g.DrawString(leftStr, GS!(36), 0); - if (!rightStr.IsEmpty) - g.DrawString(rightStr, mWidth - GS!(8), 0, .Right); - } + g.DrawString(leftStr, GS!(36), 0); + if (!rightStr.IsEmpty) + g.DrawString(rightStr, mWidth - GS!(8), 0, .Right); } if (mMenuItem.mIconImage != null) @@ -57,7 +54,7 @@ namespace Beefy.theme.dark if (mMenuItem.IsParent) { - using (g.PushColor(mMenuItem.mDisabled ? 0xFFA8A8A8 : 0xFFFFFFFF)) + using (g.PushColor(mMenuItem.mDisabled ? DarkTheme.COLOR_TEXT_DISABLED : DarkTheme.COLOR_TEXT)) g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.RightArrow), mWidth - GS!(16), 0); } } @@ -239,6 +236,20 @@ namespace Beefy.theme.dark MarkDirty(); } } + + public override void InitScrollbars(bool wantHorz, bool wantVert) + { + base.InitScrollbars(wantHorz, wantVert); + + float scrollIncrement = 0; + if (var darkMenuWidget = mScrollContent as DarkMenuWidget) + scrollIncrement = darkMenuWidget.mFont.GetLineSpacing(); + + if (mHorzScrollbar != null) + mHorzScrollbar.mScrollIncrement = scrollIncrement; + if (mVertScrollbar != null) + mVertScrollbar.mScrollIncrement = scrollIncrement; + } } public class DarkMenuWidget : MenuWidget diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf index 97225b72..77e83968 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTheme.bf @@ -197,7 +197,12 @@ namespace Beefy.theme.dark }; public static uint32 COLOR_TEXT = 0xFFFFFFFF; + public static uint32 COLOR_TEXT_DISABLED = 0xFFA8A8A8; + public static uint32 COLOR_TEXT_SELECTED = 0xFF2f5c88; public static uint32 COLOR_WINDOW = 0xFF595962; + public static uint32 COLOR_DIALOG_OUTLINE_IN = 0xFF404040; + public static uint32 COLOR_DIALOG_OUTLINE_OUT = 0xFF202020; + public static uint32 COLOR_GRID = 0x0CFFFFFF; public static uint32 COLOR_BKG = 0xFF26262A; public static uint32 COLOR_SELECTED_OUTLINE = 0xFFCFAE11; public static uint32 COLOR_MENU_FOCUSED = 0xFFE5A910; diff --git a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf index 66c68f20..3fe64668 100644 --- a/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf +++ b/BeefLibs/Beefy2D/src/theme/dark/DarkTooltip.bf @@ -33,7 +33,7 @@ namespace Beefy.theme.dark public bool mAllowResize; public bool mHasClosed; public Insets mRelWidgetMouseInsets ~ delete _; - public bool mAllowMouseInsideSelf; + public Insets mAllowMouseInsideInsets ~ delete _; public bool mAllowMouseOutside; public int mAutoCloseDelay; public bool mIsClipped; @@ -96,6 +96,21 @@ namespace Beefy.theme.dark Detach(); } + public static Point CalcSize(StringView text, float x = 0, float y = 0) + { + var font = DarkTheme.sDarkTheme.mSmallFont; + float minWidth = 0; + float minHeight = 0; + + BFApp.sApp.GetWorkspaceRectFrom((.)x, (.)y, 0, 0, var workspaceX, var workspaceY, var workspaceWidth, var workspaceHeight); + + FontMetrics fontMetrics = .(); + float height = font.Draw(null, text, x, y, 0, 0, FontOverflowMode.Overflow, &fontMetrics); + float tooltipWidth = Math.Max(fontMetrics.mMaxWidth + GS!(32), minWidth); + float tooltipHeight = Math.Clamp(height + GS!(16), minHeight, workspaceHeight); + return .(tooltipWidth, tooltipHeight); + } + void Attach(Widget widget) { if (mRelWidget != null) @@ -162,7 +177,7 @@ namespace Beefy.theme.dark int32 relX = x - mWidgetWindow.mX; int32 relY = y - mWidgetWindow.mY; - if ((relX >= mWidgetWindow.mWindowWidth - GS!(18)) && (relY >= mWidgetWindow.mWindowHeight - GS!(18))) + if ((relX >= mWidgetWindow.mWindowWidth - GS!(22)) && (relY >= mWidgetWindow.mWindowHeight - GS!(22))) return BFWindowBase.HitTestResult.BottomRight; return BFWindowBase.HitTestResult.Client; } @@ -178,10 +193,13 @@ namespace Beefy.theme.dark g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Menu), 0, 0, mWidth, mHeight); g.SetFont(mFont); - if (mIsClipped) - g.DrawString(mText, GS!(8), GS!(5), .Left, mWidth - GS!(16), .Ellipsis); - else - g.DrawString(mText, 0, GS!(5), .Centered, mWidth); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + { + if (mIsClipped) + g.DrawString(mText, GS!(8), GS!(5), .Left, mWidth - GS!(16), .Ellipsis); + else + g.DrawString(mText, 0, GS!(5), .Centered, mWidth); + } if (mAllowResize) g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.ResizeGrabber), mWidth - GS!(22), mHeight - GS!(22)); @@ -248,21 +266,42 @@ namespace Beefy.theme.dark if (mAllowMouseOutside) return; + Rect widgetRect = .(0, 0, mRelWidget.mWidth, mRelWidget.mHeight); + mRelWidgetMouseInsets?.ApplyTo(ref widgetRect); + float rootX; float rootY; - mRelWidget.SelfToRootTranslate(0, 0, out rootX, out rootY); + mRelWidget.SelfToRootTranslate(widgetRect.mX, widgetRect.mY, out rootX, out rootY); - Rect checkRect = Rect(rootX, rootY, mRelWidget.mWidth, mRelWidget.mHeight); - mRelWidgetMouseInsets?.ApplyTo(ref checkRect); + Rect checkRect = Rect(rootX, rootY, widgetRect.mWidth, widgetRect.mHeight); + if ((mRelWidget.mWidgetWindow != null) && (mRelWidget.mWidgetWindow.mHasMouseInside)) { - //checkRect.Inflate(8, 8); if (checkRect.Contains(mRelWidget.mWidgetWindow.mClientMouseX, mRelWidget.mWidgetWindow.mClientMouseY)) return; } + if (mAllowMouseInsideInsets != null) + { + if (mWidgetWindow.mHasMouseInside) + return; - if ((mWidgetWindow.mHasMouseInside) && (mAllowMouseInsideSelf)) - return; + Rect clientRect = mWidgetWindow.ClientRect; + mAllowMouseInsideInsets.ApplyTo(ref clientRect); + + var cursorScreenPos = BFApp.sApp.CursorScreenPos; + if (cursorScreenPos != null) + { + if (clientRect.Contains((.)cursorScreenPos.Value.x, (.)cursorScreenPos.Value.y)) + return; + } + + NOP!(); + + /*var screenX = (int32)rootX + mWidgetWindow.mClientX; + var screenY = (int32)rootY + mWidgetWindow.mClientY; + if (clientRect.Contains(screenX, screenY)) + return;*/ + } var checkWindow = BFApp.sApp.FocusedWindow; if ((checkWindow != null) && (checkWindow.HasParent(mWidgetWindow))) @@ -423,15 +462,6 @@ namespace Beefy.theme.dark SetLastMouseWidget(null); sMouseStillTicks = -1; } - - if (numOverWidgets > 1) - { - //int a = 0; - Debug.WriteLine("numOverWidgets > 1"); - } - - - //Debug.Assert(numOverWidgets <= 1); } } } diff --git a/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf b/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf new file mode 100644 index 00000000..6bcb182d --- /dev/null +++ b/BeefLibs/Beefy2D/src/utils/StableIndexedList.bf @@ -0,0 +1,60 @@ +using System.Collections; +namespace utils; + +class StableIndexedList +{ + public List mList = new .() ~ delete _; + public List mFreeIndices = new .() ~ delete _; + + public this() + { + mList.Add(default); // Reserve idx 0 + } + + public ref T this[int idx] + { + get + { + return ref mList[idx]; + } + + set + { + mList[idx] = value; + } + } + + public int Add(T val) + { + if (!mFreeIndices.IsEmpty) + { + int32 idx = mFreeIndices.PopBack(); + mList[idx] = val; + return idx; + } + mList.Add(val); + return mList.Count - 1; + } + + public void RemoveAt(int idx) + { + if (idx == mList.Count - 1) + { + mList.PopBack(); + } + else + { + mList[idx] = default; + mFreeIndices.Add((.)idx); + } + } + + public List.Enumerator GetEnumerator() => mList.GetEnumerator(); + + public T SafeGet(int idx) + { + if ((idx < 0) || (idx > mList.Count)) + return default; + return mList[idx]; + } +} \ No newline at end of file diff --git a/BeefLibs/Beefy2D/src/widgets/Dialog.bf b/BeefLibs/Beefy2D/src/widgets/Dialog.bf index 01723f6b..7ece807c 100644 --- a/BeefLibs/Beefy2D/src/widgets/Dialog.bf +++ b/BeefLibs/Beefy2D/src/widgets/Dialog.bf @@ -322,6 +322,11 @@ namespace Beefy.widgets mDefaultButton.MouseClicked(0, 0, 0, 0, 3); } + public virtual void GenerateClipboardText(String outText) + { + outText.AppendF("{}\n{}", mTitle, mText); + } + void WindowKeyDown(KeyDownEvent evt) { if ((evt.mKeyCode != .Alt) && (mWidgetWindow.IsKeyDown(.Alt)) && (!mWidgetWindow.IsKeyDown(.Control))) @@ -387,6 +392,29 @@ namespace Beefy.widgets evt.mHandled = true; } } + + if (evt.mKeyFlags.HasFlag(.Ctrl) && (evt.mKeyCode == (KeyCode)'C')) + { + ClipboardBlock: do + { + if (mChildWidgets != null) + { + for (var child in mChildWidgets) + { + if (child is ButtonWidget) + continue; + break ClipboardBlock; + } + } + + var clipboardText = GenerateClipboardText(.. scope String()); + if (!clipboardText.IsEmpty) + { + BFApp.sApp.SetClipboardText(clipboardText, ""); + evt.mHandled = true; + } + } + } } } } diff --git a/BeefLibs/Beefy2D/src/widgets/EditWidget.bf b/BeefLibs/Beefy2D/src/widgets/EditWidget.bf index b7c37bf9..4209b4b6 100644 --- a/BeefLibs/Beefy2D/src/widgets/EditWidget.bf +++ b/BeefLibs/Beefy2D/src/widgets/EditWidget.bf @@ -2836,7 +2836,7 @@ namespace Beefy.widgets hi = i - 1; } - if (hi < GetLineCount()) + if ((hi >= 0) && (hi < GetLineCount())) { line = (int32)hi; theChar = idx - mData.mLineStarts[hi]; diff --git a/BeefLibs/Beefy2D/src/widgets/ListView.bf b/BeefLibs/Beefy2D/src/widgets/ListView.bf index ee75eb89..199d5617 100644 --- a/BeefLibs/Beefy2D/src/widgets/ListView.bf +++ b/BeefLibs/Beefy2D/src/widgets/ListView.bf @@ -158,7 +158,7 @@ namespace Beefy.widgets } } - public void WithSelectedItems(delegate void(ListViewItem) func, bool skipSelectedChildrenOnSelectedItems = false, bool skipClosed = false) + public void WithSelectedItems(delegate void(ListViewItem item) func, bool skipSelectedChildrenOnSelectedItems = false, bool skipClosed = false) { bool selfSelected = Selected; if (selfSelected) @@ -907,6 +907,9 @@ namespace Beefy.widgets { base.KeyDown(keyCode, isRepeat); + if (mWidgetWindow == null) + return; + switch (keyCode) { case (KeyCode)'A': diff --git a/BeefLibs/Beefy2D/src/widgets/Widget.bf b/BeefLibs/Beefy2D/src/widgets/Widget.bf index 46bb27d1..4ecee709 100644 --- a/BeefLibs/Beefy2D/src/widgets/Widget.bf +++ b/BeefLibs/Beefy2D/src/widgets/Widget.bf @@ -633,7 +633,7 @@ namespace Beefy.widgets if (targetWidget.mParent == mParent) { - mParent.ParentToSelfTranslate(transX, transY, out transX, out transY); + targetWidget.ParentToSelfTranslate(transX, transY, out transX, out transY); return; } diff --git a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf index 60e3e82a..f0f40ee3 100644 --- a/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf +++ b/BeefLibs/Beefy2D/src/widgets/WidgetWindow.bf @@ -69,6 +69,7 @@ namespace Beefy.widgets public bool mWantsUpdateF; public bool mTempWantsUpdateF; + public Image mContentRenderTarget ~ delete _; public int32 mContentClientWidth; public int32 mContentClientHeight; @@ -229,11 +230,31 @@ namespace Beefy.widgets public override void PreDraw(Graphics g) { base.PreDraw(g); + + if (mContentRenderTarget != null) + { + g.mMatrix.Set(Matrix.IdentityMatrix); + } + else + { + g.mMatrix.Set(mScaleMatrix); + } - g.mMatrix.Set(mScaleMatrix); g.mMatrixStack[g.mMatrixStackIdx] = g.mMatrix; } + public override void PostDraw(Graphics g) + { + base.PostDraw(g); + + if (mContentRenderTarget != null) + { + for (var drawLayer in DrawLayer.sDrawLayers) + drawLayer.DrawToRenderTarget(mContentRenderTarget); + g.DrawQuad(mContentRenderTarget, 0, 0, 0, 0, mClientWidth, mClientHeight, 1, 1); + } + } + public override void RehupSize() { base.RehupSize(); @@ -880,8 +901,14 @@ namespace Beefy.widgets mMouseFlags = default; } - public void SetContentSize(int width, int height) + public void SetContentSize(int width, int height, bool createRenderTarget = false) { + DeleteAndNullify!(mContentRenderTarget); + if (createRenderTarget) + { + mContentRenderTarget = Image.CreateRenderTarget((.)width, (.)height); + } + mContentClientWidth = (.)width; mContentClientHeight = (.)height; RehupSize(); diff --git a/BeefLibs/FMOD/src/FMod.bf b/BeefLibs/FMOD/src/FMod.bf index 7ebd85bc..6b3482ef 100644 --- a/BeefLibs/FMOD/src/FMod.bf +++ b/BeefLibs/FMOD/src/FMod.bf @@ -2012,14 +2012,14 @@ namespace FMOD return result; } - public RESULT createSound (uint8[] data, MODE mode, ref CREATESOUNDEXINFO exinfo, out Sound sound) + public RESULT createSound (Span data, MODE mode, ref CREATESOUNDEXINFO exinfo, out Sound sound) { sound = null; exinfo.cbsize = (int32)sizeof(CREATESOUNDEXINFO); int soundraw; - RESULT result = FMOD_System_CreateSound(rawPtr, (char8*)&data[0], mode, ref exinfo, out soundraw); + RESULT result = FMOD_System_CreateSound(rawPtr, (.)data.Ptr, mode, ref exinfo, out soundraw); sound = new Sound(soundraw); return result; diff --git a/BeefLibs/MiniZ/src/Zip.bf b/BeefLibs/MiniZ/src/Zip.bf index d9ff58b4..6a9ac391 100644 --- a/BeefLibs/MiniZ/src/Zip.bf +++ b/BeefLibs/MiniZ/src/Zip.bf @@ -34,21 +34,91 @@ Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://ww using System; using System.IO; using System.Diagnostics; +using System.Collections; #define TINFL_USE_64BIT_BITBUF #define MINIZ_HAS_64BIT_REGISTERS #define MINIZ_LITTLE_ENDIAN #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES +#if BF_PLATFORM_WINDOWS +#define ALLOW_FILE_MAPPING +#endif + namespace MiniZ { class ZipFile { + class RangeStream : Stream + { + public bool mOwnsStream; + public Stream mStream ~ { if (mOwnsStream) delete _; }; + public Range mRange; + + public override bool CanRead => mStream.CanRead; + public override bool CanWrite => mStream.CanWrite; + public override int64 Length => mRange.Length; + + public override int64 Position + { + get + { + return mStream.Position - mRange.Start; + } + + set + { + mStream.Position = value + mRange.Start; + } + } + + public override Result Seek(int64 pos, SeekKind seekKind = .Absolute) + { + //return mStream.Seek(pos, seekKind); + + switch (seekKind) + { + case .Absolute: + return mStream.Seek(pos + mRange.Start, .Absolute); + case .Relative: + return mStream.Seek(pos, .Relative); + case .FromEnd: + return mStream.Seek(mRange.End - pos); + } + } + + public override Result TryRead(Span data) + { + return mStream.TryRead(data); + } + + public override Result TryRead(Span data, int timeoutMS) + { + return mStream.TryRead(data, timeoutMS); + } + + public override Result TryWrite(Span data) + { + return mStream.TryWrite(data); + } + + public override Result Close() + { + return mStream.Close(); + } + + public override Result Flush() + { + return mStream.Flush(); + } + } + public class Entry { ZipFile mZipFile; MiniZ.ZipArchiveFileStat mFileStat; int32 mFileIdx; + Span mAllocatedData ~ delete mAllocatedData.Ptr; public bool IsDirectory { @@ -65,6 +135,17 @@ namespace MiniZ return .Ok; } + public Result> ExtractToMemory() + { + if (mAllocatedData.IsEmpty) + { + mAllocatedData = .(new uint8[mFileStat.mUncompSize]*, mFileStat.mUncompSize); + if (!MiniZ.ZipReaderExtractToMem(&mZipFile.[Friend]mFile, mFileIdx, mAllocatedData.Ptr, mAllocatedData.Length, .None)) + return .Err; + } + return .Ok(mAllocatedData); + } + int GetStrLen(char8* ptr, int max) { int i = 0; @@ -97,10 +178,27 @@ namespace MiniZ outComment.Append(&mFileStat.mComment, GetStrLen(&mFileStat.mComment, mFileStat.mComment.Count)); return .Ok; } + + public DateTime GetDateTime() + { + int64 time = (int64)mFileStat.mTime * 10000000L + 116444736000000000L; + return DateTime(time); + } + + public Platform.BfpTimeStamp GetTimestamp() + { + return (.)(mFileStat.mTime * 10000000L + 116444736000000000L); + } } MiniZ.ZipArchive mFile; bool mInitialized; +#if ALLOW_FILE_MAPPING + Windows.FileHandle mShareFileHandle ~ _.Close(); + Windows.Handle mShareFileMapping ~ _.Close(); + Span mMappedData; +#endif + Stream mOwnedStream ~ delete _; public ~this() { @@ -117,6 +215,74 @@ namespace MiniZ return .Ok; } + public Result Open(StringView fileName, Range range) + { + Debug.Assert(!mInitialized); + + FileStream fileStream = new FileStream(); + if (fileStream.Open(fileName, .Read, .Read) case .Err) + { + delete fileStream; + return .Err; + } + + RangeStream rangeStream = new RangeStream() { mStream = fileStream, mOwnsStream = true, mRange = range }; + mOwnedStream = rangeStream; + if (!MiniZ.ZipReaderInitStream(&mFile, rangeStream, .None)) + return .Err; + mInitialized = true; + return .Ok; + } + + public Result Open(Span data) + { + Debug.Assert(!mInitialized); + if (!MiniZ.ZipReaderInitMem(&mFile, data.Ptr, data.Length, .None)) + return .Err; + mInitialized = true; + return .Ok; + } + + public Result OpenMapped(StringView fileName) + { +#if ALLOW_FILE_MAPPING + mShareFileHandle = Windows.CreateFileA(fileName.ToScopeCStr!(), Windows.GENERIC_READ, .Read, null, .Open, 0, default); + uint32 sizeHigh = 0; + uint32 sizeLow = (.)Windows.GetFileSize(mShareFileHandle, (.)&sizeHigh); + mShareFileMapping = Windows.CreateFileMappingA(mShareFileHandle, null, Windows.PAGE_READONLY, sizeHigh, sizeLow, null); + mMappedData = .((.)Windows.MapViewOfFile(mShareFileMapping, Windows.FILE_MAP_READ, 0, 0, sizeLow), sizeLow); + + Debug.Assert(!mInitialized); + if (!MiniZ.ZipReaderInitMem(&mFile, mMappedData.Ptr, mMappedData.Length, .None)) + return .Err; + mInitialized = true; + return .Ok; +#else + return Open(fileName); +#endif + } + + public Result OpenMapped(StringView fileName, Range range) + { +#if ALLOW_FILE_MAPPING + mShareFileHandle = Windows.CreateFileA(fileName.ToScopeCStr!(), Windows.GENERIC_READ, .Read, null, .Open, 0, default); + uint32 sizeHigh = 0; + uint32 sizeLow = (.)range.End; + + mShareFileMapping = Windows.CreateFileMappingA(mShareFileHandle, null, Windows.PAGE_READONLY, sizeHigh, sizeLow, null); + uint8* ptr = (.)Windows.MapViewOfFile(mShareFileMapping, Windows.FILE_MAP_READ, 0, 0, sizeLow); + mMappedData = .(ptr + range.Start, range.Length); + + Debug.Assert(!mInitialized); + if (!MiniZ.ZipReaderInitMem(&mFile, mMappedData.Ptr, mMappedData.Length, .None)) + return .Err; + mInitialized = true; + return .Ok; +#else + return Open(fileName, range); +#endif + } + public Result Init(Stream stream) { Debug.Assert(!mInitialized); @@ -3115,7 +3281,7 @@ namespace MiniZ return s; } - static bool zip_reader_init_mem(ZipArchive* pZip, void* pMem, int size, ZipFlags flags) + public static bool ZipReaderInitMem(ZipArchive* pZip, void* pMem, int size, ZipFlags flags) { if (!zip_reader_init_internal(pZip, flags)) return false; diff --git a/BeefLibs/corlib/src/Allocator.bf b/BeefLibs/corlib/src/Allocator.bf index 20fe04a7..90f2dfda 100644 --- a/BeefLibs/corlib/src/Allocator.bf +++ b/BeefLibs/corlib/src/Allocator.bf @@ -97,7 +97,7 @@ namespace System } } - struct AllocWrapper where T : new, delete + struct AllocWrapper where T : new where alloctype(T) : delete { alloctype(T) mVal; public alloctype(T) Val diff --git a/BeefLibs/corlib/src/Attribute.bf b/BeefLibs/corlib/src/Attribute.bf index ad27c2c2..83765a96 100644 --- a/BeefLibs/corlib/src/Attribute.bf +++ b/BeefLibs/corlib/src/Attribute.bf @@ -372,7 +372,7 @@ namespace System [AttributeUsage(.Constructor)] public struct AllowAppendAttribute : Attribute { - + public bool ZeroGap; } [AttributeUsage(.Class | .Struct)] diff --git a/BeefLibs/corlib/src/Collections/HashSet.bf b/BeefLibs/corlib/src/Collections/HashSet.bf index cd478a2d..e38d50f5 100644 --- a/BeefLibs/corlib/src/Collections/HashSet.bf +++ b/BeefLibs/corlib/src/Collections/HashSet.bf @@ -1245,7 +1245,7 @@ namespace System.Collections public int32 GetHashKey(int hashCode) { if (sizeof(int) == 4) - return (int32)hashCode; + return (int32)hashCode & 0x7FFFFFFF; #unwarn return (int32)(hashCode ^ ((hashCode >> 31) * 1171)) & 0x7FFFFFFF; } diff --git a/BeefLibs/corlib/src/Compiler.bf b/BeefLibs/corlib/src/Compiler.bf index b0785400..66705c77 100644 --- a/BeefLibs/corlib/src/Compiler.bf +++ b/BeefLibs/corlib/src/Compiler.bf @@ -63,7 +63,7 @@ namespace System mCmdInfo.Append("\n"); } - public void AddEdit(StringView dataName, StringView label, StringView defaultValue) + public void AddEdit(StringView dataName, StringView label, StringView defaultValue, bool focus = false) { mCmdInfo.AppendF($"addEdit\t"); dataName.QuoteString(mCmdInfo); @@ -71,7 +71,7 @@ namespace System label.QuoteString(mCmdInfo); mCmdInfo.Append("\t"); defaultValue.QuoteString(mCmdInfo); - mCmdInfo.Append("\n"); + mCmdInfo.AppendF($"\t{focus}\n"); } public void AddFilePath(StringView dataName, StringView label, StringView defaultValue) @@ -216,7 +216,7 @@ namespace System public override void InitUI() { - AddEdit("name", "Class Name", ""); + AddEdit("name", "Class Name", "", true); } public override void Generate(String outFileName, String outText, ref Flags generateFlags) @@ -225,10 +225,12 @@ namespace System if (name.EndsWith(".bf", .OrdinalIgnoreCase)) name.RemoveFromEnd(3); outFileName.Append(name); + + var ns = Namespace..Trim(); + if (!ns.IsEmpty) + outText.AppendF($"namespace {Namespace};\n\n"); outText.AppendF( $""" - namespace {Namespace}; - class {name} {{ }} @@ -334,6 +336,7 @@ namespace System static extern void Comptime_EmitMethodEntry(int64 methodHandle, StringView text); static extern void Comptime_EmitMethodExit(int64 methodHandle, StringView text); static extern void Comptime_EmitMixin(StringView text); + static extern String Comptime_GetStringById(int32 id); [Comptime(OnlyFromComptime=true)] public static MethodBuilder CreateMethod(Type owner, StringView methodName, Type returnType, MethodFlags methodFlags) diff --git a/BeefLibs/corlib/src/Console.bf b/BeefLibs/corlib/src/Console.bf index e3399176..f5f8fd3c 100644 --- a/BeefLibs/corlib/src/Console.bf +++ b/BeefLibs/corlib/src/Console.bf @@ -396,6 +396,7 @@ namespace System { sForegroundColor = sOriginalForegroundColor; sBackgroundColor = sOriginalBackgroundColor; + SetColors(); #if !BF_PLATFORM_WINDOWS Write("\x1B[0m"); diff --git a/BeefLibs/corlib/src/Diagnostics/Debug.bf b/BeefLibs/corlib/src/Diagnostics/Debug.bf index dd4bb851..81e86c18 100644 --- a/BeefLibs/corlib/src/Diagnostics/Debug.bf +++ b/BeefLibs/corlib/src/Diagnostics/Debug.bf @@ -25,7 +25,7 @@ namespace System.Diagnostics #if !DEBUG [SkipCall] #endif - public static void FatalError(String msg = "Fatal error encountered", String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum) + public static void FatalError(StringView msg = "Fatal error encountered", String filePath = Compiler.CallerFilePath, int line = Compiler.CallerLineNum) { String failStr = scope .()..Append(msg, " at line "); line.ToString(failStr); @@ -44,10 +44,19 @@ namespace System.Diagnostics #endif } +#if !BF_RUNTIME_DISABLE [CallingConvention(.Cdecl)] static extern void Write(char8* str, int strLen); [CallingConvention(.Cdecl)] static extern void Write(int val); +#else + static void Write(char8* str, int strLen) + { + } + static void Write(int val) + { + } +#endif public static void Write(String line) { @@ -59,7 +68,7 @@ namespace System.Diagnostics Write(sv.[Friend]mPtr, sv.[Friend]mLength); } - public static void Write(String fmt, params Span args) + public static void Write(StringView fmt, params Span args) { String str = scope String(4096); str.AppendF(fmt, params args); @@ -86,7 +95,7 @@ namespace System.Diagnostics Write(lineStr.Ptr, lineStr.Length); } - public static void WriteLine(StringView strFormat, params Object[] args) + public static void WriteLine(StringView strFormat, params Span args) { String paramStr = scope String(4096); paramStr.AppendF(strFormat, params args); diff --git a/BeefLibs/corlib/src/FFI/Function.bf b/BeefLibs/corlib/src/FFI/Function.bf index 149f59b5..0846ad7a 100644 --- a/BeefLibs/corlib/src/FFI/Function.bf +++ b/BeefLibs/corlib/src/FFI/Function.bf @@ -1,6 +1,6 @@ namespace System.FFI { - [CRepr, StaticInitPriority(100)] + [CRepr, StaticInitPriority(200)] struct FFIType { public enum TypeKind : uint16 diff --git a/BeefLibs/corlib/src/GC.bf b/BeefLibs/corlib/src/GC.bf index 6d8bed7e..2d2e6478 100644 --- a/BeefLibs/corlib/src/GC.bf +++ b/BeefLibs/corlib/src/GC.bf @@ -108,6 +108,8 @@ namespace System [CallingConvention(.Cdecl)] private extern static void Init(); [CallingConvention(.Cdecl)] + public extern static void Disable(); + [CallingConvention(.Cdecl)] public extern static void Collect(bool async = true); [CallingConvention(.Cdecl)] private extern static void StopCollecting(); @@ -136,6 +138,7 @@ namespace System [CallingConvention(.Cdecl)] public extern static void ExcludeThreadId(int thereadId); #else + public static void Disable() {} public static void Collect(bool async = true) {} private static void MarkAllStaticMembers() {} public static void DebugDumpLeaks() {} diff --git a/BeefLibs/corlib/src/IO/BufferedStream.bf b/BeefLibs/corlib/src/IO/BufferedStream.bf index be432b08..d8394282 100644 --- a/BeefLibs/corlib/src/IO/BufferedStream.bf +++ b/BeefLibs/corlib/src/IO/BufferedStream.bf @@ -38,7 +38,7 @@ namespace System.IO public ~this() { - Flush(); + FlushBuffer(); } public override Result Seek(int64 pos, SeekKind seekKind = .Absolute) @@ -84,7 +84,7 @@ namespace System.IO data.RemoveFromStart((.)spaceLeft); } - Try!(Flush()); + Try!(FlushBuffer()); if ((mBuffer == null) || (data.Length > mBuffer.Count)) { @@ -114,7 +114,7 @@ namespace System.IO if ((mWriteDirtyEnd >= 0) && (mWriteDirtyEnd != mPos)) { - Try!(Flush()); + Try!(FlushBuffer()); } int writeCount = 0; @@ -138,7 +138,7 @@ namespace System.IO } } - Try!(Flush()); + Try!(FlushBuffer()); if ((mBuffer == null) || (data.Length > mBuffer.Count)) { @@ -158,7 +158,7 @@ namespace System.IO return writeCount; } - public override Result Flush() + protected Result FlushBuffer() { if (mWriteDirtyPos >= 0) { @@ -170,9 +170,14 @@ namespace System.IO return .Ok; } + public override Result Flush() + { + return FlushBuffer(); + } + public override Result Close() { - let ret = Flush(); + let ret = FlushBuffer(); mPos = 0; mBufferPos = -Int32.MinValue; diff --git a/BeefLibs/corlib/src/IO/Directory.bf b/BeefLibs/corlib/src/IO/Directory.bf index d815fb9c..bc5634f7 100644 --- a/BeefLibs/corlib/src/IO/Directory.bf +++ b/BeefLibs/corlib/src/IO/Directory.bf @@ -135,7 +135,7 @@ namespace System.IO bfpFlags |= .Directories; if (flags.HasFlag(.Files)) bfpFlags |= .Files; - let findFileData = Platform.BfpFindFileData_FindFirstFile(useStr, bfpFlags, null); + let findFileData = Platform.Hook.BfpFindFileData_FindFirstFile(useStr, bfpFlags, null); return FileEnumerator(useStr, findFileData); } @@ -206,7 +206,7 @@ namespace System.IO { get { - return Platform.BfpFindFileData_GetFileAttributes(mFindFileData).HasFlag(.Directory); + return Platform.Hook.BfpFindFileData_GetFileAttributes(mFindFileData).HasFlag(.Directory); } } @@ -214,7 +214,7 @@ namespace System.IO { Platform.GetStrHelper(outFileName, scope (outPtr, outSize, outResult) => { - Platform.BfpFindFileData_GetFileName(mFindFileData, outPtr, outSize, (Platform.BfpFileResult*)outResult); + Platform.Hook.BfpFindFileData_GetFileName(mFindFileData, outPtr, outSize, (Platform.BfpFileResult*)outResult); }); } @@ -228,43 +228,45 @@ namespace System.IO public DateTime GetLastWriteTime() { - return DateTime.FromFileTime((int64)Platform.BfpFindFileData_GetTime_LastWrite(mFindFileData)); + return DateTime.FromFileTime((int64)Platform.Hook.BfpFindFileData_GetTime_LastWrite(mFindFileData)); } public DateTime GetLastWriteTimeUtc() { - return DateTime.FromFileTimeUtc((int64)Platform.BfpFindFileData_GetTime_LastWrite(mFindFileData)); + return DateTime.FromFileTimeUtc((int64)Platform.Hook.BfpFindFileData_GetTime_LastWrite(mFindFileData)); } public DateTime GetCreatedTime() { - return DateTime.FromFileTime((int64)Platform.BfpFindFileData_GetTime_Created(mFindFileData)); + return DateTime.FromFileTime((int64)Platform.Hook.BfpFindFileData_GetTime_Created(mFindFileData)); } public DateTime GetCreatedTimeUtc() { - return DateTime.FromFileTimeUtc((int64)Platform.BfpFindFileData_GetTime_Created(mFindFileData)); + return DateTime.FromFileTimeUtc((int64)Platform.Hook.BfpFindFileData_GetTime_Created(mFindFileData)); } public DateTime GetAccessedTime() { - return DateTime.FromFileTime((int64)Platform.BfpFindFileData_GetTime_Access(mFindFileData)); + return DateTime.FromFileTime((int64)Platform.Hook.BfpFindFileData_GetTime_Access(mFindFileData)); } public DateTime GetAccessedTimeUtc() { - return DateTime.FromFileTimeUtc((int64)Platform.BfpFindFileData_GetTime_Access(mFindFileData)); + return DateTime.FromFileTimeUtc((int64)Platform.Hook.BfpFindFileData_GetTime_Access(mFindFileData)); } public int64 GetFileSize() { - return Platform.BfpFindFileData_GetFileSize(mFindFileData); + return Platform.Hook.BfpFindFileData_GetFileSize(mFindFileData); } public Platform.BfpFileAttributes GetFileAttributes() { - return Platform.BfpFindFileData_GetFileAttributes(mFindFileData); + return Platform.Hook.BfpFindFileData_GetFileAttributes(mFindFileData); } + + public static bool operator implicit (Self self) => self.mFindFileData != null; } struct FileEnumerator : IEnumerator, IDisposable @@ -292,7 +294,7 @@ namespace System.IO { delete mSearchStr; if (mFindFileData != null) - Platform.BfpFindFileData_Release(mFindFileData); + Platform.Hook.BfpFindFileData_Release(mFindFileData); } public bool MoveNext() mut @@ -301,7 +303,7 @@ namespace System.IO if (mIdx == 0) return mFindFileData != null; - return Platform.BfpFindFileData_FindNextFile(mFindFileData); + return Platform.Hook.BfpFindFileData_FindNextFile(mFindFileData); } public Result GetNext() mut diff --git a/BeefLibs/corlib/src/IO/File.bf b/BeefLibs/corlib/src/IO/File.bf index e0d5a219..9e3fe409 100644 --- a/BeefLibs/corlib/src/IO/File.bf +++ b/BeefLibs/corlib/src/IO/File.bf @@ -141,13 +141,13 @@ namespace System.IO public static bool Exists(StringView fileName) { - return Platform.BfpFile_Exists(fileName.ToScopeCStr!()); + return Platform.Hook.BfpFile_Exists(fileName.ToScopeCStr!()); } public static Result Delete(StringView fileName) { Platform.BfpFileResult result = default; - Platform.BfpFile_Delete(fileName.ToScopeCStr!(), &result); + Platform.Hook.BfpFile_Delete(fileName.ToScopeCStr!(), &result); if ((result != .Ok) && (result != .NotFound)) return .Err(result); return .Ok; @@ -156,7 +156,7 @@ namespace System.IO public static Result Move(StringView fromPath, StringView toPath) { Platform.BfpFileResult result = default; - Platform.BfpFile_Rename(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), &result); + Platform.Hook.BfpFile_Rename(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), &result); if (result != .Ok) return .Err(result); return .Ok; @@ -165,7 +165,7 @@ namespace System.IO public static Result Copy(StringView fromPath, StringView toPath) { Platform.BfpFileResult result = default; - Platform.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), .Always, &result); + Platform.Hook.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), .Always, &result); if (result != .Ok) return .Err(result); return .Ok; @@ -174,7 +174,7 @@ namespace System.IO public static Result CopyIfNewer(StringView fromPath, StringView toPath) { Platform.BfpFileResult result = default; - Platform.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), .IfNewer, &result); + Platform.Hook.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), .IfNewer, &result); if (result != .Ok) return .Err(result); return .Ok; @@ -183,7 +183,7 @@ namespace System.IO public static Result Copy(StringView fromPath, StringView toPath, bool overwrite) { Platform.BfpFileResult result = default; - Platform.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), overwrite ? .Always : .IfNotExists, &result); + Platform.Hook.BfpFile_Copy(fromPath.ToScopeCStr!(), toPath.ToScopeCStr!(), overwrite ? .Always : .IfNotExists, &result); if (result != .Ok) return .Err(result); return .Ok; @@ -192,7 +192,7 @@ namespace System.IO public static Result SetAttributes(StringView path, FileAttributes attr) { Platform.BfpFileResult result = default; - Platform.BfpFile_SetAttributes(path.ToScopeCStr!(), (Platform.BfpFileAttributes)attr, &result); + Platform.Hook.BfpFile_SetAttributes(path.ToScopeCStr!(), (Platform.BfpFileAttributes)attr, &result); if (result != .Ok) return .Err(result); return .Ok; @@ -200,12 +200,47 @@ namespace System.IO public static Result GetLastWriteTime(StringView fullPath) { - return DateTime.FromFileTime((int64)Platform.BfpFile_GetTime_LastWrite(fullPath.ToScopeCStr!())); + return DateTime.FromFileTime((int64)Platform.Hook.BfpFile_GetTime_LastWrite(fullPath.ToScopeCStr!())); } public static Result GetLastWriteTimeUtc(StringView fullPath) { - return DateTime.FromFileTimeUtc((int64)Platform.BfpFile_GetTime_LastWrite(fullPath.ToScopeCStr!())); + return DateTime.FromFileTimeUtc((int64)Platform.Hook.BfpFile_GetTime_LastWrite(fullPath.ToScopeCStr!())); } } + + class FileInfo + { + FileFindEntry mFileFindEntry; + + public this(StringView path) + { + let findFileData = Platform.Hook.BfpFindFileData_FindFirstFile(path.ToScopeCStr!(), .Files, null); + if (findFileData == null) + return; + mFileFindEntry = .(new .(path), findFileData); + } + + public ~this() + { + if (mFileFindEntry.[Friend]mSearchStr != null) + { + delete mFileFindEntry.[Friend]mSearchStr; + Platform.Hook.BfpFindFileData_Release(mFileFindEntry.[Friend]mFindFileData); + } + } + + public bool Exists => mFileFindEntry; + public bool IsDirectory => mFileFindEntry ? mFileFindEntry.IsDirectory : default; + public void GetFileName(String outFileName) { if (mFileFindEntry) mFileFindEntry.GetFileName(outFileName); } + public void GetFilePath(String outPath) { if (mFileFindEntry) mFileFindEntry.GetFilePath(outPath); } + public DateTime GetLastWriteTime() => mFileFindEntry ? mFileFindEntry.GetLastWriteTime() : default; + public DateTime GetLastWriteTimeUtc() => mFileFindEntry ? mFileFindEntry.GetLastWriteTimeUtc() : default; + public DateTime GetCreatedTime() => mFileFindEntry ? mFileFindEntry.GetCreatedTime() : default; + public DateTime GetCreatedTimeUtc() => mFileFindEntry ? mFileFindEntry.GetCreatedTimeUtc() : default; + public DateTime GetAccessedTime() => mFileFindEntry ? mFileFindEntry.GetAccessedTime() : default; + public DateTime GetAccessedTimeUtc() => mFileFindEntry ? mFileFindEntry.GetAccessedTimeUtc() : default; + public int64 GetFileSize() => mFileFindEntry ? mFileFindEntry.GetFileSize() : default; + public Platform.BfpFileAttributes GetFileAttributes() => mFileFindEntry ? mFileFindEntry.GetFileAttributes() : default; + } } diff --git a/BeefLibs/corlib/src/IO/FileStream.bf b/BeefLibs/corlib/src/IO/FileStream.bf index c95f2472..d037a64b 100644 --- a/BeefLibs/corlib/src/IO/FileStream.bf +++ b/BeefLibs/corlib/src/IO/FileStream.bf @@ -12,12 +12,12 @@ namespace System.IO { get { - return Platform.BfpFile_Seek(mBfpFile, 0, .Relative); + return Platform.Hook.BfpFile_Seek(mBfpFile, 0, .Relative); } set { - Platform.BfpFile_Seek(mBfpFile, value, .Absolute); + Platform.Hook.BfpFile_Seek(mBfpFile, value, .Absolute); } } @@ -25,7 +25,7 @@ namespace System.IO { get { - return Platform.BfpFile_GetFileSize(mBfpFile); + return Platform.Hook.BfpFile_GetFileSize(mBfpFile); } } @@ -35,7 +35,7 @@ namespace System.IO { if (mBfpFile == null) return 0; - return Platform.BfpFile_GetSystemHandle(mBfpFile); + return Platform.Hook.BfpFile_GetSystemHandle(mBfpFile); } } @@ -46,7 +46,7 @@ namespace System.IO public override Result Seek(int64 pos, SeekKind seekKind = .Absolute) { - int64 newPos = Platform.BfpFile_Seek(mBfpFile, pos, (Platform.BfpFileSeekKind)seekKind); + int64 newPos = Platform.Hook.BfpFile_Seek(mBfpFile, pos, (Platform.BfpFileSeekKind)seekKind); // Ensure position is what was requested if ((seekKind == .Absolute) && (newPos != pos)) return .Err; @@ -56,7 +56,7 @@ namespace System.IO public override Result TryRead(Span data) { Platform.BfpFileResult result = .Ok; - int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result); + int numBytesRead = Platform.Hook.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result); if ((result != .Ok) && (result != .PartialData)) return .Err; return numBytesRead; @@ -65,7 +65,7 @@ namespace System.IO public override Result TryRead(Span data, int timeoutMS) { Platform.BfpFileResult result = .Ok; - int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result); + int numBytesRead = Platform.Hook.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result); if ((result != .Ok) && (result != .PartialData)) { switch (result) @@ -84,7 +84,7 @@ namespace System.IO public override Result TryWrite(Span data) { Platform.BfpFileResult result = .Ok; - int numBytesWritten = Platform.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result); + int numBytesWritten = Platform.Hook.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result); if ((result != .Ok) && (result != .PartialData)) return .Err; return numBytesWritten; @@ -93,7 +93,7 @@ namespace System.IO public override Result Close() { if (mBfpFile != null) - Platform.BfpFile_Release(mBfpFile); + Platform.Hook.BfpFile_Release(mBfpFile); mBfpFile = null; return .Ok; } @@ -101,7 +101,7 @@ namespace System.IO public override Result Flush() { if (mBfpFile != null) - Platform.BfpFile_Flush(mBfpFile); + Platform.Hook.BfpFile_Flush(mBfpFile); return .Ok; } @@ -159,7 +159,7 @@ namespace System.IO public Result OpenStd(Platform.BfpFileStdKind stdKind) { Platform.BfpFileResult fileResult = .Ok; - mBfpFile = Platform.BfpFile_GetStd(stdKind, &fileResult); + mBfpFile = Platform.Hook.BfpFile_GetStd(stdKind, &fileResult); mFileAccess = .ReadWrite; if ((mBfpFile == null) || (fileResult != .Ok)) @@ -218,7 +218,7 @@ namespace System.IO Platform.BfpFileAttributes fileFlags = .Normal; Platform.BfpFileResult fileResult = .Ok; - mBfpFile = Platform.BfpFile_Create(path.ToScopeCStr!(128), createKind, createFlags, fileFlags, &fileResult); + mBfpFile = Platform.Hook.BfpFile_Create(path.ToScopeCStr!(128), createKind, createFlags, fileFlags, &fileResult); if ((mBfpFile == null) || (fileResult != .Ok)) { @@ -261,7 +261,7 @@ namespace System.IO Seek(length); Platform.BfpFileResult result = .Ok; - Platform.BfpFile_Truncate(mBfpFile, &result); + Platform.Hook.BfpFile_Truncate(mBfpFile, &result); if (result != .Ok) { Seek(pos); @@ -297,7 +297,7 @@ namespace System.IO { if (mBfpFile == null) return 0; - return Platform.BfpFile_GetSystemHandle(mBfpFile); + return Platform.Hook.BfpFile_GetSystemHandle(mBfpFile); } } @@ -321,7 +321,7 @@ namespace System.IO { set { - // Matches the behavior of Platform.BfpFile_Seek(mBfpFile, value, .Absolute); + // Matches the behavior of Platform.Hook.BfpFile_Seek(mBfpFile, value, .Absolute); mPos = Math.Max(value, 0); } } @@ -355,7 +355,7 @@ namespace System.IO public Result OpenStd(Platform.BfpFileStdKind stdKind) { Platform.BfpFileResult fileResult = .Ok; - mBfpFile = Platform.BfpFile_GetStd(stdKind, &fileResult); + mBfpFile = Platform.Hook.BfpFile_GetStd(stdKind, &fileResult); mFileAccess = .ReadWrite; if ((mBfpFile == null) || (fileResult != .Ok)) @@ -413,7 +413,7 @@ namespace System.IO Platform.BfpFileAttributes fileFlags = .Normal; Platform.BfpFileResult fileResult = .Ok; - mBfpFile = Platform.BfpFile_Create(path.ToScopeCStr!(128), createKind, createFlags, fileFlags, &fileResult); + mBfpFile = Platform.Hook.BfpFile_Create(path.ToScopeCStr!(128), createKind, createFlags, fileFlags, &fileResult); if ((mBfpFile == null) || (fileResult != .Ok)) { @@ -467,7 +467,7 @@ namespace System.IO newPos = mPos + pos; } - // Matches the behaviour of Platform.BfpFile_Seek(mBfpFile, value, .Absolute); + // Matches the behaviour of Platform.Hook.BfpFile_Seek(mBfpFile, value, .Absolute); mPos = Math.Max(newPos, 0); if (seekKind == .Absolute && newPos < 0) return .Err; @@ -479,7 +479,7 @@ namespace System.IO { let ret = base.Close(); if (mBfpFile != null) - Platform.BfpFile_Release(mBfpFile); + Platform.Hook.BfpFile_Release(mBfpFile); mBfpFile = null; mFileAccess = default; @@ -489,12 +489,12 @@ namespace System.IO protected override void UpdateLength() { - mUnderlyingLength = Platform.BfpFile_GetFileSize(mBfpFile); + mUnderlyingLength = Platform.Hook.BfpFile_GetFileSize(mBfpFile); } protected Result SeekUnderlying(int64 offset, Platform.BfpFileSeekKind seekKind = .Absolute) { - int64 newPos = Platform.BfpFile_Seek(mBfpFile, offset, seekKind); + int64 newPos = Platform.Hook.BfpFile_Seek(mBfpFile, offset, seekKind); Result result = ((seekKind == .Absolute) && (newPos != offset)) ? .Err(.SeekError) : .Ok; if (result case .Ok) mBfpFilePos = newPos; @@ -507,7 +507,7 @@ namespace System.IO Try!(SeekUnderlying(pos)); Platform.BfpFileResult result = .Ok; - int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result); + int numBytesRead = Platform.Hook.BfpFile_Read(mBfpFile, data.Ptr, data.Length, -1, &result); if ((result != .Ok) && (result != .PartialData)) return .Err; mBfpFilePos += numBytesRead; @@ -520,7 +520,7 @@ namespace System.IO Try!(SeekUnderlying(pos)); Platform.BfpFileResult result = .Ok; - int numBytesRead = Platform.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result); + int numBytesRead = Platform.Hook.BfpFile_Write(mBfpFile, data.Ptr, data.Length, -1, &result); if ((result != .Ok) && (result != .PartialData)) return .Err; mBfpFilePos += numBytesRead; @@ -533,7 +533,7 @@ namespace System.IO Try!(SeekUnderlying(mPos)); Platform.BfpFileResult result = .Ok; - int numBytesRead = Platform.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result); + int numBytesRead = Platform.Hook.BfpFile_Read(mBfpFile, data.Ptr, data.Length, timeoutMS, &result); if ((result != .Ok) && (result != .PartialData)) { switch (result) @@ -560,7 +560,7 @@ namespace System.IO } Platform.BfpFileResult result = .Ok; - Platform.BfpFile_Truncate(mBfpFile, &result); + Platform.Hook.BfpFile_Truncate(mBfpFile, &result); if (result != .Ok) { Try!(SeekUnderlying(pos)); @@ -585,7 +585,7 @@ namespace System.IO { var result = base.Flush(); if (mBfpFile != null) - Platform.BfpFile_Flush(mBfpFile); + Platform.Hook.BfpFile_Flush(mBfpFile); return result; } } diff --git a/BeefLibs/corlib/src/IO/MemoryStream.bf b/BeefLibs/corlib/src/IO/MemoryStream.bf index db69a873..f4f1383d 100644 --- a/BeefLibs/corlib/src/IO/MemoryStream.bf +++ b/BeefLibs/corlib/src/IO/MemoryStream.bf @@ -116,4 +116,98 @@ namespace System.IO mPosition = 0; } } + + class FixedMemoryStream : Stream + { + Span mMemory; + int mPosition = 0; + + public override int64 Position + { + get + { + return mPosition; + } + + set + { + mPosition = (.)value; + } + } + + public override int64 Length + { + get + { + return mMemory.Length; + } + } + + public override bool CanRead + { + get + { + return true; + } + } + + public override bool CanWrite + { + get + { + return true; + } + } + + + public this(Span memory) + { + mMemory = memory; + } + + public Span Memory => mMemory; + + public override Result TryRead(Span data) + { + let count = data.Length; + if (count == 0) + return .Ok(0); + int readBytes = Math.Min(count, mMemory.Length - mPosition); + if (readBytes <= 0) + return .Ok(readBytes); + + Internal.MemCpy(data.Ptr, &mMemory[mPosition], readBytes); + mPosition += readBytes; + return .Ok(readBytes); + } + + public override Result TryWrite(Span data) + { + let count = data.Length; + if (count == 0) + return .Ok(0); + int growSize = mPosition + count - mMemory.Length; + if (growSize > 0) + return .Err; + Internal.MemCpy(&mMemory[mPosition], data.Ptr, count); + mPosition += count; + return .Ok(count); + } + + public override Result Close() + { + return .Ok; + } + + public override Result SetLength(int64 length) + { + return .Err; + } + + public void Clear() + { + mMemory.Clear(); + mPosition = 0; + } + } } diff --git a/BeefLibs/corlib/src/IO/Pipe.bf b/BeefLibs/corlib/src/IO/Pipe.bf index 35b409c2..6cf91798 100644 --- a/BeefLibs/corlib/src/IO/Pipe.bf +++ b/BeefLibs/corlib/src/IO/Pipe.bf @@ -46,7 +46,7 @@ namespace System.IO Platform.BfpFileAttributes fileFlags = .Normal; Platform.BfpFileResult fileResult = .Ok; - mBfpFile = Platform.BfpFile_Create(path, createKind, createFlags, fileFlags, &fileResult); + mBfpFile = Platform.Hook.BfpFile_Create(path, createKind, createFlags, fileFlags, &fileResult); if ((mBfpFile == null) || (fileResult != .Ok)) { @@ -76,6 +76,9 @@ namespace System.IO Platform.BfpFileCreateKind createKind = .CreateAlways; Platform.BfpFileCreateFlags createFlags = .Pipe; + if (options.HasFlag(.AllowTimeouts)) + createFlags |= .AllowTimeouts; + createKind = .OpenExisting; createFlags |= .Read; createFlags |= .Write; @@ -83,7 +86,7 @@ namespace System.IO Platform.BfpFileAttributes fileFlags = .Normal; Platform.BfpFileResult fileResult = .Ok; - mBfpFile = Platform.BfpFile_Create(path, createKind, createFlags, fileFlags, &fileResult); + mBfpFile = Platform.Hook.BfpFile_Create(path, createKind, createFlags, fileFlags, &fileResult); if ((mBfpFile == null) || (fileResult != .Ok)) { diff --git a/BeefLibs/corlib/src/IO/StringStream.bf b/BeefLibs/corlib/src/IO/StringStream.bf index 448e7d4f..9dec3d40 100644 --- a/BeefLibs/corlib/src/IO/StringStream.bf +++ b/BeefLibs/corlib/src/IO/StringStream.bf @@ -23,6 +23,7 @@ namespace System.IO mString = appendStr; } + [DisableChecks] public ~this() { if (mStringKind == .Append) diff --git a/BeefLibs/corlib/src/IRefCounted.bf b/BeefLibs/corlib/src/IRefCounted.bf index f10b3528..04f61494 100644 --- a/BeefLibs/corlib/src/IRefCounted.bf +++ b/BeefLibs/corlib/src/IRefCounted.bf @@ -1,5 +1,6 @@ using System.Threading; using System.Diagnostics; +using System.Collections; namespace System { @@ -105,6 +106,8 @@ namespace System { String emitStr = scope .(); + HashSet foundSigs = scope .(); + for (var methodInfo in typeof(T).GetMethods(.Public | .DeclaredOnly)) { if (methodInfo.IsStatic) @@ -112,11 +115,15 @@ namespace System if (!methodInfo.IsConstructor) continue; + var sig = methodInfo.GetMethodSig(.. new .()); + if (!foundSigs.Add(sig)) + continue; + emitStr.AppendF("public static RefCounted Create("); methodInfo.GetParamsDecl(emitStr); emitStr.AppendF(")\n"); emitStr.AppendF("{{\n"); - emitStr.AppendF("\treturn new [Friend] RefCountedAppend("); + emitStr.AppendF("\treturn new [Friend]RefCountedAppend("); methodInfo.GetArgsList(emitStr); emitStr.AppendF(");\n}}\n"); } @@ -207,7 +214,16 @@ namespace System if (!methodInfo.IsConstructor) continue; - emitStr.AppendF("[AllowAppend]\nprotected this("); + if (methodInfo.AllowAppendKind == .Yes) + emitStr.AppendF("[AllowAppend]\n"); + if (methodInfo.AllowAppendKind == .ZeroGap) + emitStr.AppendF("[AllowAppend(ZeroGap=true)]\n"); + if (methodInfo.CheckedKind == .Checked) + emitStr.AppendF("[Checked]\n"); + if (methodInfo.CheckedKind == .Unchecked) + emitStr.AppendF("[Unchecked]\n"); + + emitStr.AppendF("protected this("); methodInfo.GetParamsDecl(emitStr); emitStr.AppendF(")\n"); emitStr.AppendF("{{\n"); diff --git a/BeefLibs/corlib/src/Int32.bf b/BeefLibs/corlib/src/Int32.bf index 1ca40c80..691621c7 100644 --- a/BeefLibs/corlib/src/Int32.bf +++ b/BeefLibs/corlib/src/Int32.bf @@ -66,7 +66,7 @@ namespace System public override void ToString(String strBuffer) { // Dumb, make better. - char8[] strChars = scope:: char8[16]; + char8[16] strChars = ?; int32 char8Idx = 14; int32 valLeft = (int32)this; bool isNeg = true; @@ -86,7 +86,7 @@ namespace System if (isNeg) strChars[char8Idx--] = '-'; char8* char8Ptr = &strChars[char8Idx + 1]; - strBuffer.Append(char8Ptr); + strBuffer.Append(char8Ptr, 14 - char8Idx); } void ToString(String strBuffer, int minNumerals) diff --git a/BeefLibs/corlib/src/Int64.bf b/BeefLibs/corlib/src/Int64.bf index 71436789..5540c76e 100644 --- a/BeefLibs/corlib/src/Int64.bf +++ b/BeefLibs/corlib/src/Int64.bf @@ -78,29 +78,27 @@ namespace System public override void ToString(String strBuffer) { // Dumb, make better. - char8[] strChars = scope:: char8[22]; + char8[22] strChars = ?; int32 char8Idx = 20; int64 valLeft = (int64)this; bool isNeg = true; - int minNumeralsLeft = 0; if (valLeft >= 0) { valLeft = -valLeft; isNeg = false; } - while ((valLeft < 0) || (minNumeralsLeft > 0)) + while (valLeft < 0) { strChars[char8Idx] = (char8)('0' &- (valLeft % 10)); valLeft /= 10; char8Idx--; - minNumeralsLeft--; } if (char8Idx == 20) strChars[char8Idx--] = '0'; if (isNeg) strChars[char8Idx--] = '-'; char8* char8Ptr = &strChars[char8Idx + 1]; - strBuffer.Append(char8Ptr); + strBuffer.Append(char8Ptr, 20 - char8Idx); } public static Result Parse(StringView val, NumberStyles style = .Number, CultureInfo cultureInfo = null) diff --git a/BeefLibs/corlib/src/Internal.bf b/BeefLibs/corlib/src/Internal.bf index a6b6c7a5..1a6c00ad 100644 --- a/BeefLibs/corlib/src/Internal.bf +++ b/BeefLibs/corlib/src/Internal.bf @@ -78,6 +78,33 @@ namespace System [AlwaysInclude] static class Internal { + enum BfObjectFlags : uint8 + { + None = 0, + Mark1 = 0x01, + Mark2 = 0x02, + Mark3 = 0x03, + Allocated = 0x04, + StackAlloc = 0x08, + AppendAlloc = 0x10, + AllocInfo = 0x20, + AllocInfo_Short = 0x40, + Deleted = 0x80 + }; + + struct AppendAllocEntry + { + public enum Kind + { + case None; + case Object(Object obj); + case Raw(void* ptr, DbgRawAllocData* allocData); + } + + public Kind mKind; + public AppendAllocEntry* mNext; + } + [Intrinsic("cast")] public static extern Object UnsafeCastToObject(void* ptr); [Intrinsic("cast")] @@ -234,8 +261,20 @@ namespace System public static extern void BfDelegateTargetCheck(void* target); [CallingConvention(.Cdecl), AlwaysInclude] public static extern void* LoadSharedLibrary(char8* filePath); - [CallingConvention(.Cdecl), AlwaysInclude] - public static extern void LoadSharedLibraryInto(char8* filePath, void** libDest); + [AlwaysInclude, LinkName("Beef_LoadSharedLibraryInto")] + public static void LoadSharedLibraryInto(char8* filePath, void** libDest) + { + if (*libDest == null) + { + if (Runtime.LibraryLoadCallback != null) + *libDest = Runtime.LibraryLoadCallback(filePath); + } + if (*libDest == null) + { + *libDest = LoadSharedLibrary(filePath); + } + } + [CallingConvention(.Cdecl), AlwaysInclude] public static extern void* GetSharedProcAddress(void* libHandle, char8* procName); [CallingConvention(.Cdecl), AlwaysInclude] @@ -261,11 +300,11 @@ namespace System [CallingConvention(.Cdecl)] public static extern int Dbg_PrepareStackTrace(int baseAllocSize, int maxStackTraceDepth); [CallingConvention(.Cdecl)] - public static extern void Dbg_ObjectStackInit(Object object, ClassVData* classVData); + public static extern void Dbg_ObjectStackInit(Object object, ClassVData* classVData, int size, uint8 allocFlags); [CallingConvention(.Cdecl)] public static extern Object Dbg_ObjectAlloc(TypeInstance typeInst, int size); [CallingConvention(.Cdecl)] - public static extern Object Dbg_ObjectAlloc(ClassVData* classVData, int size, int align, int maxStackTraceDepth); + public static extern Object Dbg_ObjectAlloc(ClassVData* classVData, int size, int align, int maxStackTraceDepth, uint8 flags); [CallingConvention(.Cdecl)] public static extern void Dbg_ObjectPreDelete(Object obj); [CallingConvention(.Cdecl)] @@ -281,6 +320,186 @@ namespace System [CallingConvention(.Cdecl)] public static extern void Dbg_RawFree(void* ptr); +#if BF_ENABLE_OBJECT_DEBUG_FLAGS + static void AddAppendInfo(Object rootObj, AppendAllocEntry.Kind kind) + { + Compiler.Assert(sizeof(AppendAllocEntry) <= sizeof(int)*4); + + void Handle(AppendAllocEntry* headAllocEntry) + { + if (headAllocEntry.mKind case .None) + { + headAllocEntry.mKind = kind; + } + else + { + AppendAllocEntry* newAppendAllocEntry = (.)new uint8[sizeof(AppendAllocEntry)]*; + newAppendAllocEntry.mKind = kind; + newAppendAllocEntry.mNext = headAllocEntry.mNext; + headAllocEntry.mNext = newAppendAllocEntry; + } + } + + if (rootObj.[Friend]mClassVData & (int)BfObjectFlags.AllocInfo_Short != 0) + { + var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo; + uint8 allocFlag = (.)(dbgAllocInfo >> 8); + Debug.Assert(allocFlag == 1); + if ((allocFlag & 1) != 0) + { + int allocSize = (.)(dbgAllocInfo >> 16); + int capturedTraceCount = (uint8)(dbgAllocInfo); + uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj); + ptr += allocSize + capturedTraceCount * sizeof(int); + Handle((.)ptr); + } + } + else if (rootObj.[Friend]mClassVData & (int)BfObjectFlags.AllocInfo != 0) + { + var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo; + int allocSize = dbgAllocInfo; + uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj); + int info = *(int*)(ptr + allocSize); + int capturedTraceCount = info >> 8; + uint8 allocFlag = (.)info; + Debug.Assert(allocFlag == 1); + if ((allocFlag & 1) != 0) + { + ptr += allocSize + capturedTraceCount * sizeof(int) + sizeof(int); + Handle((.)ptr); + } + } + } +#endif + + public static void Dbg_ObjectAppended(Object rootObj, Object appendObj) + { +#if BF_ENABLE_OBJECT_DEBUG_FLAGS + AddAppendInfo(rootObj, .Object(appendObj)); +#endif + } + + public static void Dbg_RawAppended(Object rootObj, void* ptr, DbgRawAllocData* rawAllocData) + { +#if BF_ENABLE_OBJECT_DEBUG_FLAGS + AddAppendInfo(rootObj, .Raw(ptr, rawAllocData)); +#endif + } + + public static void Dbg_MarkAppended(Object rootObj) + { +#if BF_ENABLE_OBJECT_DEBUG_FLAGS + void Handle(AppendAllocEntry* checkAllocEntry) + { + var checkAllocEntry; + while (checkAllocEntry != null) + { + switch (checkAllocEntry.mKind) + { + case .Object(let obj): + obj.[Friend]GCMarkMembers(); + case .Raw(let rawPtr, let allocData): + ((function void(void*))allocData.mMarkFunc)(rawPtr); + default: + } + + checkAllocEntry = checkAllocEntry.mNext; + } + } + + if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo_Short != 0) + { + var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo; + uint8 allocFlag = (.)(dbgAllocInfo >> 8); + if ((allocFlag & 1) != 0) + { + int allocSize = (.)(dbgAllocInfo >> 16); + int capturedTraceCount = (uint8)(dbgAllocInfo); + uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj); + ptr += allocSize + capturedTraceCount * sizeof(int); + Handle((.)ptr); + } + } + else if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo != 0) + { + var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo; + int allocSize = dbgAllocInfo; + uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj); + int info = *(int*)(ptr + allocSize); + int capturedTraceCount = info >> 8; + uint8 allocFlag = (.)info; + if ((allocFlag & 1) != 0) + { + ptr += allocSize + capturedTraceCount * sizeof(int) + sizeof(int); + Handle((.)ptr); + } + } +#endif + } + + public static void Dbg_AppendDeleted(Object rootObj, bool doChecks) + { +#if BF_ENABLE_OBJECT_DEBUG_FLAGS + void Handle(AppendAllocEntry* headAllocEntry) + { + AppendAllocEntry* checkAllocEntry = headAllocEntry; + while (checkAllocEntry != null) + { + switch (checkAllocEntry.mKind) + { + case .Object(let obj): + if (doChecks) + { +#unwarn + if (!obj.[DisableObjectAccessChecks]IsDeleted()) + { + if (obj.GetType().HasDestructor) + Debug.FatalError("Appended object not deleted with 'delete:append'"); + } + } + case .Raw(let rawPtr, let allocData): + default: + } + + var nextAllocEntry = checkAllocEntry.mNext; + if (checkAllocEntry == headAllocEntry) + *checkAllocEntry = default; + else + delete (uint8*)checkAllocEntry; + checkAllocEntry = nextAllocEntry; + } + } + + if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo_Short != 0) + { + var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo; + uint8 allocFlag = (.)(dbgAllocInfo >> 8); + if ((allocFlag & 1) != 0) + { + int allocSize = (.)(dbgAllocInfo >> 16); + int capturedTraceCount = (uint8)(dbgAllocInfo); + uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj); + ptr += allocSize + capturedTraceCount * sizeof(int); + Handle((.)ptr); + } + } + else if (rootObj.[DisableObjectAccessChecks, Friend]mClassVData & (int)BfObjectFlags.AllocInfo != 0) + { + var dbgAllocInfo = rootObj.[DisableObjectAccessChecks, Friend]mDbgAllocInfo; + int allocSize = dbgAllocInfo; + uint8* ptr = (.)Internal.UnsafeCastToPtr(rootObj); + int info = *(int*)(ptr + allocSize); + int capturedTraceCount = info >> 8; + uint8 allocFlag = (.)info; + if ((allocFlag & 1) != 0) + { + ptr += allocSize + capturedTraceCount * sizeof(int) + sizeof(int); + Handle((.)ptr); + } + } +#endif + } + [CallingConvention(.Cdecl)] static extern void Shutdown_Internal(); @@ -292,19 +511,7 @@ namespace System } #else - enum BfObjectFlags : uint8 - { - None = 0, - Mark1 = 0x01, - Mark2 = 0x02, - Mark3 = 0x03, - Allocated = 0x04, - StackAlloc = 0x08, - AppendAlloc = 0x10, - AllocInfo = 0x20, - AllocInfo_Short = 0x40, - Deleted = 0x80 - }; + [NoReturn] static void Crash() @@ -446,7 +653,7 @@ namespace System } [DisableChecks, DisableObjectAccessChecks] - public static void Dbg_ObjectStackInit(Object obj, ClassVData* classVData) + public static void Dbg_ObjectStackInit(Object obj, ClassVData* classVData, int size, uint8 allocFlags) { #if BF_ENABLE_OBJECT_DEBUG_FLAGS obj.[Friend]mClassVData = (.)(void*)classVData; @@ -462,7 +669,7 @@ namespace System return null; } - public static Object Dbg_ObjectAlloc(ClassVData* classVData, int size, int align, int maxStackTraceDepth) + public static Object Dbg_ObjectAlloc(ClassVData* classVData, int size, int align, int maxStackTraceDepth, uint8 flags) { return null; } diff --git a/BeefLibs/corlib/src/Net/Socket.bf b/BeefLibs/corlib/src/Net/Socket.bf index e3db5ace..dcc2bc75 100644 --- a/BeefLibs/corlib/src/Net/Socket.bf +++ b/BeefLibs/corlib/src/Net/Socket.bf @@ -392,8 +392,11 @@ namespace System.Net service.sin_addr = address; service.sin_port = (uint16)htons((int16)port); - if (bind(mHandle, &service, sizeof(SockAddr_in)) == SOCKET_ERROR) + int32 size = sizeof(SockAddr_in); + if (bind(mHandle, &service, size) == SOCKET_ERROR) { + int err = WSAGetLastError(); + Close(); return .Err; } diff --git a/BeefLibs/corlib/src/Object.bf b/BeefLibs/corlib/src/Object.bf index 84c79d1f..2cd5833c 100644 --- a/BeefLibs/corlib/src/Object.bf +++ b/BeefLibs/corlib/src/Object.bf @@ -35,7 +35,7 @@ namespace System return (int32)mClassVData & 0x80 != 0; } #else - [SkipCall] + [SkipCall, NoShow] public bool IsDeleted() { return false; @@ -132,6 +132,12 @@ namespace System } #endif + [NoShow] + public virtual Object DynamicCastToSignature(int32 sig) + { + return null; + } + int IHashable.GetHashCode() { return (int)Internal.UnsafeCastToPtr(this); diff --git a/BeefLibs/corlib/src/Platform.bf b/BeefLibs/corlib/src/Platform.bf index 75db820f..bd6338c6 100644 --- a/BeefLibs/corlib/src/Platform.bf +++ b/BeefLibs/corlib/src/Platform.bf @@ -67,6 +67,11 @@ namespace System } #if !BF_RUNTIME_DISABLE + [StaticHook(typeof(SelfOuter))] + public static struct Hook + { + } + [CallingConvention(.Stdcall), CLink] public static extern uint32 BfpSystem_TickCount(); [CallingConvention(.Stdcall), CLink] @@ -124,6 +129,7 @@ namespace System [CallingConvention(.Stdcall), CLink] public static extern void* BfpTLS_GetValue(BfpTLS* tls); #else + public typealias Hook = Self; public static uint32 BfpSystem_TickCount() => Runtime.NotImplemented(); @@ -565,47 +571,47 @@ namespace System } #if !BF_RUNTIME_DISABLE - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpFile* BfpFile_Create(char8* name, BfpFileCreateKind createKind, BfpFileCreateFlags createFlags, BfpFileAttributes createdFileAttrs, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpFile* BfpFile_GetStd(BfpFileStdKind kind, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern int BfpFile_GetSystemHandle(BfpFile* file); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_Release(BfpFile* file); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern int BfpFile_Write(BfpFile* file, void* buffer, int size, int timeoutMS, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern int BfpFile_Read(BfpFile* file, void* buffer, int size, int timeoutMS, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_Flush(BfpFile* file); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern int64 BfpFile_GetFileSize(BfpFile* file); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern int64 BfpFile_Seek(BfpFile* file, int64 offset, BfpFileSeekKind seekKind); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_Truncate(BfpFile* file, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpTimeStamp BfpFile_GetTime_LastWrite(char8* path); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpFileAttributes BfpFile_GetAttributes(char8* path, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_SetAttributes(char8* path, BfpFileAttributes attribs, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_Copy(char8* oldPath, char8* newPath, BfpFileCopyKind copyKind, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_Rename(char8* oldPath, char8* newPath, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_Delete(char8* path, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern bool BfpFile_Exists(char8* path); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_GetTempPath(char8* outPath, int32* inOutPathSize, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_GetTempFileName(char8* outName, int32* inOutNameSize, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_GetFullPath(char8* inPath, char8* outPath, int32* inOutPathSize, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFile_GetActualPath(char8* inPath, char8* outPath, int32* inOutPathSize, BfpFileResult* outResult); #else @@ -660,23 +666,23 @@ namespace System }; #if !BF_RUNTIME_DISABLE - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpFindFileData* BfpFindFileData_FindFirstFile(char8* path, BfpFindFileFlags flags, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern bool BfpFindFileData_FindNextFile(BfpFindFileData* findData); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFindFileData_GetFileName(BfpFindFileData* findData, char8* outName, int32* inOutNameSize, BfpFileResult* outResult); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpTimeStamp BfpFindFileData_GetTime_LastWrite(BfpFindFileData* findData); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpTimeStamp BfpFindFileData_GetTime_Created(BfpFindFileData* findData); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpTimeStamp BfpFindFileData_GetTime_Access(BfpFindFileData* findData); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern BfpFileAttributes BfpFindFileData_GetFileAttributes(BfpFindFileData* findData); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern int64 BfpFindFileData_GetFileSize(BfpFindFileData* findData); - [CallingConvention(.Stdcall), CLink] + [CallingConvention(.Stdcall), CLink, StaticHook] public static extern void BfpFindFileData_Release(BfpFindFileData* findData); #else @@ -714,6 +720,20 @@ namespace System Documents } + public static bool SetStrHelper(StringView str, char8* outStr, int32* inOutSize, Result* outResult) + { + if (str.Length > *inOutSize + 1) + { + *inOutSize = (.)str.Length + 1; + *outResult = .InsufficientBuffer; + return false; + } + *inOutSize = (.)str.Length + 1; + Internal.MemCpy(outStr, str.Ptr, str.Length); + outStr[str.Length] = 0; + return true; + } + public static Result GetStrHelper(String outStr, delegate void (char8* outPtr, int32* outSize, Result* outResult) func) { let initSize = 4096; diff --git a/BeefLibs/corlib/src/Pointer.bf b/BeefLibs/corlib/src/Pointer.bf index a62cbedc..dd065296 100644 --- a/BeefLibs/corlib/src/Pointer.bf +++ b/BeefLibs/corlib/src/Pointer.bf @@ -18,7 +18,8 @@ namespace System public override void ToString(String strBuffer) { - strBuffer.AppendF("0x{0:A}", (UInt.Simple)(uint)(void*)mVal); + strBuffer.Append("0x"); + NumberFormatter.AddrToString((uint)(void*)mVal, strBuffer); } } @@ -35,7 +36,8 @@ namespace System { strBuffer.Append("("); typeof(T).ToString(strBuffer); - strBuffer.AppendF("*)0x{0:A}", (UInt.Simple)(uint)(void*)mVal); + strBuffer.Append("*)0x"); + NumberFormatter.AddrToString((uint)(void*)mVal, strBuffer); } } } diff --git a/BeefLibs/corlib/src/Random.bf b/BeefLibs/corlib/src/Random.bf index 3e46ed2d..4937d184 100644 --- a/BeefLibs/corlib/src/Random.bf +++ b/BeefLibs/corlib/src/Random.bf @@ -21,7 +21,7 @@ namespace System using System.Threading; // This class is thread-safe for random results, but not deterministically thread-safe - [StaticInitPriority(100)] + [StaticInitPriority(200)] public class Random { // @@ -39,7 +39,11 @@ namespace System private int32 inextp; private int32[] SeedArray = new int32[56] ~ delete _; +#if !BF_RUNTIME_DISABLE private static int32 sSeed = (int32)Platform.BfpSystem_GetTimeStamp(); +#else + private static int32 sSeed = 0; +#endif // // Public Constants diff --git a/BeefLibs/corlib/src/Reflection/AttributeInfo.bf b/BeefLibs/corlib/src/Reflection/AttributeInfo.bf index 7c66bc4f..5194d881 100644 --- a/BeefLibs/corlib/src/Reflection/AttributeInfo.bf +++ b/BeefLibs/corlib/src/Reflection/AttributeInfo.bf @@ -104,7 +104,7 @@ namespace System.Reflection case (TypeCode)typeof(TypeCode).MaxValue + 9: //BfConstType_TypeOf let argTypeId = Decode!(data); args[argIdx] = Type.[Friend]GetType((.)argTypeId); - case (TypeCode)typeof(TypeCode).MaxValue + 18: // BfConstType_Box + case (TypeCode)typeof(TypeCode).MaxValue + 19: // BfConstType_Box let boxedTypeId = Decode!(data); var boxedType = Type.[Friend]GetType_(boxedTypeId); int dataSize = boxedType.InstanceSize - boxedType.[Friend]mMemberDataOffset; @@ -216,7 +216,7 @@ namespace System.Reflection case (TypeCode)typeof(TypeCode).MaxValue + 9: //BfConstType_TypeOf let argTypeId = AttributeInfo.Decode!(mData); args[argIdx] = Variant.Create(Type.[Friend]GetType((.)argTypeId)); - case (TypeCode)typeof(TypeCode).MaxValue + 18: // BfConstType_Box + case (TypeCode)typeof(TypeCode).MaxValue + 19: // BfConstType_Box let boxedTypeId = AttributeInfo.Decode!(mData); var boxedType = Type.[Friend]GetType_(boxedTypeId); int dataSize = boxedType.InstanceSize - boxedType.[Friend]mMemberDataOffset; diff --git a/BeefLibs/corlib/src/Reflection/FieldInfo.bf b/BeefLibs/corlib/src/Reflection/FieldInfo.bf index 7ce4b5d2..6de95362 100644 --- a/BeefLibs/corlib/src/Reflection/FieldInfo.bf +++ b/BeefLibs/corlib/src/Reflection/FieldInfo.bf @@ -454,6 +454,38 @@ namespace System.Reflection return value; } + public Result GetValueReference(void* startTargetDataAddr, Type tTarget) + { + void* targetDataAddr = startTargetDataAddr; + if (targetDataAddr == null) + { + if (mFieldData.mFlags.HasFlag(FieldFlags.Const)) + { + return (T*)&mFieldData.mData; + } + + if (!mFieldData.mFlags.HasFlag(FieldFlags.Static)) + return .Err; + + targetDataAddr = null; + } + else + { + if (!tTarget.IsSubtypeOf(mTypeInstance)) + return .Err; //Invalid type; + } + + targetDataAddr = (uint8*)targetDataAddr + (int)mFieldData.mData; + + Type fieldType = Type.[Friend]GetType(mFieldData.mFieldTypeId); + + TypeCode typeCode = fieldType.[Friend]mTypeCode; + if (typeCode == TypeCode.Enum) + typeCode = fieldType.UnderlyingType.[Friend]mTypeCode; + + return (T*)targetDataAddr; + } + public Result GetValue(Object target) { void* targetDataAddr; @@ -498,6 +530,44 @@ namespace System.Reflection } } + Result DoGetValueReference(Object target) + { + void* targetDataAddr; + if (target == null) + { + if (mFieldData.mFlags.HasFlag(FieldFlags.Const)) + { + return (T*)&mFieldData.mData; + } + + if (!mFieldData.mFlags.HasFlag(FieldFlags.Static)) + return .Err; + + return GetValueReference(null, null); + } + else + { + Type tTarget; + targetDataAddr = GetDataPtrAndType(target, out tTarget); + return GetValueReference(targetDataAddr, tTarget); + } + } + + public Result GetValueReference(Object target) + { + if (FieldType != typeof(T)) + return .Err; + return DoGetValueReference(target); + } + + public Result GetValue(Object target) + { + if ((FieldType != typeof(T)) && (!FieldType.IsSubtypeOf(typeof(T)))) + return .Err; + T* result = Try!(DoGetValueReference(target)); + return *result; + } + public Result GetValueReference(Object target) { void* targetDataAddr; @@ -542,6 +612,30 @@ namespace System.Reflection } } + public Result GetValueReference(Variant target) + { + if (FieldType != typeof(T)) + return .Err; + + if (!target.HasValue) + { + if (mFieldData.mFlags.HasFlag(FieldFlags.Const)) + { + return (T*)&mFieldData.mData; + } + + if (!mFieldData.mFlags.HasFlag(FieldFlags.Static)) + return .Err; + + return GetValueReference(null, null); + } + else + { + var target; + return GetValueReference(target.DataPtr, target.VariantType); + } + } + public struct Enumerator : IEnumerator { BindingFlags mBindingFlags; diff --git a/BeefLibs/corlib/src/Reflection/MethodInfo.bf b/BeefLibs/corlib/src/Reflection/MethodInfo.bf index ea6447c2..da5a1d5a 100644 --- a/BeefLibs/corlib/src/Reflection/MethodInfo.bf +++ b/BeefLibs/corlib/src/Reflection/MethodInfo.bf @@ -59,6 +59,9 @@ namespace System.Reflection public bool IsMixin => Compiler.IsComptime ? Type.[Friend]Comptime_Method_GetInfo(mData.mComptimeMethodInstance).mMethodFlags.HasFlag(.Mixin) : mData.mMethodData.[Friend]mFlags.HasFlag(.Mixin); + public bool IsMutating => Compiler.IsComptime ? + Type.[Friend]Comptime_Method_GetInfo(mData.mComptimeMethodInstance).mMethodFlags.HasFlag(.Mutating) : + mData.mMethodData.[Friend]mFlags.HasFlag(.Mutating); public StringView Name => Compiler.IsComptime ? Type.[Friend]Comptime_Method_GetName(mData.mComptimeMethodInstance) : @@ -82,6 +85,13 @@ namespace System.Reflection Name == "~this" : mData.mMethodData.mName === "~this"; + public AllowAppendKind AllowAppendKind => Compiler.IsComptime ? + Type.[Friend]Comptime_Method_GetInfo(mData.mComptimeMethodInstance).mMethodFlags.AllowAppendKind : + mData.mMethodData.[Friend]mFlags.AllowAppendKind; + public CheckedKind CheckedKind => Compiler.IsComptime ? + Type.[Friend]Comptime_Method_GetInfo(mData.mComptimeMethodInstance).mMethodFlags.CheckedKind : + mData.mMethodData.[Friend]mFlags.CheckedKind; + public Type ReturnType => Compiler.IsComptime ? Type.[Friend]GetType((.)Type.[Friend]Comptime_Method_GetInfo(mData.mComptimeMethodInstance).mReturnTypeId) : Type.[Friend]GetType(mData.mMethodData.mReturnType); @@ -1017,6 +1027,25 @@ namespace System.Reflection strBuffer.Append(')'); } + public void GetMethodSig(String strBuffer) + { + strBuffer.Append('('); + int useParamIdx = 0; + for (int paramIdx < ParamCount) + { + var flag = GetParamFlags(paramIdx); + if (flag.HasFlag(.Implicit)) + continue; + if (useParamIdx > 0) + strBuffer.Append(", "); + if (flag.HasFlag(.Params)) + strBuffer.Append("params "); + strBuffer.Append(GetParamType(paramIdx)); + useParamIdx++; + } + strBuffer.Append(')'); + } + public void GetParamsDecl(String strBuffer) { int useParamIdx = 0; diff --git a/BeefLibs/corlib/src/Reflection/TypeInstance.bf b/BeefLibs/corlib/src/Reflection/TypeInstance.bf index a8a0a7e2..f5bbce4c 100644 --- a/BeefLibs/corlib/src/Reflection/TypeInstance.bf +++ b/BeefLibs/corlib/src/Reflection/TypeInstance.bf @@ -178,7 +178,7 @@ namespace System.Reflection int32 stackCount = Compiler.Options.AllocStackCount; if (mAllocStackCountOverride != 0) stackCount = mAllocStackCountOverride; - obj = Internal.Dbg_ObjectAlloc(mTypeClassVData, allocSize, mInstAlign, stackCount); + obj = Internal.Dbg_ObjectAlloc(mTypeClassVData, allocSize, mInstAlign, stackCount, 0); #else void* mem = new [Align(16)] uint8[allocSize]* (?); obj = Internal.UnsafeCastToObject(mem); diff --git a/BeefLibs/corlib/src/Runtime.bf b/BeefLibs/corlib/src/Runtime.bf index aecc2d2e..9582395b 100644 --- a/BeefLibs/corlib/src/Runtime.bf +++ b/BeefLibs/corlib/src/Runtime.bf @@ -14,7 +14,7 @@ namespace System public bool AVX, AVX2, AVX512; } - [StaticInitPriority(101)] + [StaticInitPriority(201)] static class Runtime { const int32 cVersion = 10; @@ -111,7 +111,7 @@ namespace System function void* (int size) mAlloc; function void (void* ptr) mFree; function void (Object obj) mObject_Delete; - void* mUnused0; + function void* (ClassVData* vdataPtr) mClassVData_GetTypeData; function Type (Object obj) mObject_GetType; function void (Object obj) mObject_GCMarkMembers; function Object (Object obj, int32 typeId) mObject_DynamicCastToTypeId; @@ -152,6 +152,16 @@ namespace System delete obj; } + static void* ClassVData_GetTypeData(ClassVData* classVData) + { +#if BF_32_BIT + Type type = Type.[Friend]GetType_(classVData.mType2); +#else + Type type = Type.[Friend]GetType_((.)(classVData.mType >> 32)); +#endif + return &type.[Friend]mSize; + } + static Type Object_GetType(Object obj) { #if BF_DBG_RUNTIME @@ -259,6 +269,7 @@ namespace System mAlloc = => Alloc; mFree = => Free; mObject_Delete = => Object_Delete; + mClassVData_GetTypeData = => ClassVData_GetTypeData; mObject_GetType = => Object_GetType; mObject_GCMarkMembers = => Object_GCMarkMembers; mObject_DynamicCastToTypeId = => Object_DynamicCastToTypeId; @@ -484,6 +495,7 @@ namespace System public static function ErrorHandlerResult(AssertError.Kind kind, String error, String filePath, int lineNum) CheckAssertError; public static function int32(char8* kind, char8* arg1, char8* arg2, int arg3) CheckErrorHandler; + public static function void*(char8* filePath) LibraryLoadCallback; static ErrorHandlerResult CheckAssertError_Impl(AssertError.Kind kind, String error, String filePath, int lineNum) { @@ -638,7 +650,7 @@ namespace System } } -#if BF_RUNTIME_DISABLE +#if BF_RUNTIME_DISABLE && !BF_CRT_DISABLE namespace System { [AlwaysInclude, StaticInitPriority(1000)] @@ -734,8 +746,8 @@ namespace System return 0; } - [LinkName(.C), AlwaysInclude] - static extern void WinMain(void* module, void* prevModule, char8* args, int32 showCmd); + /*[LinkName(.C), AlwaysInclude] + static extern void WinMain(void* module, void* prevModule, char8* args, int32 showCmd);*/ [LinkName(.C), AlwaysInclude] static extern int32 main(int argc, char8** argv); diff --git a/BeefLibs/corlib/src/StaticHookAttribute.bf b/BeefLibs/corlib/src/StaticHookAttribute.bf new file mode 100644 index 00000000..fae81a5a --- /dev/null +++ b/BeefLibs/corlib/src/StaticHookAttribute.bf @@ -0,0 +1,90 @@ +namespace System; + +[AttributeUsage(.Types | .Method)] +public struct StaticHookAttribute : Attribute, IOnTypeInit +{ + Type mSrcType; + Type mDestType; + + public this() + { + mSrcType = null; + mDestType = null; + } + + public this(Type srcType) + { + mSrcType = srcType; + mDestType = null; + } + + public this(Type srcType, Type destType) + { + mSrcType = srcType; + mDestType = destType; + } + + [Comptime] + public void OnTypeInit(Type type, Self* prev) + { + var emitStr = scope String(); + + if (mDestType != null) + { + var hookAttr = mSrcType.GetCustomAttribute().Value; + + for (var methodInfo in mDestType.GetMethods(.Static)) + { + if (!methodInfo.HasCustomAttribute()) + continue; + emitStr.AppendF($"public static function {methodInfo.ReturnType}"); + emitStr.Append("("); + methodInfo.GetParamsDecl(emitStr); + emitStr.AppendF($") s{methodInfo.Name};\n"); + } + emitStr.Append("\n"); + + emitStr.Append("static this\n{\n"); + for (var methodInfo in mDestType.GetMethods(.Static)) + { + if (!methodInfo.HasCustomAttribute()) + continue; + + emitStr.AppendF($"\ts{methodInfo.Name} = {mSrcType}.s{methodInfo.Name} ?? => {hookAttr.mSrcType}.{methodInfo.Name};\n"); + emitStr.AppendF($"\t{mSrcType}.s{methodInfo.Name} = => {mDestType}.{methodInfo.Name};\n"); + } + emitStr.Append("}\n"); + } + else + { + if (mSrcType == null) + return; + + emitStr.Append("#pragma warning disable\n"); + for (var methodInfo in mSrcType.GetMethods(.Static)) + { + if (!methodInfo.HasCustomAttribute()) + continue; + + emitStr.AppendF($"public static function {methodInfo.ReturnType}"); + emitStr.Append("("); + methodInfo.GetParamsDecl(emitStr); + emitStr.AppendF($") s{methodInfo.Name};\n"); + + emitStr.AppendF($"public static {methodInfo.ReturnType} {methodInfo.Name}"); + emitStr.Append("("); + methodInfo.GetParamsDecl(emitStr); + emitStr.Append(")\n"); + emitStr.Append("{\n"); + emitStr.AppendF($"\tif (s{methodInfo.Name} != null)\n"); + emitStr.AppendF($"\t\treturn s{methodInfo.Name}("); + methodInfo.GetArgsList(emitStr); + emitStr.AppendF($");\n\treturn {mSrcType}.{methodInfo.Name}("); + methodInfo.GetArgsList(emitStr); + emitStr.Append(");\n}\n\n"); + } + } + + Compiler.EmitTypeBody(type, emitStr); + } +} diff --git a/BeefLibs/corlib/src/String.bf b/BeefLibs/corlib/src/String.bf index e029e8d5..088cfa81 100644 --- a/BeefLibs/corlib/src/String.bf +++ b/BeefLibs/corlib/src/String.bf @@ -74,42 +74,86 @@ namespace System const uint32 cStrPtrFlag = 0x40000000; #endif - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(int count) // 0 { int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); #unwarn char8* addlPtr = append char8[bufferSize]*(?); - Init(bufferSize); mAllocSizeAndFlags = (uint_strsize)bufferSize + (int_strsize)sizeof(char8*); mLength = 0; } [AllowAppend] + public this(int count) // 0 + { + int bufferSize = count; +#unwarn + char8* addlPtr = append char8[bufferSize]*(?); + mPtrOrBuffer = addlPtr; + mAllocSizeAndFlags = (uint_strsize)bufferSize | cStrPtrFlag; + mLength = 0; + } + + [AllowAppend(ZeroGap=true)] public this() { let bufferSize = 16 - sizeof(char8*); #unwarn char8* addlPtr = append char8[bufferSize]*(?); - Init(bufferSize); mAllocSizeAndFlags = (uint_strsize)bufferSize + (int_strsize)sizeof(char8*); mLength = 0; } [AllowAppend] + public this() + { + let bufferSize = 8; +#unwarn + char8* addlPtr = append char8[bufferSize]*(?); + mPtrOrBuffer = addlPtr; + mAllocSizeAndFlags = (uint_strsize)bufferSize | cStrPtrFlag; + mLength = 0; + } + + [AllowAppend(ZeroGap=true)] public this(String str) { let count = str.mLength; + /*int a = __appendIdx; + int b = offsetof(String, mPtrOrBuffer); + if (__appendIdx == offsetof(String, mPtrOrBuffer)) + { + + }*/ int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); #unwarn char8* addlPtr = append char8[bufferSize]*(?); - Init(bufferSize); Internal.MemCpy(Ptr, str.Ptr, count); mLength = count; mAllocSizeAndFlags = (uint_strsize)bufferSize + (int_strsize)sizeof(char8*); } [AllowAppend] + public this(String str) + { + let count = str.mLength; + /*int a = __appendIdx; + int b = offsetof(String, mPtrOrBuffer); + if (__appendIdx == offsetof(String, mPtrOrBuffer)) + { + + }*/ + int bufferSize = count; +#unwarn + char8* addlPtr = append char8[bufferSize]*(?); + mPtrOrBuffer = addlPtr; + Internal.MemCpy(Ptr, str.Ptr, count); + mLength = count; + mAllocSizeAndFlags = (uint_strsize)bufferSize | cStrPtrFlag; + } + + [AllowAppend(ZeroGap=true)] public this(String str, int offset) { Debug.Assert((uint)offset <= (uint)str.Length); @@ -126,7 +170,7 @@ namespace System mLength = (int_strsize)count; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(String str, int offset, int count) { Debug.Assert((uint)offset <= (uint)str.Length); @@ -144,7 +188,7 @@ namespace System mLength = (int_strsize)count; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(char8 c, int count) { int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); @@ -158,7 +202,7 @@ namespace System mLength = (int_strsize)count; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(char8* char8Ptr) { let count = Internal.CStrLen(char8Ptr); @@ -173,7 +217,7 @@ namespace System mLength = count; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(char8* char8Ptr, int count) { int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); @@ -187,7 +231,7 @@ namespace System mLength = (int_strsize)count; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(char16* char16Ptr) { let count = UTF16.GetLengthAsUTF8(char16Ptr); @@ -200,7 +244,7 @@ namespace System UTF16.Decode(char16Ptr, this); } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(Span chars) { let count = UTF16.GetLengthAsUTF8(chars); @@ -213,14 +257,13 @@ namespace System UTF16.Decode(chars, this); } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(StringView strView) { let count = strView.Length; int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); #unwarn char8* addlPtr = append char8[bufferSize]*(?); - Init(bufferSize); let ptr = Ptr; Internal.MemCpy(ptr, strView.Ptr, strView.Length); mAllocSizeAndFlags = (uint_strsize)bufferSize + (int_strsize)sizeof(char8*); @@ -228,6 +271,20 @@ namespace System } [AllowAppend] + public this(StringView strView) + { + let count = strView.Length; + int bufferSize = count; +#unwarn + char8* addlPtr = append char8[bufferSize]*(?); + mPtrOrBuffer = addlPtr; + let ptr = Ptr; + Internal.MemCpy(ptr, strView.Ptr, strView.Length); + mAllocSizeAndFlags = (uint_strsize)bufferSize | cStrPtrFlag; + mLength = (int_strsize)strView.Length; + } + + [AllowAppend(ZeroGap=true)] public this(StringView strView, CreateFlags flags) { let count = strView.Length + (flags.HasFlag(.NullTerminate) ? 1 : 0); @@ -243,7 +300,7 @@ namespace System mLength = (int32)strView.Length; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(StringView strView, int offset) { Debug.Assert((uint)offset <= (uint)strView.Length); @@ -261,7 +318,7 @@ namespace System mLength = (int_strsize)count; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(StringView strView, int offset, int count) { Debug.Assert((uint)offset <= (uint)strView.Length); @@ -279,7 +336,7 @@ namespace System mLength = (int_strsize)count; } - [AllowAppend] + [AllowAppend(ZeroGap=true)] public this(char8[] chars, int offset, int count) { int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); @@ -293,7 +350,15 @@ namespace System mLength = (int_strsize)count; } - static int StrLengths(String[] strs) + static int StrLengths(Span strs) + { + int count = 0; + for (var str in strs) + count += str.Length; + return count; + } + + static int StrLengths(Span strs) { int count = 0; for (var str in strs) @@ -302,7 +367,7 @@ namespace System } [AllowAppend] - public this(params String[] strs) + public this(params Span strs) { int count = StrLengths(strs); int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); @@ -313,7 +378,27 @@ namespace System int curIdx = 0; for (var str in strs) { - Internal.MemCpy(ptr + curIdx, str.Ptr, str.mLength); + Internal.MemCpy(ptr + curIdx, str.Ptr, str.Length); + curIdx += str.Length; + } + + mLength = (int_strsize)count; + mAllocSizeAndFlags = (uint_strsize)bufferSize + (int_strsize)sizeof(char8*); + } + + [AllowAppend] + public this(Span strs) + { + int count = StrLengths(strs); + int bufferSize = (count == 0) ? 0 : (count - 1) & ~(sizeof(char8*) - 1); +#unwarn + char8* addlPtr = append char8[bufferSize]*(?); + Init(bufferSize); + let ptr = Ptr; + int curIdx = 0; + for (var str in strs) + { + Internal.MemCpy(ptr + curIdx, str.Ptr, str.Length); curIdx += str.Length; } @@ -642,7 +727,7 @@ namespace System public static implicit operator Span(String str) { if (str == null) - return .(null, 0); + return .((char8*)null, 0); return .(str.Ptr, str.Length); } @@ -966,7 +1051,7 @@ namespace System Append(str.Ptr + offset, length); } - public void Append(params String[] strings) + public void Append(params Span strings) { for (var str in strings) Append(str); @@ -1307,6 +1392,10 @@ namespace System while (true) { + int charsLeft = len - pos; + Reserve(mLength + charsLeft); + char8* ptr = Ptr; + int p = pos; int i = pos; while (pos < len) @@ -1333,7 +1422,8 @@ namespace System } } - Append(ch); + //Append(ch); + ptr[mLength++] = ch; } if (pos == len) break; @@ -1434,25 +1524,36 @@ namespace System } if (ch != '}') return FormatError(); pos++; - if (s == null) - s = scope:: String(128); - - s.Clear(); - IFormattable formattableArg = arg as IFormattable; - if (formattableArg != null) - formattableArg.ToString(s, fmt, provider); - else if (arg != null) - arg.ToString(s); + + if ((provider == null) && (fmt.IsEmpty) && (width == 0)) + { + if (arg == null) + Append("null"); + else + arg.ToString(this); + } else - s.Append("null"); - if (fmt != (Object)"") - fmt.Clear(); - - if (s == null) s = String.Empty; - int pad = width - s.Length; - if (!leftJustify && pad > 0) Append(' ', pad); - Append(s); - if (leftJustify && pad > 0) Append(' ', pad); + { + if (s == null) + s = scope:: String(128); + + s.Clear(); + IFormattable formattableArg = arg as IFormattable; + if (formattableArg != null) + formattableArg.ToString(s, fmt, provider); + else if (arg != null) + arg.ToString(s); + else + s.Append("null"); + if (fmt != (Object)"") + fmt.Clear(); + + if (s == null) s = String.Empty; + int pad = width - s.Length; + if (!leftJustify && pad > 0) Append(' ', pad); + Append(s); + if (leftJustify && pad > 0) Append(' ', pad); + } } return .Ok; @@ -2514,9 +2615,9 @@ namespace System } } - public void Join(String separator, params String[] values) + public void Join(StringView separator, Span values) { - for (int i = 0; i < values.Count; i++) + for (int i = 0; i < values.Length; i++) { if (i > 0) Append(separator); @@ -2524,6 +2625,17 @@ namespace System } } + public void Join(StringView separator, params Span values) + { + for (int i = 0; i < values.Length; i++) + { + if (i > 0) + Append(separator); + values[i].ToString(this); + } + } + + public StringSplitEnumerator Split(char8 c) { return StringSplitEnumerator(Ptr, Length, c, Int32.MaxValue, StringSplitOptions.None); @@ -2544,27 +2656,27 @@ namespace System return StringSplitEnumerator(Ptr, Length, separator, count, options); } - public StringSplitEnumerator Split(params char8[] separators) + public StringSplitEnumerator Split(params Span separators) { return StringSplitEnumerator(Ptr, Length, separators, Int32.MaxValue, StringSplitOptions.None); } - public StringSplitEnumerator Split(char8[] separators) + public StringSplitEnumerator Split(Span separators) { return StringSplitEnumerator(Ptr, Length, separators, Int32.MaxValue, StringSplitOptions.None); } - public StringSplitEnumerator Split(char8[] separators, int count) + public StringSplitEnumerator Split(Span separators, int count) { return StringSplitEnumerator(Ptr, Length, separators, count, StringSplitOptions.None); } - public StringSplitEnumerator Split(char8[] separators, int count, StringSplitOptions options) + public StringSplitEnumerator Split(Span separators, int count, StringSplitOptions options) { return StringSplitEnumerator(Ptr, Length, separators, count, options); } - public StringSplitEnumerator Split(char8[] separators, StringSplitOptions options) + public StringSplitEnumerator Split(Span separators, StringSplitOptions options) { return StringSplitEnumerator(Ptr, Length, separators, Int32.MaxValue, options); } @@ -2903,6 +3015,16 @@ namespace System } } + public static String GetById(int id) + { + if (Compiler.IsComptime) + { + return Compiler.[Friend]Comptime_GetStringById((.)id); + } + else + return sIdStringLiterals[id]; + } + public struct RawEnumerator : IRefEnumerator, IEnumerator { char8* mPtr; @@ -3079,15 +3201,15 @@ namespace System int_strsize mPos; int_strsize mMatchPos; - public this(char8* ptr, int strLength, char8[] separators, int count, StringSplitOptions splitOptions) + public this(char8* ptr, int strLength, Span separators, int count, StringSplitOptions splitOptions) { mPtr = ptr; mStrLen = (int_strsize)strLength; - if (separators?.Count > 0) + if (separators.Length > 0) { mFirstSeparator = separators[0]; mSeparatorPtr = &separators[0]; - mSeparatorCount = (.)separators.Count; + mSeparatorCount = (.)separators.Length; } else { @@ -3281,7 +3403,7 @@ namespace System { StringSplitOptions mSplitOptions; StringView mFirstSeparator; - StringView[] mSeparators; + Span mSeparators; char8* mPtr; int_strsize mStrLen; int32 mCurCount; @@ -3290,11 +3412,11 @@ namespace System int_strsize mMatchPos; int_strsize mMatchLen; - public this(char8* ptr, int strLength, StringView[] separators, int count, StringSplitOptions splitOptions) + public this(char8* ptr, int strLength, Span separators, int count, StringSplitOptions splitOptions) { mPtr = ptr; mStrLen = (int_strsize)strLength; - if (separators?.Count > 0) + if (separators.Length > 0) mFirstSeparator = separators[0]; else mFirstSeparator = .(); @@ -3414,7 +3536,7 @@ namespace System } else { - if (mFirstSeparator.IsNull && (mSeparators == null || mSeparators.IsEmpty) && mPtr[mMatchPos].IsWhiteSpace) + if (mFirstSeparator.IsNull && (mSeparators.IsEmpty) && mPtr[mMatchPos].IsWhiteSpace) { foundMatch = true; mMatchLen = 1; @@ -3424,9 +3546,9 @@ namespace System foundMatch = true; mMatchLen = (int_strsize)mFirstSeparator.Length; } - else if (mSeparators != null) + else if (!mSeparators.IsEmpty) { - for (int i = 1; i < mSeparators.Count; i++) + for (int i = 1; i < mSeparators.Length; i++) { if (mSeparators[i].Length <= mStrLen - mMatchPos && StringView(&mPtr[mMatchPos], mSeparators[i].Length) == mSeparators[i]) { @@ -4273,27 +4395,27 @@ namespace System return StringStringSplitEnumerator(Ptr, Length, separator, count, options); } - public StringStringSplitEnumerator Split(params StringView[] separators) + public StringStringSplitEnumerator Split(params Span separators) { return StringStringSplitEnumerator(Ptr, Length, separators, Int32.MaxValue, StringSplitOptions.None); } - public StringStringSplitEnumerator Split(StringView[] separators) + public StringStringSplitEnumerator Split(Span separators) { return StringStringSplitEnumerator(Ptr, Length, separators, Int32.MaxValue, StringSplitOptions.None); } - public StringStringSplitEnumerator Split(StringView[] separators, int count) + public StringStringSplitEnumerator Split(Span separators, int count) { return StringStringSplitEnumerator(Ptr, Length, separators, count, StringSplitOptions.None); } - public StringStringSplitEnumerator Split(StringView[] separators, int count, StringSplitOptions options) + public StringStringSplitEnumerator Split(Span separators, int count, StringSplitOptions options) { return StringStringSplitEnumerator(Ptr, Length, separators, count, options); } - public StringStringSplitEnumerator Split(StringView[] separators, StringSplitOptions options) + public StringStringSplitEnumerator Split(Span separators, StringSplitOptions options) { return StringStringSplitEnumerator(Ptr, Length, separators, Int32.MaxValue, options); } diff --git a/BeefLibs/corlib/src/System.bf b/BeefLibs/corlib/src/System.bf index 6cd97b92..99fb41af 100644 --- a/BeefLibs/corlib/src/System.bf +++ b/BeefLibs/corlib/src/System.bf @@ -108,6 +108,7 @@ static str.AppendF(format, args); }*/ + [Warn("StackStringFormat has been deprecated and will be removed in the future. Consider switching to string interpolation or 'scope:: String()..AppendF'.")] public static mixin StackStringFormat(String format, var arg1) { var str = scope:: String(); @@ -115,6 +116,7 @@ static str } + [Warn("StackStringFormat has been deprecated and will be removed in the future. Consider switching to string interpolation or 'scope:: String()..AppendF'.")] public static mixin StackStringFormat(String format, var arg1, var arg2) { var str = scope:: String(); @@ -122,6 +124,7 @@ static str } + [Warn("StackStringFormat has been deprecated and will be removed in the future. Consider switching to string interpolation or 'scope:: String()..AppendF'.")] public static mixin StackStringFormat(String format, var arg1, var arg2, var arg3) { var str = scope:: String(); diff --git a/BeefLibs/corlib/src/Text/Encoding.bf b/BeefLibs/corlib/src/Text/Encoding.bf index acbead21..eb5f7871 100644 --- a/BeefLibs/corlib/src/Text/Encoding.bf +++ b/BeefLibs/corlib/src/Text/Encoding.bf @@ -2,7 +2,7 @@ using System.Diagnostics; using System.Threading; namespace System.Text { - [StaticInitPriority(100)] + [StaticInitPriority(200)] abstract class Encoding { public enum DecodeError diff --git a/BeefLibs/corlib/src/Threading/Thread.bf b/BeefLibs/corlib/src/Threading/Thread.bf index bcd1c318..8c301c63 100644 --- a/BeefLibs/corlib/src/Threading/Thread.bf +++ b/BeefLibs/corlib/src/Threading/Thread.bf @@ -8,7 +8,7 @@ namespace System.Threading public delegate void ThreadStart(); public delegate void ParameterizedThreadStart(Object obj); - [StaticInitPriority(100)] + [StaticInitPriority(200)] public sealed class Thread { private int mInternalThread; @@ -21,14 +21,15 @@ namespace System.Threading bool mAutoDelete = true; bool mJoinOnDelete; + bool mIsBackground; - static Monitor sMonitor = new .() ~ delete _; + static Monitor sMonitor = new .() ~ DeleteAndNullify!(_); static Event sOnExit ~ _.Dispose(); Event mOnExit ~ _.Dispose(); public static Thread sMainThread ~ delete _; - [StaticInitPriority(102)] + [StaticInitPriority(202)] struct RuntimeThreadInit { static Object Thread_Alloc() @@ -86,6 +87,8 @@ namespace System.Threading thread.InformThreadNameChange(thread.mName); if (thread.mPriority != .Normal) thread.SetPriorityNative((.)thread.mPriority); + if (thread.mIsBackground) + thread.SetBackgroundNative(thread.mIsBackground); int32 stackStart = 0; thread.SetStackStart((void*)&stackStart); @@ -239,27 +242,27 @@ namespace System.Threading } } - public void Start() - { - StartInternal(); - } + public void Start() + { + StartInternal(); + } public void Start(bool autoDelete) { mAutoDelete = autoDelete; Start(); } - - public void Start(Object parameter) - { - if (mDelegate is ThreadStart) + + public void Start(Object parameter) { - Runtime.FatalError(); + if (mDelegate is ThreadStart) + { + Runtime.FatalError(); + } + mThreadStartArg = parameter; + StartInternal(); } - mThreadStartArg = parameter; - StartInternal(); - } - + public void Start(Object parameter, bool autoDelete) { mAutoDelete = autoDelete; @@ -384,10 +387,13 @@ namespace System.Threading public ~this() { - using (sMonitor.Enter()) + if (sMonitor != null) { - mOnExit(); - sOnExit(); + using (sMonitor.Enter()) + { + mOnExit(); + sOnExit(); + } } if (mJoinOnDelete) @@ -403,8 +409,13 @@ namespace System.Threading public bool IsBackground { - get { return IsBackgroundNative(); } - set { SetBackgroundNative(value); } + get { return mIsBackground; } + set + { + mIsBackground = value; + if (mInternalThread != 0) + SetBackgroundNative(mIsBackground); + } } public void SetJoinOnDelete(bool joinOnDelete) diff --git a/BeefLibs/corlib/src/TimeZoneInfo.bf b/BeefLibs/corlib/src/TimeZoneInfo.bf index f227a407..ac689966 100644 --- a/BeefLibs/corlib/src/TimeZoneInfo.bf +++ b/BeefLibs/corlib/src/TimeZoneInfo.bf @@ -53,7 +53,7 @@ namespace System { NoThrowOnInvalidTime = 2 } - [StaticInitPriority(100)] + [StaticInitPriority(200)] sealed public class TimeZoneInfo : IEquatable { // ---- SECTION: members supporting exposed properties -------------* @@ -2423,8 +2423,8 @@ namespace System { if (dynamicKey.GetValue(c_firstEntryValue) case .Ok(let val)) first = val.Get(); int32 last = -1; - if (dynamicKey.GetValue(c_lastEntryValue) case .Ok(let val)) - last = val.Get(); + if (dynamicKey.GetValue(c_lastEntryValue) case .Ok(let val2)) + last = val2.Get(); if ((first == -1) || (last == -1) || (first > last)) { rules = null; diff --git a/BeefLibs/corlib/src/Tuple.bf b/BeefLibs/corlib/src/Tuple.bf new file mode 100644 index 00000000..3a45d312 --- /dev/null +++ b/BeefLibs/corlib/src/Tuple.bf @@ -0,0 +1,5 @@ +namespace System; + +struct Tuple +{ +} \ No newline at end of file diff --git a/BeefLibs/corlib/src/Type.bf b/BeefLibs/corlib/src/Type.bf index 4101cc93..5bb91e66 100644 --- a/BeefLibs/corlib/src/Type.bf +++ b/BeefLibs/corlib/src/Type.bf @@ -5,6 +5,195 @@ using System.Diagnostics; namespace System { + public class TypeDeclaration + { + protected enum Flags : uint8 + { + DeclaredInDependency = 1, + DeclaredInDependent = 2, + DeclaredInCurrent = 4, + AlwaysVisible = 8, + SometimesVisible = 0x10 + } + + protected TypeId mTypeId; + protected TypeId mOuterTypeId; + protected TypeFlags mTypeFlags; + protected Flags mFlags; + protected TypeCode mTypeCode; + + public static TypeDeclaration.Enumerator TypeDeclarations + { + get + { + return .(); + } + } + + public TypeCode TypeCode => mTypeCode; + public TypeId TypeId => mTypeId; + public TypeDeclaration BaseType + { + get + { + if (Compiler.IsComptime) + { + int32 baseTypeId = Type.[Friend]Comptime_Type_GetBaseType((.)mTypeId); + return Type.[Friend]Comptime_GetTypeDeclarationById(baseTypeId); + } + return null; + } + } + public TypeDeclaration OuterType + { + get + { + if (Compiler.IsComptime) + return Type.[Friend]Comptime_GetTypeDeclarationById((.)mOuterTypeId); + return null; + } + } + + public bool DeclaredInDependency => mFlags.HasFlag(.DeclaredInDependency); + public bool DeclaredInDependent => mFlags.HasFlag(.DeclaredInDependent); + public bool DeclaredInCurrent => mFlags.HasFlag(.DeclaredInCurrent); + public bool AlwaysVisible => mFlags.HasFlag(.AlwaysVisible); + public bool SometimesVisible => mFlags.HasFlag(.SometimesVisible); + + public Type ResolvedType => Compiler.IsComptime ? Type.[Friend]Comptime_GetTypeById((.)mTypeId) : null; + + public void GetFullName(String strBuffer) + { + if (Compiler.IsComptime) + strBuffer.Append(Type.[Friend]Comptime_Type_ToString((.)mTypeId)); + } + + public void GetName(String strBuffer) + { + if (Compiler.IsComptime) + strBuffer.Append(Type.[Friend]Comptime_TypeName_ToString((.)mTypeId)); + } + + public void GetNamespace(String strBuffer) + { + if (Compiler.IsComptime) + strBuffer.Append(Type.[Friend]Comptime_Namespace_ToString((.)mTypeId)); + } + + public bool HasCustomAttribute() where T : Attribute + { + if (Compiler.IsComptime) + { + int32 attrIdx = -1; + Type attrType = null; + repeat + { + attrType = Type.[Friend]Comptime_Type_GetCustomAttributeType((int32)TypeId, ++attrIdx); + if (attrType == typeof(T)) + return true; + } + while (attrType != null); + return false; + } + return false; + } + + public Result GetCustomAttribute() where T : Attribute + { + if (Compiler.IsComptime) + { + int32 attrIdx = -1; + Type attrType = null; + repeat + { + attrType = Type.[Friend]Comptime_Type_GetCustomAttributeType((int32)TypeId, ++attrIdx); + if (attrType == typeof(T)) + { + T val = ?; + if (Type.[Friend]Comptime_Type_GetCustomAttribute((int32)TypeId, attrIdx, &val)) + return val; + } + } + while (attrType != null); + return .Err; + } + return .Err; + } + + [Comptime] + public AttributeInfo.ComptimeTypeCustomAttributeEnumerator GetCustomAttributes() + { + return .((int32)TypeId); + } + + public struct Enumerator : IEnumerator + { + int32 mCurId; + + public Result GetNext() mut + { + while (true) + { + if (!Compiler.IsComptime) + { + Runtime.FatalError("Runtime type declarations are not supported"); + } + else + { + var typeDecl = Type.[Friend]Comptime_GetNextTypeDeclaration(mCurId); + if (typeDecl != null) + { + mCurId = (.)typeDecl.TypeId; + return .Ok(typeDecl); + } + return .Err; + } + } + } + } + + public static TypeDeclaration GetById(TypeId typeId) => Type.[Friend]Comptime_GetTypeDeclarationById((.)typeId); + + public static Result GetByName(StringView typeName) + { + if (Compiler.IsComptime) + { + var type = Type.[Friend]Comptime_GetTypeDeclarationByName(typeName); + if (type == null) + return .Err; + return type; + } + + return .Err; + } + + public override void ToString(String strBuffer) + { + GetFullName(strBuffer); + } + + public bool HasDeclaredField(StringView fieldName) + { + if (Compiler.IsComptime) + return Type.[Friend]Comptime_Type_HasDeclaredMember((.)mTypeId, 0, fieldName); + return false; + } + + public bool HasDeclaredMethod(StringView fieldName) + { + if (Compiler.IsComptime) + return Type.[Friend]Comptime_Type_HasDeclaredMember((.)mTypeId, 1, fieldName); + return false; + } + + public bool HasDeclaredProperty(StringView fieldName) + { + if (Compiler.IsComptime) + return Type.[Friend]Comptime_Type_HasDeclaredMember((.)mTypeId, 2, fieldName); + return false; + } + } + struct ClassVData { public int mType; @@ -32,6 +221,21 @@ namespace System protected uint8 mAlign; protected uint8 mAllocStackCountOverride; + public TypeDeclaration TypeDeclaration + { + get + { + if (!Compiler.IsComptime) + { + Runtime.FatalError("Runtime type declarations are not supported"); + } + else + { + return Comptime_GetTypeDeclarationById((.)mTypeId); + } + } + } + public static TypeId TypeIdEnd { get @@ -48,6 +252,14 @@ namespace System } } + public static TypeDeclaration.Enumerator TypeDeclarations + { + get + { + return .(); + } + } + public int32 Size { get @@ -453,6 +665,17 @@ namespace System } } + public virtual TypeInstance WrappedType + { + get + { + if (Compiler.IsComptime) + return Comptime_GetWrappedType((.)mTypeId) as TypeInstance; + + return null; + } + } + public virtual TypeInstance.InterfaceEnumerator Interfaces { get @@ -551,9 +774,17 @@ namespace System public int64 mData; } + static extern TypeDeclaration Comptime_GetTypeDeclarationById(int32 typeId); + static extern TypeDeclaration Comptime_GetTypeDeclarationByName(StringView name); + static extern TypeDeclaration Comptime_GetNextTypeDeclaration(int32 lastTypeId); + static extern int32 Comptime_Type_GetBaseType(int32 typeId); + static extern bool Comptime_Type_HasDeclaredMember(int32 typeId, int32 kind, StringView name); static extern Type Comptime_GetTypeById(int32 typeId); + static extern Type Comptime_GetWrappedType(int32 typeId); static extern Type Comptime_GetTypeByName(StringView name); static extern String Comptime_Type_ToString(int32 typeId); + static extern String Comptime_TypeName_ToString(int32 typeId); + static extern String Comptime_Namespace_ToString(int32 typeId); static extern Type Comptime_GetSpecializedType(Type unspecializedType, Span typeArgs); static extern bool Comptime_Type_GetCustomAttribute(int32 typeId, int32 attributeIdx, void* dataPtr); static extern bool Comptime_Field_GetCustomAttribute(int32 typeId, int32 fieldIdx, int32 attributeIdx, void* dataPtr); @@ -660,6 +891,15 @@ namespace System return type == this || (type.IsTypedPrimitive && type.UnderlyingType == this); } + public virtual bool ImplementsInterface(Type checkInterface) + { + var wrappedType = WrappedType; + if (wrappedType != null) + return wrappedType.ImplementsInterface(checkInterface); + + return false; + } + public virtual Result GetField(String fieldName) { return .Err; @@ -752,8 +992,9 @@ namespace System #if !BF_REFLECT_MINIMAL GetFullName(strBuffer); #else - strBuffer.Append("Type#"); - mTypeId.ToString(strBuffer); + strBuffer.Append("comptype("); + ((int32)mTypeId).ToString(strBuffer); + strBuffer.Append(")"); #endif } @@ -766,7 +1007,7 @@ namespace System while (true) { if (Compiler.IsComptime) - Runtime.FatalError("Comptime type enumeration not supported"); + Runtime.FatalError("Comptime type enumeration not supported. Consider enumerating over TypeDeclaration.Types"); if (mCurId >= sTypeCount) return .Err; @@ -838,7 +1079,13 @@ namespace System namespace System.Reflection { - public struct TypeId : int32 {} + public struct TypeId : int32 + { + public override void ToString(String strBuffer) + { + strBuffer.AppendF($"TypeId#{(int32)this}"); + } + } [Ordered, AlwaysInclude(AssumeInstantiated=true)] public class TypeInstance : Type @@ -1036,6 +1283,19 @@ namespace System.Reflection } } + public override bool ImplementsInterface(Type checkInterface) + { + for (int ifaceIdx < mInterfaceCount) + { + if (mInterfaceDataPtr[ifaceIdx].mInterfaceType == checkInterface.TypeId) + return true; + } + var baseType = BaseType; + if (baseType != null) + return baseType.ImplementsInterface(checkInterface); + return false; + } + public override void GetFullName(String strBuffer) { if (mTypeFlags.HasFlag(TypeFlags.Tuple)) @@ -1317,7 +1577,8 @@ namespace System.Reflection public override void GetFullName(String strBuffer) { strBuffer.Append("const "); - switch (GetType(mValueType)) + var type = GetType(mValueType); + switch (type) { case typeof(float): (*(float*)&mValue).ToString(strBuffer); @@ -1332,6 +1593,10 @@ namespace System.Reflection strBuffer.Append('\''); case typeof(uint64), typeof(uint): (*(uint64*)&mValue).ToString(strBuffer); + case typeof(String): + int32 stringId = *(int32*)&mValue; + String str = String.GetById(stringId); + str.Quote(strBuffer); default: mValue.ToString(strBuffer); } @@ -1409,7 +1674,11 @@ namespace System.Reflection { if (i > 0) strBuffer.Append(", "); - Type.GetType(mResolvedTypeRefs[i]).GetFullName(strBuffer); + var genericArg = Type.GetType(mResolvedTypeRefs[i]); + if (genericArg != null) + genericArg.GetFullName(strBuffer); + else + strBuffer.Append("???"); } strBuffer.Append('>'); } @@ -1445,7 +1714,7 @@ namespace System.Reflection int32 stackCount = Compiler.Options.AllocStackCount; if (mAllocStackCountOverride != 0) stackCount = mAllocStackCountOverride; - obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, stackCount); + obj = Internal.Dbg_ObjectAlloc([Friend]mTypeClassVData, arraySize, [Friend]mInstAlign, stackCount, 0); #else void* mem = new [Align(16)] uint8[arraySize]* (?); obj = Internal.UnsafeCastToObject(mem); @@ -1505,6 +1774,7 @@ namespace System.Reflection Static = 0x200000, Abstract = 0x400000, + HasAppendWantMark = 0x800000, } public enum FieldFlags : uint16 @@ -1527,9 +1797,9 @@ namespace System.Reflection Appended = 0x1000, } - public enum MethodFlags : uint16 + public enum MethodFlags : uint32 { - MethodAccessMask = 0x0007, + case MethodAccessMask = 0x0007, Protected = 0x0003, Public = 0x0006, @@ -1547,5 +1817,26 @@ namespace System.Reflection ThisCall = 0x3000, // Purposely resuing StdCall|FastCall Mutating = 0x4000, Constructor = 0x8000, + AppendBit0 = 0x10000, + AppendBit1 = 0x20000, + CheckedBit0 = 0x40000, + CheckedBit1 = 0x80000; + + public AllowAppendKind AllowAppendKind => (.)(int32)(this / AppendBit0); + public CheckedKind CheckedKind => (.)(int32)(this / CheckedBit0); + } + + public enum AllowAppendKind + { + No, + Yes, + ZeroGap + } + + public enum CheckedKind + { + NotSet, + Checked, + Unchecked } } diff --git a/BeefLibs/corlib/src/UInt32.bf b/BeefLibs/corlib/src/UInt32.bf index d174ae0c..dc585908 100644 --- a/BeefLibs/corlib/src/UInt32.bf +++ b/BeefLibs/corlib/src/UInt32.bf @@ -61,7 +61,7 @@ namespace System public override void ToString(String strBuffer) { // Dumb, make better. - char8[] strChars = scope:: char8[16]; + char8[16] strChars = ?; int32 char8Idx = 14; uint32 valLeft = (uint32)this; while (valLeft > 0) @@ -73,7 +73,7 @@ namespace System if (char8Idx == 14) strChars[char8Idx--] = '0'; char8* char8Ptr = &strChars[char8Idx + 1]; - strBuffer.Append(char8Ptr); + strBuffer.Append(char8Ptr, 14 - char8Idx); } void ToString(String strBuffer, int minNumerals) diff --git a/BeefLibs/corlib/src/UInt64.bf b/BeefLibs/corlib/src/UInt64.bf index c12e1b7e..59dd6910 100644 --- a/BeefLibs/corlib/src/UInt64.bf +++ b/BeefLibs/corlib/src/UInt64.bf @@ -73,7 +73,7 @@ namespace System public override void ToString(String strBuffer) { // Dumb, make better. - char8[] strChars = scope:: char8[22]; + char8[22] strChars = ?; int32 char8Idx = 20; uint64 valLeft = (uint64)this; while (valLeft > 0) @@ -85,7 +85,7 @@ namespace System if (char8Idx == 20) strChars[char8Idx--] = '0'; char8* char8Ptr = &strChars[char8Idx + 1]; - strBuffer.Append(char8Ptr); + strBuffer.Append(char8Ptr, 20 - char8Idx); } public static Result Parse(StringView val, NumberStyles style = .Number, CultureInfo cultureInfo = null) diff --git a/BeefLibs/corlib/src/Void.bf b/BeefLibs/corlib/src/Void.bf index 13fb811f..3109cb24 100644 --- a/BeefLibs/corlib/src/Void.bf +++ b/BeefLibs/corlib/src/Void.bf @@ -1,10 +1,15 @@ namespace System { - struct Void : void + struct Void : void, IHashable { public override void ToString(String strBuffer) { strBuffer.Append("void"); } + + public int GetHashCode() + { + return 0; + } } } diff --git a/BeefLibs/corlib/src/Windows.bf b/BeefLibs/corlib/src/Windows.bf index 716f3acb..be1ce04f 100644 --- a/BeefLibs/corlib/src/Windows.bf +++ b/BeefLibs/corlib/src/Windows.bf @@ -107,6 +107,11 @@ namespace System public int32 Height => bottom - top; } + public struct Point : this(int32 x, int32 y) + { + + } + [CRepr] public struct OpenFileName { @@ -364,11 +369,15 @@ namespace System return .Ok; } - public Result GetValue(StringView name, String outData) + public Result GetValue(StringView name, String outData, bool* isExpanded = null) { + if (isExpanded != null) + *isExpanded = false; bool gotData = false; GetValue(name, scope [?] (regType, regData) => { + if (isExpanded != null) + *isExpanded = regType == Windows.REG_EXPAND_SZ; if ((regType == Windows.REG_SZ) || (regType == Windows.REG_EXPAND_SZ)) { gotData = true; @@ -469,7 +478,7 @@ namespace System public Result SetValueExpand(StringView name, StringView strValue) { - let result = Windows.RegSetValueExA(this, name.ToScopeCStr!(), 0, Windows.REG_EXPAND_SZ, strValue.ToScopeCStr!(), (uint32)strValue.Length + 1); + let result = Windows.RegSetValueExA(this, name.ToScopeCStr!(), 0, Windows.REG_EXPAND_SZ, strValue.ToScopeCStr!(), (uint32)strValue.Length + 1); if (result != 0) return .Err; return .Ok; @@ -1703,6 +1712,12 @@ namespace System [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] public static extern HWnd SetActiveWindow(HWnd wnd); + [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] + public static extern HWnd SetForegroundWindow(HWnd wnd); + + [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] + public static extern HWnd GetForegroundWindow(); + [CLink, CallingConvention(.Stdcall)] public static extern int CallWindowProcA(int wndProc, HWnd hWnd, int32 msg, int wParam, int lParam); @@ -1749,6 +1764,12 @@ namespace System [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] public static extern IntBool SetWindowPos(HWnd hWnd, HWnd hWndAfter, int x, int y, int cx, int cy, int flags); + [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] + public static extern IntBool GetClientRect(HWnd hWnd, out Rect rect); + + [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] + public static extern IntBool ClientToScreen(HWnd hWnd, ref Point rect); + [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] public static extern IntBool PostMessageW(HWnd hWnd, int32 msg, int wParam, int lParam); @@ -1756,7 +1777,7 @@ namespace System public static extern int32 SendMessageW(HWnd hWnd, int32 msg, int wParam, int lParam); [Import("user32.lib"), CLink, CallingConvention(.Stdcall)] - public static extern int32 SendMessageTimeoutW(HWnd hWnd, int32 msg, int wParam, int lParam, int32 flags, int32 timeout, int32* result); + public static extern int32 SendMessageTimeoutW(HWnd hWnd, int32 msg, int wParam, int lParam, int32 flags, int32 timeout, int* result); [Import("user32.lib "), CLink, CallingConvention(.Stdcall)] public static extern HWnd SetFocus(HWnd hWnd); diff --git a/BeefRT/dbg/DbgInternal.cpp b/BeefRT/dbg/DbgInternal.cpp index 754dc74a..90a03c33 100644 --- a/BeefRT/dbg/DbgInternal.cpp +++ b/BeefRT/dbg/DbgInternal.cpp @@ -49,10 +49,10 @@ namespace bf BFRT_EXPORT static void Dbg_ObjectCreatedEx(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData); BFRT_EXPORT static void Dbg_ObjectAllocated(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData); BFRT_EXPORT static void Dbg_ObjectAllocatedEx(bf::System::Object* result, intptr size, bf::System::ClassVData* classVData); - BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::Reflection::TypeInstance* typeInst, intptr size); - BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDept); + BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::Reflection::TypeInstance* typeInst, intptr size); + BFRT_EXPORT static Object* Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDept, uint8 allocFlags); BFRT_EXPORT static void Dbg_MarkObjectDeleted(bf::System::Object* obj); - BFRT_EXPORT static void Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData); + BFRT_EXPORT static void Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData, intptr size, uint8 allocFlags); BFRT_EXPORT static void Dbg_ObjectPreDelete(bf::System::Object* obj); BFRT_EXPORT static void Dbg_ObjectPreCustomDelete(bf::System::Object* obj); @@ -315,19 +315,20 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::Reflection::TypeInstan //#define DBG_OBJECTEND -bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDepth) +bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData, intptr size, intptr align, intptr maxStackTraceDepth, uint8 allocFlags) { void* stackTrace[1024]; int capturedTraceCount = 0; intptr allocSize = size; bool largeAllocInfo = false; - + if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0) { if (maxStackTraceDepth > 1) { capturedTraceCount = BF_CAPTURE_STACK(1, (intptr*)stackTrace, min((int)maxStackTraceDepth, 1024)); const intptr maxSmallObjectSize = ((intptr)1 << ((sizeof(intptr) - 2) * 8)) - 1; + if ((capturedTraceCount > 255) || (size >= maxSmallObjectSize)) { largeAllocInfo = true; @@ -336,6 +337,12 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData else allocSize += capturedTraceCount * sizeof(intptr); } + + // Append want mark + if ((allocFlags & 1) != 0) + { + allocSize += 4 * sizeof(intptr); + } } #ifdef DBG_OBJECTEND @@ -385,7 +392,7 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData intptr dbgAllocInfo; auto classVDataVal = (intptr)classVData | (intptr)BfObjectFlag_Allocated; - if (maxStackTraceDepth <= 1) + if ((maxStackTraceDepth <= 1) && (allocFlags == 0)) dbgAllocInfo = (intptr)BF_RETURN_ADDRESS; else { @@ -393,13 +400,13 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData { classVDataVal |= (intptr)BfObjectFlag_AllocInfo; dbgAllocInfo = size; - *(intptr*)((uint8*)result + size) = capturedTraceCount; + *(intptr*)((uint8*)result + size) = (capturedTraceCount << 8) | allocFlags; memcpy((uint8*)result + size + sizeof(intptr), stackTrace, capturedTraceCount * sizeof(intptr)); } else { classVDataVal |= (intptr)BfObjectFlag_AllocInfo_Short; - dbgAllocInfo = (size << 16) | capturedTraceCount; + dbgAllocInfo = (size << 16) | (((intptr)allocFlags) << 8) | capturedTraceCount; memcpy((uint8*)result + size, stackTrace, capturedTraceCount * sizeof(intptr)); } } @@ -424,13 +431,27 @@ bf::System::Object* Internal::Dbg_ObjectAlloc(bf::System::ClassVData* classVData return result; } -void Internal::Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData) +void Internal::Dbg_ObjectStackInit(bf::System::Object* result, bf::System::ClassVData* classVData, intptr totalSize, uint8 allocFlags) { BF_ASSERT((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0); result->mClassVData = (intptr)classVData | (intptr)BfObjectFlag_StackAlloc; #ifndef BFRT_NODBGFLAGS - result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS; + if ((allocFlags & 1) == 0) + { + result->mDbgAllocInfo = (intptr)BF_RETURN_ADDRESS; + } + else + { + int size = (int)(totalSize - sizeof(intptr) - sizeof(intptr) * 4); + + int capturedTraceCount = 1; + *(intptr*)((uint8*)result + size) = (intptr)BF_RETURN_ADDRESS; + memset((uint8*)result + size + sizeof(intptr), 0, sizeof(intptr) * 4); + result->mDbgAllocInfo = ((intptr)size << 16) | (((intptr)allocFlags) << 8) | capturedTraceCount; + BF_FULL_MEMORY_FENCE(); + result->mClassVData |= (intptr)BfObjectFlag_AllocInfo_Short; + } #endif } diff --git a/BeefRT/dbg/gc.cpp b/BeefRT/dbg/gc.cpp index 07ecf378..5bc20ea6 100644 --- a/BeefRT/dbg/gc.cpp +++ b/BeefRT/dbg/gc.cpp @@ -2180,6 +2180,12 @@ void BFGC::AddPendingThread(BfInternalThread* internalThread) mPendingThreads.TryAdd(internalThread->mThreadId, internalThread); } +void BFGC::Disable() +{ + StopCollecting(); + mGracelessShutdown = true; +} + void BFGC::Shutdown() { if (mShutdown) @@ -2664,10 +2670,13 @@ void BFGC::WriteDebugDumpState() { //const bf::System::Type* bfTypeRootData = ((bf::System::Type*)obj->GetTypeSafe())->mTypeRootData; bf::System::Type* bfType = obj->GetTypeSafe(); - while ((int) debugInfoVector.size() <= bfType->mTypeId) + + auto typeData = bfType->GetTypeData(); + + while ((int) debugInfoVector.size() <= typeData->mTypeId) debugInfoVector.push_back(DebugInfo()); - DebugInfo* debugInfo = &debugInfoVector[bfType->mTypeId]; + DebugInfo* debugInfo = &debugInfoVector[typeData->mTypeId]; debugInfo->mType = obj->GetTypeSafe(); debugInfo->mCount++; int objSize = BFGetObjectSize(obj); @@ -2896,6 +2905,11 @@ void GC::RemoveStackMarkableObject(Object* obj) gBFGC.RemoveStackMarkableObject(obj); } +void GC::Disable() +{ + gBFGC.Disable(); +} + void GC::Shutdown() { gBFGC.Shutdown(); diff --git a/BeefRT/dbg/gc.h b/BeefRT/dbg/gc.h index 130605b6..6151a9a7 100644 --- a/BeefRT/dbg/gc.h +++ b/BeefRT/dbg/gc.h @@ -397,6 +397,7 @@ public: void AddStackMarkableObject(bf::System::Object* obj); void RemoveStackMarkableObject(bf::System::Object* obj); void AddPendingThread(BfInternalThread* internalThread); + void Disable(); void Shutdown(); void InitDebugDump(); void EndDebugDump(); @@ -474,7 +475,8 @@ namespace bf BFRT_EXPORT static void AddPendingThread(void* internalThreadInfo); public: - BFRT_EXPORT static void Shutdown(); + BFRT_EXPORT static void Disable(); + BFRT_EXPORT static void Shutdown(); BFRT_EXPORT static void Collect(bool async); BFRT_EXPORT static void Report(); BFRT_EXPORT static void Mark(Object* obj); diff --git a/BeefRT/dbg/gc_raw.cpp b/BeefRT/dbg/gc_raw.cpp index f24c8554..df73ad38 100644 --- a/BeefRT/dbg/gc_raw.cpp +++ b/BeefRT/dbg/gc_raw.cpp @@ -115,16 +115,18 @@ void BFGC::RawMarkSpan(tcmalloc_raw::Span* span, int expectedStartPage) if (rawAllocData != NULL) { if (rawAllocData->mMarkFunc != NULL) - { + { intptr extraDataSize = sizeof(intptr); if (rawAllocData->mMaxStackTrace == 1) { - extraDataSize += sizeof(intptr); + extraDataSize += sizeof(intptr) + 2; + extraDataSize += *(uint16*)((uint8*)spanPtr + elementSize - sizeof(intptr) - sizeof(intptr) - 2); } else if (rawAllocData->mMaxStackTrace > 1) { intptr stackTraceCount = *(intptr*)((uint8*)spanPtr + elementSize - sizeof(intptr) - sizeof(intptr)); - extraDataSize += (1 + stackTraceCount) * sizeof(intptr); + extraDataSize += (1 + stackTraceCount) * sizeof(intptr) + 2; + extraDataSize += *(uint16*)((uint8*)spanPtr + elementSize - sizeof(intptr) - sizeof(intptr) - stackTraceCount * sizeof(intptr) - 2); } struct MarkTarget @@ -132,17 +134,21 @@ void BFGC::RawMarkSpan(tcmalloc_raw::Span* span, int expectedStartPage) }; typedef void(MarkTarget::*MarkFunc)(); MarkFunc markFunc = *(MarkFunc*)&rawAllocData->mMarkFunc; - - // It's possible we can overestimate elemCount, particularly for large allocations. This doesn't cause a problem - // because we can safely mark on complete random memory -- pointer values are always validated before being followed - intptr elemStride = BF_ALIGN(rawAllocData->mType->mSize, rawAllocData->mType->mAlign); - if (elemStride > 0) + + auto typeData = rawAllocData->mType->GetTypeData(); + if (typeData != NULL) { - intptr dataSize = elementSize - extraDataSize; - intptr elemCount = dataSize / elemStride; - for (intptr elemIdx = 0; elemIdx < elemCount; elemIdx++) + // It's possible we can overestimate elemCount, particularly for large allocations. This doesn't cause a problem + // because we can safely mark on complete random memory -- pointer values are always validated before being followed + intptr elemStride = BF_ALIGN(typeData->mSize, typeData->mAlign); + if (elemStride > 0) { - (((MarkTarget*)((uint8*)spanPtr + elemIdx * elemStride))->*markFunc)(); + intptr dataSize = elementSize - extraDataSize; + intptr elemCount = dataSize / elemStride; + for (intptr elemIdx = 0; elemIdx < elemCount; elemIdx++) + { + (((MarkTarget*)((uint8*)spanPtr + elemIdx * elemStride))->*markFunc)(); + } } } } @@ -282,13 +288,13 @@ void BFGC::RawReportHandleSpan(tcmalloc_raw::Span* span, int expectedStartPage, if (rawAllocData->mType != NULL) { - intptr typeSize; - if ((gBfRtDbgFlags & BfRtFlags_ObjectHasDebugFlags) != 0) - typeSize = rawAllocData->mType->mSize; - else - typeSize = ((bf::System::Type_NOFLAGS*)rawAllocData->mType)->mSize; - if (typeSize > 0) - rawLeakInfo.mDataCount = (elementSize - extraDataSize) / typeSize; + bf::System::Type_NOFLAGS* typeData = rawAllocData->mType->GetTypeData(); + if (typeData != NULL) + { + intptr typeSize = typeData->mSize; + if (typeSize > 0) + rawLeakInfo.mDataCount = (elementSize - extraDataSize) / typeSize; + } } else rawLeakInfo.mDataCount = 1; @@ -558,8 +564,8 @@ void* BfRawAllocate(intptr size, bf::System::DbgRawAllocData* rawAllocData, void markOffsetPtr = (uint16*)((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr) - 2); } else if (rawAllocData->mMaxStackTrace > 1) - { - memcpy((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr), &stackTraceCount, sizeof(intptr)); + { + *(intptr*)((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr)) = stackTraceCount; memcpy((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr) - stackTraceCount*sizeof(intptr), stackTraceInfo, stackTraceCount*sizeof(intptr)); markOffsetPtr = (uint16*)((uint8*)result + totalSize - sizeof(intptr) - sizeof(intptr) - stackTraceCount * sizeof(intptr) - 2); } diff --git a/BeefRT/rt/BfObjects.h b/BeefRT/rt/BfObjects.h index f6a7b983..07f7b38d 100644 --- a/BeefRT/rt/BfObjects.h +++ b/BeefRT/rt/BfObjects.h @@ -59,6 +59,8 @@ namespace bf { namespace System { + class Type_NOFLAGS; + struct DbgRawAllocData { Type* mType; @@ -84,7 +86,7 @@ namespace bf void*(*Alloc)(intptr size); void(*Free)(void* ptr); void(*Object_Delete)(bf::System::Object* obj); - void* mUnused0; + Type_NOFLAGS*(*ClassVData_GetTypeDataPtr)(bf::System::ClassVData* classVData); bf::System::Type* (*Object_GetType)(bf::System::Object* obj); void(*Object_GCMarkMembers)(bf::System::Object* obj); bf::System::Object* (*Object_DynamicCastToTypeId)(bf::System::Object* obj, int typeId); @@ -196,28 +198,25 @@ namespace bf typedef int32 TypeId; + enum BfTypeFlags : uint32 + { + BfTypeFlag_HasAppendWantMark = 0x800000 + }; + class Type : public Object { - public: - int32 mSize; - TypeId mTypeId; - TypeId mBoxedId; - uint16 mTypeFlags; - int32 mMemberDataOffset; - uint8 mTypeCode; - uint8 mAlign; - + public: Beefy::String GetFullName(); + Type_NOFLAGS* GetTypeData(); }; class Type_NOFLAGS { - public: - intptr mClassVData; + public: int32 mSize; TypeId mTypeId; TypeId mBoxedId; - uint16 mTypeFlags; + BfTypeFlags mTypeFlags; int32 mMemberDataOffset; uint8 mTypeCode; uint8 mAlign; diff --git a/BeefRT/rt/Internal.cpp b/BeefRT/rt/Internal.cpp index 37564ea9..a172ddf8 100644 --- a/BeefRT/rt/Internal.cpp +++ b/BeefRT/rt/Internal.cpp @@ -306,6 +306,7 @@ void bf::System::Console::PutChars(char* ptr, int len) for (int i = 0; i < len; i++) putchar(ptr[i]); + fflush(stdout); } void bf::System::Console::ReopenHandles() diff --git a/BeefRT/rt/Object.cpp b/BeefRT/rt/Object.cpp index 87c94262..41cf7506 100644 --- a/BeefRT/rt/Object.cpp +++ b/BeefRT/rt/Object.cpp @@ -20,3 +20,11 @@ Beefy::String bf::System::Type::GetFullName() BFRTCALLBACKS.Object_Delete(strObj); return str; } + +bf::System::Type_NOFLAGS* bf::System::Type::GetTypeData() +{ + if ((BFRTFLAGS & BfRtFlags_ObjectHasDebugFlags) != 0) + return BFRTCALLBACKS.ClassVData_GetTypeDataPtr((bf::System::ClassVData*)(mClassVData & ~0xFF)); + else + return BFRTCALLBACKS.ClassVData_GetTypeDataPtr((bf::System::ClassVData*)(mClassVData)); +} \ No newline at end of file diff --git a/BeefRT/rt/Thread.cpp b/BeefRT/rt/Thread.cpp index b006c372..93b0ab84 100644 --- a/BeefRT/rt/Thread.cpp +++ b/BeefRT/rt/Thread.cpp @@ -17,6 +17,7 @@ BF_TLS_DECLSPEC Thread* Thread::sCurrentThread; #endif static volatile int gLiveThreadCount; +static volatile int gBackgroundThreadCount; static Beefy::SyncEvent gThreadsDoneEvent; #ifdef BF_PLATFORM_WINDOWS @@ -132,8 +133,12 @@ static void BF_CALLTYPE CStartProc(void* threadParam) bool isAutoDelete = gBfRtCallbacks.Thread_IsAutoDelete(thread); gBfRtCallbacks.Thread_ThreadProc(thread); - bool isLastThread = BfpSystem_InterlockedExchangeAdd32((uint32*)&gLiveThreadCount, -1) == 1; + + if (internalThread->mIsBackground) + BfpSystem_InterlockedExchangeAdd32((uint32*)&gBackgroundThreadCount, -1); + bool isLastThread = (int32)BfpSystem_InterlockedExchangeAdd32((uint32*)&gLiveThreadCount, -1) <= gBackgroundThreadCount + 1; + //printf("Stopping thread\n"); bool wantsDelete = false; @@ -158,7 +163,7 @@ static void BF_CALLTYPE CStartProc(void* threadParam) delete internalThread; if (isLastThread) - gThreadsDoneEvent.Set(false); + gThreadsDoneEvent.Set(false); //printf("Thread stopped\n"); } @@ -168,7 +173,7 @@ void BfInternalThread::WaitForAllDone() if ((gBfRtFlags & BfRtFlags_NoThreadExitWait) != 0) return; - while (gLiveThreadCount != 0) + while (gLiveThreadCount > gBackgroundThreadCount) { // Clear out any old done events gThreadsDoneEvent.WaitFor(); @@ -303,12 +308,23 @@ void Thread::InternalFinalize() bool Thread::IsBackgroundNative() { - return false; + auto internalThread = GetInternalThread(); + if (internalThread == NULL) + return false; + return internalThread->mIsBackground; } void Thread::SetBackgroundNative(bool isBackground) { + auto internalThread = GetInternalThread(); + if (internalThread == NULL) + return; + if (isBackground != internalThread->mIsBackground) + { + internalThread->mIsBackground = isBackground; + BfpSystem_InterlockedExchangeAdd32((uint32*)&gBackgroundThreadCount, isBackground ? 1 : -1); + } } int Thread::GetThreadStateNative() diff --git a/BeefRT/rt/Thread.h b/BeefRT/rt/Thread.h index 22ea4d93..a074240d 100644 --- a/BeefRT/rt/Thread.h +++ b/BeefRT/rt/Thread.h @@ -111,6 +111,7 @@ public: bool mStarted; bool mJoinOnDelete; bool mIsManualInit; + bool mIsBackground; BfpThread* mThreadHandle; intptr mThreadId; Beefy::CritSect mCritSect; @@ -126,6 +127,7 @@ public: mIsSuspended = false; mJoinOnDelete = true; mIsManualInit = false; + mIsBackground = false; mStackStart = 0; mThreadId = 0; } diff --git a/BeefTools/BeefCon/src/Program.bf b/BeefTools/BeefCon/src/Program.bf index 08a52b8e..09f45a1e 100644 --- a/BeefTools/BeefCon/src/Program.bf +++ b/BeefTools/BeefCon/src/Program.bf @@ -48,7 +48,7 @@ class Program { while (true) { - switch (mPipe.ReadMessage(-1)) + switch (mPipe.ReadMessage(20)) { case .Ok(let msg): uint8* ptr = msg.Ptr + 1; diff --git a/BeefTools/LogViewer/src/StatusBar.bf b/BeefTools/LogViewer/src/StatusBar.bf index a8d37c9f..0fc6062d 100644 --- a/BeefTools/LogViewer/src/StatusBar.bf +++ b/BeefTools/LogViewer/src/StatusBar.bf @@ -1,3 +1,4 @@ +using System; using Beefy.widgets; using Beefy.gfx; @@ -16,8 +17,8 @@ namespace LogViewer g.FillRect(0, 0, mWidth, mHeight); var lineAndColumn = gApp.mBoard.mDocEdit.mEditWidgetContent.CursorLineAndColumn; - g.DrawString(StackStringFormat!("Ln {0}", lineAndColumn.mLine + 1), mWidth - 160, 2); - g.DrawString(StackStringFormat!("Col {0}", lineAndColumn.mColumn + 1), mWidth - 80, 2); + g.DrawString(scope $"Ln {lineAndColumn.mLine + 1}", mWidth - 160, 2); + g.DrawString(scope $"Col {lineAndColumn.mColumn + 1}", mWidth - 80, 2); if ((gApp.mBoard.mFilterDirtyCountdown != 0) || (gApp.mBoard.mRefreshing)) g.DrawString("Refreshing", 8, 2); diff --git a/BeefySysLib/BeefySysLib.cpp b/BeefySysLib/BeefySysLib.cpp index f15fc3e7..bfaf3143 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 "img/ImgEffects.h" #include "util/AllocDebug.h" @@ -445,10 +446,11 @@ BF_EXPORT int BF_CALLTYPE BFWindow_GetDPI(BFWindow* window) BF_EXPORT TextureSegment* BF_CALLTYPE Gfx_CreateRenderTarget(int width, int height, int destAlpha) { - Texture* texture = gBFApp->mRenderDevice->CreateRenderTarget(width, height, destAlpha != 0); + Texture* texture = gBFApp->mRenderDevice->CreateRenderTarget(width, height, destAlpha != 0); + texture->mResetClear = true; TextureSegment* aTextureSegment = new TextureSegment(); - aTextureSegment->InitFromTexture(texture); + aTextureSegment->InitFromTexture(texture); return aTextureSegment; } @@ -474,12 +476,12 @@ BF_EXPORT TextureSegment* BF_CALLTYPE Gfx_LoadTexture(const char* fileName, int BF_EXPORT void BF_CALLTYPE Gfx_Texture_SetBits(TextureSegment* textureSegment, int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32* bits) { - textureSegment->mTexture->SetBits(destX, destY, destWidth, destHeight, srcPitch, bits); + textureSegment->SetBits(destX, destY, destWidth, destHeight, srcPitch, bits); } BF_EXPORT void BF_CALLTYPE Gfx_Texture_GetBits(TextureSegment* textureSegment, int srcX, int srcY, int srcWidth, int srcHeight, int destPitch, uint32* bits) { - textureSegment->mTexture->GetBits(srcX, srcY, srcWidth, srcHeight, destPitch, bits); + textureSegment->GetBits(srcX, srcY, srcWidth, srcHeight, destPitch, bits); } BF_EXPORT void BF_CALLTYPE Gfx_Texture_Delete(TextureSegment* textureSegment) @@ -516,6 +518,93 @@ BF_EXPORT void BF_CALLTYPE Gfx_ModifyTextureSegment(TextureSegment* destTextureS destTextureSegment->mScaleY = (float)abs(srcHeight); } +int32 FormatI32(const StringView& str) +{ + String val = String(str); + if (val.StartsWith('#')) + { + return (int32)strtoll(val.c_str() + 1, NULL, 16); + } + if (val.StartsWith("0x")) + { + return (int32)strtoll(val.c_str() + 2, NULL, 16); + } + return atoi(val.c_str()); +} + +BF_EXPORT void BF_CALLTYPE Gfx_ApplyEffect(TextureSegment* destTextureSegment, TextureSegment* srcTextureSegment, char* optionsPtr) +{ + BaseImageEffect* effect = NULL; + defer({ + delete effect; + }); + + bool needsPremultiply = false; + + String options = optionsPtr; + for (auto line : options.Split('\n')) + { + int eqPos = (int)line.IndexOf('='); + if (eqPos != -1) + { + StringView cmd = StringView(line, 0, eqPos); + StringView value = StringView(line, eqPos + 1); + + if (cmd == "Effect") + { + if (value == "Stroke") + { + auto strokeEffect = new ImageStrokeEffect(); + effect = strokeEffect; + strokeEffect->mPosition = 'OutF'; + strokeEffect->mSize = 1; + strokeEffect->mColorFill.mColor = 0xFF000000; + } + } + if (cmd == "Size") + { + int32 size = FormatI32(value); + if (auto strokeEffect = dynamic_cast(effect)) + { + strokeEffect->mSize = size; + } + } + if (cmd == "Color") + { + uint32 color = (uint32)FormatI32(value); + if (auto strokeEffect = dynamic_cast(effect)) + { + strokeEffect->mColorFill.mColor = color; + if ((color & 0x00FFFFFF) != 0) + needsPremultiply = true; + } + } + } + } + + if (effect != NULL) + { + ImageData destImageData; + ImageData srcImageData; + + RectF srcRect = srcTextureSegment->GetRect(); + RectF destRect = destTextureSegment->GetRect(); + + destTextureSegment->GetImageData(destImageData); + srcImageData.CreateNew(destImageData.mWidth, destImageData.mHeight); + + srcTextureSegment->GetImageData(srcImageData, + (int)(destRect.width - srcRect.width) / 2, + (int)(destRect.height - srcRect.height) / 2); + + effect->Apply(NULL, &srcImageData, &destImageData); + if (needsPremultiply) + destImageData.PremultiplyAlpha(); + + destTextureSegment->SetImageData(destImageData); + } +} + BF_EXPORT TextureSegment* BF_CALLTYPE Gfx_CreateTextureSegment(TextureSegment* textureSegment, int srcX, int srcY, int srcWidth, int srcHeight) { Texture* texture = textureSegment->mTexture; @@ -742,10 +831,10 @@ BF_EXPORT void BF_CALLTYPE RenderState_SetWireframe(RenderState* renderState, bo BF_EXPORT void BF_CALLTYPE RenderState_SetClip(RenderState* renderState, float x, float y, float width, float height) { BF_ASSERT((width >= 0) && (height >= 0)); - renderState->mClipRect.mX = x; - renderState->mClipRect.mY = y; - renderState->mClipRect.mWidth = width; - renderState->mClipRect.mHeight = height; + renderState->mClipRect.x = x; + renderState->mClipRect.y = y; + renderState->mClipRect.width = width; + renderState->mClipRect.height = height; if (!renderState->mClipped) renderState->SetClipped(true); } diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj index 66e34fd1..24f095c5 100644 --- a/BeefySysLib/BeefySysLib.vcxproj +++ b/BeefySysLib/BeefySysLib.vcxproj @@ -177,6 +177,7 @@ third_party\AK\lib\release;$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Lib\x86;$(FrameworkSDKDir)\lib $(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSDK_IncludePath);$(WindowsSdkDir)include;$(FrameworkSDKDir)\include;C:\Program Files (x86)\Microsoft DirectX SDK (June 2007)\Include;C:\temp\wx\wxMSW-2.8.12\include;C:\temp\wx\wxMSW-2.8.12\include\msvc $(SolutionDir)\IDE\dist\ + $(ProjectName)64_Static @@ -414,11 +415,12 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" MaxSpeed - true + false true - WIN32;NDEBUG;_WINDOWS;_USRDLL;BFSYSLIB_DYNAMIC;%(PreprocessorDefinitions) + WIN32;NDEBUG;_WINDOWS;_USRDLL;BFSYSLIB_DYNAMIC;BF_NO_FBX;FT2_BUILD_LIBRARY;%(PreprocessorDefinitions) MultiThreaded - ./; ./platform/win/; third_party/agg-2.4/include; third_party/agg-2.4/include/platform/win32; third_party/; third_party/libffi/i686-pc-cygwin; third_party/libffi/i686-pc-cygwin/include; third_party/libffi/include + ./;./platform/win/;./platform/sdl/;third_party/agg-2.4/include;third_party/agg-2.4/include/platform/win32;third_party/;third_party/libffi/i686-pc-cygwin;third_party/libffi/i686-pc-cygwin/include;third_party/libffi/include;third_party/SDL2-2.0.1/include;../extern/fbxsdk/include;third_party/freetype/include;../BeefLibs/SDL2/include + false Windows @@ -461,6 +463,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + @@ -482,7 +485,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"false true true - true + false false @@ -492,7 +495,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"true false true - true + false false @@ -1947,6 +1950,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + @@ -1982,6 +1986,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + @@ -2000,7 +2005,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"true false true - true + false false @@ -2009,7 +2014,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"true false true - true + false false @@ -2184,6 +2189,8 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\" + + diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters index 64a87168..55154045 100644 --- a/BeefySysLib/BeefySysLib.vcxproj.filters +++ b/BeefySysLib/BeefySysLib.vcxproj.filters @@ -737,6 +737,12 @@ src\third_party\putty + + src\util + + + src\img + @@ -1132,6 +1138,15 @@ src\third_party\putty + + src\util + + + src\util + + + src\img + diff --git a/BeefySysLib/BeefySysLib_static.vcxproj b/BeefySysLib/BeefySysLib_static.vcxproj index 9f5b1345..ac170baa 100644 --- a/BeefySysLib/BeefySysLib_static.vcxproj +++ b/BeefySysLib/BeefySysLib_static.vcxproj @@ -199,6 +199,7 @@ + @@ -908,6 +909,7 @@ + diff --git a/BeefySysLib/BeefySysLib_static.vcxproj.filters b/BeefySysLib/BeefySysLib_static.vcxproj.filters index 9af9f333..9bc062c7 100644 --- a/BeefySysLib/BeefySysLib_static.vcxproj.filters +++ b/BeefySysLib/BeefySysLib_static.vcxproj.filters @@ -590,6 +590,9 @@ src\third_party\putty + + src\img + @@ -907,6 +910,9 @@ src\third_party\putty + + src\img + diff --git a/BeefySysLib/CMakeLists.txt b/BeefySysLib/CMakeLists.txt index 51edf9ea..f14c5951 100644 --- a/BeefySysLib/CMakeLists.txt +++ b/BeefySysLib/CMakeLists.txt @@ -142,6 +142,7 @@ file(GLOB SRC_FILES img/ImageUtils.cpp img/ImgEffects.cpp img/JPEGData.cpp + img/BMPData.cpp img/PNGData.cpp img/PSDReader.cpp img/PVRData.cpp diff --git a/BeefySysLib/CachedDataStream.cpp b/BeefySysLib/CachedDataStream.cpp index 66ed5ed8..c4a5dba0 100644 --- a/BeefySysLib/CachedDataStream.cpp +++ b/BeefySysLib/CachedDataStream.cpp @@ -36,13 +36,13 @@ int CachedDataStream::GetSize() return mStream->GetSize(); } -void CachedDataStream::Read(void* ptr, int size) +int CachedDataStream::Read(void* ptr, int size) { Flush(); - mStream->Read(ptr, size); + return mStream->Read(ptr, size); } -void CachedDataStream::Write(void* ptr, int size) +int CachedDataStream::Write(void* ptr, int size) { while (size > 0) { @@ -59,6 +59,7 @@ void CachedDataStream::Write(void* ptr, int size) size -= writeBytes; mDataPtr += writeBytes; } + return size; } int CachedDataStream::GetPos() diff --git a/BeefySysLib/CachedDataStream.h b/BeefySysLib/CachedDataStream.h index 6be84023..1c10c9db 100644 --- a/BeefySysLib/CachedDataStream.h +++ b/BeefySysLib/CachedDataStream.h @@ -24,8 +24,8 @@ public: virtual bool Eof() override; virtual int GetSize() override; - virtual void Read(void* ptr, int size) override; - virtual void Write(void* ptr, int size) override; + virtual int Read(void* ptr, int size) override; + virtual int Write(void* ptr, int size) override; virtual int GetPos() override; virtual void Seek(int size) override; diff --git a/BeefySysLib/Common.cpp b/BeefySysLib/Common.cpp index abedc863..a8dcb36c 100644 --- a/BeefySysLib/Common.cpp +++ b/BeefySysLib/Common.cpp @@ -1380,6 +1380,35 @@ void Beefy::BFFatalError(const char* message, const char* file, int line) BFFatalError(String(message), String(file), line); } -void MakeUpper(const StringImpl& theString) +bool Beefy::ParseMemorySpan(const StringImpl& str, void*& outPtr, int& outSize, StringImpl* outKey) { +#ifndef BF_SMALL + static int anonymousIdx = 0; + + if (str.StartsWith("@")) + { + int colon = (int)str.IndexOf(':'); + String addrStr = str.Substring(1, colon - 1); + String lenStr = str.Substring(colon + 1); + outPtr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16); + outSize = (int)strtol(lenStr.c_str(), NULL, 10); + + if (outKey != NULL) + { + int nextColon = (int)str.IndexOf(':', colon + 1); + if (nextColon > 0) + { + *outKey = str.Substring(nextColon + 1); + } + else + { + int dotPos = (int)str.IndexOf('.', colon + 1); + *outKey = StrFormat("ANON_%d", anonymousIdx++) + str.Substring(dotPos); + } + } + + return true; + } +#endif + return false; } diff --git a/BeefySysLib/Common.h b/BeefySysLib/Common.h index 609faf68..49a9c4c4 100644 --- a/BeefySysLib/Common.h +++ b/BeefySysLib/Common.h @@ -242,6 +242,7 @@ bool FileExists(const StringImpl& path, String* outActualName = NULL); bool DirectoryExists(const StringImpl& path, String* outActualName = NULL); bool RecursiveCreateDirectory(const StringImpl& dirName); bool RecursiveDeleteDirectory(const StringImpl& dirName); +bool ParseMemorySpan(const StringImpl& str, void*& outPtr, int& outSize, StringImpl* outKey = NULL); #define CHARTAG(val) FromBIGEndian(val) diff --git a/BeefySysLib/DataStream.h b/BeefySysLib/DataStream.h index 26a46a84..40ef38f7 100644 --- a/BeefySysLib/DataStream.h +++ b/BeefySysLib/DataStream.h @@ -21,8 +21,8 @@ public: virtual bool Eof() = 0; virtual int GetSize() = 0; - virtual void Read(void* ptr, int size) = 0; - virtual void Write(void* ptr, int size) = 0; + virtual int Read(void* ptr, int size) = 0; + virtual int Write(void* ptr, int size) = 0; virtual void WriteZeros(int size); virtual void Align(int size); diff --git a/BeefySysLib/FileHandleStream.cpp b/BeefySysLib/FileHandleStream.cpp index dca12bf0..27d08a4c 100644 --- a/BeefySysLib/FileHandleStream.cpp +++ b/BeefySysLib/FileHandleStream.cpp @@ -62,10 +62,12 @@ int FileHandleStream::GetSize() return ::GetFileSize(mFileHandle, NULL); } -void FileHandleStream::Read(void* ptr, int size) +int FileHandleStream::Read(void* ptr, int size) { if (mCacheBuffer != NULL) { + int totalReadSize = 0; + while (true) { int buffOffset = mVFilePos - mCacheReadPos; @@ -74,7 +76,7 @@ void FileHandleStream::Read(void* ptr, int size) // If inside memcpy(ptr, mCacheBuffer + buffOffset, size); mVFilePos += size; - return; + return size; } else if ((buffOffset >= 0) && (buffOffset < mCacheSize)) { @@ -84,6 +86,7 @@ void FileHandleStream::Read(void* ptr, int size) ptr = (uint8*)ptr + subSize; size -= subSize; + totalReadSize += subSize; } mCacheReadPos = mVFilePos & ~(4096 - 1); @@ -95,7 +98,9 @@ void FileHandleStream::Read(void* ptr, int size) // Zero out underflow bytes memset((uint8*)ptr + aSize, 0, mCacheSize - aSize); } + totalReadSize += aSize; } + return totalReadSize; } else { @@ -106,12 +111,13 @@ void FileHandleStream::Read(void* ptr, int size) // Zero out underflow bytes memset((uint8*)ptr + aSize, 0, size - aSize); } + return aSize; } } -void FileHandleStream::Write(void* ptr, int size) +int FileHandleStream::Write(void* ptr, int size) { - ::WriteFile(mFileHandle, ptr, size, NULL, NULL); + return (int)::WriteFile(mFileHandle, ptr, size, NULL, NULL); } int FileHandleStream::GetPos() diff --git a/BeefySysLib/FileHandleStream.h b/BeefySysLib/FileHandleStream.h index 31576866..97408476 100644 --- a/BeefySysLib/FileHandleStream.h +++ b/BeefySysLib/FileHandleStream.h @@ -23,9 +23,9 @@ public: bool Eof() override; int GetSize() override; using DataStream::Read; - void Read(void* ptr, int size) override; + int Read(void* ptr, int size) override; using DataStream::Write; - void Write(void* ptr, int size) override; + int Write(void* ptr, int size) override; int GetPos() override; void Seek(int size) override; diff --git a/BeefySysLib/FileStream.cpp b/BeefySysLib/FileStream.cpp index 08ee6f3d..1913db68 100644 --- a/BeefySysLib/FileStream.cpp +++ b/BeefySysLib/FileStream.cpp @@ -99,10 +99,12 @@ int FileStream::GetSize() return aSize; } -void FileStream::Read(void* ptr, int size) +int FileStream::Read(void* ptr, int size) { if (mCacheBuffer != NULL) { + int totalReadSize = 0; + while (true) { int buffOffset = mVFilePos - mCacheReadPos; @@ -111,7 +113,7 @@ void FileStream::Read(void* ptr, int size) // If inside memcpy(ptr, mCacheBuffer + buffOffset, size); mVFilePos += size; - return; + return size; } else if ((buffOffset >= 0) && (buffOffset < mCacheSize)) { @@ -121,6 +123,8 @@ void FileStream::Read(void* ptr, int size) ptr = (uint8*) ptr + subSize; size -= subSize; + + totalReadSize += subSize; } mCacheReadPos = mVFilePos & ~(4096-1); @@ -131,7 +135,11 @@ void FileStream::Read(void* ptr, int size) // Zero out underflow bytes memset((uint8*) ptr + aSize, 0, mCacheSize - aSize); } + + totalReadSize += aSize; } + + return totalReadSize; } else { @@ -142,12 +150,13 @@ void FileStream::Read(void* ptr, int size) memset((uint8*) ptr + aSize, 0, size - aSize); mReadPastEnd = true; } + return aSize; } } -void FileStream::Write(void* ptr, int size) +int FileStream::Write(void* ptr, int size) { - fwrite(ptr, 1, size, mFP); + return (int)fwrite(ptr, 1, size, mFP); } int FileStream::GetPos() @@ -250,7 +259,7 @@ int SysFileStream::GetSize() return (int)BfpFile_GetFileSize(mFile); } -void SysFileStream::Read(void* ptr, int size) +int SysFileStream::Read(void* ptr, int size) { int readSize = (int)BfpFile_Read(mFile, ptr, size, -1, NULL); if (readSize != size) @@ -258,11 +267,12 @@ void SysFileStream::Read(void* ptr, int size) // Zero out underflow bytes memset((uint8*)ptr + readSize, 0, size - readSize); } + return readSize; } -void SysFileStream::Write(void* ptr, int size) +int SysFileStream::Write(void* ptr, int size) { - BfpFile_Write(mFile, ptr, size, -1, NULL); + return (int)BfpFile_Write(mFile, ptr, size, -1, NULL); } int SysFileStream::GetPos() diff --git a/BeefySysLib/FileStream.h b/BeefySysLib/FileStream.h index 9405767b..30112cc1 100644 --- a/BeefySysLib/FileStream.h +++ b/BeefySysLib/FileStream.h @@ -28,9 +28,9 @@ public: bool Eof() override; int GetSize() override; using DataStream::Read; - void Read(void* ptr, int size) override; + int Read(void* ptr, int size) override; using DataStream::Write; - void Write(void* ptr, int size) override; + int Write(void* ptr, int size) override; int GetPos() override; void Seek(int size) override; @@ -67,9 +67,9 @@ public: bool Eof() override; int GetSize() override; using DataStream::Read; - void Read(void* ptr, int size) override; + int Read(void* ptr, int size) override; using DataStream::Write; - void Write(void* ptr, int size) override; + int Write(void* ptr, int size) override; int GetPos() override; void Seek(int size) override; diff --git a/BeefySysLib/MTRand.cpp b/BeefySysLib/MTRand.cpp new file mode 100644 index 00000000..e69de29b diff --git a/BeefySysLib/MemStream.cpp b/BeefySysLib/MemStream.cpp index c4316cb5..112160f2 100644 --- a/BeefySysLib/MemStream.cpp +++ b/BeefySysLib/MemStream.cpp @@ -44,17 +44,19 @@ int MemStream::GetSize() return mSize; } -void MemStream::Read(void* ptr, int size) +int MemStream::Read(void* ptr, int size) { memcpy(ptr, mData + mPos, size); mPos += size; + return size; } -void Beefy::MemStream::Write(void* ptr, int size) +int Beefy::MemStream::Write(void* ptr, int size) { memcpy(mData + mPos, ptr, size); mPos += size; + return size; } int MemStream::GetPos() @@ -79,7 +81,7 @@ SafeMemStream::SafeMemStream(void* data, int size, bool freeMemory) : MemStream( mFailed = false; } -void SafeMemStream::Read(void* ptr, int size) +int SafeMemStream::Read(void* ptr, int size) { if (mPos + size > mSize) { @@ -91,6 +93,7 @@ void SafeMemStream::Read(void* ptr, int size) memcpy(ptr, mData + mPos, size); } mPos += size; + return size; } ////////////////////////////////////////////////////////////////////////// @@ -110,16 +113,18 @@ int DynMemStream::GetSize() return (int)mData.size(); } -void DynMemStream::Read(void* ptr, int size) +int DynMemStream::Read(void* ptr, int size) { memcpy(ptr, (uint8*)&mData.front() + mPos, size); mPos += size; + return size; } -void DynMemStream::Write(void* ptr, int size) +int DynMemStream::Write(void* ptr, int size) { mData.Insert(mPos, (uint8*)ptr, size); mPos += size; + return size; } void DynMemStream::Write(uint8 val) diff --git a/BeefySysLib/MemStream.h b/BeefySysLib/MemStream.h index 0a9d6512..682310bc 100644 --- a/BeefySysLib/MemStream.h +++ b/BeefySysLib/MemStream.h @@ -24,9 +24,9 @@ public: bool Eof() override; int GetSize() override; using DataStream::Read; - void Read(void* ptr, int size) override; + int Read(void* ptr, int size) override; using DataStream::Write; - void Write(void* ptr, int size) override; + int Write(void* ptr, int size) override; int GetPos() override; void Seek(int size) override; @@ -41,7 +41,7 @@ public: SafeMemStream(void* data, int size, bool freeMemory); using DataStream::Read; - void Read(void* ptr, int size) override; + int Read(void* ptr, int size) override; }; class DynMemStream : public DataStream @@ -56,9 +56,9 @@ public: bool Eof() override; int GetSize() override; using DataStream::Read; - void Read(void* ptr, int size) override; + int Read(void* ptr, int size) override; using DataStream::Write; - void Write(void* ptr, int size) override; + int Write(void* ptr, int size) override; void Write(uint8 val) override; int GetPos() override; diff --git a/BeefySysLib/gfx/FTFont.cpp b/BeefySysLib/gfx/FTFont.cpp index 4a14c9e2..d783d76c 100644 --- a/BeefySysLib/gfx/FTFont.cpp +++ b/BeefySysLib/gfx/FTFont.cpp @@ -136,24 +136,38 @@ bool FTFont::Load(const StringImpl& fileName, float pointSize) FTFontManager::Face* face = NULL; + String key = fileName; + void* memPtr = NULL; + int memLen = 0; + bool isMemory = ParseMemorySpan(fileName, memPtr, memLen, &key); + FTFontManager::Face** facePtr = NULL; - if (gFTFontManager.mFaces.TryAdd(fileName, NULL, &facePtr)) + if (gFTFontManager.mFaces.TryAdd(key, NULL, &facePtr)) { face = new FTFontManager::Face(); *facePtr = face; - face->mFileName = fileName; + face->mFileName = key; FT_Face ftFace = NULL; String useFileName = fileName; int faceIdx = 0; - int atPos = (int)useFileName.IndexOf('@'); + int atPos = (int)useFileName.IndexOf('@', 1); if (atPos != -1) { faceIdx = atoi(useFileName.c_str() + atPos + 1); useFileName.RemoveToEnd(atPos); } - auto error = FT_New_Face(gFTLibrary, useFileName.c_str(), faceIdx, &ftFace); + + if (isMemory) + { + FT_New_Memory_Face(gFTLibrary, (FT_Byte*)memPtr, memLen, faceIdx, &ftFace); + } + else + { + FT_New_Face(gFTLibrary, useFileName.c_str(), faceIdx, &ftFace); + } + face->mFTFace = ftFace; } else diff --git a/BeefySysLib/gfx/RenderDevice.cpp b/BeefySysLib/gfx/RenderDevice.cpp index 046a53ca..a33fe10b 100644 --- a/BeefySysLib/gfx/RenderDevice.cpp +++ b/BeefySysLib/gfx/RenderDevice.cpp @@ -6,6 +6,7 @@ #include "img/TGAData.h" #include "img/PNGData.h" #include "img/PVRData.h" +#include "img/BMPData.h" #include "img/BFIData.h" #include "img/JPEGData.h" #include "util/PerfTimer.h" @@ -33,6 +34,8 @@ RenderTarget::RenderTarget() mHasBeenDrawnTo = false; mHasBeenTargeted = false; mResizeNum = 0; + mWantsClear = true; + mResetClear = false; } RenderWindow::RenderWindow() @@ -142,6 +145,12 @@ Texture* RenderDevice::LoadTexture(const StringImpl& fileName, int flags) imageData = new JPEGData(); else if (ext == ".pvr") imageData = new PVRData(); + else if (ext == ".bmp") + { + BMPData* bmpData = new BMPData(); + bmpData->mHasTransFollowing = (flags & TextureFlag_HasTransFollowing) == 0;; + imageData = bmpData; + } else { return NULL; // Unknown format @@ -150,14 +159,12 @@ Texture* RenderDevice::LoadTexture(const StringImpl& fileName, int flags) if (!handled) { imageData->mWantsAlphaPremultiplied = (flags & TextureFlag_NoPremult) == 0; - if (fileName.StartsWith("@")) - { - int colon = (int)fileName.IndexOf(':'); - String addrStr = fileName.Substring(1, colon - 1); - String lenStr = fileName.Substring(colon + 1); - void* addr = (void*)(intptr)strtoll(addrStr.c_str(), NULL, 16); - int len = (int)strtol(lenStr.c_str(), NULL, 10); - if (!imageData->LoadFromMemory(addr, len)) + + void* memPtr = NULL; + int memLen = 0; + if (ParseMemorySpan(fileName, memPtr, memLen)) + { + if (!imageData->LoadFromMemory(memPtr, memLen)) { failed = true; delete imageData; diff --git a/BeefySysLib/gfx/RenderDevice.h b/BeefySysLib/gfx/RenderDevice.h index c189fbdb..6704b7d0 100644 --- a/BeefySysLib/gfx/RenderDevice.h +++ b/BeefySysLib/gfx/RenderDevice.h @@ -156,6 +156,7 @@ enum TextureFlag : int8 TextureFlag_Additive = 1, TextureFlag_NoPremult = 2, TextureFlag_AllowRead = 4, + TextureFlag_HasTransFollowing = 8 }; struct VertexDefData @@ -200,7 +201,7 @@ public: bool mClipped; bool mTexWrap; bool mWireframe; - Rect mClipRect; + RectF mClipRect; CullMode mCullMode; Topology3D mTopology; @@ -212,7 +213,7 @@ public: virtual void SetTexWrap(bool wrap) { mTexWrap = wrap; } virtual void SetWireframe(bool wireframe) { mWireframe = wireframe; } virtual void SetClipped(bool clipped) { mClipped = clipped; } - virtual void SetClipRect(const Rect& rect) { mClipRect = rect; } + virtual void SetClipRect(const RectF& rect) { mClipRect = rect; } virtual void SetWriteDepthBuffer(bool writeDepthBuffer) { mWriteDepthBuffer = writeDepthBuffer; } virtual void SetDepthFunc(DepthFunc depthFunc) { mDepthFunc = depthFunc; } virtual void SetTopology(Topology3D topology) { mTopology = topology; } diff --git a/BeefySysLib/gfx/RenderTarget.h b/BeefySysLib/gfx/RenderTarget.h index fb3435f6..ca2f31d4 100644 --- a/BeefySysLib/gfx/RenderTarget.h +++ b/BeefySysLib/gfx/RenderTarget.h @@ -12,6 +12,8 @@ public: int mResizeNum; bool mHasBeenTargeted; bool mHasBeenDrawnTo; + bool mWantsClear; + bool mResetClear; public: RenderTarget(); diff --git a/BeefySysLib/gfx/Texture.cpp b/BeefySysLib/gfx/Texture.cpp index 7b16a4e1..189c306c 100644 --- a/BeefySysLib/gfx/Texture.cpp +++ b/BeefySysLib/gfx/Texture.cpp @@ -1,6 +1,7 @@ #include "Texture.h" #include "util/AllocDebug.h" +#include "img/ImageData.h" USING_NS_BF; @@ -31,3 +32,50 @@ void TextureSegment::InitFromTexture(Texture* texture) mScaleX = (float) mTexture->mWidth; mScaleY = (float) mTexture->mHeight; } + +void TextureSegment::SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32* bits) +{ + int x1 = (int)(mU1 * mTexture->mWidth + 0.5f); + int y1 = (int)(mV1 * mTexture->mHeight + 0.5f); + mTexture->SetBits(destX + x1, destY + y1, destWidth, destHeight, srcPitch, bits); +} + +void TextureSegment::GetBits(int srcX, int srcY, int srcWidth, int srcHeight, int destPitch, uint32* bits) +{ + int x1 = (int)(mU1 * mTexture->mWidth + 0.5f); + int y1 = (int)(mV1 * mTexture->mHeight + 0.5f); + mTexture->GetBits(srcX + x1, srcY + y1, srcWidth, srcHeight, destPitch, bits); +} + +void TextureSegment::GetImageData(ImageData& imageData) +{ + int x1 = (int)(mU1 * mTexture->mWidth + 0.5f); + int x2 = (int)(mU2 * mTexture->mWidth + 0.5f); + int y1 = (int)(mV1 * mTexture->mHeight + 0.5f); + int y2 = (int)(mV2 * mTexture->mHeight + 0.5f); + imageData.CreateNew(x2 - x1, y2 - y1); + mTexture->GetBits(x1, y1, x2 - x1, y2 - y1, x2 - x1, imageData.mBits); +} + +void TextureSegment::GetImageData(ImageData& imageData, int destX, int destY) +{ + int x1 = (int)(mU1 * mTexture->mWidth + 0.5f); + int x2 = (int)(mU2 * mTexture->mWidth + 0.5f); + int y1 = (int)(mV1 * mTexture->mHeight + 0.5f); + int y2 = (int)(mV2 * mTexture->mHeight + 0.5f); + mTexture->GetBits(x1, y1, x2 - x1, y2 - y1, imageData.mWidth, imageData.mBits + destX + destY * imageData.mWidth); +} + +void TextureSegment::SetImageData(ImageData& imageData) +{ + SetBits(0, 0, imageData.mWidth, imageData.mHeight, imageData.mStride, imageData.mBits); +} + +RectF TextureSegment::GetRect() +{ + float x1 = mU1 * mTexture->mWidth; + float x2 = mU2 * mTexture->mWidth; + float y1 = mV1 * mTexture->mHeight; + float y2 = mV2 * mTexture->mHeight; + return RectF(x1, y1, x2 - x1, y2 - y1); +} diff --git a/BeefySysLib/gfx/Texture.h b/BeefySysLib/gfx/Texture.h index 1c7ead24..dd89db95 100644 --- a/BeefySysLib/gfx/Texture.h +++ b/BeefySysLib/gfx/Texture.h @@ -2,6 +2,7 @@ #include "Common.h" #include "RenderTarget.h" +#include "../util/Rect.h" NS_BF_BEGIN; @@ -38,6 +39,15 @@ public: public: void InitFromTexture(Texture* texture); + + virtual void SetBits(int destX, int destY, int destWidth, int destHeight, int srcPitch, uint32* bits); + virtual void GetBits(int srcX, int srcY, int srcWidth, int srcHeight, int destPitch, uint32* bits); + + void GetImageData(ImageData& imageData); + void GetImageData(ImageData& imageData, int destX, int destY); + void SetImageData(ImageData& imageData); + + RectF GetRect(); }; NS_BF_END; diff --git a/BeefySysLib/img/BMPData.cpp b/BeefySysLib/img/BMPData.cpp new file mode 100644 index 00000000..1019e9fc --- /dev/null +++ b/BeefySysLib/img/BMPData.cpp @@ -0,0 +1,531 @@ +#include "BMPData.h" +#include +#include +#include +#include + +USING_NS_BF; + +#pragma warning(disable:4996) + +int BMPData::Read(void* ptr, int elemSize, int elemCount) +{ + int maxReadCount = (mSrcDataLen - mReadPos) / elemSize; + if (elemCount > maxReadCount) + elemCount = maxReadCount; + memcpy(ptr, mSrcData + mReadPos, elemCount * elemSize); + mReadPos += elemCount * elemSize; + return elemCount; +} + +unsigned char BMPData::ReadC() +{ + if (mReadPos >= mSrcDataLen) + return 0; + return mSrcData[mReadPos++]; +} + +BMPData::BMPData() +{ + mHasTransFollowing = false; + mReadPos = 0; +} + +#define BITMAP_MAGIC_NUMBER 19778 + +typedef struct bmp_file_header_s bmp_file_header_t; +struct bmp_file_header_s +{ + /* int16_t magic_number; */ /* because of padding, we don't put it into the struct */ + int32_t size; + int32_t app_id; + int32_t offset; +}; + +typedef struct bmp_bitmap_info_header_s bmp_bitmap_info_header_t; +struct bmp_bitmap_info_header_s +{ + int32_t header_size; + int32_t width; + int32_t height; + int16_t num_planes; + int16_t bpp; + int32_t compression; + int32_t image_size; + int32_t horizontal_resolution; + int32_t vertical_resolution; + int32_t colors_used; + int32_t colors_important; +}; + +//typedef struct bmp_palette_element_s bmp_palette_element_t; + + +bool BMPData::ReadPixelsRLE8(bmp_palette_element_t* palette) +{ + unsigned char byte, index, i, * p; + unsigned char x_ofs, y_ofs; + char keepreading = 1; + int current_line = 0; + unsigned char* dest = (unsigned char*)mBits; + + p = dest; + while (keepreading) { /* in each loop, we read a pair of bytes */ + byte = ReadC(); /* read the first byte */ + if (byte) { + index = ReadC(); /* read the second byte */ + for (i = 0; i < byte; i++) { + *p = palette[index].red; p++; /* unindex pixels on the fly */ + *p = palette[index].green; p++; + *p = palette[index].blue; p++; + *p = 0xFF; p++; /* add alpha */ + } + } + else { + byte = ReadC(); /* read the second byte */ + switch (byte) + { + case 0: /* skip the end of the current line and go to the next line */ + current_line++; + p = dest + current_line * mWidth * 4; + break; + case 1: /* stop reading */ + keepreading = 0; + break; + case 2: /* skip y_ofs lines and x_ofs columns. This means that the ignored pixels will be + filled with black (Or maybe i didn't understand the spec ?). This has already be done : + before starting to load pixel data, we filled all the dest[] array with 0x00 by a memset() */ + x_ofs = ReadC(); + y_ofs = ReadC(); + current_line += y_ofs; + p += y_ofs * mWidth * 4 + x_ofs * 4; + break; + default: /* get the next n pixels, where n = byte */ + for (i = 0; i < byte; i++) { + index = ReadC(); + *p = palette[index].red; p++; /* unindex pixels on the fly */ + *p = palette[index].green; p++; + *p = palette[index].blue; p++; + *p = 0xFF; p++; /* add alpha */ + } + /* if n is not a multiple of 2, then skip one byte in the file, to respect int16_t alignment */ + if (byte % 2) mReadPos++; + break; + } + } + /* the place to which p is pointing is guided by the content of the file. A corrupted file could make p point to a wrong localization. + Hence, we must check that we don't point outside of the dest[] array. This may prevent an error of segmentation */ + if (p >= dest + mWidth * mHeight * 4) keepreading = 0; + } + + return true; +} + +bool BMPData::ReadPixelsRLE4(bmp_palette_element_t* palette) +{ + unsigned char byte1, byte2, index1, index2, * p; + unsigned char x_ofs, y_ofs; + unsigned char bitmask = 0x0F; /* bit mask : 00001111 */ + char keepreading = 1; + int current_line = 0; + int i; + + unsigned char* dest = (unsigned char*)mBits; + + p = dest; + while (keepreading) { + byte1 = ReadC(); + if (byte1) { /* encoded mode */ + byte2 = ReadC(); + index1 = byte2 >> 4; /* get the first 4 bits of byte2 */ + index2 = byte2 & bitmask; /* get the next 4 bits of byte2 */ + for (i = 0; i < (byte1 / 2); i++) { + *p = palette[index1].red; p++; + *p = palette[index1].green; p++; + *p = palette[index1].blue; p++; + *p = 0x0F; p++; + *p = palette[index2].red; p++; + *p = palette[index2].green; p++; + *p = palette[index2].blue; p++; + *p = 0x0F; p++; + } + if (byte1 % 2) { + *p = palette[index1].red; p++; + *p = palette[index1].green; p++; + *p = palette[index1].blue; p++; + *p = 0x0F; p++; + } + } + else { /* absolute mode */ + byte2 = ReadC(); + switch (byte2) + { + case 0: /* skip the end of the current line and go to the next line */ + current_line++; + p = dest + current_line * mWidth * 4; + break; + case 1: /* stop reading */ + keepreading = 0; + break; + case 2: /* skip y_ofs lines and x_ofs column */ + x_ofs = ReadC(); + y_ofs = ReadC(); + current_line += y_ofs; + p += y_ofs * mWidth * 4 + x_ofs * 4; + break; + default: /* get the next n pixels, where n = byte2 */ + for (i = 0; i < (byte2 / 2); i++) { + byte1 = ReadC(); + index1 = byte1 >> 4; + *p = palette[index1].red; p++; + *p = palette[index1].green; p++; + *p = palette[index1].blue; p++; + *p = 0x0F; p++; + index2 = byte1 & bitmask; + *p = palette[index2].red; p++; + *p = palette[index2].green; p++; + *p = palette[index2].blue; p++; + *p = 0x0F; p++; + } + if (byte2 % 2) { + byte1 = ReadC(); + index1 = byte1 >> 4; + *p = palette[index1].red; p++; + *p = palette[index1].green; p++; + *p = palette[index1].blue; p++; + *p = 0x0F; p++; + } + if (((byte2 + 1) / 2) % 2) /* int16_t alignment */ + mReadPos += 1; + break; + } + } + /* the place to which p is pointing is guided by the content of the file. A corrupted file could make p point to a wrong localization. + Hence, we must check that we don't point outside of the dest[] array. This may prevent an error of segmentation */ + if (p >= dest + mWidth * mHeight * 4) keepreading = 0; + } + + return true; +} + +bool BMPData::ReadPixels32() +{ + int i, j; + unsigned char px[4], * p; + + unsigned char* dest = (unsigned char*)mBits; + + for (i = 0; i < mHeight; i++) + { + p = dest + (mHeight - i - 1) * mWidth * 4; + for (j = 0; j < mWidth; j++) + { + Read(px, 4, 1); /* convert BGRX to RGBA */ + *p = px[2]; p++; // R + *p = px[1]; p++; + *p = px[0]; p++; + *p = px[3]; p++; + } + } + + return true; +} + +bool BMPData::ReadPixels24() +{ + int i, j; + unsigned char px[3], * p; + + unsigned char* dest = (unsigned char*)mBits; + + for (i = 0; i < mHeight; i++) + { + p = dest + (mHeight - i - 1) * mWidth * 4; + for (j = 0; j < mWidth; j++) + { + Read(px, 3, 1); + *p = px[2]; p++; /* convert BGR to RGBA */ + *p = px[1]; p++; + *p = px[0]; p++; + *p = 0xFF; p++; /* add alpha component */ + } + if (mWidth * 3 % 4 != 0) + mReadPos += 4 - (mWidth * 3 % 4); /* if the width is not a multiple of 4, skip the end of the line */ + } + + return true; +} + +/* Expected format : XBGR 0 11111 11111 11111 */ +bool BMPData::ReadPixels16() +{ + uint16_t pxl, r, g, b; + uint16_t bitmask = 0x1F; + unsigned char* p; + int i, j; + + unsigned char* dest = (unsigned char*)mBits; + + p = dest; + for (i = 0; i < mHeight; i++) + { + for (j = 0; j < mWidth; j++) + { + Read(&pxl, 2, 1); + b = (pxl >> 10) & bitmask; + g = (pxl >> 5) & bitmask; + r = pxl & bitmask; + *p = r * 8; p++; /* fix me */ + *p = g * 8; p++; + *p = b * 8; p++; + *p = 0xFF; p++; + } + if ((2 * mWidth) % 4 != 0) + mReadPos += 4 - ((2 * mWidth) % 4); + } + + return true; +} + +bool BMPData::ReadPixels8(bmp_palette_element_t* palette) +{ + int i, j; + unsigned char px, * p; + + unsigned char* dest = (unsigned char*)mBits; + + p = dest; + for (i = 0; i < mHeight; i++) { + for (j = 0; j < mWidth; j++) { + Read(&px, 1, 1); + *p = palette[px].red; p++; + *p = palette[px].green; p++; + *p = palette[px].blue; p++; + *p = 0xFF; p++; + } + if (mWidth % 4 != 0) + mReadPos += 4 - (mWidth % 4); + } + + return true; +} + +bool BMPData::ReadPixels4(bmp_palette_element_t* palette) +{ + int size = (mWidth + 1) / 2; /* byte alignment */ + unsigned char* row_stride = new unsigned char[size]; /* not C90 but convenient here */ + defer({ delete row_stride; }); + + unsigned char index, byte, * p; + unsigned char bitmask = 0x0F; /* bit mask : 00001111 */ + + unsigned char* dest = (unsigned char*)mBits; + + p = dest; + for (int i = 0; i < mHeight; i++) + { + Read(row_stride, size, 1); + for (int j = 0; j < mWidth; j++) + { + byte = row_stride[j / 2]; + index = (j % 2) ? bitmask & byte : byte >> 4; + *p = palette[index].red; p++; + *p = palette[index].green; p++; + *p = palette[index].blue; p++; + *p = 0xFF; p++; + } + if (size % 4 != 0) + mReadPos += 4 - (size % 4); + } + + return true; +} + +bool BMPData::ReadPixels1(bmp_palette_element_t* palette) +{ + int size = (mWidth + 7) / 8; /* byte alignment */ + unsigned char* row_stride = new unsigned char[size]; /* not C90 but convenient here */ + defer({ delete row_stride; }); + + unsigned char index, byte, * p; + unsigned char bitmask = 0x01; /* bit mask : 00000001 */ + int bit; + + unsigned char* dest = (unsigned char*)mBits; + + p = dest; + for (int i = 0; i < mHeight; i++) { + Read(row_stride, size, 1); + for (int j = 0; j < mWidth; j++) { + bit = (j % 8) + 1; + byte = row_stride[j / 8]; + index = byte >> (8 - bit); + index &= bitmask; + *p = palette[index].red; p++; + *p = palette[index].green; p++; + *p = palette[index].blue; p++; + *p = 0xFF; p++; + } + if (size % 4 != 0) + mReadPos += 4 - (size % 4); + } + + return true; +} + +bool BMPData::ReadData() +{ + int16_t magic_number; + bmp_file_header_t file_header; + bmp_bitmap_info_header_t info_header; + bmp_palette_element_t* palette = NULL; + + Read(&magic_number, 2, 1); + if (magic_number == BITMAP_MAGIC_NUMBER) + { + Read((void*)&file_header, 12, 1); + Read((void*)&info_header, 40, 1); + mReadPos = file_header.offset; + } + else + { + mReadPos = 0; + Read((void*)&info_header, 40, 1); + } + + /* info_header sanity checks */ + /* accepted headers : bitmapinfoheader, bitmapv4header, bitmapv5header */ + if (!(info_header.header_size == 40 || info_header.header_size == 108 || info_header.header_size == 124)) { + return false; + } + if (info_header.num_planes != 1) { + return false; + } + if (info_header.compression == 4 || info_header.compression == 5) { + return false; + } + if (info_header.height < 0) { + return false; + } + + /* load palette, if present */ + if (info_header.bpp <= 8) { + mReadPos = 14 + info_header.header_size; + if ((info_header.bpp == 1) && (info_header.colors_used == 0)) info_header.colors_used = 2; + if ((info_header.bpp == 4) && (info_header.colors_used == 0)) info_header.colors_used = 16; + if ((info_header.bpp == 8) && (info_header.colors_used == 0)) info_header.colors_used = 256; + palette = (bmp_palette_element_t*)malloc(info_header.colors_used * sizeof(bmp_palette_element_t)); + if (!palette) { + return false; + } + else + Read((void*)palette, sizeof(bmp_palette_element_t), info_header.colors_used); + } + + /* memory allocation */ + //buf = malloc(info_header.width * info_header.height * 4); + mWidth = info_header.width; + mHeight = info_header.height; + mBits = new uint32[mWidth * mHeight]; + + memset(mBits, 0x00, info_header.width * info_header.height * 4); + + /* load image data */ + switch (info_header.bpp) + { + case 32: + ReadPixels32(); + break; + case 24: + ReadPixels24(); + break; + case 16: + ReadPixels16(); + break; + case 8: + if (info_header.compression == 1) + ReadPixelsRLE8(palette); + else + ReadPixels8(palette); + break; + case 4: + if (info_header.compression == 2) + ReadPixelsRLE4(palette); + else + ReadPixels4(palette); + break; + case 1: + ReadPixels1(palette); + break; + } + + if (info_header.colors_used) free(palette); + + if (mHasTransFollowing) + { + mHeight /= 2; + auto newBits = new uint32[mWidth * mHeight]; + memcpy(newBits, mBits + mHeight * mWidth, mWidth * mHeight * 4); + delete mBits; + mBits = newBits; + } + + return true; +} + +bool BMPData::WriteToFile(const StringImpl& path) +{ + FILE* file; + int16_t magic_number; + bmp_file_header_t file_header; + bmp_bitmap_info_header_t info_header; + unsigned char sample[3], * p; + int i, j; + + file = fopen(path.c_str(), "wb"); + if (!file) + { + return false; + } + + magic_number = BITMAP_MAGIC_NUMBER; + + file_header.size = mWidth * mHeight * 4 + 54; + file_header.app_id = 0; + file_header.offset = 54; + + info_header.header_size = 40; + info_header.width = mWidth; + info_header.height = mHeight; + info_header.num_planes = 1; + info_header.bpp = 24; + info_header.compression = 0; + info_header.image_size = mWidth * mHeight * 4; + info_header.horizontal_resolution = 0; + info_header.vertical_resolution = 0; + info_header.colors_used = 0; + info_header.colors_important = 0; + + fwrite(&magic_number, sizeof(magic_number), 1, file); + fwrite(&file_header, sizeof(bmp_file_header_t), 1, file); + fwrite(&info_header, sizeof(bmp_bitmap_info_header_t), 1, file); + + p = (unsigned char*)mBits; + for (i = 0; i < mHeight; i++) + { + for (j = 0; j < mWidth; j++) + { + /* convert RGBA to BGR */ + sample[2] = *p; p++; + sample[1] = *p; p++; + sample[0] = *p; p++; + p++; + + fwrite(sample, 3, 1, file); + } + } + + fclose(file); + + return 0; +} diff --git a/BeefySysLib/img/BMPData.h b/BeefySysLib/img/BMPData.h new file mode 100644 index 00000000..23341652 --- /dev/null +++ b/BeefySysLib/img/BMPData.h @@ -0,0 +1,41 @@ +#pragma once + +#include "ImageData.h" + +NS_BF_BEGIN; + +struct bmp_palette_element_s +{ + unsigned char blue; + unsigned char green; + unsigned char red; + unsigned char reserved; /* alpha ? */ +}; +typedef struct bmp_palette_element_s bmp_palette_element_t; + +class BMPData : public ImageData +{ +public: + int mReadPos; + bool mHasTransFollowing; + + int Read(void* ptr, int elemSize, int elemCount); + unsigned char ReadC(); + + bool ReadPixelsRLE8(bmp_palette_element_t* palette); + bool ReadPixelsRLE4(bmp_palette_element_t* palette); + bool ReadPixels32(); + bool ReadPixels24(); + bool ReadPixels16(); + bool ReadPixels8(bmp_palette_element_t* palette); + bool ReadPixels4(bmp_palette_element_t* palette); + bool ReadPixels1(bmp_palette_element_t* palette); + +public: + BMPData(); + + bool ReadData(); + bool WriteToFile(const StringImpl& path); +}; + +NS_BF_END; diff --git a/BeefySysLib/img/ImageData.cpp b/BeefySysLib/img/ImageData.cpp index 137c3e57..b9e70676 100644 --- a/BeefySysLib/img/ImageData.cpp +++ b/BeefySysLib/img/ImageData.cpp @@ -16,6 +16,7 @@ ImageData::ImageData() mY = 0; mWidth = 0; mHeight = 0; + mStride = 0; mWantsAlphaPremultiplied = true; mAlphaPremultiplied = false; mIsAdditive = false; @@ -67,7 +68,7 @@ void ImageData::CreateNew(int x, int y, int width, int height, bool clear) void ImageData::CreateNew(int width, int height, bool clear) { - mWidth = width; + mWidth = mStride = width; mHeight = height; mBits = new uint32[mWidth*mHeight]; if (clear) @@ -89,7 +90,7 @@ void ImageData::CopyFrom(ImageData* img, int x, int y) { for (int x = destStartX; x < destEndX; x++) { - mBits[x + y * mWidth] = img->mBits[(x + srcXOfs) + (y + srcYOfs)*img->mWidth]; + mBits[x + y * mStride] = img->mBits[(x + srcXOfs) + (y + srcYOfs)*img->mStride]; } } } diff --git a/BeefySysLib/img/ImageData.h b/BeefySysLib/img/ImageData.h index 000555cf..d59997b3 100644 --- a/BeefySysLib/img/ImageData.h +++ b/BeefySysLib/img/ImageData.h @@ -20,6 +20,7 @@ public: int mY; int mWidth; int mHeight; + int mStride; void* mHWBits; int mHWBitsLength; int mHWBitsType; diff --git a/BeefySysLib/img/ImgEffects.cpp b/BeefySysLib/img/ImgEffects.cpp index c01dd534..f269d5db 100644 --- a/BeefySysLib/img/ImgEffects.cpp +++ b/BeefySysLib/img/ImgEffects.cpp @@ -241,6 +241,7 @@ ImageData* ImageEffects::GetDestImage(ImageData* usingImage) return mSwapImages[1]; ImageData* anImage = new ImageData(); + anImage->mStride = usingImage->mStride; anImage->mWidth = usingImage->mWidth; anImage->mHeight = usingImage->mHeight; anImage->mBits = new uint32[anImage->mWidth*anImage->mHeight]; diff --git a/BeefySysLib/platform/sdl/GLRenderDevice.cpp b/BeefySysLib/platform/sdl/GLRenderDevice.cpp index 29665763..28d3a558 100644 --- a/BeefySysLib/platform/sdl/GLRenderDevice.cpp +++ b/BeefySysLib/platform/sdl/GLRenderDevice.cpp @@ -737,9 +737,9 @@ void GLRenderDevice::PhysSetRenderState(RenderState* renderState) if (renderState->mClipped) { glEnable(GL_SCISSOR_TEST); - glScissor((GLsizei)renderState->mClipRect.mX, - mPhysRenderWindow->mHeight - (GLsizei)renderState->mClipRect.mY - (GLsizei)renderState->mClipRect.mHeight, - (GLsizei)renderState->mClipRect.mWidth, (GLsizei)renderState->mClipRect.mHeight); + glScissor((GLsizei)renderState->mClipRect.x, + mPhysRenderWindow->mHeight - (GLsizei)renderState->mClipRect.y - (GLsizei)renderState->mClipRect.height, + (GLsizei)renderState->mClipRect.width, (GLsizei)renderState->mClipRect.height); } else { diff --git a/BeefySysLib/platform/win/DXRenderDevice.cpp b/BeefySysLib/platform/win/DXRenderDevice.cpp index 4a3e1ecc..757debfd 100644 --- a/BeefySysLib/platform/win/DXRenderDevice.cpp +++ b/BeefySysLib/platform/win/DXRenderDevice.cpp @@ -5,6 +5,7 @@ #include "img/ImageData.h" #include "util/PerfTimer.h" #include "util/BeefPerf.h" +#include "Span.h" #include "FileStream.h" #include "DDS.h" @@ -225,12 +226,14 @@ void DXShader::ReleaseNative() mConstBuffer = NULL; } -extern "C" -typedef HRESULT(WINAPI* Func_D3DX10CompileFromFileW)(LPCWSTR pSrcFile, CONST D3D10_SHADER_MACRO* pDefines, LPD3D10INCLUDE pInclude, +extern "C" typedef HRESULT(WINAPI* Func_D3DX10CompileFromFileW)(LPCWSTR pSrcFile, CONST D3D10_SHADER_MACRO* pDefines, LPD3D10INCLUDE pInclude, LPCSTR pFunctionName, LPCSTR pProfile, UINT Flags1, UINT Flags2, ID3D10Blob** ppShader, ID3D10Blob** ppErrorMsgs); - static Func_D3DX10CompileFromFileW gFunc_D3DX10CompileFromFileW; +extern "C" typedef HRESULT(WINAPI* Func_D3DX10Compile)(void* srcData, size_t srcSize, char* sourceName, CONST D3D10_SHADER_MACRO* pDefines, LPD3D10INCLUDE pInclude, + LPCSTR pFunctionName, LPCSTR pProfile, UINT Flags1, UINT Flags2, ID3D10Blob** ppShader, ID3D10Blob** ppErrorMsgs); +static Func_D3DX10Compile gFunc_D3DX10Compile; + static bool LoadDXShader(const StringImpl& filePath, const StringImpl& entry, const StringImpl& profile, ID3D10Blob** outBuffer) { HRESULT hr; @@ -239,7 +242,7 @@ static bool LoadDXShader(const StringImpl& filePath, const StringImpl& entry, co bool useCache = false; auto srcDate = ::BfpFile_GetTime_LastWrite(filePath.c_str()); auto cacheDate = ::BfpFile_GetTime_LastWrite(outObj.c_str()); - if (cacheDate >= srcDate) + if ((cacheDate != 0) && (cacheDate >= srcDate)) useCache = true; if (!useCache) @@ -257,16 +260,37 @@ static bool LoadDXShader(const StringImpl& filePath, const StringImpl& entry, co if (!useCache) { - if (gFunc_D3DX10CompileFromFileW == NULL) - { - auto lib = LoadLibraryA("D3DCompiler_47.dll"); - if (lib != NULL) - gFunc_D3DX10CompileFromFileW = (Func_D3DX10CompileFromFileW)::GetProcAddress(lib, "D3DCompileFromFile"); - } + bool useCompile = true; + HRESULT dxResult; ID3D10Blob* errorMessage = NULL; - auto dxResult = gFunc_D3DX10CompileFromFileW(UTF8Decode(filePath).c_str(), NULL, NULL, entry.c_str(), profile.c_str(), - D3D10_SHADER_DEBUG | D3D10_SHADER_ENABLE_STRICTNESS, 0, outBuffer, &errorMessage); + if (useCompile) + { + if (gFunc_D3DX10Compile == NULL) + { + auto lib = LoadLibraryA("D3DCompiler_47.dll"); + if (lib != NULL) + gFunc_D3DX10Compile = (Func_D3DX10Compile)::GetProcAddress(lib, "D3DCompile"); + } + + int memSize = 0; + uint8* memPtr = LoadBinaryData(filePath, &memSize); + + dxResult = gFunc_D3DX10Compile(memPtr, memSize, "Shader", NULL, NULL, entry.c_str(), profile.c_str(), + D3D10_SHADER_DEBUG | D3D10_SHADER_ENABLE_STRICTNESS, 0, outBuffer, &errorMessage); + } + else + { + if (gFunc_D3DX10CompileFromFileW == NULL) + { + auto lib = LoadLibraryA("D3DCompiler_47.dll"); + if (lib != NULL) + gFunc_D3DX10CompileFromFileW = (Func_D3DX10CompileFromFileW)::GetProcAddress(lib, "D3DCompileFromFile"); + } + + dxResult = gFunc_D3DX10CompileFromFileW(UTF8Decode(filePath).c_str(), NULL, NULL, entry.c_str(), profile.c_str(), + D3D10_SHADER_DEBUG | D3D10_SHADER_ENABLE_STRICTNESS, 0, outBuffer, &errorMessage); + } if (DXFAILED(dxResult)) { @@ -310,6 +334,36 @@ static bool LoadDXShader(const StringImpl& filePath, const StringImpl& entry, co return true; } +static bool LoadDXShader(Span fileData, const StringImpl& entry, const StringImpl& profile, ID3D10Blob** outBuffer) +{ + HRESULT hr; + + if (gFunc_D3DX10Compile == NULL) + { + auto lib = LoadLibraryA("D3DCompiler_47.dll"); + if (lib != NULL) + gFunc_D3DX10Compile = (Func_D3DX10Compile)::GetProcAddress(lib, "D3DCompile"); + } + + ID3D10Blob* errorMessage = NULL; + auto dxResult = gFunc_D3DX10Compile(fileData.mVals, fileData.mSize, "ShaderSource", NULL, NULL, entry.c_str(), profile.c_str(), + D3D10_SHADER_DEBUG | D3D10_SHADER_ENABLE_STRICTNESS, 0, outBuffer, &errorMessage); + + if (DXFAILED(dxResult)) + { + if (errorMessage != NULL) + { + BF_FATAL(StrFormat("Vertex shader load failed: %s", (char*)errorMessage->GetBufferPointer()).c_str()); + errorMessage->Release(); + } + else + BF_FATAL("Shader load failed"); + return false; + } + + return true; +} + bool DXShader::Load() { //HRESULT hr; @@ -318,8 +372,35 @@ bool DXShader::Load() ID3D10Blob* vertexShaderBuffer = NULL; ID3D10Blob* pixelShaderBuffer = NULL; - LoadDXShader(mSrcPath + ".fx", "VS", "vs_4_0", &vertexShaderBuffer); - LoadDXShader(mSrcPath + ".fx", "PS", "ps_4_0", &pixelShaderBuffer); + void* memPtr = NULL; + int memSize = 0; + if (ParseMemorySpan(mSrcPath, memPtr, memSize)) + { + int crPos = (int)mSrcPath.IndexOf('\n'); + if (crPos != -1) + { + void* memPtr2 = NULL; + int memSize2 = 0; + if (ParseMemorySpan(mSrcPath.Substring(crPos + 1), memPtr2, memSize2)) + { + D3D10CreateBlob(memSize, &vertexShaderBuffer); + memcpy(vertexShaderBuffer->GetBufferPointer(), memPtr, memSize); + D3D10CreateBlob(memSize2, &pixelShaderBuffer); + memcpy(pixelShaderBuffer->GetBufferPointer(), memPtr2, memSize2); + } + } + else + { + Span span((uint8*)memPtr, memSize); + LoadDXShader(span, "VS", "vs_4_0", &vertexShaderBuffer); + LoadDXShader(span, "PS", "ps_4_0", &pixelShaderBuffer); + } + } + else + { + LoadDXShader(mSrcPath + ".fx", "VS", "vs_4_0", &vertexShaderBuffer); + LoadDXShader(mSrcPath + ".fx", "PS", "ps_4_0", &pixelShaderBuffer); + } defer( { @@ -567,7 +648,7 @@ void DXTexture::PhysSetAsTarget() mRenderDevice->mD3DDeviceContext->RSSetViewports(1, &viewPort); } - //if (!mHasBeenDrawnTo) + if (mWantsClear) { float bgColor[4] = {1, (rand() % 256) / 256.0f, 0.5, 1}; mRenderDevice->mD3DDeviceContext->ClearRenderTargetView(mD3DRenderTargetView, bgColor); @@ -576,6 +657,8 @@ void DXTexture::PhysSetAsTarget() //mRenderDevice->mD3DDevice->ClearRenderTargetView(mD3DRenderTargetView, D3DXVECTOR4(1, 0.5, 0.5, 1)); mHasBeenDrawnTo = true; + if (mResetClear) + mWantsClear = false; } } @@ -611,6 +694,9 @@ void DXTexture::SetBits(int destX, int destY, int destWidth, int destHeight, int void DXTexture::GetBits(int srcX, int srcY, int srcWidth, int srcHeight, int destPitch, uint32* bits) { + if ((srcWidth <= 0) || (srcHeight <= 0)) + return; + D3D11_TEXTURE2D_DESC texDesc; texDesc.ArraySize = 1; texDesc.BindFlags = 0; @@ -676,11 +762,11 @@ void DXDrawBatch::Render(RenderDevice* renderDevice, RenderWindow* renderWindow) return; if ((mRenderState->mClipped) && - ((mRenderState->mClipRect.mWidth == 0) || (mRenderState->mClipRect.mHeight == 0))) + ((mRenderState->mClipRect.width == 0) || (mRenderState->mClipRect.height == 0))) return; if (mRenderState->mClipped) - BF_ASSERT((mRenderState->mClipRect.mWidth > 0) && (mRenderState->mClipRect.mHeight > 0)); + BF_ASSERT((mRenderState->mClipRect.width > 0) && (mRenderState->mClipRect.height > 0)); DXRenderDevice* aRenderDevice = (DXRenderDevice*)renderDevice; /*if ((mDrawLayer->mRenderWindow != NULL) && (aRenderDevice->mPhysRenderWindow != mDrawLayer->mRenderWindow)) @@ -845,10 +931,10 @@ void DXRenderDevice::PhysSetRenderState(RenderState* renderState) if (renderState->mClipped) { D3D11_RECT rects[1]; - rects[0].left = (int)renderState->mClipRect.mX; - rects[0].right = (int) (renderState->mClipRect.mX + renderState->mClipRect.mWidth); - rects[0].top = (int) renderState->mClipRect.mY; - rects[0].bottom = (int) (renderState->mClipRect.mY + renderState->mClipRect.mHeight); + rects[0].left = (int)renderState->mClipRect.x; + rects[0].right = (int) (renderState->mClipRect.x + renderState->mClipRect.width); + rects[0].top = (int) renderState->mClipRect.y; + rects[0].bottom = (int) (renderState->mClipRect.y + renderState->mClipRect.height); mD3DDeviceContext->RSSetScissorRects(1, rects); } setRasterizerState = true; @@ -1297,9 +1383,9 @@ void DXRenderState::SetTexWrap(bool wrap) InvalidateRasterizerState(); } -void DXRenderState::SetClipRect(const Rect& rect) +void DXRenderState::SetClipRect(const RectF& rect) { - BF_ASSERT((rect.mWidth >= 0) && (rect.mHeight >= 0)); + BF_ASSERT((rect.width >= 0) && (rect.height >= 0)); mClipRect = rect; InvalidateRasterizerState(); } @@ -2106,8 +2192,12 @@ Texture* DXRenderDevice::LoadTexture(const StringImpl& fileName, int flags) return aTexture; } + String pathEx = fileName; + if ((flags & TextureFlag_Additive) != 0) + pathEx += ":add"; + DXTexture* aTexture = NULL; - if (mTextureMap.TryGetValue(fileName, &aTexture)) + if ((!fileName.StartsWith('@')) && (mTextureMap.TryGetValue(pathEx, &aTexture))) { aTexture->AddRef(); return aTexture; @@ -2251,7 +2341,7 @@ Texture* DXRenderDevice::LoadTexture(const StringImpl& fileName, int flags) aTexture = (DXTexture*)RenderDevice::LoadTexture(fileName, flags); if (aTexture != NULL) { - aTexture->mPath = fileName; + aTexture->mPath = pathEx; mTextureMap[aTexture->mPath] = aTexture; } diff --git a/BeefySysLib/platform/win/DXRenderDevice.h b/BeefySysLib/platform/win/DXRenderDevice.h index 2dea4d9a..940615b6 100644 --- a/BeefySysLib/platform/win/DXRenderDevice.h +++ b/BeefySysLib/platform/win/DXRenderDevice.h @@ -219,7 +219,7 @@ public: virtual void SetClipped(bool clipped); virtual void SetTexWrap(bool clipped); - virtual void SetClipRect(const Rect& rect); + virtual void SetClipRect(const RectF& rect); virtual void SetWriteDepthBuffer(bool writeDepthBuffer); virtual void SetDepthFunc(DepthFunc depthFunc); }; diff --git a/BeefySysLib/platform/win/Platform.cpp b/BeefySysLib/platform/win/Platform.cpp index cd11f266..fe64d26b 100644 --- a/BeefySysLib/platform/win/Platform.cpp +++ b/BeefySysLib/platform/win/Platform.cpp @@ -3016,6 +3016,9 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK if ((createFlags & BfpFileCreateFlag_NoBuffering) != 0) desiredAccess |= FILE_FLAG_NO_BUFFERING; + if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0) + attributes |= FILE_FLAG_OVERLAPPED; + HANDLE handle = ::CreateFileW(wPath.c_str(), desiredAccess, shareMode, NULL, creationDisposition, attributes, NULL); if (handle == INVALID_HANDLE_VALUE) { @@ -3043,6 +3046,13 @@ BFP_EXPORT BfpFile* BFP_CALLTYPE BfpFile_Create(const char* path, BfpFileCreateK BfpFile* bfpFile = new BfpFile(); bfpFile->mHandle = handle; + + if ((createFlags & BfpFileCreateFlag_AllowTimeouts) != 0) + bfpFile->mAsyncData = new BfpAsyncData(); + + if ((createFlags & BfpFileCreateFlag_Pipe) != 0) + bfpFile->mIsPipe = true; + return bfpFile; } diff --git a/BeefySysLib/platform/win/WinBFApp.cpp b/BeefySysLib/platform/win/WinBFApp.cpp index f95ae381..1a648c81 100644 --- a/BeefySysLib/platform/win/WinBFApp.cpp +++ b/BeefySysLib/platform/win/WinBFApp.cpp @@ -204,7 +204,7 @@ WinBFWindow::WinBFWindow(BFWindow* parent, const StringImpl& title, int x, int y if (mMenu != NULL) { - WinBFMenu* placeholderMenu = (WinBFMenu*) AddMenuItem(mMenu, 0, ": Placeholder Menu Item :", NULL, NULL, false, -1, false); + WinBFMenu* placeholderMenu = (WinBFMenu*) AddMenuItem(mMenu, 0, "", NULL, NULL, false, -1, false); placeholderMenu->mIsPlaceholder = true; } diff --git a/BeefySysLib/util/Array.h b/BeefySysLib/util/Array.h index 6781e333..38b6c988 100644 --- a/BeefySysLib/util/Array.h +++ b/BeefySysLib/util/Array.h @@ -4,16 +4,16 @@ NS_BF_BEGIN; -template class AllocatorCLib { public: + template T* allocate(intptr count) { return (T*)malloc(sizeof(T) * count); } - void deallocate(T* ptr) + void deallocate(void* ptr) { free(ptr); } @@ -27,9 +27,14 @@ public: { free(ptr); } + + bool deallocateAll() + { + return false; + } }; -template > +template class ArrayBase : public TAlloc { public: @@ -445,7 +450,7 @@ protected: void SetBufferSize(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -625,7 +630,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -653,7 +658,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -682,7 +687,7 @@ public: { intptr newSize = BF_MAX(this->mSize + count, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -764,7 +769,7 @@ public: protected: void SetBufferSize(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -928,7 +933,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -956,7 +961,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -984,7 +989,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -1102,7 +1107,7 @@ public: } }; -template > +template class Array : public ArrayImpl::value> { public: diff --git a/BeefySysLib/util/BSpline.cpp b/BeefySysLib/util/BSpline.cpp index 5ca93c66..acf1a477 100644 --- a/BeefySysLib/util/BSpline.cpp +++ b/BeefySysLib/util/BSpline.cpp @@ -34,9 +34,9 @@ void BSpline2D::AddPt(float x, float y) delete mUVals; mUVals = NULL; - Point2D pt; - pt.mX = x; - pt.mY = y; + PointF pt; + pt.x = x; + pt.y = y; mInputPoints.push_back(pt); } @@ -44,9 +44,8 @@ void BSpline2D::Calculate() { int n = (int) mInputPoints.size(); int t = 1; - Point2D* control = &mInputPoints[0]; + PointF* control = &mInputPoints[0]; - mUVals=new int[n+t+1]; compute_intervals(mUVals, n, t); @@ -112,7 +111,7 @@ void BSpline2D::Evaluate(float pct, float* x, float* y) int t = (int)mInputPoints.size() - 3; // ???? t = 1; - Point2D* control = &mInputPoints[0]; + PointF* control = &mInputPoints[0]; // initialize the variables that will hold our outputted point @@ -122,8 +121,8 @@ void BSpline2D::Evaluate(float pct, float* x, float* y) for (k=0; k<=n; k++) { temp = blend(t,t,mUVals,pct); // same blend is used for each dimension coordinate - oX = oX + (control[k]).mX * temp; - oY = oY + (control[k]).mY * temp; + oX = oX + (control[k]).x * temp; + oY = oY + (control[k]).y * temp; } *x = oX; diff --git a/BeefySysLib/util/BSpline.h b/BeefySysLib/util/BSpline.h index eef0a657..5b39611f 100644 --- a/BeefySysLib/util/BSpline.h +++ b/BeefySysLib/util/BSpline.h @@ -8,7 +8,7 @@ NS_BF_BEGIN; class BSpline2D { public: - Array mInputPoints; + Array mInputPoints; int* mUVals; public: diff --git a/BeefySysLib/util/BeefPerf.h b/BeefySysLib/util/BeefPerf.h index b08f42fc..d6730b7d 100644 --- a/BeefySysLib/util/BeefPerf.h +++ b/BeefySysLib/util/BeefPerf.h @@ -320,7 +320,7 @@ public: #define BP_EXPORT BF_EXPORT #define BP_CALLTYPE BF_CALLTYPE #else -#define BP_EXPORT +#define BP_EXPORT extern "C" #define BP_CALLTYPE #endif diff --git a/BeefySysLib/util/BumpAllocator.h b/BeefySysLib/util/BumpAllocator.h index 5b2037b3..9a136fda 100644 --- a/BeefySysLib/util/BumpAllocator.h +++ b/BeefySysLib/util/BumpAllocator.h @@ -255,23 +255,24 @@ class BumpAllocator : public BumpAllocatorT<0x2000> }; -template -class AllocatorBump +template +class AllocatorBumpT { public: BumpAllocatorT* mAlloc; - AllocatorBump() + AllocatorBumpT() { mAlloc = NULL; } + template T* allocate(intptr count) { return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T)); } - void deallocate(T* ptr) + void deallocate(void* ptr) { } @@ -286,26 +287,9 @@ public: } }; -template -class RawAllocatorBump +class AllocatorBump : public AllocatorBumpT<0x2000> { -public: - BumpAllocatorT* mAlloc; - RawAllocatorBump() - { - mAlloc = NULL; - } - - void* rawAllocate(intptr size) - { - return mAlloc->AllocBytes(size, 16); - } - - void rawDeallocate(void* ptr) - { - - } }; NS_BF_END diff --git a/BeefySysLib/util/CatmullRom.cpp b/BeefySysLib/util/CatmullRom.cpp index 93e0a060..8552fc86 100644 --- a/BeefySysLib/util/CatmullRom.cpp +++ b/BeefySysLib/util/CatmullRom.cpp @@ -2,7 +2,7 @@ USING_NS_BF; -Point2D Beefy::CatmullRomEvaluate(Point2D &p0, Point2D &p1, Point2D &p2, Point2D &p3, float tension, float t) +PointF Beefy::CatmullRomEvaluate(PointF &p0, PointF &p1, PointF &p2, PointF &p3, float tension, float t) { float t2 = t * t; float t3 = t2 * t; @@ -17,8 +17,8 @@ Point2D Beefy::CatmullRomEvaluate(Point2D &p0, Point2D &p1, Point2D &p2, Point2D float b3 = s * (t3 - 2 * t2 + t) + (-2 * t3 + 3 * t2); // s(t3 - 2 t2 + t)P3 + (-2 t3 + 3 t2)P3 float b4 = s * (t3 - t2); // s(t3 - t2)P4 - float x = (p0.mX*b1 + p1.mX*b2 + p2.mX*b3 + p3.mX*b4); - float y = (p0.mY*b1 + p1.mY*b2 + p2.mY*b3 + p3.mY*b4); + float x = (p0.x*b1 + p1.x*b2 + p2.x*b3 + p3.x*b4); + float y = (p0.y*b1 + p1.y*b2 + p2.y*b3 + p3.y*b4); - return Point2D(x,y); + return PointF(x,y); } diff --git a/BeefySysLib/util/CatmullRom.h b/BeefySysLib/util/CatmullRom.h index c0842c2d..2625b7f3 100644 --- a/BeefySysLib/util/CatmullRom.h +++ b/BeefySysLib/util/CatmullRom.h @@ -5,6 +5,6 @@ NS_BF_BEGIN; -Point2D CatmullRomEvaluate(Point2D &p0, Point2D &p1, Point2D &p2, Point2D &p3, float tension, float t); +PointF CatmullRomEvaluate(PointF &p0, PointF &p1, PointF &p2, PointF &p3, float tension, float t); NS_BF_END; diff --git a/BeefySysLib/util/CubicFuncSpline.cpp b/BeefySysLib/util/CubicFuncSpline.cpp index f0fb969b..e28622c7 100644 --- a/BeefySysLib/util/CubicFuncSpline.cpp +++ b/BeefySysLib/util/CubicFuncSpline.cpp @@ -25,7 +25,7 @@ void CubicFuncSpline::AddPt(float x, float y) intpoly = NULL; slopes = NULL; - mInputPoints.push_back(Point2D(x, y)); + mInputPoints.push_back(PointF(x, y)); } int CubicFuncSpline::GetLength() @@ -42,7 +42,7 @@ void CubicFuncSpline::Lagrange() for( i = 0; i < nPts; i++ ) { lagpoly[i+0*nPts] = 1.0; - float fac = mInputPoints[i].mY; + float fac = mInputPoints[i].y; j = 0; for( k = 0; k < nPts; k++ ) { @@ -50,10 +50,10 @@ void CubicFuncSpline::Lagrange() continue; lagpoly[i+(j+1)*nPts] = lagpoly[i+j*nPts]; for( jj = j; jj > 0; jj-- ) - lagpoly[i+jj*nPts] = lagpoly[i+(jj-1)*nPts] - lagpoly[i+jj*nPts]*mInputPoints[k].mX; - lagpoly[i+0*nPts] *= -mInputPoints[k].mX; + lagpoly[i+jj*nPts] = lagpoly[i+(jj-1)*nPts] - lagpoly[i+jj*nPts]*mInputPoints[k].x; + lagpoly[i+0*nPts] *= -mInputPoints[k].x; j++; - fac /= ( mInputPoints[i].mX - mInputPoints[k].mX ); + fac /= ( mInputPoints[i].x - mInputPoints[k].x ); } for( j = 0; j < nPts; j++ ) lagpoly[i+j*nPts] *= fac; @@ -75,9 +75,9 @@ void CubicFuncSpline::ComputeSplineSlopes() for( i = 0; i < n; i++ ) { - h[i] = mInputPoints[i+1].mX - mInputPoints[i].mX; + h[i] = mInputPoints[i+1].x - mInputPoints[i].x; hinv[i] = 1.0f / h[i]; - g[i] = 3 * ( mInputPoints[i+1].mY-mInputPoints[i].mY ) * hinv[i] * hinv[i]; + g[i] = 3 * ( mInputPoints[i+1].y-mInputPoints[i].y ) * hinv[i] * hinv[i]; } a[0] = 2 * hinv[0]; b[0] = g[0]; @@ -119,7 +119,7 @@ float CubicFuncSpline::Evaluate(float x) Calculate(); int idx = (int) 0; - while ((idx < (int) mInputPoints.size()) && (x > mInputPoints[idx].mX)) + while ((idx < (int) mInputPoints.size()) && (x > mInputPoints[idx].x)) idx++; if ((idx == mInputPoints.size()) || (idx == 0)) @@ -128,13 +128,13 @@ float CubicFuncSpline::Evaluate(float x) if (idx == mInputPoints.size()) idx--; float s1 = slopes[idx]; - return mInputPoints[idx].mY + (x - mInputPoints[idx].mX) * s1; + return mInputPoints[idx].y + (x - mInputPoints[idx].x) * s1; } - float x0 = mInputPoints[idx-1].mX; - float x1 = mInputPoints[idx].mX; - float y0 = mInputPoints[idx-1].mY; - float y1 = mInputPoints[idx].mY; + float x0 = mInputPoints[idx-1].x; + float x1 = mInputPoints[idx].x; + float y0 = mInputPoints[idx-1].y; + float y1 = mInputPoints[idx].y; float s0 = slopes[idx-1]; float s1 = slopes[idx]; diff --git a/BeefySysLib/util/CubicFuncSpline.h b/BeefySysLib/util/CubicFuncSpline.h index 24b378ca..93083ea2 100644 --- a/BeefySysLib/util/CubicFuncSpline.h +++ b/BeefySysLib/util/CubicFuncSpline.h @@ -9,7 +9,7 @@ NS_BF_BEGIN; class CubicFuncSpline { public: - std::vector mInputPoints; + std::vector mInputPoints; float* lagpoly; float* intpoly; float* slopes; diff --git a/BeefySysLib/util/CubicSpline.cpp b/BeefySysLib/util/CubicSpline.cpp index 0b07c6d3..2e4dbcfe 100644 --- a/BeefySysLib/util/CubicSpline.cpp +++ b/BeefySysLib/util/CubicSpline.cpp @@ -72,7 +72,7 @@ void CubicSpline2D::AddPt(float x, float y) delete mYCubicArray; mYCubicArray = NULL; - mInputPoints.push_back(Point2D(x, y)); + mInputPoints.push_back(PointF(x, y)); } int CubicSpline2D::GetLength() @@ -86,15 +86,15 @@ void CubicSpline2D::Calculate() std::vector yVals; for (int i = 0; i < (int) mInputPoints.size(); i++) { - xVals.push_back(mInputPoints[i].mX); - yVals.push_back(mInputPoints[i].mY); + xVals.push_back(mInputPoints[i].x); + yVals.push_back(mInputPoints[i].y); } mXCubicArray = SolveCubic(xVals); mYCubicArray = SolveCubic(yVals); } -Point2D CubicSpline2D::Evaluate(float t) +PointF CubicSpline2D::Evaluate(float t) { if (mXCubicArray == NULL) Calculate(); @@ -105,5 +105,5 @@ Point2D CubicSpline2D::Evaluate(float t) if (idx >= (int) mInputPoints.size() - 1) return mInputPoints[mInputPoints.size() - 1]; - return Point2D(mXCubicArray[idx].Evaluate(frac), mYCubicArray[idx].Evaluate(frac)); + return PointF(mXCubicArray[idx].Evaluate(frac), mYCubicArray[idx].Evaluate(frac)); } \ No newline at end of file diff --git a/BeefySysLib/util/CubicSpline.h b/BeefySysLib/util/CubicSpline.h index 087cde0a..8905c151 100644 --- a/BeefySysLib/util/CubicSpline.h +++ b/BeefySysLib/util/CubicSpline.h @@ -38,7 +38,7 @@ public: class CubicSpline2D { public: - std::vector mInputPoints; + std::vector mInputPoints; CubicVal* mXCubicArray; CubicVal* mYCubicArray; @@ -53,7 +53,7 @@ public: int GetLength(); void Calculate(); - Point2D Evaluate(float t); + PointF Evaluate(float t); }; NS_BF_END; diff --git a/BeefySysLib/util/Deque.h b/BeefySysLib/util/Deque.h index 096bb671..130b0c09 100644 --- a/BeefySysLib/util/Deque.h +++ b/BeefySysLib/util/Deque.h @@ -8,7 +8,7 @@ NS_BF_BEGIN; #define DEQUE_IDX(i) (this->mVals[((i) + this->mOffset) % this->mAllocSize]) #define DEQUE_IDX_ON(arr, i) ((arr).mVals[((i) + (arr).mOffset) % (arr).mAllocSize]) -template > +template class DequeBase : public TAlloc { public: @@ -370,7 +370,7 @@ protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -707,7 +707,7 @@ class DequeImpl : public DequeBase protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -1043,7 +1043,7 @@ public: } }; -template > +template class Deque : public DequeImpl::value> { public: diff --git a/BeefySysLib/util/Dictionary.h b/BeefySysLib/util/Dictionary.h index 11912fd7..d9f5341e 100644 --- a/BeefySysLib/util/Dictionary.h +++ b/BeefySysLib/util/Dictionary.h @@ -9,7 +9,7 @@ NS_BF_BEGIN; #ifdef NEW_DICTIONAY -template > +template class Dictionary : public TAlloc { public: diff --git a/BeefySysLib/util/HashSet.h b/BeefySysLib/util/HashSet.h index 725c2876..d72ff78d 100644 --- a/BeefySysLib/util/HashSet.h +++ b/BeefySysLib/util/HashSet.h @@ -5,7 +5,7 @@ NS_BF_BEGIN; -template > +template class HashSet : public TAlloc { public: diff --git a/BeefySysLib/util/MTRand.cpp b/BeefySysLib/util/MTRand.cpp new file mode 100644 index 00000000..125183f6 --- /dev/null +++ b/BeefySysLib/util/MTRand.cpp @@ -0,0 +1,157 @@ +/* +Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright +notice, this list of conditions and the following disclaimer in the +documentation and/or other materials provided with the distribution. + +3. The names of its contributors may not be used to endorse or promote +products derived from this software without specific prior written +permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +Any feedback is very welcome. +http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html +email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ + +#include "MTRand.h" + +USING_NS_BF; + +/* Period parameters */ +#define MTRAND_M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + +/* Tempering parameters */ +#define TEMPERING_MASK_B 0x9d2c5680 +#define TEMPERING_MASK_C 0xefc60000 +#define TEMPERING_SHIFT_U(y) (y >> 11) +#define TEMPERING_SHIFT_S(y) (y << 7) +#define TEMPERING_SHIFT_T(y) (y << 15) +#define TEMPERING_SHIFT_L(y) (y >> 18) + + +MTRand::MTRand(const std::string& theSerialData) +{ + SRand(theSerialData); + mti = MTRAND_N + 1; /* mti==MTRAND_N+1 means mt[MTRAND_N] is not initialized */ +} + +MTRand::MTRand(unsigned long seed) +{ + SRand(seed); +} + +MTRand::MTRand() +{ + SRand(4357); +} + +void MTRand::SRand(const std::string& theSerialData) +{ + if (theSerialData.size() == MTRAND_N * 4) + { + memcpy(mt, theSerialData.c_str(), MTRAND_N * 4); + } + else + SRand(4357); +} + +void MTRand::SRand(unsigned long seed) +{ + if (seed == 0) + seed = 4357; + + /* setting initial seeds to mt[MTRAND_N] using */ + /* the generator Line 25 of Table 1 in */ + /* [KNUTH 1981, The Art of Computer Programming */ + /* Vol. 2 (2nd Ed.), pp102] */ + mt[0] = seed & 0xffffffffUL; + for (mti = 1; mti < MTRAND_N; mti++) + { + mt[mti] = + (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti); + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + mt[mti] &= 0xffffffffUL; + /* for >32 bit machines */ + } +} + +unsigned long MTRand::Next() +{ + unsigned long y; + static unsigned long mag01[2] = { 0x0, MATRIX_A }; + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (mti >= MTRAND_N) { /* generate MTRAND_N words at one time */ + int kk; + + for (kk = 0; kk < MTRAND_N - MTRAND_M; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + MTRAND_M] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + for (; kk < MTRAND_N - 1; kk++) { + y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK); + mt[kk] = mt[kk + (MTRAND_M - MTRAND_N)] ^ (y >> 1) ^ mag01[y & 0x1UL]; + } + y = (mt[MTRAND_N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK); + mt[MTRAND_N - 1] = mt[MTRAND_M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL]; + + mti = 0; + } + + y = mt[mti++]; + y ^= TEMPERING_SHIFT_U(y); + y ^= TEMPERING_SHIFT_S(y) & TEMPERING_MASK_B; + y ^= TEMPERING_SHIFT_T(y) & TEMPERING_MASK_C; + y ^= TEMPERING_SHIFT_L(y); + + y &= 0x7FFFFFFF; + + /*char aStr[256]; + sprintf(aStr, "Rand=%d\r\n", y); + OutputDebugString(aStr);*/ + + return y; +} + +unsigned long MTRand::Next(unsigned long range) +{ + return Next() % range; +} + +String MTRand::Serialize() +{ + String aString; + + aString.Append(' ', MTRAND_N * 4); + memcpy((char*)aString.c_str(), mt, MTRAND_N * 4); + + return aString; +} \ No newline at end of file diff --git a/BeefySysLib/util/MTRand.h b/BeefySysLib/util/MTRand.h new file mode 100644 index 00000000..9b42512a --- /dev/null +++ b/BeefySysLib/util/MTRand.h @@ -0,0 +1,28 @@ +#pragma once + +#include "Common.h" + +NS_BF_BEGIN; + +#define MTRAND_N 624 + +class MTRand +{ + unsigned long mt[MTRAND_N]; /* the array for the state vector */ + int mti; + +public: + MTRand(const std::string& theSerialData); + MTRand(unsigned long seed); + MTRand(); + + void SRand(const std::string& theSerialData); + void SRand(unsigned long seed); + unsigned long Next(); + unsigned long Next(unsigned long range); + float Next(float range); + + String Serialize(); +}; + +NS_BF_END \ No newline at end of file diff --git a/BeefySysLib/util/MultiDictionary.h b/BeefySysLib/util/MultiDictionary.h new file mode 100644 index 00000000..2d197225 --- /dev/null +++ b/BeefySysLib/util/MultiDictionary.h @@ -0,0 +1,580 @@ +#pragma once + +#include "../Common.h" +#include "Array.h" + +NS_BF_BEGIN; + +struct MultiDictionaryFuncs : AllocatorCLib +{ + template + size_t GetHash(const T& value) + { + return BeefHash()(value); + } + + template + bool Matches(const T& lhs, const T& rhs) + { + return lhs == rhs; + } +}; + +template +class MultiDictionary : public TFuncs +{ +public: + struct Entry + { + TKey mKey; + TValue mValue; + int mNext; + int mHashCode; + }; + + struct EntryRef + { + public: + MultiDictionary* mSet; + int mIndex; + + public: + EntryRef() + { + mSet = NULL; + mIndex = -1; + } + + EntryRef(MultiDictionary* set, int index) + { + mSet = set; + mIndex = index; + } + + Entry* operator*() + { + return &this->mSet->mEntries[this->mIndex]; + } + + Entry* operator->() + { + return &this->mSet->mEntries[this->mIndex]; + } + + operator bool() const + { + return this->mIndex != -1; + } + }; + + struct Iterator + { + public: + MultiDictionary* mDict; + int mCurEntry; + int mCurBucket; + + protected: + Iterator() + { + this->mDict = NULL; + this->mCurBucket = 0; + this->mCurEntry = -1; + } + + public: + Iterator(MultiDictionary* dict) + { + this->mDict = dict; + this->mCurBucket = 0; + this->mCurEntry = -1; + } + + Iterator& operator++() + { + if (this->mCurEntry != -1) + { + this->mCurEntry = this->mDict->mEntries[this->mCurEntry].mNext; + if (this->mCurEntry != -1) + return *this; + this->mCurBucket++; + } + + if (mDict->mHashHeads == NULL) + { + this->mCurBucket = this->mDict->mHashSize; + return *this; // At end + } + + while (this->mCurBucket < mDict->mHashSize) + { + this->mCurEntry = this->mDict->mHashHeads[mCurBucket]; + if (this->mCurEntry != -1) + return *this; + this->mCurBucket++; + } + + return *this; // At end + } + + TKey& GetKey() + { + return this->mDict->mEntries[this->mCurEntry].mKey; + } + + TValue& GetValue() + { + return this->mDict->mEntries[this->mCurEntry].mValue; + } + + bool operator!=(const Iterator& itr) const + { + return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket)); + } + + operator bool() const + { + return this->mCurEntry != -1; + } + + void MoveToNextHashMatch() + { + int wantHash = this->mDict->mEntries[this->mCurEntry].mHashCode; + do + { + this->mCurEntry = this->mDict->mEntries[this->mCurEntry].mNext; + } while ((this->mCurEntry != -1) && (this->mDict->mEntries[this->mCurEntry].mHashCode != wantHash)); + if (this->mCurEntry == -1) + this->mCurBucket = mDict->mHashSize; + } + }; + + struct MatchIterator : public Iterator + { + public: + TKey mKey; + + public: + MatchIterator(const Iterator& iterator) + { + this->mDict = iterator.mDict; + this->mCurBucket = iterator.mCurBucket; + this->mCurEntry = iterator.mCurEntry; + } + + MatchIterator(MultiDictionary* dict, const TKey& key) : Iterator(dict) + { + mKey = key; + } + + MatchIterator& operator++() + { + while (true) + { + MoveToNextHashMatch(); + if (*this == mDict->end()) + break; + if (mKey == GetKey()) + break; + } + return *this; + } + }; + +protected: + + int GetPrimeish(int min) + { + // This is a minimal effort to help address-aligned dataa + return (min | 1); + } + + int ExpandSize(int oldSize) + { + int newSize = 2 * oldSize; + + // Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow. + // Note that this check works even when mAllocSize overflowed thanks to the (uint) cast + /*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize) + { + Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength"); + return MaxPrimeArrayLength; + }*/ + + return GetPrimeish(newSize); + } + + void ResizeEntries() + { + ResizeEntries(ExpandSize(mCount)); + } + + void ResizeEntries(int newSize) + { + BF_ASSERT(newSize >= mAllocSize); + Entry* newEntries = TFuncs::allocate(newSize); + + for (int i = 0; i < mCount; i++) + { + auto& newEntry = newEntries[i]; + auto& oldEntry = mEntries[i]; + newEntry.mHashCode = oldEntry.mHashCode; + newEntry.mNext = oldEntry.mNext; + new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey)); + new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue)); + } + for (int i = mCount; i < newSize; i++) + { + newEntries[i].mHashCode = -1; + } + + TFuncs::deallocate(mEntries); + + mEntries = newEntries; + mAllocSize = (int)newSize; + } + + void FreeIdx(int entryIdx) + { + this->mEntries[entryIdx].mNext = this->mFreeList; + this->mFreeList = entryIdx; + this->mFreeCount++; + } + + int AllocEntry() + { + int index; + if (this->mFreeCount > 0) + { + index = this->mFreeList; + this->mFreeList = this->mEntries[index].mNext; + this->mFreeCount--; + } + else + { + if (this->mCount == this->mAllocSize) + ResizeEntries(); + index = mCount; + this->mCount++; + } + return index; + } + +public: + int* mHashHeads; + int mAllocSize; + Entry* mEntries; + int mFreeList; + int mFreeCount; + + static const int cDefaultHashSize = 17; + int mHashSize; + int mCount; + + MultiDictionary() + { + this->mHashHeads = NULL; + this->mHashSize = cDefaultHashSize; + this->mEntries = NULL; + this->mAllocSize = 0; + this->mCount = 0; + this->mFreeList = -1; + this->mFreeCount = 0; + } + + ~MultiDictionary() + { + this->Clear(); + } + + void EnsureFreeCount(int wantFreeCount) + { + int freeCount = mFreeCount + (mAllocSize - mCount); + if (freeCount >= wantFreeCount) + return; + ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount)); + } + + int GetCount() const + { + return mCount - mFreeCount; + } + + int size() const + { + return mCount - mFreeCount; + } + + EntryRef AddRaw(int hash) + { + if (this->mHashHeads == NULL) + { + this->mHashHeads = TFuncs::allocate(mHashSize); + memset(this->mHashHeads, -1, sizeof(int) * mHashSize); + } + + int index = AllocEntry(); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int headEntry = this->mHashHeads[hashIdx]; + + Entry* newEntry = &mEntries[index]; + newEntry->mValue = T(); + newEntry->mNext = headEntry; + newEntry->mHashCode = hash; + + mHashHeads[hashIdx] = index; + + return EntryRef(this, index); + } + + void Add(TKey key, TValue value) + { + if (this->mHashHeads == NULL) + { + this->mHashHeads = TFuncs::allocate(mHashSize); + memset(this->mHashHeads, -1, sizeof(int) * mHashSize); + } + + int index = AllocEntry(); + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int headEntry = this->mHashHeads[hashIdx]; + + Entry* newEntry = &mEntries[index]; + newEntry->mKey = key; + newEntry->mValue = value; + newEntry->mNext = headEntry; + newEntry->mHashCode = hash; + + mHashHeads[hashIdx] = index; + } + + void AddAfter(TKey key, TValue value, Entry* afterEntry) + { + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + BF_ASSERT(hash == afterEntry->mHashCode); + + int index = AllocEntry(); + Entry* newEntry = &mEntries[index]; + newEntry->mKey = key; + newEntry->mValue = value; + newEntry->mNext = afterEntry->mNext; + newEntry->mHashCode = hash; + + afterEntry->mNext = index; + } + + void Rehash(int newHashSize) + { + auto newHashHeads = TFuncs::allocate(newHashSize); + memset(newHashHeads, -1, sizeof(int) * newHashSize); + + if (mHashHeads != NULL) + { + SizedArray entryList; + for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++) + { + int checkEntryIdx = mHashHeads[hashIdx]; + if (checkEntryIdx != -1) + { + // We want to keep elements with equal hashes in their insert order so we need to + // iterate through the linked list in reverse + entryList.Clear(); + + while (checkEntryIdx != -1) + { + entryList.Add(checkEntryIdx); + checkEntryIdx = mEntries[checkEntryIdx].mNext; + } + + for (int i = (int)entryList.mSize - 1; i >= 0; i--) + { + int checkEntryIdx = entryList[i]; + auto checkEntry = &mEntries[checkEntryIdx]; + int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize; + checkEntry->mNext = newHashHeads[newHashIdx]; + newHashHeads[newHashIdx] = checkEntryIdx; + } + } + } + + TFuncs::deallocate(mHashHeads); + } + mHashHeads = newHashHeads; + mHashSize = newHashSize; + } + + void CheckRehash() + { + // Make the lookup load reasonable + if (mHashSize < mCount) + { + this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1); + } + } + + template + bool TryGet(const TKey& key, TKey* outKey, TValue* outValue) + { + if (mHashHeads == NULL) + return false; + + this->CheckRehash(); + + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int checkEntryIdx = this->mHashHeads[hashIdx]; + while (checkEntryIdx != -1) + { + Entry* checkEntry = &mEntries[checkEntryIdx]; + if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey))) + { + if (outKey != NULL) + *outKey = checkEntry->mKey; + if (outValue != NULL) + *outValue = checkEntry->mValue; + return true; + } + checkEntryIdx = checkEntry->mNext; + } + return false; + } + + template + MatchIterator TryGet(const TKey& key) + { + if (mHashHeads == NULL) + return end(); + + this->CheckRehash(); + + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + int checkEntryIdx = this->mHashHeads[hashIdx]; + while (checkEntryIdx != -1) + { + auto checkEntry = &this->mEntries[checkEntryIdx]; + if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey))) + { + MatchIterator itr(this, key); + itr.mCurEntry = checkEntryIdx; + itr.mCurBucket = hashIdx; + return itr; + } + checkEntryIdx = checkEntry->mNext; + } + + return end(); + } + + template + bool Contains(const TKey& key) + { + return TryGet(key) != end(); + } + + template + int GetCount(const TKey& key) + { + int count = 0; + for (auto itr = TryGet(key); itr != end(); ++itr) + count++; + return count; + } + + template + bool Remove(const TKey& key) + { + if (mHashHeads == NULL) + return false; + + this->CheckRehash(); + + int hash = TFuncs::GetHash(key); + int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize; + + int* srcCheckEntryPtr = &this->mHashHeads[hashIdx]; + int checkEntryIdx = *srcCheckEntryPtr; + while (checkEntryIdx != -1) + { + auto checkEntry = &mEntries[checkEntryIdx]; + if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey))) + { + *srcCheckEntryPtr = checkEntry->mNext; + FreeIdx(checkEntryIdx); + return true; + } + srcCheckEntryPtr = &checkEntry->mNext; + checkEntryIdx = checkEntry->mNext; + } + return false; + } + + Iterator Erase(const Iterator& itr) + { + Iterator next = itr; + ++next; + + bool found = false; + + auto entryIdx = itr.mCurEntry; + auto entry = &mEntries[entryIdx]; + int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize; + + int* srcCheckEntryPtr = &this->mHashHeads[hashIdx]; + int checkEntryIdx = *srcCheckEntryPtr; + while (checkEntryIdx != -1) + { + auto checkEntry = &mEntries[checkEntryIdx]; + if (checkEntryIdx == itr.mCurEntry) + { + *srcCheckEntryPtr = checkEntry->mNext; + found = true; + } + srcCheckEntryPtr = &checkEntry->mNext; + checkEntryIdx = checkEntry->mNext; + } + + BF_ASSERT(found); + FreeIdx(entryIdx); + + return next; + } + + void Clear() + { + if (!TFuncs::deallocateAll()) + { + auto itr = begin(); + auto endItr = end(); + while (itr != endItr) + { + auto entry = itr.mCurEntry; + ++itr; + FreeIdx(entry); + } + TFuncs::deallocate(this->mHashHeads); + TFuncs::deallocate(this->mEntries); + } + + this->mHashSize = cDefaultHashSize; + this->mHashHeads = NULL; + this->mEntries = NULL; + this->mCount = 0; + } + + Iterator begin() + { + return ++Iterator(this); + } + + Iterator end() + { + Iterator itr(this); + itr.mCurBucket = this->mHashSize; + return itr; + } +}; + +NS_BF_END; \ No newline at end of file diff --git a/BeefySysLib/util/MultiHashSet.h b/BeefySysLib/util/MultiHashSet.h index 79b04b11..3cf904bb 100644 --- a/BeefySysLib/util/MultiHashSet.h +++ b/BeefySysLib/util/MultiHashSet.h @@ -30,7 +30,7 @@ struct MultiHashSetFuncs } }; -template > +template class MultiHashSet : public TFuncs { public: @@ -146,6 +146,8 @@ public: this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext; } while ((this->mCurEntry != -1) && (this->mSet->mEntries[this->mCurEntry].mHashCode != wantHash)); + if (this->mCurEntry == -1) + this->mCurBucket = mSet->mHashSize; } }; @@ -180,7 +182,7 @@ protected: void ResizeEntries(int newSize) { BF_ASSERT(newSize >= mAllocSize); - Entry* newEntries = (Entry*)TFuncs::Allocate(sizeof(Entry) * newSize, alignof(Entry)); + Entry* newEntries = TFuncs::template allocate(newSize); for (int i = 0; i < mCount; i++) { @@ -195,7 +197,7 @@ protected: newEntries[i].mHashCode = -1; } - TFuncs::Deallocate(mEntries); + TFuncs::deallocate(mEntries); mEntries = newEntries; mAllocSize = (int)newSize; @@ -276,7 +278,7 @@ public: { if (this->mHashHeads == NULL) { - this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int)); + this->mHashHeads = TFuncs::template allocate(mHashSize); memset(this->mHashHeads, -1, sizeof(int) * mHashSize); } @@ -298,7 +300,7 @@ public: { if (this->mHashHeads == NULL) { - this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int)); + this->mHashHeads = TFuncs::template allocate(mHashSize); memset(this->mHashHeads, -1, sizeof(int) * mHashSize); } @@ -332,7 +334,7 @@ public: void Rehash(int newHashSize) { - auto newHashHeads = (int*)TFuncs::Allocate(sizeof(int) * newHashSize, alignof(int)); + auto newHashHeads = TFuncs::template allocate(newHashSize); memset(newHashHeads, -1, sizeof(int) * newHashSize); if (mHashHeads != NULL) @@ -364,7 +366,7 @@ public: } } - TFuncs::Deallocate(mHashHeads); + TFuncs::deallocate(mHashHeads); } mHashHeads = newHashHeads; mHashSize = newHashSize; @@ -492,7 +494,7 @@ public: void Clear() { - if (!TFuncs::DeallocateAll()) + if (!TFuncs::deallocateAll()) { auto itr = begin(); auto endItr = end(); @@ -502,8 +504,8 @@ public: ++itr; FreeIdx(entry); } - TFuncs::Deallocate(this->mHashHeads); - TFuncs::Deallocate(this->mEntries); + TFuncs::deallocate(this->mHashHeads); + TFuncs::deallocate(this->mEntries); } this->mHashSize = cDefaultHashSize; diff --git a/BeefySysLib/util/Point.h b/BeefySysLib/util/Point.h index 06528339..816a7d32 100644 --- a/BeefySysLib/util/Point.h +++ b/BeefySysLib/util/Point.h @@ -4,18 +4,33 @@ NS_BF_BEGIN; -class Point2D +template +class Point { public: - float mX; - float mY; + T x; + T y; public: - Point2D(float x = 0, float y = 0) + Point(T x = 0, T y = 0) { - mX = x; - mY = y; + this->x = x; + this->y = y; + } + + Point operator+(Point rhs) + { + return Point(x + rhs.x, y + rhs.y); + } + + Point operator-(Point rhs) + { + return Point(x - rhs.x, y - rhs.y); } }; +typedef Point PointD; +typedef Point PointF; +typedef Point PointI32; + NS_BF_END; diff --git a/BeefySysLib/util/PolySpline.cpp b/BeefySysLib/util/PolySpline.cpp index 8c862cc9..f879bf1c 100644 --- a/BeefySysLib/util/PolySpline.cpp +++ b/BeefySysLib/util/PolySpline.cpp @@ -19,7 +19,7 @@ void PolySpline2D::AddPt(float x, float y) - mInputPoints.push_back(Point2D(x, y)); + mInputPoints.push_back(PointF(x, y)); } int PolySpline2D::GetLength() @@ -34,11 +34,11 @@ void PolySpline2D::Calculate() mCoefs = new float[n]; for (int j=0; j mInputPoints; + std::vector mInputPoints; public: diff --git a/BeefySysLib/util/Rect.h b/BeefySysLib/util/Rect.h index 814ba284..4b38d47a 100644 --- a/BeefySysLib/util/Rect.h +++ b/BeefySysLib/util/Rect.h @@ -1,29 +1,130 @@ #pragma once #include "Common.h" +#include "Point.h" NS_BF_BEGIN; +template class Rect { public: - float mX; - float mY; - float mWidth; - float mHeight; + T x; + T y; + T width; + T height; public: Rect() { - mX = 0; - mY = 0; - mWidth = 0; - mHeight = 0; + x = 0; + y = 0; + width = 0; + height = 0; + } + + Rect(T x, T y, T width, T height) + { + this->x = x; + this->y = y; + this->width = width; + this->height = height; + } + + bool operator==(const Rect& r2) + { + return (x == r2.x) && (y == r2.y) && (width == r2.width) && (height == r2.height); } bool operator!=(const Rect& r2) { - return (mX != r2.mX) || (mY != r2.mY) || (mWidth != r2.mWidth) || (mHeight != r2.mHeight); + return (x != r2.x) || (y != r2.y) || (width != r2.width) || (height != r2.height); + } + + bool Contains(T x, T y) + { + return (x >= this->x) && (y >= this->y) && (x < this->x + width) && (y < this->y + height); + } + + bool Contains(Point pt) + { + return (pt.x >= this->x) && (y >= this->y) && (x < this->x + width) && (y < this->y + height); + } + + T GetRight() + { + return x + width; + } + + T GetBottom() + { + return y + height; + } + + Rect Intersection(Rect rect) + { + T x1 = Max(x, rect.x); + T x2 = Min(x + width, rect.x + rect.width); + T y1 = Max(y, rect.y); + T y2 = Min(y + height, rect.y + rect.height); + if (((x2 - x1) < 0) || ((y2 - y1) < 0)) + return Rect(); + else + return Rect(x1, y1, x2 - x1, y2 - y1); + } + + bool Intersects(Rect rect) + { + T x1 = BF_MAX(x, rect.x); + T x2 = BF_MIN(x + width, rect.x + rect.width); + T y1 = BF_MAX(y, rect.y); + T y2 = BF_MIN(y + height, rect.y + rect.height); + if (((x2 - x1) <= 0) || ((y2 - y1) <= 0)) + return false; + else + return true; + } + + Rect Union(Rect rect) + { + T x1 = Min(x, rect.x); + T x2 = Max(x + width, rect.x + rect.width); + T y1 = Min(y, rect.y); + T y2 = Max(y + height, rect.y + rect.height); + return Rect(x1, y1, x2 - x1, y2 - y1); + } + + void Include(Point pt) + { + T left = x; + T top = y; + T right = x + width; + T bottom = y + height; + x = BF_MIN(pt.x, left); + y = BF_MIN(pt.y, top); + width = BF_MAX(pt.x, right) - x; + height = BF_MAX(pt.y, bottom) - y; + } + + void Inflate(T x, T y) + { + this->x -= x; + this->width += x + x; + this->y -= y; + this->height += y + y; + } +}; + +typedef Rect RectD; +typedef Rect RectF; +typedef Rect RectI32; + +template <> +struct BeefHash +{ + size_t operator()(RectI32 val) + { + return (size_t)val.x * 4790557 + (size_t)val.y * 6578863 + (size_t)val.width * 6273881 + (size_t)val.height * 9501077; } }; diff --git a/BeefySysLib/util/SizedArray.h b/BeefySysLib/util/SizedArray.h index 51071e59..1a6215be 100644 --- a/BeefySysLib/util/SizedArray.h +++ b/BeefySysLib/util/SizedArray.h @@ -6,7 +6,7 @@ NS_BF_BEGIN; -template > +template class SizedArrayBase : protected TAlloc { public: @@ -378,7 +378,7 @@ protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -533,7 +533,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -562,7 +562,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -638,7 +638,7 @@ public: protected: void Grow(intptr newSize) { - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (this->mSize > 0) @@ -761,7 +761,7 @@ public: { intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1; - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -790,7 +790,7 @@ public: { intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1); - T* newVals = TAlloc::allocate(newSize); + T* newVals = TAlloc::template allocate(newSize); if (this->mVals != NULL) { if (idx > 0) // Copy left of idx @@ -854,14 +854,14 @@ public: } }; -template > +template class SizedArrayImpl : public SizedArrayBaseT::value> { public: typedef SizedArrayBaseT::value> _Base; }; -template > +template class SizedArray : public SizedArrayImpl { public: diff --git a/BeefySysLib/util/String.cpp b/BeefySysLib/util/String.cpp index b718bf16..7eee2859 100644 --- a/BeefySysLib/util/String.cpp +++ b/BeefySysLib/util/String.cpp @@ -654,7 +654,7 @@ void StringImpl::ReplaceLargerHelper(const StringView& find, const StringView& r intptr moveOffset = replace.mLength - find.mLength; - for (intptr startIdx = 0; startIdx < mLength - find.mLength; startIdx++) + for (intptr startIdx = 0; startIdx <= mLength - find.mLength; startIdx++) { if (EqualsHelper(GetPtr() + startIdx, find.mPtr, find.mLength)) { @@ -720,7 +720,7 @@ void StringImpl::Replace(const StringView& find, const StringView & replace) intptr inIdx = 0; intptr outIdx = 0; - while (inIdx < mLength - find.mLength) + while (inIdx <= mLength - find.mLength) { if (EqualsHelper(ptr + inIdx, findPtr, find.mLength)) { diff --git a/BeefySysLib/util/String.h b/BeefySysLib/util/String.h index 4804d7c3..d5945303 100644 --- a/BeefySysLib/util/String.h +++ b/BeefySysLib/util/String.h @@ -551,10 +551,12 @@ protected: if ((mAllocSizeAndFlags & AttrFlags) == StrPtrFlag) { // It's a reference - char* newPtr = AllocPtr(this->mLength); - memcpy(newPtr, this->mPtr, this->mLength + 1); + int allocSize = (int)BF_MAX(GetAllocSize(), this->mLength + 1); + char* newPtr = AllocPtr(allocSize); + memcpy(newPtr, this->mPtr, this->mLength); + newPtr[this->mLength] = 0; this->mPtr = newPtr; - mAllocSizeAndFlags = this->mLength | DynAllocFlag | StrPtrFlag; + mAllocSizeAndFlags = allocSize | DynAllocFlag | StrPtrFlag; } } diff --git a/Debugger32/Debugger32.vcxproj b/Debugger32/Debugger32.vcxproj index b69779d5..cf4c0544 100644 --- a/Debugger32/Debugger32.vcxproj +++ b/Debugger32/Debugger32.vcxproj @@ -147,7 +147,7 @@ Level3 Disabled BF_DBG_32;WIN32;_DEBUG;_WINDOWS;_USRDLL;IDEHELPER_EXPORTS;BFSYSLIB_DYNAMIC;%(PreprocessorDefinitions) - ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include + ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include false -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) false @@ -189,7 +189,7 @@ true true BF_DBG_32;WIN32;NDEBUG;_WINDOWS;_USRDLL;IDEHELPER_EXPORTS;BFSYSLIB_DYNAMIC;%(PreprocessorDefinitions) - ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include + ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include MultiThreaded false true diff --git a/Debugger64/Debugger64.vcxproj b/Debugger64/Debugger64.vcxproj index 93d39f2b..406a08de 100644 --- a/Debugger64/Debugger64.vcxproj +++ b/Debugger64/Debugger64.vcxproj @@ -147,7 +147,7 @@ Level3 Disabled BF_DBG_64;WIN32;_DEBUG;_WINDOWS;_USRDLL;IDEHELPER_EXPORTS;BFSYSLIB_DYNAMIC;%(PreprocessorDefinitions) - ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include + ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include false -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) false @@ -189,7 +189,7 @@ true true BF_DBG_64;WIN32;NDEBUG;_WINDOWS;_USRDLL;IDEHELPER_EXPORTS;BFSYSLIB_DYNAMIC;%(PreprocessorDefinitions) - ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include + ../;../IDEHelper;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include MultiThreaded false true diff --git a/IDE/BeefProj.toml b/IDE/BeefProj.toml index 31a748ca..3ae2269e 100644 --- a/IDE/BeefProj.toml +++ b/IDE/BeefProj.toml @@ -7,13 +7,13 @@ TargetType = "BeefGUIApplication" StartupObject = "IDE.Program" [Platform.Windows] -IconFile = "$(WorkspaceDir)/Resources/Beef.ico" -ManifestFile = "$(WorkspaceDir)/Resources/IDE.manifest" +IconFile = "$(ProjectDir)/Resources/Beef.ico" +ManifestFile = "$(ProjectDir)/Resources/IDE.manifest" Description = "Beef IDE" Company = "BeefyTech LLC" Product = "Beef IDE" Copyright = "Copyright 2019 BeefyTech" -FileVersion = "0.43.5" +FileVersion = "0.43.6" ProductVersion = "0000000000000000" [Configs.Debug.Win32] @@ -21,11 +21,11 @@ TargetName = "" OtherLinkFlags = "" [Configs.Debug.Win64] -TargetDirectory = "$(WorkspaceDir)/dist" +TargetDirectory = "$(ProjectDir)/dist" TargetName = "BeefIDE_d" OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib" -DebugCommandArguments = "-proddir=\"$(WorkspaceDir)\\..\\BeefBuild\"" -DebugWorkingDirectory = "$(WorkspaceDir)\\.." +DebugCommandArguments = "-proddir=\"$(ProjectDir)\\..\\BeefBuild\"" +DebugWorkingDirectory = "$(ProjectDir)\\.." EnvironmentVars = ["_NO_DEBUG_HEAP=1"] PreprocessorMacros = ["DEBUG,HASGIT"] @@ -34,12 +34,12 @@ TargetName = "" OtherLinkFlags = "" [Configs.Release.Win64] -TargetDirectory = "$(WorkspaceDir)/dist" +TargetDirectory = "$(ProjectDir)/dist" TargetName = "BeefIDE" OtherLinkFlags = "Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib Beef042RT64.lib IDEHelper64.lib BeefySysLib64.lib" -DebugCommandArguments = "-proddir=C:\\Beef\\IDE\\Tests\\Test1 -test=scripts\\Enums.txt -testNoExit" +DebugCommandArguments = "-proddir=C:\\Beef\\IDE\\Tests\\Test1 -test=scripts\\Breakpoints.txt" DebugWorkingDirectory = "$(ProjectDir)\\dist" -EnvironmentVars = ["_NO_DEBUG_HEAP=1"] +EnvironmentVars = ["z_NO_DEBUG_HEAP=1"] PreprocessorMacros = ["RELEASE,HASGIT"] [Configs.Debug2.Win32] @@ -47,16 +47,16 @@ TargetName = "" OtherLinkFlags = "" [Configs.Debug2.Win64] -TargetDirectory = "$(WorkspaceDir)/dist" +TargetDirectory = "$(ProjectDir)/dist" TargetName = "BeefIDE_d2" OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib BeefySysLib64_d.lib wsock32.lib" BeefLibType = "DynamicDebug" -DebugCommandArguments = "-proddir=C:\\proj\\BeefTest" +DebugCommandArguments = "-workspace=C:\\proj\\BeefTest" DebugWorkingDirectory = "c:\\Beef\\IDE\\Tests\\EmptyTest" EnvironmentVars = ["_NO_DEBUG_HEAP=1"] [Configs.Paranoid.Win64] -TargetDirectory = "$(WorkspaceDir)/dist" +TargetDirectory = "$(ProjectDir)/dist" TargetName = "BeefIDE_p" OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib BeefySysLib64_d.lib wsock32.lib" @@ -70,7 +70,7 @@ BeefLibType = "Static" BeefLibType = "Static" [Configs.Debug3.Win64] -TargetDirectory = "$(WorkspaceDir)/dist" +TargetDirectory = "$(ProjectDir)/dist" TargetName = "BeefIDE_d3" OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib BeefySysLib64_d.lib wsock32.lib" BeefLibType = "DynamicDebug" diff --git a/IDE/Tests/CompileFail001/src/Cases.bf b/IDE/Tests/CompileFail001/src/Cases.bf index 4104590b..aecb3a69 100644 --- a/IDE/Tests/CompileFail001/src/Cases.bf +++ b/IDE/Tests/CompileFail001/src/Cases.bf @@ -10,15 +10,18 @@ namespace IDETest { Result iResult = .Err; - if ((iResult case .Ok(var val0)) || (true)) //FAIL + if ((iResult case .Ok(var val0)) && (iResult case .Ok(var val1))) { - + val0 = 0; + val1 = 0; } + val0 = 0; + val1 = 0; //FAIL - int val1; - if ((true) || (iResult case .Ok(out val1))) + int val2; + if ((true) || (iResult case .Ok(out val2))) { - int a = val1; //FAIL + int a = val2; //FAIL } } diff --git a/IDE/Tests/CompileFail001/src/Generics.bf b/IDE/Tests/CompileFail001/src/Generics.bf index fdc7fa11..4deee28d 100644 --- a/IDE/Tests/CompileFail001/src/Generics.bf +++ b/IDE/Tests/CompileFail001/src/Generics.bf @@ -150,6 +150,8 @@ namespace IDETest TestExt.InnerB c; TestExt.InnerC d; TestExt.InnerC e; //FAIL + + List badArgType; //FAIL } } } diff --git a/IDE/Tests/Test1/scripts/HotSwap_Virtual02.txt b/IDE/Tests/Test1/scripts/HotSwap_Virtual02.txt new file mode 100644 index 00000000..5a3364f1 --- /dev/null +++ b/IDE/Tests/Test1/scripts/HotSwap_Virtual02.txt @@ -0,0 +1,13 @@ +ShowFile("src/HotSwap_Virtuals02.bf") +GotoText("//Test_Start") +ToggleBreakpoint() +RunWithCompiling() +StepOver() + +ToggleCommentAt("ClassA_GetVal01") +Compile() +StepOver() + +ToggleCommentAt("ClassB_GetVal04") +Compile() +StepOver() \ No newline at end of file diff --git a/IDE/Tests/Test1/src/HotSwap_Virtuals02.bf b/IDE/Tests/Test1/src/HotSwap_Virtuals02.bf new file mode 100644 index 00000000..6c778196 --- /dev/null +++ b/IDE/Tests/Test1/src/HotSwap_Virtuals02.bf @@ -0,0 +1,67 @@ +using System; +#pragma warning disable 168 + +namespace IDETest; + + + +class Virtuals02 +{ + class ClassA + { + public virtual int GetVal01() => 100; + public + /*ClassA_GetVal01 + virtual + */ + int GetVal02() => 200; + public virtual int GetVal03() => 300; + } + + class ClassB : ClassA + { + public override int GetVal03() + { + return 301; + } + public virtual int GetVal04() => 400; + public + /*ClassB_GetVal04 + virtual + */ + int GetVal05() => 500; + } + + static void TestFuncs() + { + ClassA ca = scope .(); + ClassB cb = scope .(); + int val; + + void** funcs = (.)(void*)(cb.[Friend]mClassVData & ~0xFF); + int valA1 = ca.GetVal01(); + + int valA2 = ca.GetVal02(); + int valB1 = cb.GetVal01(); + int valB2 = cb.GetVal02(); + int valB3 = cb.GetVal03(); + int valB4 = cb.GetVal04(); + int valB5 = cb.GetVal05(); + + Runtime.Assert(valA1 == 100); + Runtime.Assert(valA2 == 200); + Runtime.Assert(valB1 == 100); + Runtime.Assert(valB2 == 200); + Runtime.Assert(valB3 == 301); + Runtime.Assert(valB4 == 400); + Runtime.Assert(valB5 == 500); + } + + public static void Test() + { + //Test_Start + TestFuncs(); + TestFuncs(); + TestFuncs(); + } +} \ No newline at end of file diff --git a/IDE/Tests/Test1/src/Program.bf b/IDE/Tests/Test1/src/Program.bf index e17d8dd2..f0c8cff0 100644 --- a/IDE/Tests/Test1/src/Program.bf +++ b/IDE/Tests/Test1/src/Program.bf @@ -33,6 +33,7 @@ namespace IDETest Unions.Test(); UsingFields.Test(); Virtuals.Test(); + Virtuals02.Test(); Bug001.Test(); Bug002.Test(); diff --git a/IDE/dist/BeefDbgVis.toml b/IDE/dist/BeefDbgVis.toml index 94b42a37..be17730d 100644 --- a/IDE/dist/BeefDbgVis.toml +++ b/IDE/dist/BeefDbgVis.toml @@ -567,6 +567,30 @@ Value = "*($T1*)&mKey" Name = "[Value]" Value = "*($T2*)&mValue" +[[Type]] +Name = "Beefy::MultiDictionary<*, *, *>" +DisplayString = "{{ count={mCount - mFreeCount} }}" +[[Type.Expand.Item]] +Name = "[Count]" +Value = "mCount - mFreeCount" +[Type.Expand.DictionaryItems] +Size = "mCount - mFreeCount" +Buckets = "mHashHeads" +Entries = "mEntries" +Key = "mKey" +Value = "mValue" +Next = "mNext" + +[[Type]] +Name = "Beefy::MultiDictionary<*, *, *>::Entry" +DisplayString = "{{[{mKey}, {mValue}]}}" +[[Type.Expand.Item]] +Name = "[Key]" +Value = "mKey" +[[Type.Expand.Item]] +Name = "[Value]" +Value = "mValue" + [[Type]] Name = "Beefy::HashSet<*, *>" DisplayString = "{{ count={mCount - mFreeCount} }}" diff --git a/IDE/dist/IDE_CreateStable.bat b/IDE/dist/IDE_CreateStable.bat index 2754811f..03bc43db 100644 --- a/IDE/dist/IDE_CreateStable.bat +++ b/IDE/dist/IDE_CreateStable.bat @@ -8,6 +8,9 @@ IF %ERRORLEVEL% NEQ 0 GOTO FAILED @ECHO ---- Building BeefySysLib (Release) ---- CALL ../../bin/msbuild.bat ..\..\BeefySysLib\BeefySysLib.vcxproj /p:Configuration=Release /p:Platform=x64 /p:SolutionDir=%cd%\..\..\ /v:m IF %ERRORLEVEL% NEQ 0 GOTO FAILED +@ECHO ---- Building BeefySysLib (Release Static) ---- +CALL ../../bin/msbuild.bat ..\..\BeefySysLib\BeefySysLib.vcxproj /p:Configuration="Release Static" /p:Platform=x64 /p:SolutionDir=%cd%\..\..\ /v:m +IF %ERRORLEVEL% NEQ 0 GOTO FAILED REM @ECHO ---- Building libhunspell (Debug) ---- REM CALL ../../bin/msbuild.bat ..\..\libhunspell\libhunspell.vcxproj /p:Configuration=Debug /p:Platform=x64 /p:SolutionDir=%cd%\..\ /v:m diff --git a/IDE/mintest/minlib/src/System/Object.bf b/IDE/mintest/minlib/src/System/Object.bf index f2a47c3f..431680cf 100644 --- a/IDE/mintest/minlib/src/System/Object.bf +++ b/IDE/mintest/minlib/src/System/Object.bf @@ -213,6 +213,11 @@ namespace System public static extern bool Equals(T val1, T val2); } + struct Tuple + { + + } + struct Function : int { diff --git a/IDE/mintest/minlib/src/System/Type.bf b/IDE/mintest/minlib/src/System/Type.bf index 2a9fb9dd..234265dc 100644 --- a/IDE/mintest/minlib/src/System/Type.bf +++ b/IDE/mintest/minlib/src/System/Type.bf @@ -15,6 +15,15 @@ namespace System // including the vtable and interface slots } + public class TypeDeclaration + { + protected TypeId mTypeId; + protected TypeId mBaseTypeId; + protected TypeId mOuterTypeId; + protected TypeFlags mTypeFlags; + protected TypeCode mTypeCode; + } + [Ordered, AlwaysInclude(AssumeInstantiated=true)] public class Type { @@ -1338,7 +1347,7 @@ namespace System.Reflection EnumCase = 0x0400 } - public enum MethodFlags : uint16 + public enum MethodFlags : uint32 { MethodAccessMask = 0x0007, PrivateScope = 0x0000, // Member not referenceable. @@ -1373,5 +1382,9 @@ namespace System.Reflection ThisCall = 0x3000, // Purposely resuing StdCall|FastCall Mutating = 0x4000, Constructor = 0x8000, + AppendBit0 = 0x10000, + AppendBit1 = 0x20000, + CheckedBit0 = 0x40000, + CheckedBit1 = 0x80000 } } diff --git a/IDE/src/BuildContext.bf b/IDE/src/BuildContext.bf index d4c42310..9d758036 100644 --- a/IDE/src/BuildContext.bf +++ b/IDE/src/BuildContext.bf @@ -695,23 +695,32 @@ namespace IDE return false; } - String compilerExePath = scope String(); + // Set for auto-install without prompting + gApp.mSettings.mEmscriptenPendingInstall = false; + + String wasmPath = Path.GetAbsolutePath("../wasm", gApp.mInstallDir, .. scope .()); + if (!Directory.Exists(wasmPath)) + Path.GetAbsolutePath("../../wasm", gApp.mInstallDir, wasmPath..Clear()); + IDEUtils.FixFilePath(wasmPath); + if (gApp.mSettings.mEmscriptenPath.IsEmpty) { - // Set for auto-install without prompting gApp.mSettings.mEmscriptenPendingInstall = true; + } + else if (!File.Exists(scope $"{wasmPath}/EmsdkDep1_Done.txt")) + { + gApp.mSettings.mEmscriptenPendingInstall = true; + } + String compilerExePath = scope String(); + if (gApp.mSettings.mEmscriptenPendingInstall) + { #if CLI gApp.Fail("Emscripten path not configured. Check Wasm configuration in File\\Preferences\\Settings."); return false; #else if (gApp.mSettings.mEmscriptenPendingInstall) { - String wasmPath = Path.GetAbsolutePath("../wasm", gApp.mInstallDir, .. scope .()); - if (!Directory.Exists(wasmPath)) - Path.GetAbsolutePath("../../wasm", gApp.mInstallDir, wasmPath..Clear()); - IDEUtils.FixFilePath(wasmPath); - var runCmd = gApp.QueueRun(scope $"{wasmPath}/fetch_wasm.bat", "", wasmPath, .UTF8); runCmd.mOnlyIfNotFailed = true; @@ -849,8 +858,7 @@ namespace IDE if ((workspaceOptions.mEnableObjectDebugFlags) || (workspaceOptions.mAllocType == .Debug) - || (workspaceOptions.mAllocType == .Stomp) - || (workspaceOptions.mAllocStackTraceDepth > 0)) + || (workspaceOptions.mAllocType == .Stomp)) { outDbg.Append("Beef", IDEApp.sRTVersionStr, "Dbg"); outDbg.Append((Workspace.PlatformType.GetPtrSizeByName(gApp.mPlatformName) == 4) ? "32" : "64"); diff --git a/IDE/src/Compiler/BfCompiler.bf b/IDE/src/Compiler/BfCompiler.bf index 919cfa4f..29b92133 100644 --- a/IDE/src/Compiler/BfCompiler.bf +++ b/IDE/src/Compiler/BfCompiler.bf @@ -987,7 +987,11 @@ namespace IDE.Compiler { if (!curWatches.Contains(kv.key)) { - gApp.mFileWatcher.RemoveWatch(kv.key, kv.value); + var watchFile = scope String(kv.key); + if ((watchFile.EndsWith(Path.DirectorySeparatorChar)) || (watchFile.EndsWith(Path.AltDirectorySeparatorChar))) + watchFile.Append("*"); + gApp.mFileWatcher.RemoveWatch(watchFile, kv.value); + //Debug.Assert(!gApp.mFileWatcher.HasDependentObject(kv.value)); oldKeys.Add(kv.key); } } diff --git a/IDE/src/Debugger/DebugManager.bf b/IDE/src/Debugger/DebugManager.bf index abb95fc0..f6765d61 100644 --- a/IDE/src/Debugger/DebugManager.bf +++ b/IDE/src/Debugger/DebugManager.bf @@ -432,7 +432,8 @@ namespace IDE.Debugger public int32 mActiveCallStackIdx; public Event mBreakpointsChangedDelegate ~ _.Dispose(); public Breakpoint mRunToCursorBreakpoint; - public int32 mDebugIdx; + public int32 mSessionIdx; + public int32 mStateIdx; public bool IsRunning { @@ -466,6 +467,17 @@ namespace IDE.Debugger Debugger_Delete(); } + public void IncrementSessionIdx() + { + mSessionIdx++; + mStateIdx++; + } + + public void IncrementStateIdx() + { + mStateIdx++; + } + public void Reset() { for (var breakpoint in mBreakpointList) @@ -532,10 +544,8 @@ namespace IDE.Debugger public void HotLoad(String[] objectFileNames, int hotIdx) { - - String filenamesStr = scope String(); - filenamesStr.Join("\n", params objectFileNames); + filenamesStr.Join("\n", objectFileNames); Debugger_HotLoad(filenamesStr, (int32)hotIdx); // The hot load will bind breakpoints to any new methods, but the old versions diff --git a/IDE/src/FileWatcher.bf b/IDE/src/FileWatcher.bf index 39b2f34a..5976cb71 100644 --- a/IDE/src/FileWatcher.bf +++ b/IDE/src/FileWatcher.bf @@ -581,6 +581,18 @@ namespace IDE #endif } + public bool HasDependentObject(Object dependentObject) + { + for (var watchedFileKV in mWatchedFiles) + { + if (watchedFileKV.value.mDependentObjects.Contains(dependentObject)) + return true; + } + if (mDependencyChangeSet.Contains(Internal.UnsafeCastToPtr(dependentObject))) + return true; + return false; + } + public void Update(delegate void(String, String, WatcherChangeTypes) fileChangeHandler = null) { while (true) diff --git a/IDE/src/IDEApp.bf b/IDE/src/IDEApp.bf index 9a59a847..e3f09e65 100644 --- a/IDE/src/IDEApp.bf +++ b/IDE/src/IDEApp.bf @@ -119,7 +119,7 @@ namespace IDE public class IDEApp : BFApp { public static String sRTVersionStr = "042"; - public const String cVersion = "0.43.5"; + public const String cVersion = "0.43.6"; #if BF_PLATFORM_WINDOWS public static readonly String sPlatform64Name = "Win64"; @@ -135,9 +135,6 @@ namespace IDE public static readonly String sPlatform32Name = "Unknown32"; #endif - public const uint32 cDialogOutlineLightColor = 0xFF404040; - public const uint32 cDialogOutlineDarkColor = 0xFF202020; - public static bool sExitTest; public Verbosity mVerbosity = .Default; @@ -262,7 +259,7 @@ namespace IDE #if IDE_C_SUPPORT public ClangCompiler mDepClang ~ delete _; #endif - // The Beef resolve system is up-to-date with the projects' files, + // The Beef resolve system is up-to-date with the projects' files, // but the Clang resolver only has open files in it public bool mNoResolve = false; public bool mDeterministic = false; @@ -323,7 +320,7 @@ namespace IDE }; public int32 mFileDataDataRevision; - /*public Point mLastAbsMousePos; + /*public Point mLastAbsMousePos; public Point mLastRelMousePos; public int32 mMouseStillTicks; public Widget mLastMouseWidget;*/ @@ -794,11 +791,11 @@ namespace IDE mMainBreakpoint = null; }*/ - /*delete mBfBuildCompiler; + /*delete mBfBuildCompiler; delete mBfBuildSystem; delete mDepClang; - - + + delete mBfResolveCompiler; delete mBfResolveSystem; delete mResolveClang; @@ -1096,7 +1093,7 @@ namespace IDE } else { - Fail(StackStringFormat!("Failed to load minidump '{0}'", mCrashDumpPath)); + Fail(scope String()..AppendF("Failed to load minidump '{0}'", mCrashDumpPath)); DeleteAndNullify!(mCrashDumpPath); } } @@ -1230,7 +1227,7 @@ namespace IDE Dialog aDialog; if (changedList.Count == 1) { - aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", StackStringFormat!("Save changes to '{0}' before closing?", changedList[0]), DarkTheme.sDarkTheme.mIconWarning); + aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", scope String()..AppendF("Save changes to '{0}' before closing?", changedList[0]), DarkTheme.sDarkTheme.mIconWarning); } else { @@ -1532,7 +1529,7 @@ namespace IDE if (Utils.WriteTextFile(path, useText) case .Err) { if (showErrors) - Fail(StackStringFormat!("Failed to write file '{0}'", path)); + Fail(scope String()..AppendF("Failed to write file '{0}'", path)); return false; } } @@ -1665,7 +1662,7 @@ namespace IDE lineEndingKind = editData.mLineEndingKind; } - // Lock file watcher to synchronize the 'file changed' notification so we don't + // Lock file watcher to synchronize the 'file changed' notification so we don't // think a file was externally saved using (mFileWatcher.mMonitor.Enter()) { @@ -2313,7 +2310,7 @@ namespace IDE if (Directory.CreateDirectory(mWorkspace.mDir) case .Err) { - Fail(StackStringFormat!("Failed to create workspace directory '{0}'", mWorkspace.mDir)); + Fail(scope String()..AppendF("Failed to create workspace directory '{0}'", mWorkspace.mDir)); return false; } } @@ -2331,7 +2328,7 @@ namespace IDE if (!SafeWriteTextFile(workspaceFileName, tomlString)) { - Fail(StackStringFormat!("Failed to write workspace file '{0}'", workspaceFileName)); + Fail(scope String()..AppendF("Failed to write workspace file '{0}'", workspaceFileName)); return false; } } @@ -2520,9 +2517,16 @@ namespace IDE { if (mDebugger.mIsRunning) { + if (mDebugger.mIsComptimeDebug) + CancelBuild(); mDebugger.StopDebugging(); while (mDebugger.GetRunState() != .Terminated) { + if (mDebugger.mIsComptimeDebug) + { + if (!mBfBuildCompiler.IsPerformingBackgroundOperation()) + break; + } mDebugger.Update(); } mDebugger.mIsRunning = false; @@ -2530,6 +2534,9 @@ namespace IDE } mDebugger.DisposeNativeBreakpoints(); mWantsRehupCallstack = false; + + if (mDebugger.mIsComptimeDebug) + mDebugger.Detach(); } void CloseWorkspace() @@ -2574,9 +2581,9 @@ namespace IDE { var sourceViewPanel = tab.mContent as SourceViewPanel; if (sourceViewPanel != null) - { + { docPanels.Add(sourceViewPanel); - } + } }); for (var docPanel in docPanels) CloseDocument(docPanel);*/ @@ -2806,7 +2813,7 @@ namespace IDE else { int32 spanSize = -cmd; - + charId += spanSize; charIdx += spanSize; @@ -2913,7 +2920,7 @@ namespace IDE hadLoad = true; var projectPath = project.mProjectPath; - + if (project.mDeferState == .Pending) { hasDeferredProjects = true; @@ -2928,7 +2935,7 @@ namespace IDE AddProjectToWorkspace(project, false); if (addToUI) - mProjectPanel.InitProject(project, null); + mProjectPanel?.InitProject(project, null); } } if (!hadLoad) @@ -2948,7 +2955,7 @@ namespace IDE if (loadFailed) { - mProjectPanel.RebuildUI(); + mProjectPanel?.RebuildUI(); } } @@ -3130,9 +3137,9 @@ namespace IDE if (projSpec.mVerSpec.Parse(data) case .Err) { - var err = scope String(); - err.AppendF("Unable to parse version specifier for {0} in {1}", projectName, workspaceFileName); - Fail(err); + var errStr = scope String(); + errStr.AppendF("Unable to parse version specifier for {0} in {1}", projectName, workspaceFileName); + Fail(errStr); LoadFailed(); continue; } @@ -3348,7 +3355,7 @@ namespace IDE case .SemVer(let semVer): // case .Git(let url, let ver): - + var checkPath = scope String(); if (mPackMan.CheckLock(projectName, checkPath, var projectFailed)) { @@ -3379,7 +3386,7 @@ namespace IDE /*if (!project.Load(projectFilePath)) { - Fail(StackStringFormat!("Failed to load project {0}", projectFilePath)); + Fail(scope String()..AppendF("Failed to load project {0}", projectFilePath)); delete project; return .Err(.LoadFailed); } @@ -3883,7 +3890,7 @@ namespace IDE { #if !CLI /*SaveFileDialog dialog = scope .(); - + let activeWindow = GetActiveWindow(); dialog.OverwritePrompt = true; dialog.SetFilter("Debug Session (*.bfdbg)|*.bfdbg"); @@ -4073,7 +4080,7 @@ namespace IDE if (mMainWindow == null) { - Internal.FatalError(StackStringFormat!("FAILED: {0}", text)); + Internal.FatalError(scope String()..AppendF("FAILED: {0}", text)); } Beep(MessageBeepType.Error); @@ -4680,7 +4687,7 @@ namespace IDE int defLine; int defColumn; mResolveClang.CancelBackground(); - + int defIdx = sourceViewPanel.mEditWidget.Content.GetTextIdx(line, lineChar); if (mResolveClang.FindDefinition(sourceViewPanel.mFilePath, defIdx, defFile, out defLine, out defColumn)) @@ -4694,7 +4701,7 @@ namespace IDE } else #endif - /*{ + /*{ ResolveParams resolveParams = scope ResolveParams(); sourceViewPanel.Classify(ResolveType.GoToDefinition, resolveParams); if (resolveParams.mOutFileName != null) @@ -4887,7 +4894,7 @@ namespace IDE { if (mDebugger.mIsRunning) { - if (mExecutionPaused) + if ((mExecutionPaused) && (mDebugger.IsPaused())) { DebuggerUnpaused(); mDebugger.StepInto(IsInDisassemblyMode()); @@ -4905,7 +4912,7 @@ namespace IDE mStepCount++; if (mDebugger.mIsRunning) { - if (mExecutionPaused) + if ((mExecutionPaused) && (mDebugger.IsPaused())) { DebuggerUnpaused(); mDebugger.StepOver(IsInDisassemblyMode()); @@ -4925,7 +4932,7 @@ namespace IDE [IDECommand] void StepOut() { - if (mExecutionPaused) + if ((mExecutionPaused) && (mDebugger.IsPaused())) { DebuggerUnpaused(); mDebugger.StepOut(IsInDisassemblyMode()); @@ -6635,20 +6642,33 @@ namespace IDE return tabButton; } - public DisassemblyPanel ShowDisassemblyPanel(bool clearData = false) + public DisassemblyPanel ShowDisassemblyPanel(bool clearData = false, bool setFocus = false) { DisassemblyPanel disassemblyPanel = null; + TabbedView.TabButton disassemblyTab = null; + WithTabs(scope [&] (tab) => { if ((disassemblyPanel == null) && (tab.mContent is DisassemblyPanel)) { + disassemblyTab = tab; disassemblyPanel = (DisassemblyPanel)tab.mContent; - disassemblyPanel.ClearQueuedData(); - tab.Activate(); } }); + + if (disassemblyTab != null) + { + var window = disassemblyTab.mWidgetWindow; + if ((setFocus) && (window != null) && (!HasModalDialogs()) && (!mRunningTestScript)) + window.SetForeground(); + } + if (disassemblyPanel != null) + { + disassemblyPanel.ClearQueuedData(); + disassemblyTab.Activate(); return disassemblyPanel; + } TabbedView tabbedView = GetDefaultDocumentTabbedView(); disassemblyPanel = new DisassemblyPanel(); @@ -6916,8 +6936,8 @@ namespace IDE var editWidgetContent = (SourceEditWidgetContent)editWidget.Content; //mEditWidget.mVertScrollbar.mScrollIncrement = editWidgetContent.mFont.GetLineSpacing(); - editWidgetContent.mHiliteColor = 0xFF384858; - editWidgetContent.mUnfocusedHiliteColor = 0x80384858; + editWidgetContent.mHiliteColor = mSettings.mUISettings.mColors.mCodeHilite; + editWidgetContent.mUnfocusedHiliteColor = mSettings.mUISettings.mColors.mCodeHiliteUnfocused; editWidgetContent.mHiliteCurrentLine = mSettings.mEditorSettings.mHiliteCurrentLine; return editWidget; @@ -7112,7 +7132,7 @@ namespace IDE mFileEditData.Add(editData); projectSource.mEditData = editData; projectSource.mEditData.mLastFileTextVersion = projectSource.mEditData.mEditWidget.Content.mData.mCurTextVersionId; - } + } } return projectSource.mEditData;*/ } @@ -7172,6 +7192,12 @@ namespace IDE } } + void ActivateWindow(WidgetWindow window) + { + if ((setFocus) && (window != null) && (!HasModalDialogs()) && (!mRunningTestScript)) + window.SetForeground(); + } + if (showType != SourceShowType.New) { delegate void(TabbedView.TabButton) tabFunc = scope [&] (tabButton) => @@ -7220,8 +7246,7 @@ namespace IDE //sourceViewPanel.QueueFullRefresh(true); } - if ((sourceViewPanel.mWidgetWindow != null) && (!HasModalDialogs()) && (!mRunningTestScript)) - sourceViewPanel.mWidgetWindow.SetForeground(); + ActivateWindow(sourceViewPanelTab.mWidgetWindow); sourceViewPanelTab.Activate(setFocus); sourceViewPanelTab.mTabbedView.FinishTabAnim(); if (setFocus) @@ -7237,6 +7262,7 @@ namespace IDE //ShowSourceFile(filePath, projectSource, showTemp, setFocus); DarkTabbedView tabbedView = GetDefaultDocumentTabbedView(); + ActivateWindow(tabbedView.mWidgetWindow); sourceViewPanel = new SourceViewPanel(); bool success; if (useProjectSource != null) @@ -7436,7 +7462,7 @@ namespace IDE Path.GetFileName(sourceViewPanel.mFilePath, fileName); else fileName.Append("untitled"); - Dialog aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", StackStringFormat!("Save changes to '{0}' before closing?", fileName), DarkTheme.sDarkTheme.mIconWarning); + Dialog aDialog = ThemeFactory.mDefault.CreateDialog("Save file?", scope String()..AppendF("Save changes to '{0}' before closing?", fileName), DarkTheme.sDarkTheme.mIconWarning); aDialog.mDefaultButton = aDialog.AddButton("Save", new (evt) => { SaveFile(sourceViewPanel); CloseDocument(sourceViewPanel); }); aDialog.AddButton("Don't Save", new (evt) => CloseDocument(sourceViewPanel)); aDialog.mEscButton = aDialog.AddButton("Cancel"); @@ -7509,7 +7535,7 @@ namespace IDE hasFocus = true; } - /*if (sourceViewPanel != null) + /*if (sourceViewPanel != null) hasFocus = sourceViewPanel.mEditWidget.mHasFocus;*/ if ((sourceViewPanel != null) && (sourceViewPanel.HasUnsavedChanges())) @@ -8000,7 +8026,7 @@ namespace IDE } else { - var disassemblyPanel = ShowDisassemblyPanel(true); + var disassemblyPanel = ShowDisassemblyPanel(true, setFocus); if (aliasFilePath != null) String.NewOrSet!(disassemblyPanel.mAliasFilePath, aliasFilePath); disassemblyPanel.Show(addr, filePath, line, column, hotIdx, defLineStart, defLineEnd); @@ -8097,7 +8123,7 @@ namespace IDE DragDropFile(key); return; } - Fail(StackStringFormat!("Unhandled command line param: {0}", key)); + Fail(scope String()..AppendF("Unhandled command line param: {0}", key)); } public override bool HandleCommandLineParam(String key, String value) @@ -8241,12 +8267,14 @@ namespace IDE if (fullDir.EndsWith("BeefSpace.toml", .OrdinalIgnoreCase)) fullDir.RemoveFromEnd("BeefSpace.toml".Length); - if ((File.Exists(fullDir)) || (IsBeefFile(fullDir))) + //TODO: Properly implement 'composite files' + /*if ((File.Exists(fullDir)) || + ((IsBeefFile(fullDir)) && (!Directory.Exists(fullDir)))) { mWorkspace.mCompositeFile = new CompositeFile(fullDir); delete fullDir; } - else + else*/ mWorkspace.mDir = fullDir; case "-file": DragDropFile(value); @@ -8265,6 +8293,8 @@ namespace IDE mDeferredOpen = .DebugSession; else if (filePath.EndsWith(".dmp", .OrdinalIgnoreCase)) mDeferredOpen = .CrashDump; + else if (filePath.EndsWith("BeefSpace.toml", .OrdinalIgnoreCase)) + mDeferredOpen = .Workspace; else mDeferredOpen = .File; @@ -9146,8 +9176,8 @@ namespace IDE /*var buffer = scope String(); if (streamReader.Read(buffer) case .Err) - break; - using (mDebugOutputMonitor.Enter()) + break; + using (mDebugOutputMonitor.Enter()) mDebugOutput.Add(new String(buffer));*/ count++; @@ -9169,10 +9199,10 @@ namespace IDE { var buffer = scope String(); if (streamReader.ReadLine(buffer) case .Err) - break; + break; - using (IDEApp.sApp.mMonitor.Enter()) - executionInstance.mDeferredOutput.Add(new String(buffer)); + using (IDEApp.sApp.mMonitor.Enter()) + executionInstance.mDeferredOutput.Add(new String(buffer)); } }*/ @@ -9333,13 +9363,19 @@ namespace IDE executionInstance.mStopwatch.Start(); executionInstance.mProcess = process; - executionInstance.mOutputThread = new Thread(new => ReadOutputThread); - executionInstance.mOutputThread.Start(executionInstance, false); + if (startInfo.RedirectStandardOutput) + { + executionInstance.mOutputThread = new Thread(new => ReadOutputThread); + executionInstance.mOutputThread.Start(executionInstance, false); + } - executionInstance.mErrorThread = new Thread(new => ReadErrorThread); - executionInstance.mErrorThread.Start(executionInstance, false); + if (startInfo.RedirectStandardError) + { + executionInstance.mErrorThread = new Thread(new => ReadErrorThread); + executionInstance.mErrorThread.Start(executionInstance, false); + } - if (stdInData != null) + if ((startInfo.RedirectStandardInput) && (stdInData != null)) { executionInstance.mStdInData = new String(stdInData); executionInstance.mInputThread = new Thread(new => WriteInputThread); @@ -9474,7 +9510,7 @@ namespace IDE } if ((executionInstance == null) && (mExecutionQueue.Count == 0)) - { + { OutputLine("Compilation finished."); }*/ } @@ -9649,29 +9685,32 @@ namespace IDE if ((processCompileCmd.mHadBeef) && (mVerbosity >= .Normal)) OutputLine("Beef compilation time: {0:0.00}s", processCompileCmd.mStopwatch.ElapsedMilliseconds / 1000.0f); - var compileInstance = mWorkspace.mCompileInstanceList.Back; - compileInstance.mCompileResult = .Failure; - if (processCompileCmd.mBfPassInstance.mCompileSucceeded) - { - //foreach (var sourceViewPanel in GetSourceViewPanels()) - WithSourceViewPanels(scope (sourceViewPanel) => - { - sourceViewPanel.mHasChangedSinceLastCompile = false; - }); - compileInstance.mCompileResult = .Success; - } - else + if (!mWorkspace.mCompileInstanceList.IsEmpty) { + var compileInstance = mWorkspace.mCompileInstanceList.Back; compileInstance.mCompileResult = .Failure; - } + if (processCompileCmd.mBfPassInstance.mCompileSucceeded) + { + //foreach (var sourceViewPanel in GetSourceViewPanels()) + WithSourceViewPanels(scope (sourceViewPanel) => + { + sourceViewPanel.mHasChangedSinceLastCompile = false; + }); + compileInstance.mCompileResult = .Success; + } + else + { + compileInstance.mCompileResult = .Failure; + } - ProcessBeefCompileResults(processCompileCmd.mBfPassInstance, processCompileCmd.mCompileKind, processCompileCmd.mHotProject, processCompileCmd.mStopwatch); - processCompileCmd.mBfPassInstance = null; + ProcessBeefCompileResults(processCompileCmd.mBfPassInstance, processCompileCmd.mCompileKind, processCompileCmd.mHotProject, processCompileCmd.mStopwatch); + processCompileCmd.mBfPassInstance = null; - if (mHotResolveState != .None) - { - if (compileInstance.mCompileResult == .Success) - compileInstance.mCompileResult = .PendingHotLoad; + if (mHotResolveState != .None) + { + if (compileInstance.mCompileResult == .Success) + compileInstance.mCompileResult = .PendingHotLoad; + } } if (processCompileCmd.mProfileCmd != null) @@ -9754,7 +9793,7 @@ namespace IDE { mDepClang.QueueCheckDependencies(projectSource, ClangCompiler.DepCheckerType.Clang); } - } + } }); if (!completedCompileCmd.mFailed) mDepClang.mDoDependencyCheck = false; @@ -9765,9 +9804,9 @@ namespace IDE CompileResult(buildCompletedCmd.mHotProjectName, !buildCompletedCmd.mFailed); if (buildCompletedCmd.mFailed) - OutputLineSmart("ERROR: BUILD FAILED"); - if ((mVerbosity >= .Detailed) && (buildCompletedCmd.mStopwatch != null)) - OutputLine("Total build time: {0:0.00}s", buildCompletedCmd.mStopwatch.ElapsedMilliseconds / 1000.0f); + OutputLineSmart("ERROR: BUILD FAILED. Total build time: {0:0.00}s", buildCompletedCmd.mStopwatch.ElapsedMilliseconds / 1000.0f); + else if ((mVerbosity >= .Detailed) && (buildCompletedCmd.mStopwatch != null)) + OutputLineSmart("SUCCESS: Build completed with no errors. Total build time: {0:0.00}s", buildCompletedCmd.mStopwatch.ElapsedMilliseconds / 1000.0f); if (mDebugger?.mIsComptimeDebug == true) DebuggerComptimeStop(); @@ -10154,8 +10193,8 @@ namespace IDE } // Project options are inherently thread safe. Resolve-system project settings - // Can only be changed from the Resolve BfCompiler thread, and Build settings - // are only changed before background compilation begins. We also call this + // Can only be changed from the Resolve BfCompiler thread, and Build settings + // are only changed before background compilation begins. We also call this // during WorkspaceLoad, but the resolve threads aren't processing then. public bool SetupBeefProjectSettings(BfSystem bfSystem, BfCompiler bfCompiler, Project project) { @@ -10165,7 +10204,7 @@ namespace IDE Workspace.Options workspaceOptions = GetCurWorkspaceOptions(); if (options == null) { - //Fail(StackStringFormat!("Failed to retrieve options for {0}", project.mProjectName)); + //Fail(scope String()..AppendF("Failed to retrieve options for {0}", project.mProjectName)); bfProject.SetDisabled(true); return false; } @@ -10327,7 +10366,7 @@ namespace IDE string clangArgsStr = String.Join("\n", clangArgs); long hash = 0; - for (int i = 0; i < clangArgsStr.Length; i++) + for (int i = 0; i < clangArgsStr.Length; i++) hash = (hash << 5) - hash + clangArgsStr[i]; return String.Format("{0:X16}", hash); }*/ @@ -11433,7 +11472,7 @@ namespace IDE Project depProject = FindProject(dep.mProjectName); if (depProject == null) { - OutputLine(StackStringFormat!("Unable to find project '{0}', a dependency of project '{1}'", dep.mProjectName, project.mProjectName)); + OutputLine(scope String()..AppendF("Unable to find project '{0}', a dependency of project '{1}'", dep.mProjectName, project.mProjectName)); return false; } if (!GetDependentProjectList(depProject, orderedProjectList, useProjectStack)) @@ -11466,12 +11505,12 @@ namespace IDE mLastTestFailed = true; } - protected virtual void CompileFailed() + protected virtual void CompileFailed(Stopwatch stopwatch) { if (mTestManager != null) mTestManager.BuildFailed(); if (mVerbosity > .Quiet) - OutputLine("Compile failed."); + OutputLineSmart("ERROR-SOFT: Compile failed. Total build time: {0:0.00}s", stopwatch.ElapsedMilliseconds / 1000.0f); mLastCompileFailed = true; if (mRunningTestScript) @@ -11635,7 +11674,7 @@ namespace IDE if ((passInstance.mFailed) && (passInstance.mCompileSucceeded)) { // This can happen if we can't load a Beef file - CompileFailed(); + CompileFailed(startStopWatch); passInstance.mCompileSucceeded = false; } @@ -11667,7 +11706,7 @@ namespace IDE if (!mDebugger.mIsRunning) { OutputErrorLine("Hot compile failed - target no longer running"); - CompileFailed(); + CompileFailed(startStopWatch); return; } @@ -11699,7 +11738,7 @@ namespace IDE SkipProjectCompile(project, hotProject); } CompileResult((hotProject != null) ? hotProject.mProjectName : null, false); - CompileFailed(); + CompileFailed(startStopWatch); return; } @@ -11751,7 +11790,7 @@ namespace IDE } if (!success) - CompileFailed(); + CompileFailed(startStopWatch); if (success) { @@ -11912,9 +11951,9 @@ namespace IDE startupCode.AppendF( """ using System; - + namespace {}; - + class {} {{ public static int Main(String[] args) @@ -12162,7 +12201,7 @@ namespace IDE String err = """ Beef requires the Microsoft C++ build tools for Visual Studio 2013 or later, but they don't seem to be installed. - + Install just Microsoft Visual C++ Build Tools or the entire Visual Studio suite from: https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2022 """; @@ -12279,7 +12318,7 @@ namespace IDE passInstance = CompileBeef(hotProject, hotIdx, lastCompileHadMessages, let hadBeef); if (passInstance == null) { - CompileFailed(); + CompileFailed(scope .()); return false; } @@ -12447,7 +12486,7 @@ namespace IDE CheckDebugVisualizers(); mDebugger.mIsRunning = true; - mDebugger.mDebugIdx++; + mDebugger.IncrementSessionIdx(); WithSourceViewPanels(scope (sourceView) => { sourceView.RehupAlias(); @@ -12465,8 +12504,8 @@ namespace IDE if ((mTargetStartWithStep) && (mMainBreakpoint == null)) { - // The idea is that we don't want to step into static initializers, so we - // temporarily break on _main and then we single step + // The idea is that we don't want to step into static initializers, so we + // temporarily break on _main and then we single step //mMainBreakpoint = mDebugger.CreateSymbolBreakpoint("_ZN3Hey4Dude3Bro9TestClass4MainEv"); if ((project.mGeneralOptions.mTargetType == Project.TargetType.BeefConsoleApplication) || (project.mGeneralOptions.mTargetType == Project.TargetType.BeefGUIApplication)) @@ -12506,7 +12545,7 @@ namespace IDE CheckDebugVisualizers(); mDebugger.mIsRunning = true; - mDebugger.mDebugIdx++; + mDebugger.IncrementSessionIdx(); mDebugger.RehupBreakpoints(true); mDebugger.Run(); mIsAttachPendingSourceShow = true; @@ -12951,7 +12990,7 @@ namespace IDE { case .Ok: case .Err: - Fail(StackStringFormat!("Unable to locate process id {0}", mProcessAttachId)); + Fail(scope String()..AppendF("Unable to locate process id {0}", mProcessAttachId)); } if (debugProcess.IsAttached) { @@ -12981,13 +13020,13 @@ namespace IDE if (mDebugger.OpenMiniDump(mCrashDumpPath)) { mDebugger.mIsRunning = true; - mDebugger.mDebugIdx++; + mDebugger.IncrementSessionIdx(); mExecutionPaused = false; // Make this false so we can detect a Pause immediately mIsAttachPendingSourceShow = true; } else { - Fail(StackStringFormat!("Failed to load minidump '{0}'", mCrashDumpPath)); + Fail(scope String()..AppendF("Failed to load minidump '{0}'", mCrashDumpPath)); } } else if (mLaunchData != null) @@ -13179,7 +13218,7 @@ namespace IDE /*for (var window in gApp.mWindows) { - + window.SetMinimumSize(GS!()); }*/ } @@ -13293,6 +13332,7 @@ namespace IDE void DebuggerPaused() { + mDebugger.IncrementStateIdx(); mDebugger.mActiveCallStackIdx = 0; mExecutionPaused = true; mDebugger.GetRunState(); @@ -13306,6 +13346,8 @@ namespace IDE void WithWatchPanels(delegate void(WatchPanel watchPanel) dlg) { + if (mWatchPanel == null) + return; dlg(mWatchPanel); dlg(mAutoWatchPanel); for (let window in mWindows) @@ -13607,14 +13649,14 @@ namespace IDE if (mBfResolveSystem != null) mBfResolveSystem.AddProject(project); } - + foreach (var project in mWorkspace.mProjects) { project.WithProjectItems(scope (projectItem) => { var projectSource = projectItem as ProjectSource; if (projectSource != null) - { + { var resolveCompiler = GetProjectCompilerForFile(projectSource.mPath); if (resolveCompiler == mBfResolveCompiler) resolveCompiler.QueueProjectSource(projectSource); @@ -13980,9 +14022,11 @@ namespace IDE if (checkBreakpoint.mMemoryAddress == memoryAddress) breakpoint = checkBreakpoint; } - String infoString = StackStringFormat!("Memory breakpoint hit: '0x{0:X08}'", (int64)memoryAddress); + String infoString = scope .(); if (breakpoint != null) - infoString = StackStringFormat!("Memory breakpoint hit: '0x{0:X08}' ({1})", (int64)memoryAddress, breakpoint.mMemoryWatchExpression); + infoString.AppendF("Memory breakpoint hit: '0x{0:X08}' ({1})", (int64)memoryAddress, breakpoint.mMemoryWatchExpression); + else + infoString.AppendF("Memory breakpoint hit: '0x{0:X08}'", (int64)memoryAddress); OutputLine(infoString); if (!mRunningTestScript) { @@ -14045,8 +14089,8 @@ namespace IDE OutputFormatted(deferredOutput, deferredMsgType == "dbgEvalMsg"); } - /*if (hadMessages) - mNoDebugMessagesTick = 0; + /*if (hadMessages) + mNoDebugMessagesTick = 0; else if (IDEApp.sApp.mIsUpdateBatchStart) mNoDebugMessagesTick++; if (mNoDebugMessagesTick < 10) @@ -14178,11 +14222,11 @@ namespace IDE mDebugger.GetCurrentException(exceptionLine); var exceptionData = String.StackSplit!(exceptionLine, '\n'); - String exHeader = StackStringFormat!("Exception {0}", exceptionData[1]); + String exHeader = scope String()..AppendF("Exception {0}", exceptionData[1]); if (exceptionData.Count >= 3) exHeader = exceptionData[2]; - String exString = StackStringFormat!("{0} at {1}", exHeader, exceptionData[0]); + String exString = scope String()..AppendF("{0} at {1}", exHeader, exceptionData[0]); OutputLine(exString); if (!IsCrashDump) @@ -14331,7 +14375,7 @@ namespace IDE /*public bool CheckMouseover(Widget checkWidget, int32 wantTicks, out Point mousePoint) { mousePoint = Point(Int32.MinValue, Int32.MinValue); - if (checkWidget != mLastMouseWidget) + if (checkWidget != mLastMouseWidget) return false; checkWidget.RootToSelfTranslate(mLastRelMousePos.x, mLastRelMousePos.y, out mousePoint.x, out mousePoint.y); return mMouseStillTicks == wantTicks; @@ -14362,7 +14406,7 @@ namespace IDE foreach (var window in mWindows) { var widgetWindow = window as WidgetWindow; - + widgetWindow.RehupMouse(false); var windowOverWidget = widgetWindow.mCaptureWidget ?? widgetWindow.mOverWidget; if ((windowOverWidget != null) && (widgetWindow.mAlpha == 1.0f) && (widgetWindow.mCaptureWidget == null)) @@ -14371,7 +14415,7 @@ namespace IDE numOverWidgets++; if (overWidget != mLastMouseWidget) { - SetLastMouseWidget(overWidget); + SetLastMouseWidget(overWidget); mMouseStillTicks = -1; } @@ -14389,7 +14433,7 @@ namespace IDE } if (overWidget == null) - { + { SetLastMouseWidget(null); mMouseStillTicks = -1; } @@ -14399,7 +14443,7 @@ namespace IDE //int a = 0; } - Debug.Assert(numOverWidgets <= 1); + Debug.Assert(numOverWidgets <= 1); }*/ public void FileRenamed(ProjectFileItem projectFileItem, String oldPath, String newPath) diff --git a/IDE/src/IDEUtils.bf b/IDE/src/IDEUtils.bf index ddbf5eab..e049fa3b 100644 --- a/IDE/src/IDEUtils.bf +++ b/IDE/src/IDEUtils.bf @@ -307,8 +307,8 @@ namespace IDE public static void DrawOutline(Graphics g, Widget widget, int inflateX = 0, int32 inflateY = 0) { - DrawOutline(g, widget, inflateX, inflateY, IDEApp.cDialogOutlineLightColor); - DrawOutline(g, widget, inflateX - 1, inflateY - 1, IDEApp.cDialogOutlineDarkColor); + DrawOutline(g, widget, inflateX, inflateY, DarkTheme.COLOR_DIALOG_OUTLINE_OUT); + DrawOutline(g, widget, inflateX - 1, inflateY - 1, DarkTheme.COLOR_DIALOG_OUTLINE_IN); } public static void ClampMenuCoords(ref float x, ref float y, ScrollableWidget scrollableWidget, Insets insets = null) diff --git a/IDE/src/Settings.bf b/IDE/src/Settings.bf index d4f47c41..9a10958c 100644 --- a/IDE/src/Settings.bf +++ b/IDE/src/Settings.bf @@ -153,7 +153,8 @@ namespace IDE public List mSymbolSearchPath = new .() ~ DeleteContainerAndItems!(_); public List mAutoFindPaths = new .() ~ DeleteContainerAndItems!(_); public int32 mProfileSampleRate = 1000; - public bool mAutoEvaluateProperties = false; + public bool mAutoEvaluatePropertiesOnHover = false; + public bool mAutoRefreshWatches = false; public void Serialize(StructuredData sd) { @@ -194,7 +195,8 @@ namespace IDE sd.RemoveIfEmpty(); } sd.Add("ProfileSampleRate", mProfileSampleRate); - sd.Add("AutoEvaluateProperties", mAutoEvaluateProperties); + sd.Add("AutoEvaluateProperties", mAutoEvaluatePropertiesOnHover); + sd.Add("AutoRefreshWatches", mAutoRefreshWatches); } public void Deserialize(StructuredData sd) @@ -233,7 +235,8 @@ namespace IDE } } sd.Get("ProfileSampleRate", ref mProfileSampleRate); - sd.Get("AutoEvaluateProperties", ref mAutoEvaluateProperties); + sd.Get("AutoEvaluateProperties", ref mAutoEvaluatePropertiesOnHover); + sd.Get("AutoRefreshWatches", ref mAutoRefreshWatches); } public void Apply() @@ -264,6 +267,9 @@ namespace IDE gApp.mDebugger.SetSourcePathRemap(remapStr); mProfileSampleRate = Math.Clamp(mProfileSampleRate, 10, 10000); + + gApp.mDebugger.IncrementStateIdx(); + gApp.RefreshWatches(); } public void SetDefaults() @@ -305,7 +311,12 @@ namespace IDE public class Colors { public Color mText = 0xFFFFFFFF; + public Color mTextDisabled = 0xFFA8A8A8; + public Color mTextSelected = 0xFF2f5c88; public Color mWindow = 0xFF44444D; + public Color mDialogOutlineIn = 0xFF404040; + public Color mDialogOutlineOut = 0xFF202020; + public Color mGrid = 0x0CFFFFFF; public Color mBackground = 0xFF1C1C24; public Color mSelectedOutline = 0xFFCFAE11; public Color mMenuFocused = 0xFFE5A910; @@ -340,10 +351,13 @@ namespace IDE public Color mError = 0xFFFF0000; public Color mBuildError = 0xFFFF8080; public Color mBuildWarning = 0xFFFFFF80; + public Color mBuildSuccess = 0xFF80FF80; public Color mVisibleWhiteSpace = 0xFF9090C0; public Color mCurrentLineHilite = 0xFF4C4C54; public Color mCurrentLineNumberHilite = 0x18FFFFFF; public Color mCharPairHilite = 0x1DFFFFFF; + public Color mCodeHilite = 0xFF384858; + public Color mCodeHiliteUnfocused = 0x80384858; public void Deserialize(StructuredData sd) { @@ -356,7 +370,12 @@ namespace IDE } GetColor("Text", ref mText); + GetColor("TextDisabled", ref mTextDisabled); + GetColor("TextSelected", ref mTextSelected); GetColor("Window", ref mWindow); + GetColor("DialogOutlineIn", ref mDialogOutlineIn); + GetColor("DialogOutlineOut", ref mDialogOutlineOut); + GetColor("Grid", ref mGrid); GetColor("Background", ref mBackground); GetColor("SelectedOutline", ref mSelectedOutline); GetColor("MenuFocused", ref mMenuFocused); @@ -408,10 +427,13 @@ namespace IDE GetColor("Error", ref mError); GetColor("BuildError", ref mBuildError); GetColor("BuildWarning", ref mBuildWarning); + GetColor("BuildSuccess", ref mBuildSuccess); GetColor("VisibleWhiteSpace", ref mVisibleWhiteSpace); GetColor("CurrentLineHilite", ref mCurrentLineHilite); GetColor("CurrentLineNumberHilite", ref mCurrentLineNumberHilite); GetColor("CharPairHilite", ref mCharPairHilite); + GetColor("CodeHilite", ref mCodeHilite); + GetColor("CodeHiliteUnfocused", ref mCodeHiliteUnfocused); } public void Apply() @@ -437,10 +459,16 @@ namespace IDE SourceEditWidgetContent.sTextColors[(.)SourceElementType.Error] = mError; SourceEditWidgetContent.sTextColors[(.)SourceElementType.BuildError] = mBuildError; SourceEditWidgetContent.sTextColors[(.)SourceElementType.BuildWarning] = mBuildWarning; + SourceEditWidgetContent.sTextColors[(.)SourceElementType.BuildSuccess] = mBuildSuccess; SourceEditWidgetContent.sTextColors[(.)SourceElementType.VisibleWhiteSpace] = mVisibleWhiteSpace; DarkTheme.COLOR_TEXT = mText; + DarkTheme.COLOR_TEXT_DISABLED = mTextDisabled; + DarkTheme.COLOR_TEXT_SELECTED = mTextSelected; DarkTheme.COLOR_WINDOW = mWindow; + DarkTheme.COLOR_DIALOG_OUTLINE_IN = mDialogOutlineIn; + DarkTheme.COLOR_DIALOG_OUTLINE_OUT = mDialogOutlineOut; + DarkTheme.COLOR_GRID = mGrid; DarkTheme.COLOR_BKG = mBackground; DarkTheme.COLOR_SELECTED_OUTLINE = mSelectedOutline; DarkTheme.COLOR_MENU_FOCUSED = mMenuFocused; @@ -670,6 +698,7 @@ namespace IDE public List mFonts = new .() ~ DeleteContainerAndItems!(_); public float mFontSize = 12; + public float mLineHeightScale = 1.0f; public AutoCompleteShowKind mAutoCompleteShowKind = .PanelIfVisible; public bool mAutoCompleteRequireControl = true; public bool mAutoCompleteRequireTab = false; @@ -704,6 +733,7 @@ namespace IDE sd.Add(str); } sd.Add("FontSize", mFontSize); + sd.Add("LineHeightScale", mLineHeightScale); sd.Add("AutoCompleteShowKind", mAutoCompleteShowKind); sd.Add("AutoCompleteRequireControl", mAutoCompleteRequireControl); sd.Add("AutoCompleteRequireTab", mAutoCompleteRequireTab); @@ -741,6 +771,7 @@ namespace IDE } sd.Get("UIScale", ref gApp.mSettings.mUISettings.mScale); // Legacy sd.Get("FontSize", ref mFontSize); + sd.Get("LineHeightScale", ref mLineHeightScale); sd.Get("AutoCompleteShowKind", ref mAutoCompleteShowKind); sd.Get("AutoCompleteRequireControl", ref mAutoCompleteRequireControl); sd.Get("AutoCompleteRequireTab", ref mAutoCompleteRequireTab); @@ -1337,6 +1368,7 @@ namespace IDE { gApp.mSettings.mUISettings.mScale = Math.Clamp(gApp.mSettings.mUISettings.mScale, 50, 400); gApp.mSettings.mEditorSettings.mFontSize = Math.Clamp(gApp.mSettings.mEditorSettings.mFontSize, 6.0f, 72.0f); + gApp.mSettings.mEditorSettings.mLineHeightScale = Math.Clamp(gApp.mSettings.mEditorSettings.mLineHeightScale, 0.125f, 10.0f); mUISettings.Apply(); mEditorSettings.Apply(); @@ -1349,7 +1381,6 @@ namespace IDE mKeySettings.Apply(); mDebuggerSettings.Apply(); - for (var window in gApp.mWindows) { if (var widgetWindow = window as WidgetWindow) @@ -1360,7 +1391,12 @@ namespace IDE for (let value in gApp.mFileEditData.Values) if (value.mEditWidget != null) - ((SourceEditWidgetContent)value.mEditWidget.Content).mHiliteCurrentLine = gApp.mSettings.mEditorSettings.mHiliteCurrentLine; + { + var ewc = (SourceEditWidgetContent)value.mEditWidget.Content; + ewc.mHiliteCurrentLine = gApp.mSettings.mEditorSettings.mHiliteCurrentLine; + ewc.mLineHeightScale = gApp.mSettings.mEditorSettings.mLineHeightScale; + ewc.RehupLineCoords(); + } if (!mWakaTimeKey.IsEmpty) { diff --git a/IDE/src/TestManager.bf b/IDE/src/TestManager.bf index 04d40ebc..02a63fd5 100644 --- a/IDE/src/TestManager.bf +++ b/IDE/src/TestManager.bf @@ -639,7 +639,7 @@ namespace IDE gApp.mDebugger.RehupBreakpoints(true); gApp.mDebugger.Run(); gApp.mDebugger.mIsRunning = true; - gApp.mDebugger.mDebugIdx++; + gApp.mDebugger.IncrementSessionIdx(); } mTestInstance.mThread.Start(false); diff --git a/IDE/src/ui/AboutDialog.bf b/IDE/src/ui/AboutDialog.bf index d5481fd4..d4e9f868 100644 --- a/IDE/src/ui/AboutDialog.bf +++ b/IDE/src/ui/AboutDialog.bf @@ -238,17 +238,11 @@ namespace IDE.ui MarkDirty(); } - public override void KeyDown(KeyCode keyCode, bool isRepeat) - { - base.KeyDown(keyCode, isRepeat); - if ((keyCode == (.)'C') && (mWidgetWindow.GetKeyFlags(true) == .Ctrl)) - { - String versionInfo = scope String(); - versionInfo.AppendF("Beef IDE Version {}", gApp.mVersionInfo.FileVersion); - versionInfo.AppendF(" Build {}", gApp.mVersionInfo.ProductVersion); - gApp.SetClipboardText(versionInfo); - } + public override void GenerateClipboardText(String outText) + { + outText.AppendF("Beef IDE Version {}", gApp.mVersionInfo.FileVersion); + outText.AppendF(" Build {}", gApp.mVersionInfo.ProductVersion); } } } diff --git a/IDE/src/ui/AttachDialog.bf b/IDE/src/ui/AttachDialog.bf index 7f2aaa5a..06bf82ef 100644 --- a/IDE/src/ui/AttachDialog.bf +++ b/IDE/src/ui/AttachDialog.bf @@ -308,8 +308,8 @@ namespace IDE.ui //Outline(g, mCategorySelector, 0, 0, 0xFF404040); //Outline(g, mCategorySelector, -1, -1, 0xFF202020); - Outline(g, mFileList, 0, 0, IDEApp.cDialogOutlineLightColor); - Outline(g, mFileList, -1, -1, IDEApp.cDialogOutlineDarkColor); + Outline(g, mFileList, 0, 0, DarkTheme.COLOR_DIALOG_OUTLINE_OUT); + Outline(g, mFileList, -1, -1, DarkTheme.COLOR_DIALOG_OUTLINE_IN); } void CheckFileDates() diff --git a/IDE/src/ui/AutoComplete.bf b/IDE/src/ui/AutoComplete.bf index 9dc13e96..8eccab69 100644 --- a/IDE/src/ui/AutoComplete.bf +++ b/IDE/src/ui/AutoComplete.bf @@ -413,6 +413,11 @@ namespace IDE.ui g.PushColor(DarkTheme.COLOR_MENU_FOCUSED); defer:loop g.PopColor(); } + else + { + g.PushColor(DarkTheme.COLOR_TEXT); + defer:loop g.PopColor(); + } let str = StringView(mEntryDisplay, index, @c.NextIndex - index); @@ -512,9 +517,6 @@ namespace IDE.ui public void UpdateWidth() { - if (mWidgetWindow == null) - return; - int firstEntry = (int)(-(int)mScrollContent.mY / mItemSpacing); int lastEntry = (int)((-(int)mScrollContent.mY + mScrollContentContainer.mHeight) / mItemSpacing); @@ -536,6 +538,8 @@ namespace IDE.ui mMaxWidth = Math.Max(mMaxWidth, entryWidth); } + if (mWidgetWindow == null) + return; float docWidth = 0.0f; float docHeight = 0; @@ -557,8 +561,8 @@ namespace IDE.ui int drawScreenX = (.)(mWidgetWindow.mX + mWidth - mDocWidth); gApp.GetWorkspaceRectFrom(drawScreenX, mWidgetWindow.mY, 0, 0, var workspaceX, var workspaceY, var workspaceWidth, var workspaceHeight); - float maxWidth = workspaceWidth - drawScreenX - GS!(8); - float newDocWidth = Math.Min(docWidth, workspaceWidth - drawScreenX - GS!(8)); + float maxWidth = workspaceWidth - (drawScreenX - workspaceX) - GS!(8); + float newDocWidth = Math.Min(docWidth, workspaceWidth - (drawScreenX - workspaceX) - GS!(8)); newDocWidth = Math.Max(newDocWidth, GS!(80)); if ((docWidth > maxWidth) || (lineCount > 1)) { @@ -703,6 +707,8 @@ namespace IDE.ui public void SelectDirection(int32 dir) { + mAutoComplete.HasInteracted = true; + if (mEntryList.IsEmpty) return; int32 newSelection = mSelectIdx + dir; @@ -932,6 +938,9 @@ namespace IDE.ui mWidth = extWidth; mHeight = extHeight; + if ((mWidth <= 0) || (mHeight <= 0)) + return; + if (resizeWindow) { if (mOwnsWindow) @@ -1069,8 +1078,12 @@ namespace IDE.ui { var font = IDEApp.sApp.mCodeFont; + extHeight = 0; extWidth = 0; + if (mSelectIdx < 0) + return; + float curX = GS!(8); float curY = GS!(5); @@ -1274,6 +1287,8 @@ namespace IDE.ui float mWantX; float mWantY; + public bool HasInteracted; + public this(EditWidget targetEditWidget) { mTargetEditWidget = targetEditWidget; @@ -1295,7 +1310,7 @@ namespace IDE.ui var textIdx; // This makes typing '..' NOT move the window after pressing the second '.' - if (mTargetEditWidget.Content.SafeGetChar(textIdx - 2) == '.') + if (mTargetEditWidget.Content.SafeGetChar(textIdx - 2) == '.') { textIdx--; } @@ -1572,7 +1587,7 @@ namespace IDE.ui mInsertStartIdx--; }*/ - if ((mInvokeWidget != null) && (mInvokeWidget.mEntryList.Count > 0)) + if ((mInvokeWidget != null) && (mInvokeSrcPositions != null) && (mInvokeWidget.mEntryList.Count > 0)) { var data = mTargetEditWidget.Content.mData; @@ -1807,7 +1822,7 @@ namespace IDE.ui if (!fts_fuzzy_match(filter.CStr(), entry.mEntryDisplay.CStr(), ref score, &matches, matches.Count)) { - entry.SetMatches(Span(null, 0)); + entry.SetMatches(Span((uint8*)null, 0)); entry.mScore = score; return false; } @@ -2032,7 +2047,7 @@ namespace IDE.ui if (mInvokeWidget != null) { prevInvokeSelect = mInvokeWidget.mSelectIdx; - if ((mInvokeWidget.mEntryList.Count > 0) && (!mInvokeSrcPositions.IsEmpty)) + if ((mInvokeWidget.mEntryList.Count > 0) && (mInvokeSrcPositions != null) && (!mInvokeSrcPositions.IsEmpty) && (mInvokeWidget.mSelectIdx >= 0)) { if (IsInPanel()) { @@ -2042,8 +2057,11 @@ namespace IDE.ui { mInvokeWidget.mOwnsWindow = true; mInvokeWidget.ResizeContent(false); - UpdateWindow(ref mInvokeWindow, mInvokeWidget, mInvokeSrcPositions[0], (int32)mInvokeWidget.mWidth, (int32)mInvokeWidget.mHeight); - mInvokeWidget.ResizeContent(true); + if ((mInvokeWidget.mWidth > 0) && (mInvokeWidget.mHeight > 0)) + { + UpdateWindow(ref mInvokeWindow, mInvokeWidget, mInvokeSrcPositions[0], (int32)mInvokeWidget.mWidth, (int32)mInvokeWidget.mHeight); + mInvokeWidget.ResizeContent(true); + } } } else @@ -2209,6 +2227,7 @@ namespace IDE.ui } contentHeight += GS!(8); mAutoCompleteListWidget.ResizeContent(windowWidth, contentHeight, wantScrollbar); + //mAutoCompleteListWidget.UpdateWidth(); if ((mInsertStartIdx != -1) && (!IsInPanel())) { UpdateWindow(ref mListWindow, mAutoCompleteListWidget, mInsertStartIdx, windowWidth, windowHeight); @@ -2963,7 +2982,7 @@ namespace IDE.ui return; } - bool isExplicitInsert = (keyChar == '\0') || (keyChar == '\t') || (keyChar == '\n'); + bool isExplicitInsert = (keyChar == '\0') || (keyChar == '\t') || (keyChar == '\n') || (keyChar == '\r'); String insertText = entry.mEntryInsert ?? entry.mEntryDisplay; if ((!isExplicitInsert) && (insertText.Contains('\t'))) @@ -2977,11 +2996,15 @@ namespace IDE.ui //insertText = insertText.Substring(0, insertText.Length - 1); String implText = null; int tabIdx = insertText.IndexOf('\t'); - if (tabIdx != -1) + int splitIdx = tabIdx; + int crIdx = insertText.IndexOf('\r'); + if ((crIdx != -1) && (tabIdx != -1) && (crIdx < tabIdx)) + splitIdx = crIdx; + if (splitIdx != -1) { implText = scope:: String(); - implText.Append(insertText, tabIdx); - insertText.RemoveToEnd(tabIdx); + implText.Append(insertText, splitIdx); + insertText.RemoveToEnd(splitIdx); } String prevText = scope String(); mTargetEditWidget.Content.ExtractString(editSelection.mStartPos, editSelection.mEndPos - editSelection.mStartPos, prevText); diff --git a/IDE/src/ui/BinaryDataWidget.bf b/IDE/src/ui/BinaryDataWidget.bf index fe372aba..a038232e 100644 --- a/IDE/src/ui/BinaryDataWidget.bf +++ b/IDE/src/ui/BinaryDataWidget.bf @@ -693,7 +693,7 @@ namespace IDE.ui for (int32 c in scope int32[] ( 4, 8, 16, 32, 64 )) { - columnChoice = menuItem.AddItem(StackStringFormat!("{0} bytes", c)); + columnChoice = menuItem.AddItem(scope String()..AppendF("{0} bytes", c)); if ((mAutoResizeType == .Manual) && (c == (int32)mBytesPerDisplayLine)) columnChoice.mIconImage = DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Check); columnChoice.mOnMenuItemSelected.Add(new (evt) => @@ -877,15 +877,15 @@ namespace IDE.ui // column header using (g.PushClip(0, 0, mWidth, GS!(mColumnHeaderHeight))) { - using (g.PushColor(0xFFFFFFFF)) + using (g.PushColor(DarkTheme.COLOR_TEXT)) { g.SetFont(mFont); float strViewColumnStart = GS!(mColumnDisplayStart) + mBytesPerDisplayLine*GS!(mColumnDisplayStride) + GS!(mStrViewDisplayStartOffset); for (int i=0; i 0xF) ? GS!(4) : 0), GS!(3), FontAlign.Left); - g.DrawString(StackStringFormat!("{0:X1}", i & 0xF), strViewColumnStart + i*GS!(mStrViewDisplayStride), GS!(3), FontAlign.Left); + g.DrawString(scope String()..AppendF("{0:X1}", i), GS!(mColumnDisplayStart) + i*GS!(mColumnDisplayStride) + GS!(mColumnDisplayStride)*0.125f - ((i > 0xF) ? GS!(4) : 0), GS!(3), FontAlign.Left); + g.DrawString(scope String()..AppendF("{0:X1}", i & 0xF), strViewColumnStart + i*GS!(mStrViewDisplayStride), GS!(3), FontAlign.Left); } } } @@ -908,7 +908,7 @@ namespace IDE.ui float displayAdj = (float)(-mShowPositionDisplayOffset * lineSpacing); using (g.PushTranslate(0, displayAdj)) { - using (g.PushColor(0xFFFFFFFF)) + using (g.PushColor(DarkTheme.COLOR_TEXT)) { //ulong lineStart = mCurPosition / mBytesPerDisplayLine; int lockSize = lineCount * mBytesPerDisplayLine; diff --git a/IDE/src/ui/BreakpointPanel.bf b/IDE/src/ui/BreakpointPanel.bf index 567a19e6..fcd1c9dc 100644 --- a/IDE/src/ui/BreakpointPanel.bf +++ b/IDE/src/ui/BreakpointPanel.bf @@ -652,17 +652,15 @@ namespace IDE.ui subItem.mOnMouseClick.Add(new => ListViewItemClicked); } var listViewItem = (BreakpointListViewItem)root.GetChildAtIndex(breakIdx); - listViewItem.mTextColor = Color.White; + listViewItem.mTextColor = DarkTheme.COLOR_TEXT; listViewItem.mIsBold = breakpoint.IsActiveBreakpoint(); var locString = scope String(); breakpoint.ToString_Location(locString); listViewItem.Label = locString; - if (breakpoint.IsBound()) - listViewItem.mTextColor = 0xFFFFFFFF; - else - listViewItem.mTextColor = 0x80FFFFFF; + if (!breakpoint.IsBound()) + listViewItem.mTextColor = (0x00FFFFFF & DarkTheme.COLOR_TEXT) | 0x80000000; // Condition var subItem = listViewItem.GetSubItem(2); diff --git a/IDE/src/ui/ConditionDialog.bf b/IDE/src/ui/ConditionDialog.bf index efd13222..db810cfd 100644 --- a/IDE/src/ui/ConditionDialog.bf +++ b/IDE/src/ui/ConditionDialog.bf @@ -369,14 +369,17 @@ namespace IDE.ui { base.Draw(g); - g.DrawString("Breakpoint Condition", mConditionEdit.mX, mConditionEdit.mY - GS!(20)); - g.DrawString("Thread Id", mThreadEdit.mX, mThreadEdit.mY - GS!(20)); - g.DrawString("Log String", mLogEdit.mX, mLogEdit.mY - GS!(20)); - g.DrawString("Break on Hit Count", mHitCountCombo.mX, mHitCountEdit.mY - GS!(19)); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + { + g.DrawString("Breakpoint Condition", mConditionEdit.mX, mConditionEdit.mY - GS!(20)); + g.DrawString("Thread Id", mThreadEdit.mX, mThreadEdit.mY - GS!(20)); + g.DrawString("Log String", mLogEdit.mX, mLogEdit.mY - GS!(20)); + g.DrawString("Break on Hit Count", mHitCountCombo.mX, mHitCountEdit.mY - GS!(19)); - var str = scope String(); - str.AppendF("Current: {0}", mStartingHitCount); - g.DrawString(str, mWidth - GS!(16) - GS!(8), mHitCountEdit.mY - GS!(19), .Right); + var str = scope String(); + str.AppendF("Current: {0}", mStartingHitCount); + g.DrawString(str, mWidth - GS!(16) - GS!(8), mHitCountEdit.mY - GS!(19), .Right); + } } } } diff --git a/IDE/src/ui/DiagnosticsPanel.bf b/IDE/src/ui/DiagnosticsPanel.bf index 664223d0..e26417b8 100644 --- a/IDE/src/ui/DiagnosticsPanel.bf +++ b/IDE/src/ui/DiagnosticsPanel.bf @@ -255,7 +255,8 @@ namespace IDE.ui { using (g.PushClip(0, 0, width, height)) { - using (g.PushColor(0xFF00FF00)) + // Using override to prevent the color turning black when using a dark text color. + using (g.PushColorOverride(0xFF00FF00)) { float prevY = 0; @@ -439,10 +440,13 @@ namespace IDE.ui using (g.PushColor(0x20FFFFFF)) g.FillRect(0, 0, mWidth, mHeight); - DrawData(g, mData, mTimes, dataRect.mX, dataRect.mY, dataRect.mWidth, dataRect.mHeight); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + { + DrawData(g, mData, mTimes, dataRect.mX, dataRect.mY, dataRect.mWidth, dataRect.mHeight); - g.SetFont(DarkTheme.sDarkTheme.mSmallBoldFont); - g.DrawString(mLabel, -GS!(0), -GS!(0), .Centered, dataRect.mX); + g.SetFont(DarkTheme.sDarkTheme.mSmallBoldFont); + g.DrawString(mLabel, -GS!(0), -GS!(0), .Centered, dataRect.mX); + } } } diff --git a/IDE/src/ui/DisassemblyPanel.bf b/IDE/src/ui/DisassemblyPanel.bf index 183b0255..154525ed 100644 --- a/IDE/src/ui/DisassemblyPanel.bf +++ b/IDE/src/ui/DisassemblyPanel.bf @@ -890,7 +890,7 @@ namespace IDE.ui { var lineData = mLineDatas[lineIdx]; if (lineData.mSourceFile != null) - g.DrawString(StackStringFormat!("{0}", lineData.mSourceLineNum + 1), GS!(8), GS!(2) + lineIdx * lineSpacing, FontAlign.Right, mEditWidget.mX - GS!(14)); + g.DrawString(scope String()..AppendF("{0}", lineData.mSourceLineNum + 1), GS!(8), GS!(2) + lineIdx * lineSpacing, FontAlign.Right, mEditWidget.mX - GS!(14)); } } @@ -1338,7 +1338,7 @@ namespace IDE.ui { if (mLineDatas[line].mAddrEnd != (int)0) { - String nextAddr = StackStringFormat!("0x{0:x}L", (int64)mLineDatas[line].mAddrEnd); + String nextAddr = scope String()..AppendF("0x{0:x}L", (int64)mLineDatas[line].mAddrEnd); nextAddr.Append(" +"); debugExpr.Replace("rip +", nextAddr); } diff --git a/IDE/src/ui/ErrorsPanel.bf b/IDE/src/ui/ErrorsPanel.bf index baff80f3..f7f79343 100644 --- a/IDE/src/ui/ErrorsPanel.bf +++ b/IDE/src/ui/ErrorsPanel.bf @@ -26,6 +26,43 @@ namespace IDE.ui public int mLine; public int mColumn; + public override void MouseClicked(float x, float y, float origX, float origY, int32 btn) + { + base.MouseClicked(x, y, origX, origY, btn); + + if (btn == 1) + { + Menu menu = new Menu(); + var menuItemCopySingle = menu.AddItem("Copy"); + menuItemCopySingle.mOnMenuItemSelected.Add(new (evt) => + { + String buffer = scope .(); + mListView.GetRoot().WithSelectedItems(scope (item) => + { + var errorItem = (ErrorsListViewItem)item; + if (!buffer.IsEmpty) + buffer.Append("\n"); + errorItem.CopyError(buffer); + }); + if (!buffer.IsEmpty) + gApp.SetClipboardText(buffer); + }); + + MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu); + menuWidget.Init(this, x, y); + } + } + + public void CopyError (String buffer) + { + var preffix = scope String(); + Font.StrRemoveColors(this.mSubItems[0].mLabel, preffix); + preffix.ToUpper(); + var description = this.mSubItems[1].mLabel; + + buffer.AppendF("{}: {} at line {}:{} in {}", preffix, description, this.mLine, this.mColumn, this.mFilePath); + } + public override void DrawSelect(Graphics g) { bool hasFocus = mListView.mHasFocus; diff --git a/IDE/src/ui/FindAndReplaceDialog.bf b/IDE/src/ui/FindAndReplaceDialog.bf index d7290fcd..a571ed62 100644 --- a/IDE/src/ui/FindAndReplaceDialog.bf +++ b/IDE/src/ui/FindAndReplaceDialog.bf @@ -67,7 +67,7 @@ namespace IDE.ui AddDialogComponent(mMatchCaseCheckbox); mMatchWholeWordCheckbox = new DarkCheckBox(); - mMatchWholeWordCheckbox.Label = "Match &whole case"; + mMatchWholeWordCheckbox.Label = "Match &whole word"; AddDialogComponent(mMatchWholeWordCheckbox); mAbortButton = new DarkButton(); @@ -322,11 +322,14 @@ namespace IDE.ui { base.Draw(g); - g.DrawString("Find what:", 6, mEditWidget.mY - GS!(18)); - if (mReplaceWidget != null) - g.DrawString("Replace with:", GS!(6), mReplaceWidget.mY - GS!(18)); - g.DrawString("Look in:", GS!(6), mLocationCombo.mY - GS!(18)); - g.DrawString("Look at these file types:", GS!(6), mFileTypesCombo.mY - GS!(18)); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + { + g.DrawString("Find what:", 6, mEditWidget.mY - GS!(18)); + if (mReplaceWidget != null) + g.DrawString("Replace with:", GS!(6), mReplaceWidget.mY - GS!(18)); + g.DrawString("Look in:", GS!(6), mLocationCombo.mY - GS!(18)); + g.DrawString("Look at these file types:", GS!(6), mFileTypesCombo.mY - GS!(18)); + } } public override void Update() diff --git a/IDE/src/ui/FindResultsPanel.bf b/IDE/src/ui/FindResultsPanel.bf index b13e85b1..46e53581 100644 --- a/IDE/src/ui/FindResultsPanel.bf +++ b/IDE/src/ui/FindResultsPanel.bf @@ -185,9 +185,10 @@ namespace IDE.ui char8* linePtr = line.Ptr; - bool lineMatched; + bool lineMatched = false; if (mSearchOptions.mMatchWholeWord) { + line.EnsureNullTerminator(); bool isNewStart = true; int lineIdx = 0; for (let c32 in line.DecodedChars) diff --git a/IDE/src/ui/GenerateDialog.bf b/IDE/src/ui/GenerateDialog.bf index 926edf70..c9ecae7e 100644 --- a/IDE/src/ui/GenerateDialog.bf +++ b/IDE/src/ui/GenerateDialog.bf @@ -581,12 +581,15 @@ namespace IDE.ui uiEntry.mName = partItr.GetNext().Value.UnQuoteString(.. new .()); uiEntry.mLabel = partItr.GetNext().Value.UnQuoteString(.. new .()); var defaultValue = partItr.GetNext().Value.UnQuoteString(.. scope .()); + var focus = partItr.GetNext().Value; DarkEditWidget editWidget = new DarkEditWidget(); uiEntry.mWidget = editWidget; editWidget.SetText(defaultValue); editWidget.mEditWidgetContent.SelectAll(); editWidget.mOnSubmit.Add(new => EditSubmitHandler); AddWidget(editWidget); + if (focus == "True") + editWidget.SetFocus(); mUIEntries.Add(uiEntry); mTabWidgets.Add(editWidget); case "addFilePath", "addFolderPath": @@ -834,9 +837,12 @@ namespace IDE.ui g.DrawString(label, widget.mX + GS!(6), widget.mY - GS!(20)); } - DrawLabel(mKindBar, mRegenerating ? "Regenerating ..." : "Generator"); - for (var uiEntry in mUIEntries) - DrawLabel(uiEntry.mWidget, uiEntry.mLabel); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + { + DrawLabel(mKindBar, mRegenerating ? "Regenerating ..." : "Generator"); + for (var uiEntry in mUIEntries) + DrawLabel(uiEntry.mWidget, uiEntry.mLabel); + } } public override void DrawAll(Graphics g) diff --git a/IDE/src/ui/GoToLineDialog.bf b/IDE/src/ui/GoToLineDialog.bf index 4549cb05..20c94076 100644 --- a/IDE/src/ui/GoToLineDialog.bf +++ b/IDE/src/ui/GoToLineDialog.bf @@ -37,7 +37,7 @@ namespace IDE.ui mDefaultButton = AddButton("OK", new (evt) => GotoLineSubmit(true)); mEscButton = AddButton("Cancel", new (evt) => Cancel()); - mEditWidget = AddEdit(StackStringFormat!("{0}", line + 1)); + mEditWidget = AddEdit(scope String()..AppendF("{0}", line + 1)); mEditWidget.mOnContentChanged.Add(new (evt) => GotoLineSubmit(false)); } diff --git a/IDE/src/ui/HoverWatch.bf b/IDE/src/ui/HoverWatch.bf index b7cd9ac8..a5bf5af7 100644 --- a/IDE/src/ui/HoverWatch.bf +++ b/IDE/src/ui/HoverWatch.bf @@ -775,7 +775,7 @@ namespace IDE.ui flags |= DebugManager.EvalExpressionFlags.DeselectCallStackIdx; if (mAllowSideEffects) flags |= .AllowSideEffects | .AllowCalls; - if (gApp.mSettings.mDebuggerSettings.mAutoEvaluateProperties) + if (gApp.mSettings.mDebuggerSettings.mAutoEvaluatePropertiesOnHover) flags |= .AllowProperties; if (watch.mResultType == .RawText) flags |= .RawStr; @@ -1311,6 +1311,7 @@ namespace IDE.ui float addHeight = nameHeight - listViewItem.mSelfHeight; listViewItem.mSelfHeight = nameHeight; + listViewItem.mFreezeHeight = true; height += addHeight; } @@ -1322,6 +1323,8 @@ namespace IDE.ui height += GS!(2); listView.Resize(popupX, popupY, useWidth, height); + listView.mListSizeDirty = true; + listView.UpdateListSize(); ResizeWindow(); } diff --git a/IDE/src/ui/NewBreakpointDialog.bf b/IDE/src/ui/NewBreakpointDialog.bf index 812bc978..b66d895e 100644 --- a/IDE/src/ui/NewBreakpointDialog.bf +++ b/IDE/src/ui/NewBreakpointDialog.bf @@ -140,7 +140,8 @@ namespace IDE.ui { base.Draw(g); - g.DrawString((mBreakpointKind == .Memory) ? "Breakpoint Address" : "Symbol Name", mAddressEdit.mX, mAddressEdit.mY - GS!(20)); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + g.DrawString((mBreakpointKind == .Memory) ? "Breakpoint Address" : "Symbol Name", mAddressEdit.mX, mAddressEdit.mY - GS!(20)); //g.DrawString("Project Directory", mDialogEditWidget.mX, mDialogEditWidget.mY - 20); } diff --git a/IDE/src/ui/NewProjectDialog.bf b/IDE/src/ui/NewProjectDialog.bf index 89345e28..a327ab59 100644 --- a/IDE/src/ui/NewProjectDialog.bf +++ b/IDE/src/ui/NewProjectDialog.bf @@ -119,7 +119,7 @@ namespace IDE.ui String projectFilePath = scope String()..Append(projDirectory, "/BeefProj.toml"); if (File.Exists(projectFilePath)) { - gApp.Fail(scope String()..AppendF("A Beef projects already exists at '{0}'", projDirectory)); + gApp.Fail(scope String()..AppendF("A Beef project already exists at '{0}'", projDirectory)); return false; } diff --git a/IDE/src/ui/OpenFileInSolutionDialog.bf b/IDE/src/ui/OpenFileInSolutionDialog.bf index 835121fb..15c9a328 100644 --- a/IDE/src/ui/OpenFileInSolutionDialog.bf +++ b/IDE/src/ui/OpenFileInSolutionDialog.bf @@ -365,8 +365,8 @@ namespace IDE.ui //Outline(g, mCategorySelector, 0, 0, 0xFF404040); //Outline(g, mCategorySelector, -1, -1, 0xFF202020); - Outline(g, mFileList, 0, 0, IDEApp.cDialogOutlineLightColor); - Outline(g, mFileList, -1, -1, IDEApp.cDialogOutlineDarkColor); + Outline(g, mFileList, 0, 0, DarkTheme.COLOR_DIALOG_OUTLINE_OUT); + Outline(g, mFileList, -1, -1, DarkTheme.COLOR_DIALOG_OUTLINE_IN); } void CheckFileDates() diff --git a/IDE/src/ui/OutputPanel.bf b/IDE/src/ui/OutputPanel.bf index a60696f8..5c544766 100644 --- a/IDE/src/ui/OutputPanel.bf +++ b/IDE/src/ui/OutputPanel.bf @@ -287,7 +287,7 @@ namespace IDE.ui public void WriteSmart(StringView text) { - for (var line in text.Split('\n')) + LineLoop: for (var line in text.Split('\n')) { if (@line.Pos != 0) Write("\n"); @@ -302,10 +302,21 @@ namespace IDE.ui mQueuedDisplayChanges.Add(QueuedDisplayChange(mQueuedText.Length, "ERROR".Length, (.)SourceElementType.BuildError)); } + if (line.StartsWith("ERROR-SOFT:")) + { + var str = scope:LineLoop String(line); + str.Replace("ERROR-SOFT", "ERROR"); + line = str; + mQueuedDisplayChanges.Add(QueuedDisplayChange(mQueuedText.Length, "ERROR".Length, (.)SourceElementType.BuildError)); + } if ((line.StartsWith("WARNING:")) || (line.StartsWith("WARNING("))) { mQueuedDisplayChanges.Add(QueuedDisplayChange(mQueuedText.Length, "WARNING".Length, (.)SourceElementType.BuildWarning)); } + if (line.StartsWith("SUCCESS:")) + { + mQueuedDisplayChanges.Add(QueuedDisplayChange(mQueuedText.Length, "SUCCESS".Length, (.)SourceElementType.BuildSuccess)); + } Write(line); } } diff --git a/IDE/src/ui/PanelHeader.bf b/IDE/src/ui/PanelHeader.bf index 8d0ed6d9..8c1da1e9 100644 --- a/IDE/src/ui/PanelHeader.bf +++ b/IDE/src/ui/PanelHeader.bf @@ -141,7 +141,7 @@ namespace IDE.ui var editWidget = new WatchStringEdit(mTooltipText, null); tooltip.mRelWidgetMouseInsets = new Insets(0, 0, GS!(-8), 0); - tooltip.mAllowMouseInsideSelf = true; + tooltip.mAllowMouseInsideInsets = new .(); tooltip.AddWidget(editWidget); tooltip.mOnResized.Add(new (widget) => editWidget.Resize(GS!(6), GS!(6), widget.mWidth - GS!(6) * 2, widget.mHeight - GS!(6) * 2)); tooltip.mOnResized(tooltip); diff --git a/IDE/src/ui/ProfileDialog.bf b/IDE/src/ui/ProfileDialog.bf index e3224155..348fcbc7 100644 --- a/IDE/src/ui/ProfileDialog.bf +++ b/IDE/src/ui/ProfileDialog.bf @@ -95,9 +95,12 @@ namespace IDE.ui { base.Draw(g); - DrawLabel(g, mDescEdit, "Profile Description (Optional)"); - DrawLabel(g, mThreadCombo, "Thread"); - DrawLabel(g, mSampleRateEdit, "Sample Rate"); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + { + DrawLabel(g, mDescEdit, "Profile Description (Optional)"); + DrawLabel(g, mThreadCombo, "Thread"); + DrawLabel(g, mSampleRateEdit, "Sample Rate"); + } } public override void CalcSize() diff --git a/IDE/src/ui/ProfilePanel.bf b/IDE/src/ui/ProfilePanel.bf index 745de034..58828ca2 100644 --- a/IDE/src/ui/ProfilePanel.bf +++ b/IDE/src/ui/ProfilePanel.bf @@ -684,24 +684,27 @@ namespace IDE.ui { base.Draw(g); + g.PushColor(DarkTheme.COLOR_TEXT); + defer g.PopColor(); + g.SetFont(DarkTheme.sDarkTheme.mSmallFont); g.DrawString("Session", mSessionComboBox.mX - GS!(2), mSessionComboBox.mY, .Right); g.DrawString("Thread", mThreadComboBox.mX - GS!(2), mThreadComboBox.mY, .Right); if ((mProfiler != null) && (mShowOverview != null)) { - g.DrawString(StackStringFormat!("Rate: {0} Hz", mShowOverview.mSamplesPerSecond), GS!(320), GS!(2)); + g.DrawString(scope String()..AppendF("Rate: {0} Hz", mShowOverview.mSamplesPerSecond), GS!(320), GS!(2)); int32 seconds = (mShowOverview.mRecordedTicks / 1000); - g.DrawString(StackStringFormat!("Length: {0}:{1:00}.{2}", seconds / 60, seconds % 60, (mShowOverview.mRecordedTicks % 1000)/100), GS!(320), GS!(22)); + g.DrawString(scope String()..AppendF("Length: {0}:{1:00}.{2}", seconds / 60, seconds % 60, (mShowOverview.mRecordedTicks % 1000)/100), GS!(320), GS!(22)); seconds = (mShowOverview.mEndedTicks / 1000); if (seconds > 60*60) - g.DrawString(StackStringFormat!("Age: {0}:{1:00}:{2:00}", seconds / 60 / 60, (seconds / 60) % 60, seconds % 60), GS!(420), GS!(22)); + g.DrawString(scope String()..AppendF("Age: {0}:{1:00}:{2:00}", seconds / 60 / 60, (seconds / 60) % 60, seconds % 60), GS!(420), GS!(22)); else - g.DrawString(StackStringFormat!("Age: {0}:{1:00}", seconds / 60, seconds % 60), GS!(420), GS!(22)); + g.DrawString(scope String()..AppendF("Age: {0}:{1:00}", seconds / 60, seconds % 60), GS!(420), GS!(22)); - g.DrawString(StackStringFormat!("Samples: {0}", mShowOverview.mTotalActualSamples), GS!(420), GS!(2)); - g.DrawString(StackStringFormat!("Missed Samples: {0}", mShowOverview.mTotalVirtualSamples - mShowOverview.mTotalActualSamples), GS!(550), GS!(2)); + g.DrawString(scope String()..AppendF("Samples: {0}", mShowOverview.mTotalActualSamples), GS!(420), GS!(2)); + g.DrawString(scope String()..AppendF("Missed Samples: {0}", mShowOverview.mTotalVirtualSamples - mShowOverview.mTotalActualSamples), GS!(550), GS!(2)); } if (mTickCreated != 0) diff --git a/IDE/src/ui/ProjectPanel.bf b/IDE/src/ui/ProjectPanel.bf index 43d04552..e3a96f43 100644 --- a/IDE/src/ui/ProjectPanel.bf +++ b/IDE/src/ui/ProjectPanel.bf @@ -1257,9 +1257,9 @@ namespace IDE.ui { String errorStr; if (alreadyHadFileList.Count == 1) - errorStr = StackStringFormat!("Project already contained file: {0}", alreadyHadFileList[0]); + errorStr = scope:: String()..AppendF("Project already contained file: {0}", alreadyHadFileList[0]); else - errorStr = StackStringFormat!("Project already contained {0} of the {1} files specified", alreadyHadFileList.Count, totalFileCount); + errorStr = scope:: String()..AppendF("Project already contained {0} of the {1} files specified", alreadyHadFileList.Count, totalFileCount); IDEApp.sApp.Fail(errorStr); } } @@ -1991,7 +1991,7 @@ namespace IDE.ui if (projectCount == 1) { - aDialog = ThemeFactory.mDefault.CreateDialog("Remove Project", StackStringFormat!("Remove project '{0}' from the workspace?", selectedProjectItem.mProject.mProjectName)); + aDialog = ThemeFactory.mDefault.CreateDialog("Remove Project", scope String()..AppendF("Remove project '{0}' from the workspace?", selectedProjectItem.mProject.mProjectName)); } else { @@ -2009,7 +2009,7 @@ namespace IDE.ui if (fileCount + folderCount == 1) { aDialog = ThemeFactory.mDefault.CreateDialog((fileCount > 0) ? "Delete File" : "Delete Folder", - StackStringFormat!("Choose Remove to remove '{0}' from '{1}'.\n\nChoose Delete to permanently delete '{0}'.", selectedProjectItem.mName, selectedProjectItem.mProject.mProjectName)); + scope String()..AppendF("Choose Remove to remove '{0}' from '{1}'.\n\nChoose Delete to permanently delete '{0}'.", selectedProjectItem.mName, selectedProjectItem.mProject.mProjectName)); } else { @@ -2034,12 +2034,12 @@ namespace IDE.ui if (projectsReferenced.Count == 1) { aDialog = ThemeFactory.mDefault.CreateDialog(title, - StackStringFormat!("Choose Remove to remove the selected {0} from '{1}'.\n\nChoose Delete to permanently delete the selected items.", typeDeleting, selectedProjectItem.mProject.mProjectName)); + scope String()..AppendF("Choose Remove to remove the selected {0} from '{1}'.\n\nChoose Delete to permanently delete the selected items.", typeDeleting, selectedProjectItem.mProject.mProjectName)); } else { aDialog = ThemeFactory.mDefault.CreateDialog(title, - StackStringFormat!("Choose Remove to removed the selected {0}.\n\nChoose Delete to permanently delete the selected items.", typeDeleting)); + scope String()..AppendF("Choose Remove to removed the selected {0}.\n\nChoose Delete to permanently delete the selected items.", typeDeleting)); } } @@ -2580,7 +2580,7 @@ namespace IDE.ui /*if ((IDEApp.IsBeefFile(prevPath) != IDEApp.IsBeefFile(newPath)) || (IDEApp.IsClangSourceFile(prevPath) != IDEApp.IsClangSourceFile(newPath))) { - IDEApp.sApp.Fail(StackStringFormat!("Invalid file extension change, cannot rename '{0}' to '{1}'", prevPath, newPath)); + IDEApp.sApp.Fail(scope String()..AppendF("Invalid file extension change, cannot rename '{0}' to '{1}'", prevPath, newPath)); return; }*/ @@ -2798,7 +2798,7 @@ namespace IDE.ui if (!File.Exists(filePath)) { - gApp.Fail(StackStringFormat!("Project file not found: {0}", filePath)); + gApp.Fail(scope String()..AppendF("Project file not found: {0}", filePath)); return null; } @@ -2823,7 +2823,7 @@ namespace IDE.ui Path.GetFileNameWithoutExtension(filePath, projName); if (gApp.mWorkspace.FindProject(projName) != null) { - gApp.Fail(StackStringFormat!("A project named '{0}' name already exists in the workspace.", projName)); + gApp.Fail(scope String()..AppendF("A project named '{0}' name already exists in the workspace.", projName)); return null; } @@ -2841,7 +2841,7 @@ namespace IDE.ui InitProject(proj, workspaceFolder); if (failed) { - gApp.Fail(StackStringFormat!("Failed to load project: {0}", filePath)); + gApp.Fail(scope String()..AppendF("Failed to load project: {0}", filePath)); return proj; } diff --git a/IDE/src/ui/ProjectProperties.bf b/IDE/src/ui/ProjectProperties.bf index aa728de0..091dbe15 100644 --- a/IDE/src/ui/ProjectProperties.bf +++ b/IDE/src/ui/ProjectProperties.bf @@ -884,7 +884,7 @@ namespace IDE.ui if (projectSpec.mVerSpec case .Git(let url, let ver)) { dependencyEntry.SetValue(1, url); - dependencyEntry.SetValue(2, ver.mVersion); + dependencyEntry.SetValue(2, ver?.mVersion); } } } diff --git a/IDE/src/ui/PropertiesDialog.bf b/IDE/src/ui/PropertiesDialog.bf index 5f9d7638..8593e660 100644 --- a/IDE/src/ui/PropertiesDialog.bf +++ b/IDE/src/ui/PropertiesDialog.bf @@ -195,6 +195,7 @@ namespace IDE.ui None, BrowseForFile, BrowseForFolder, + Percent } public enum MultiEncodeKind @@ -1036,8 +1037,10 @@ namespace IDE.ui } else if (curVariantType.[Friend]mTypeCode == .Float) { - if (float.Parse(newValue) case .Ok(let floatVal)) + if (float.Parse(newValue) case .Ok(var floatVal)) { + if (editingProp.mFlags.HasFlag(.Percent)) + floatVal /= 100; editingProp.mCurValue = Variant.Create(floatVal); } else @@ -1536,7 +1539,7 @@ namespace IDE.ui } if (i < strVals.Count) { - childItem.Label = StackStringFormat!("#{0}", i + 1); + childItem.Label = scope String()..AppendF("#{0}", i + 1); childSubItem.mTextColor = DarkTheme.COLOR_TEXT; } else @@ -1600,6 +1603,8 @@ namespace IDE.ui { let valStr = scope String(); float floatVal = propEntry.mCurValue.Get(); + if (propEntry.mFlags.HasFlag(.Percent)) + floatVal *= 100; floatVal.ToString(valStr); valueItem.Label = valStr; } @@ -2042,7 +2047,7 @@ namespace IDE.ui let width = GetValueEditWidth(subItem); propEntry.mEditInsets.mRight = GS!(22); browseButton.mLabelYOfs = GS!(-2); - browseButton.Resize(width - GS!(21), GS!(1), GS!(20), subItem.mHeight - 2); + browseButton.Resize(width - GS!(21), GS!(1), GS!(20), Math.Max(subItem.mHeight - 2, 0)); }); subItem.AddWidget(browseButton); diff --git a/IDE/src/ui/QuickFind.bf b/IDE/src/ui/QuickFind.bf index dbba189b..40046394 100644 --- a/IDE/src/ui/QuickFind.bf +++ b/IDE/src/ui/QuickFind.bf @@ -182,7 +182,7 @@ namespace IDE.ui mEditWidget.SetFocus(); replaceCount = Replace(true); if (replaceCount > 0) - IDEApp.sApp.MessageDialog("Replace Results", StackStringFormat!("{0} instance(s) replaced.", replaceCount)); + IDEApp.sApp.MessageDialog("Replace Results", scope String()..AppendF("{0} instance(s) replaced.", replaceCount)); if (replaceCount != -1) { diff --git a/IDE/src/ui/RenameSymbolDialog.bf b/IDE/src/ui/RenameSymbolDialog.bf index 056af1ca..56539b26 100644 --- a/IDE/src/ui/RenameSymbolDialog.bf +++ b/IDE/src/ui/RenameSymbolDialog.bf @@ -367,11 +367,7 @@ namespace IDE.ui { String lineStr = scope String(); editWidgetContent.ExtractString(lineStart, idx - lineStart, lineStr); - - //String fileName = editData.mFilePath; - //String fileName = scope String(); - //projectSource.GetFullImportPath(fileName); - + lineStr.Trim(); gApp.mFindResultsPanel.QueueLine(editData, lineNum, 0, lineStr); wantsLine = false; @@ -719,7 +715,7 @@ namespace IDE.ui editWidgetContent.mData.mUndoManager.Add(insertTextAction); editWidgetContent.PhysInsertAtCursor(newStr, false); - if (spanStart <= cursorPos) + if (spanStart + strLenDiff <= cursorPos) cursorPos += strLenDiff; } diff --git a/IDE/src/ui/SettingsDialog.bf b/IDE/src/ui/SettingsDialog.bf index 340ed4bb..f2799664 100644 --- a/IDE/src/ui/SettingsDialog.bf +++ b/IDE/src/ui/SettingsDialog.bf @@ -98,6 +98,7 @@ namespace IDE.ui category.mTextColor = Color.Mult(DarkTheme.COLOR_TEXT, cHeaderColor); AddPropertiesItem(category, "Font", "mFonts"); AddPropertiesItem(category, "Font Size", "mFontSize"); + AddPropertiesItem(category, "Line Height Scale", "mLineHeightScale", null, .Percent); AddPropertiesItem(category, "Autocomplete", "mAutoCompleteShowKind"); AddPropertiesItem(category, "Autocomplete Require Control", "mAutoCompleteRequireControl"); AddPropertiesItem(category, "Autocomplete Require Tab", "mAutoCompleteRequireTab"); @@ -347,7 +348,8 @@ namespace IDE.ui AddPropertiesItem(root, "Symbol File Locations", "mSymbolSearchPath"); AddPropertiesItem(root, "Auto Find Paths", "mAutoFindPaths"); AddPropertiesItem(root, "Profile Sample Rate", "mProfileSampleRate"); - AddPropertiesItem(root, "Auto Evaluate Properties", "mAutoEvaluateProperties"); + AddPropertiesItem(root, "Auto Eval Properties on Hover", "mAutoEvaluatePropertiesOnHover"); + AddPropertiesItem(root, "Auto Refresh Side Effects", "mAutoRefreshWatches"); } protected override void ResetSettings() diff --git a/IDE/src/ui/SourceEditWidgetContent.bf b/IDE/src/ui/SourceEditWidgetContent.bf index abdd83ce..8a145c72 100644 --- a/IDE/src/ui/SourceEditWidgetContent.bf +++ b/IDE/src/ui/SourceEditWidgetContent.bf @@ -1,3 +1,4 @@ +#pragma warning disable 168 using System; using System.Collections; using System.Text; @@ -806,6 +807,7 @@ namespace IDE.ui 0xFFFF8080, // BuildError 0xFFFFFF80, // BuildWarning + 0xFF80FF80, // BuildSuccess 0xFF9090C0, // VisibleWhiteSpace ) ~ delete _; @@ -856,6 +858,7 @@ namespace IDE.ui SetFont(IDEApp.sApp.mCodeFont, true, true); //SetFont(DarkTheme.sDarkTheme.mSmallFont, false, false); + mLineHeightScale = Math.Clamp(gApp.mSettings.mEditorSettings.mLineHeightScale, 0.125f, 10.0f); mWantsTabsAsSpaces = gApp.mSettings.mEditorSettings.mTabsOrSpaces == .Spaces; mTabLength = gApp.mSettings.mEditorSettings.mTabSize; mTabSize = mFont.GetWidth(scope String(' ', gApp.mSettings.mEditorSettings.mTabSize)); @@ -1209,11 +1212,12 @@ namespace IDE.ui if ((flags & ~(uint8)SourceElementFlags.Skipped) == 0) return; + let lineSpacing = LineHeight; if ((flags & (uint8)SourceElementFlags.SymbolReference) != 0) { bool isRenameSymbol = (IDEApp.sApp.mSymbolReferenceHelper != null) && (IDEApp.sApp.mSymbolReferenceHelper.mKind == SymbolReferenceHelper.Kind.Rename); using (g.PushColor(isRenameSymbol ? (uint32)0x28FFFFFF : (uint32)0x18FFFFFF)) - g.FillRect(x, y, width, mFont.GetLineSpacing()); + g.FillRect(x, y, width, lineSpacing); DrawSectionFlagsOver(g, x, y, width, (uint8)(flags & ~(uint8)SourceElementFlags.SymbolReference)); return; @@ -1222,7 +1226,7 @@ namespace IDE.ui if ((flags & (uint8)SourceElementFlags.Find_CurrentSelection) != 0) { using (g.PushColor(0x504C575C)) - g.FillRect(x, y, width, mFont.GetLineSpacing()); + g.FillRect(x, y, width, lineSpacing); DrawSectionFlagsOver(g, x, y, width, (uint8)(flags & ~(uint8)(SourceElementFlags.Find_CurrentSelection | .Find_Matches))); return; @@ -1231,7 +1235,7 @@ namespace IDE.ui if ((flags & (uint8)SourceElementFlags.Find_Matches) != 0) { using (g.PushColor(0x50D0C090)) - g.FillRect(x, y, width, mFont.GetLineSpacing()); + g.FillRect(x, y, width, lineSpacing); DrawSectionFlagsOver(g, x, y, width, (uint8)(flags & ~(uint8)SourceElementFlags.Find_Matches)); return; @@ -1275,7 +1279,7 @@ namespace IDE.ui if (underlineColor != 0) { using (g.PushColor(underlineColor)) - gApp.DrawSquiggle(g, x, y, width); + gApp.DrawSquiggle(g, x, y + GetTextOffset(), width); } } } @@ -3015,6 +3019,7 @@ namespace IDE.ui didLineComment = false; lineStartCol = 0; + int appendedCount = 0; for (int i = minPos; i < maxPos; i++) { var c = mData.mText[i].mChar; @@ -3052,6 +3057,8 @@ namespace IDE.ui InsertAtCursor(str); didLineComment = true; maxPos += str.Length; + if (i <= startTextPos + appendedCount) + appendedCount += str.Length; } } mSelection = EditSelection(minPos, maxPos); @@ -3059,7 +3066,10 @@ namespace IDE.ui if (undoBatchStart != null) mData.mUndoManager.Add(undoBatchStart.mBatchEnd); - CursorLineAndColumn = startLineAndCol; + if (appendedCount > 0) + CursorTextPos = startTextPos + appendedCount; + else + CursorLineAndColumn = startLineAndCol; if (!hadSelection) mSelection = null; @@ -3889,7 +3899,10 @@ namespace IDE.ui } else if (c == '.') { - doAutocomplete = true; + if (mAutoComplete?.HasInteracted == true) + { + doAutocomplete = true; + } } } } @@ -4188,8 +4201,10 @@ namespace IDE.ui //int cursorTextPos = CursorTextPos; if (cursorTextPos < mData.mTextLength) { - charUnderCursor = (char8)mData.mText[cursorTextPos].mChar; - cursorInOpenSpace = ((charUnderCursor == ')') || (charUnderCursor == ']') || (charUnderCursor == ';') || (charUnderCursor == (char8)0) || (charUnderCursor.IsWhiteSpace)); + let charData = mData.mText[cursorTextPos]; + let cursorInLiteral = (SourceElementType)charData.mDisplayTypeId == .Literal; + charUnderCursor = (char8)charData.mChar; + cursorInOpenSpace = ((!cursorInLiteral) && ((charUnderCursor == ')') || (charUnderCursor == ']') || (charUnderCursor == ';') || (charUnderCursor == (char8)0) || (charUnderCursor.IsWhiteSpace))); if (((keyChar == '(') && (charUnderCursor == ')')) || ((keyChar == '[') && (charUnderCursor == ']'))) @@ -4224,7 +4239,7 @@ namespace IDE.ui } else { - if ((keyChar == '"') || (keyChar == '\'')) + if ((!cursorInLiteral) && ((keyChar == '"') || (keyChar == '\''))) cursorInOpenSpace = true; } } @@ -5077,7 +5092,7 @@ namespace IDE.ui int callInstLoc = (int)int64.Parse(callInstLocStr, System.Globalization.NumberStyles.HexNumber); if (callData.Count == 1) { - callMenuItem = stepIntoSpecificMenu.AddItem(StackStringFormat!("Indirect call at 0x{0:X}", callInstLoc)); + callMenuItem = stepIntoSpecificMenu.AddItem(scope String()..AppendF("Indirect call at 0x{0:X}", callInstLoc)); } else { @@ -5175,6 +5190,33 @@ namespace IDE.ui } } + void SetCollapseLineOpen(int line, bool wantOpen, bool includeChildren) + { + List collapseOpenList = scope .(); + CollapseEntry* parentCollapse = null; + + for (var collapse in mOrderedCollapseEntries) + { + if (parentCollapse == null) + { + if (collapse.mAnchorLine == line) + { + parentCollapse = collapse; + collapseOpenList.Add((.)@collapse.Index); + if (!includeChildren) + break; + } + } + else if ((collapse.mAnchorLine >= parentCollapse.mStartLine) && (collapse.mAnchorLine < parentCollapse.mEndLine)) + { + collapseOpenList.Add((.)@collapse.Index); + } + } + + for (int32 collapseIdx in collapseOpenList) + SetCollapseOpen(collapseIdx, wantOpen); + } + public override void MouseDown(float x, float y, int32 btn, int32 btnCount) { int line = GetLineAt(y); @@ -5187,10 +5229,13 @@ namespace IDE.ui embed.MouseDown(GetEmbedRect(line, embed), x, y, btn, btnCount); if (btn == 0) { - if (btnCount % 2 == 0) + bool hasCtrl = mWidgetWindow.IsKeyDown(.Control); + if ((btnCount % 2 == 0) || (hasCtrl)) { if (var collapseSummary = embed as SourceEditWidgetContent.CollapseSummary) - SetCollapseOpen(collapseSummary.mCollapseIndex, true); + { + SetCollapseLineOpen(line, true, hasCtrl); + } else if (var emitEmbed = embed as EmitEmbed) { emitEmbed.mIsOpen = !emitEmbed.mIsOpen; @@ -5198,6 +5243,28 @@ namespace IDE.ui } } } + else if (btn == 1) + { + float useX = x; + float useY = y; + + Menu menu = new Menu(); + + var menuItem = menu.AddItem("Expand"); + menuItem.mOnMenuItemSelected.Add(new (evt) => + { + SetCollapseLineOpen(line, true, false); + }); + + menuItem = menu.AddItem("Expand All|Ctrl+Click"); + menuItem.mOnMenuItemSelected.Add(new (evt) => + { + SetCollapseLineOpen(line, true, true); + }); + + MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu); + menuWidget.Init(this, useX, useY); + } return; } } @@ -5794,7 +5861,7 @@ namespace IDE.ui } orderedEmitEmbeds.Sort(scope (lhs, rhs) => lhs.line <=> rhs.line); - float fontHeight = mFont.GetLineSpacing(); + float fontHeight = LineHeight; int prevJumpIdx = -1; float jumpCoordSpacing = GetJumpCoordSpacing(); @@ -6373,8 +6440,10 @@ namespace IDE.ui let height = mFont.GetHeight() + GS!(2); using (g.PushColor(DarkTheme.COLOR_CHAR_PAIR_HILITE)) { - g.FillRect(x1, y1, charWidth, height); - g.FillRect(x2, y2, charWidth, height); + float offset = GetTextOffset(); + + g.FillRect(x1, y1 + offset, charWidth, height); + g.FillRect(x2, y2 + offset, charWidth, height); } } } @@ -6543,7 +6612,6 @@ namespace IDE.ui } } - public void SetCollapseOpen(int collapseIdx, bool wantOpen, bool immediate = false, bool keepCursorVisible = false) { var entry = mOrderedCollapseEntries[collapseIdx]; diff --git a/IDE/src/ui/SourceViewPanel.bf b/IDE/src/ui/SourceViewPanel.bf index ac4362a0..994a63dc 100644 --- a/IDE/src/ui/SourceViewPanel.bf +++ b/IDE/src/ui/SourceViewPanel.bf @@ -47,6 +47,7 @@ namespace IDE.ui BuildError, BuildWarning, + BuildSuccess, VisibleWhiteSpace } @@ -95,7 +96,7 @@ namespace IDE.ui base.MouseDown(x, y, btn, btnCount); } - public override void MouseWheel(float x, float y, float deltaX, float deltaY) + public override void MouseWheel(MouseEvent evt) { var sewc = mEditWidgetContent as SourceEditWidgetContent; if ((sewc.mSourceViewPanel != null) && (sewc.mSourceViewPanel.mEmbedParent != null)) @@ -104,19 +105,26 @@ namespace IDE.ui { if ((mVertScrollbar != null) && (mVertScrollbar.mAllowMouseWheel)) { - mVertScrollbar.MouseWheel(x, y, 0, deltaY); + mVertScrollbar.MouseWheel(evt.mX, evt.mY, evt.mWheelDeltaX, evt.mWheelDeltaY); return; } } var target = sewc.mSourceViewPanel.mEmbedParent.mEditWidget.mEditWidgetContent; - SelfToOtherTranslate(target, x, y, var transX, var transY); - target.MouseWheel(transX, transY, deltaX, deltaY); + + MouseEvent parentEvt = scope .(); + parentEvt.mWheelDeltaX = evt.mWheelDeltaX; + parentEvt.mWheelDeltaY = evt.mWheelDeltaY; + parentEvt.mSender = evt.mSender; + + // Keep passing it up until some is interested in using it... + SelfToOtherTranslate(target, evt.mX, evt.mY, out parentEvt.mX, out parentEvt.mY); + + target.MouseWheel(evt); return; } - - base.MouseWheel(x, y, deltaX, deltaY); + base.MouseWheel(evt); } public override void GotFocus() @@ -3809,7 +3817,7 @@ namespace IDE.ui var text = scope String(); if (gApp.LoadTextFile(mFilePath, text) case .Err) { - gApp.Fail(StackStringFormat!("Failed to open file '{0}'", mFilePath)); + gApp.Fail(scope String()..AppendF("Failed to open file '{0}'", mFilePath)); return; } @@ -4542,7 +4550,7 @@ namespace IDE.ui { float editX = GetEditX(); - float lineSpacing = ewc.mFont.GetLineSpacing(); + float lineSpacing = ewc.LineHeight; int cursorLineNumber = mEditWidget.mEditWidgetContent.CursorLineAndColumn.mLine; bool hiliteCurrentLine = mEditWidget.mHasFocus; @@ -4711,6 +4719,8 @@ namespace IDE.ui }*/ } + float offset = ewc.GetTextOffset(); + if ((gApp.mSettings.mEditorSettings.mShowLineNumbers) && (mEmbedKind == .None)) { String lineStr = scope String(16); @@ -4739,7 +4749,8 @@ namespace IDE.ui case 2: lineStr.AppendF("{0}", (lineIdx + 1) % 100); default: lineStr.AppendF("{0}", lineIdx + 1); } - g.DrawString(lineStr, 0, GS!(2) + ewc.mLineCoords[lineIdx], FontAlign.Right, editX - GS!(14)); + using (g.PushColor(DarkTheme.COLOR_TEXT)) + g.DrawString(lineStr, 0, GS!(2) + ewc.mLineCoords[lineIdx] + offset, FontAlign.Right, editX - GS!(14)); } } } @@ -4778,7 +4789,7 @@ namespace IDE.ui { using (g.PushColor(0xFFA5A5A5)) { - g.FillRect(editX - (int)GS!(7.5f), ewc.mLineCoords[lineIdx] - (int)GS!(0.5f), (int)GS!(1.5f), lineSpacing); + g.FillRect(editX - (int)GS!(7.5f), ewc.mLineCoords[lineIdx] - offset - (int)GS!(0.5f), (int)GS!(1.5f), lineSpacing + offset); g.FillRect(editX - (int)GS!(7.5f), ewc.mLineCoords[lineIdx] + lineSpacing - (int)GS!(1.5f), GS!(5), (int)GS!(1.5f)); } } @@ -4850,7 +4861,7 @@ namespace IDE.ui mLinePointerDrawData.mUpdateCnt = gApp.mUpdateCnt; mLinePointerDrawData.mDebuggerContinueIdx = gApp.mDebuggerContinueIdx; g.Draw(img, mEditWidget.mX - GS!(20) - sDrawLeftAdjust, - 0 + ewc.GetLineY(lineNum, 0)); + 0 + ewc.GetLineY(lineNum, 0) + ewc.GetTextOffset()); } if (mMousePos != null && mIsDraggingLinePointer) @@ -4860,7 +4871,7 @@ namespace IDE.ui { using (g.PushColor(0x7FFFFFFF)) g.Draw(img, mEditWidget.mX - GS!(20) - sDrawLeftAdjust, - 0 + ewc.GetLineY(dragLineNum, 0)); + 0 + ewc.GetLineY(dragLineNum, 0) + ewc.GetTextOffset()); } } } @@ -5121,10 +5132,16 @@ namespace IDE.ui if (!mIsBeefSource) return; - var bfSystem = IDEApp.sApp.mBfResolveSystem; + var bfSystem = IDEApp.sApp.mBfResolveSystem; if (bfSystem == null) return; - var parser = bfSystem.CreateEmptyParser(null); + let projectSource = FilteredProjectSource; + BfProject bfProject = null; + if ((projectSource != null) && (mIsBeefSource)) + { + bfProject = bfSystem.GetBfProject(projectSource.mProject); + } + var parser = bfSystem.CreateEmptyParser(bfProject); defer delete parser; var text = scope String(); mEditWidget.GetText(text); @@ -5179,7 +5196,7 @@ namespace IDE.ui public void GotoLine() { - GoToLineDialog aDialog = new GoToLineDialog("Go To Line", StackStringFormat!("Line Number ({0}-{1})", 1, mEditWidget.Content.GetLineCount())); + GoToLineDialog aDialog = new GoToLineDialog("Go To Line", scope String()..AppendF("Line Number ({0}-{1})", 1, mEditWidget.Content.GetLineCount())); aDialog.Init(this); aDialog.PopupWindow(mWidgetWindow); } @@ -5645,8 +5662,22 @@ namespace IDE.ui if ((mHoverResolveTask == null) && ((debugExpr == null) || (!debugExpr.StartsWith(':')))) { + bool wantDebugEval = false; + if ((gApp.mDebugger.mIsRunning) && (!String.IsNullOrEmpty(debugExpr))) + { + wantDebugEval = true; + if (FilteredProjectSource != null) + { + // This is active Beef source + if ((debugExpr[0].IsNumber) || (debugExpr.StartsWith('\'')) || (debugExpr.StartsWith('"'))) + { + // Literal, don't debug eval + wantDebugEval = false; + } + } + } - if (((!gApp.mDebugger.mIsRunning) || (!mHoverWatch.HasDisplay)) && // Don't show extended information for debug watches + if (((!wantDebugEval) || (!mHoverWatch.HasDisplay)) && // Don't show extended information for debug watches (!handlingHoverResolveTask) && (ResolveCompiler != null) && (!ResolveCompiler.mThreadWorkerHi.mThreadRunning) && (gApp.mSettings.mEditorSettings.mHiliteCursorReferences) && (!gApp.mDeterministic)) { ResolveParams resolveParams = new .(); @@ -7335,7 +7366,13 @@ namespace IDE.ui int collapseIndex = collapseVal & CollapseRegionView.cIdMask; var entry = ewc.mOrderedCollapseEntries[collapseIndex]; - ewc.SetCollapseOpen(collapseIndex, !entry.mIsOpen); + + if ((mWidgetWindow.IsKeyDown(.Control))) + { + ewc.[Friend]SetCollapseLineOpen(lineClick, !entry.mIsOpen, true); + } + else + ewc.SetCollapseOpen(collapseIndex, !entry.mIsOpen); } return; } @@ -7521,7 +7558,7 @@ namespace IDE.ui SourceEditWidgetContent ewc = (.)mEditWidget.Content; Rect linePointerRect = .( mEditWidget.mX - GS!(20) - sDrawLeftAdjust, - 0 + ewc.GetLineY(mLinePointerDrawData.mLine, 0), + 0 + ewc.GetLineY(mLinePointerDrawData.mLine, 0) + ewc.GetTextOffset(), GS!(15), GS!(15) ); @@ -7538,7 +7575,7 @@ namespace IDE.ui else if (mIsDraggingLinePointer) { SourceEditWidgetContent ewc = (.)mEditWidget.Content; - float linePos = ewc.GetLineY(GetLineAt(0, mMousePos.Value.y), 0); + float linePos = ewc.GetLineY(GetLineAt(0, mMousePos.Value.y), 0) + ewc.GetTextOffset(); Rect visibleRange = mEditWidget.GetVisibleContentRange(); if (visibleRange.Top > linePos) diff --git a/IDE/src/ui/StartupPanel.bf b/IDE/src/ui/StartupPanel.bf index ca6822c4..f11c17d6 100644 --- a/IDE/src/ui/StartupPanel.bf +++ b/IDE/src/ui/StartupPanel.bf @@ -1,3 +1,5 @@ +#pragma warning disable 168 + using System; using System.IO; using System.Collections; @@ -9,6 +11,7 @@ using IDE.util; using Beefy.events; using Beefy.theme; using System.Diagnostics; +using Beefy.utils; namespace IDE.ui { @@ -17,6 +20,7 @@ namespace IDE.ui class RecentWorkspacesScrollWidget : ScrollableWidget { public Font mTitleFont; + public bool mHasIcons; public this() { @@ -31,7 +35,7 @@ namespace IDE.ui public override void Resize(float x, float y, float width, float height) { - const float MARGIN = 3; + const float MARGIN = 0; float currentY = 0; float fillWidth = width - (mVertScrollbar?.Width).GetValueOrDefault(); @@ -40,7 +44,7 @@ namespace IDE.ui { for (let widget in mScrollContent.mChildWidgets) { - widget.Resize(0, currentY, fillWidth, GS!(30)); + widget.Resize(0, currentY, fillWidth, GS!(33)); currentY += widget.Height + MARGIN; } } @@ -60,7 +64,7 @@ namespace IDE.ui g.SetFont(mTitleFont); using (g.PushColor(gApp.mSettings.mUISettings.mColors.mText)) - g.DrawString("Recent Workspaces", 0, GS!(-30), .Centered, mWidth, .Ellipsis); + g.DrawString("Recent Workspaces", GS!(2), GS!(-30), .Centered, mWidth - GS!(4), .Ellipsis); base.Draw(g); } @@ -70,6 +74,8 @@ namespace IDE.ui { public static Font s_Font; append String mPath; + public bool mTriedLoadIcon; + public Image mIcon ~ delete _; public bool mPinned; public RecentWorkspacesScrollWidget mRecentsParent; @@ -91,8 +97,67 @@ namespace IDE.ui } + if (!mTriedLoadIcon) + { + mTriedLoadIcon = true; + + StructuredData sd = scope .(); + if (sd.Load(scope $"{mPath}/BeefProj.toml") case .Ok) + { + using (sd.Open("Platform")) + { + using (sd.Open("Windows")) + { + var iconFileName = sd.GetString("IconFile", .. scope .()); + iconFileName.Replace("$(ProjectDir)", mPath); + iconFileName.Replace("$(WorkspaceDir)", mPath); + var iconFilePath = IO.Path.GetAbsolutePath(iconFileName, mPath, .. scope .()); + + if (File.Exists(iconFilePath)) + { + int wantSize = 32; + if (var image = ResourceGen.LoadIcon(iconFilePath, wantSize)) + { + if ((image.mWidth != wantSize) || (image.mHeight != wantSize)) + { + image.Scale(wantSize / Math.Max(image.mWidth, image.mHeight)); + } + mIcon = image; + mRecentsParent.mHasIcons = true; + } + } + } + } + } + } + g.SetFont(s_Font); - g.DrawString(mPath, 10, 0, .Left, mWidth - 10); + using (g.PushColor(gApp.mSettings.mUISettings.mColors.mText)) + { + String drawStr = scope String(); + int lastSlash = Math.Max(mPath.LastIndexOf('\\'), mPath.LastIndexOf('/')); + if (lastSlash != -1) + { + drawStr.Append(Font.EncodeColor(0x80FFFFFF)); + drawStr.Append(mPath.Substring(0, lastSlash + 1)); + drawStr.Append(Font.EncodePopColor()); + drawStr.Append(mPath.Substring(lastSlash + 1)); + } + else + { + drawStr.Append(mPath); + } + + float drawX = GS!(50); + g.DrawString(drawStr, drawX, GS!(3), .Left, mWidth - drawX - GS!(2), .Ellipsis); + } + if (mIcon != null) + g.DrawCentered(mIcon, GS!(24), mHeight / 2); + else + { + using (g.PushColor(0x80FFFFFF)) + g.DrawCentered(DarkTheme.sDarkTheme.GetImage(.Workspace), GS!(24), mHeight / 2); + } } public override void MouseEnter() diff --git a/IDE/src/ui/StatusBar.bf b/IDE/src/ui/StatusBar.bf index f3db2678..2e917124 100644 --- a/IDE/src/ui/StatusBar.bf +++ b/IDE/src/ui/StatusBar.bf @@ -355,22 +355,22 @@ namespace IDE.ui lineCount++; } - String str = StackStringFormat!("Sel {0} | {1}", sel.MaxPos - sel.MinPos, lineCount); + String str = scope String()..AppendF("Sel {0} | {1}", sel.MaxPos - sel.MinPos, lineCount); float curX = mWidth - GS!(240); g.DrawString(str, curX, 0); leftX = curX + g.mFont.GetWidth(str); } else if (showIndex) - g.DrawString(StackStringFormat!("Idx {0}", activeEditWidget.Content.CursorTextPos), mWidth - GS!(240), 0); + g.DrawString(scope String()..AppendF("Idx {0}", activeEditWidget.Content.CursorTextPos), mWidth - GS!(240), 0); if (leftX >= mWidth - GS!(142)) { - g.DrawString(StackStringFormat!("Ln {0}:{1}", lineAndColumn.mLine + 1, lineAndColumn.mColumn + 1), mWidth - GS!(32), 0, .Right); + g.DrawString(scope String()..AppendF("Ln {0}:{1}", lineAndColumn.mLine + 1, lineAndColumn.mColumn + 1), mWidth - GS!(32), 0, .Right); } else { - g.DrawString(StackStringFormat!("Ln {0}", lineAndColumn.mLine + 1), Math.Max(leftX + GS!(8), mWidth - GS!(150)), 0); - g.DrawString(StackStringFormat!("Col {0}", lineAndColumn.mColumn + 1), mWidth - GS!(78), 0); + g.DrawString(scope String()..AppendF("Ln {0}", lineAndColumn.mLine + 1), Math.Max(leftX + GS!(8), mWidth - GS!(150)), 0); + g.DrawString(scope String()..AppendF("Col {0}", lineAndColumn.mColumn + 1), mWidth - GS!(78), 0); } } } @@ -532,7 +532,7 @@ namespace IDE.ui if (gApp.mSettings.mEnableDevMode) { using (g.PushColor(DarkTheme.COLOR_TEXT)) - g.DrawString(StackStringFormat!("FPS: {0}", gApp.mLastFPS), GS!(32), 0); + g.DrawString(scope String()..AppendF("FPS: {0}", gApp.mLastFPS), GS!(32), 0); String resolveStr = scope String(); let bfResolveCompiler = gApp.mBfResolveCompiler; @@ -590,7 +590,35 @@ namespace IDE.ui if (Rect(GS!(6), 0, GS!(20), mHeight).Contains(x, y)) { - gApp.mErrorsPanel.ShowErrorNext(); + bool hasError = (gApp.mErrorsPanel.mErrorCount > 0); + bool hasWarning = (gApp.mErrorsPanel.mWarningCount > 0); + + if (btn == 0) + { + gApp.mErrorsPanel.ShowErrorNext(); + } + else if ((btn == 1) && ((hasError || hasWarning))) + { + float useX = x; + float useY = y; + + Menu menu = new Menu(); + + var menuItem = menu.AddItem("Goto Next Error"); + menuItem.mOnMenuItemSelected.Add(new (evt) => + { + gApp.mErrorsPanel.ShowErrorNext(); + }); + + menuItem = menu.AddItem("Clean Beef"); + menuItem.mOnMenuItemSelected.Add(new (evt) => + { + gApp.Cmd_CleanBeef(); + }); + + MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu); + menuWidget.Init(this, useX, useY); + } return; } } diff --git a/IDE/src/ui/TargetedPropertiesDialog.bf b/IDE/src/ui/TargetedPropertiesDialog.bf index 9dd34944..703a3ad5 100644 --- a/IDE/src/ui/TargetedPropertiesDialog.bf +++ b/IDE/src/ui/TargetedPropertiesDialog.bf @@ -50,7 +50,7 @@ namespace IDE.ui if (!mTargetedProperties.mActiveConfigName.IsEmpty) { - String dispStr = StackStringFormat!("Active({0})", mTargetedProperties.mActiveConfigName); + String dispStr = scope String()..AppendF("Active({0})", mTargetedProperties.mActiveConfigName); item = menu.AddItem(dispStr); item.mOnMenuItemSelected.Add(new (evt) => { SelectConfig(mTargetedProperties.mActiveConfigName); }); } @@ -549,7 +549,7 @@ namespace IDE.ui if (!mActiveConfigName.IsEmpty) { - String dispStr = StackStringFormat!("Active({0})", mActiveConfigName); + String dispStr = scope String()..AppendF("Active({0})", mActiveConfigName); item = menu.AddItem(dispStr); item.mOnMenuItemSelected.Add(new (evt) => { SelectConfig(mActiveConfigName); }); } @@ -613,7 +613,7 @@ namespace IDE.ui { if (!platformName.IsEmpty) { - String dispStr = (IDEApp.sApp.mPlatformName == platformName) ? StackStringFormat!("Active({0})", platformName) : platformName; + String dispStr = (IDEApp.sApp.mPlatformName == platformName) ? scope String()..AppendF("Active({0})", platformName) : platformName; item = menu.AddItem(dispStr); item.mOnMenuItemSelected.Add(new (evt) => { SelectPlatform(platformName); }); } @@ -692,7 +692,7 @@ namespace IDE.ui { if (mConfigNames.Count == 1) { - String dispStr = ((mConfigNames.Count == 1) && (mActiveConfigName == mConfigNames[0])) ? StackStringFormat!("Active({0})", mConfigNames[0]) : mConfigNames[0]; + String dispStr = ((mConfigNames.Count == 1) && (mActiveConfigName == mConfigNames[0])) ? scope String()..AppendF("Active({0})", mConfigNames[0]) : mConfigNames[0]; mConfigComboBox.Label = dispStr; } else @@ -716,7 +716,7 @@ namespace IDE.ui { if (mPlatformNames.Count == 1) { - String dispStr = ((mPlatformNames.Count == 1) && (mActivePlatformName == mPlatformNames[0])) ? StackStringFormat!("Active({0})", mPlatformNames[0]) : mPlatformNames[0]; + String dispStr = ((mPlatformNames.Count == 1) && (mActivePlatformName == mPlatformNames[0])) ? scope String()..AppendF("Active({0})", mPlatformNames[0]) : mPlatformNames[0]; mPlatformComboBox.Label = dispStr; } else diff --git a/IDE/src/ui/WatchPanel.bf b/IDE/src/ui/WatchPanel.bf index dfd1b387..2143f380 100644 --- a/IDE/src/ui/WatchPanel.bf +++ b/IDE/src/ui/WatchPanel.bf @@ -54,6 +54,8 @@ namespace IDE.ui public bool mIsNewExpression; public bool mIsPending; public bool mUsedLock; + public bool? mAutoRefresh; + public int32 mDebuggerStateIdx; public int32 mCurStackIdx; public int mMemoryBreakpointAddr; public int32 mHadStepCount; @@ -66,6 +68,24 @@ namespace IDE.ui public int32 mSeriesFirstVersion = -1; public int32 mSeriesVersion = -1; + public bool AutoRefresh + { + get + { + if (mAutoRefresh != null) + return mAutoRefresh.Value; + return gApp.mSettings.mDebuggerSettings.mAutoRefreshWatches; + } + + set + { + if (value == gApp.mSettings.mDebuggerSettings.mAutoRefreshWatches) + mAutoRefresh = null; + else + mAutoRefresh = value; + } + } + public bool IsConstant { get @@ -1644,8 +1664,8 @@ namespace IDE.ui if (mShowStatusBar) { g.DrawString(textPosString, 16, textY, .Left, mWidth - GS!(140), .Ellipsis); - g.DrawString(StackStringFormat!("Ln {0}", line + 1), mWidth - GS!(130), textY); - g.DrawString(StackStringFormat!("Col {0}", col + 1), mWidth - GS!(70), textY); + g.DrawString(scope String()..AppendF("Ln {0}", line + 1), mWidth - GS!(130), textY); + g.DrawString(scope String()..AppendF("Col {0}", col + 1), mWidth - GS!(70), textY); } //using (g.PushColor(0xD0FFFFFF)) @@ -1793,7 +1813,8 @@ namespace IDE.ui public bool mFindMismatch; public bool mChildHasMatch; public String mColoredLabel ~ delete _; - bool mWantRemoveSelf; + public bool mWantRemoveSelf; + public bool mFreezeHeight; public WatchRefreshButton mWatchRefreshButton; public ActionButton mActionButton; @@ -1941,7 +1962,7 @@ namespace IDE.ui if (tooltip != null) { tooltip.mRelWidgetMouseInsets = new Insets(0, 0, GS!(-8), 0); - tooltip.mAllowMouseInsideSelf = true; + tooltip.mAllowMouseInsideInsets = new .(GS!(-12), GS!(-12), GS!(-4), GS!(-4)); tooltip.AddWidget(watchStringEdit); tooltip.mOnResized.Add(new (widget) => watchStringEdit.Resize(GS!(6), GS!(6), widget.mWidth - GS!(6) * 2, widget.mHeight - GS!(6) * 2)); tooltip.mOnResized(tooltip); @@ -1968,7 +1989,32 @@ namespace IDE.ui { mWatchRefreshButton = new WatchRefreshButton(); mWatchRefreshButton.Resize(GS!(-16), 0, GS!(20), GS!(20)); - mWatchRefreshButton.mOnMouseDown.Add(new (evt) => RefreshWatch()); + mWatchRefreshButton.mOnMouseDown.Add(new (evt) => + { + if (evt.mBtn == 0) + RefreshWatch(); + if (evt.mBtn == 1) + { + Menu menu = new Menu(); + Menu anItem; + anItem = menu.AddItem("Refresh"); + anItem.mOnMenuItemSelected.Add(new (item) => { RefreshWatch(); }); + + anItem = menu.AddItem("Auto Refresh"); + if (mWatchEntry.AutoRefresh) + anItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check); + anItem.mOnMenuItemSelected.Add(new (item) => + { + mWatchEntry.AutoRefresh = !mWatchEntry.AutoRefresh; + if (mWatchEntry.AutoRefresh) + RefreshWatch(); + }); + + MenuWidget menuWidget = ThemeFactory.mDefault.CreateMenuWidget(menu); + menuWidget.Init(mWatchRefreshButton, evt.mX, mHeight + GS!(2)); + } + + }); var typeSubItem = GetSubItem(columnIdx); typeSubItem.AddWidget(mWatchRefreshButton); mListView.mListSizeDirty = true; @@ -2024,7 +2070,7 @@ namespace IDE.ui if ((mDisabled) && (allowRefresh)) { - AddRefreshButton(); + AddRefreshButton(); } else if (mWatchRefreshButton != null) { @@ -2061,7 +2107,7 @@ namespace IDE.ui return retVal; } - void RefreshWatch() + public void RefreshWatch() { var parentWatchListViewItem = mParentItem as WatchListViewItem; if (parentWatchListViewItem != null) @@ -2635,9 +2681,9 @@ namespace IDE.ui } var dispStr = scope String(); - dispStr.AppendF(mWatchSeriesInfo.mDisplayTemplate, params formatParams); + dispStr.AppendF(mWatchSeriesInfo.mDisplayTemplate, params formatParams).IgnoreError(); var evalStr = scope String(); - evalStr.AppendF(mWatchSeriesInfo.mEvalTemplate, params formatParams); + evalStr.AppendF(mWatchSeriesInfo.mEvalTemplate, params formatParams).IgnoreError(); watchListView.mWatchOwner.SetupListViewItem(curWatchListViewItem, dispStr, evalStr); curWatchListViewItem.mWatchEntry.mSeriesFirstVersion = mWatchSeriesInfo.mSeriesFirstVersion; @@ -2860,7 +2906,10 @@ namespace IDE.ui base.UpdateAll(); if (mWantRemoveSelf) + { mParentItem?.RemoveChildItem(this); + return; + } if (mColumnIdx == 0) { @@ -2890,7 +2939,9 @@ namespace IDE.ui wantHeight = 0; else wantHeight = watchListView.mFont.GetLineSpacing(); - if (mSelfHeight != wantHeight) + + var dataItem = GetSubItem(1) as WatchListViewItem; + if ((mSelfHeight != wantHeight) && (!mFreezeHeight) && (dataItem.mCustomContentWidget == null)) { mSelfHeight = wantHeight; watchListView.mListSizeDirty = true; @@ -2997,7 +3048,12 @@ namespace IDE.ui for (WatchListViewItem watchListViewItem in childItems) { var watchEntry = watchListViewItem.mWatchEntry; - data.Add(watchEntry.mEvalStr); + using (data.CreateObject()) + { + data.Add("EvalStr", watchEntry.mEvalStr); + if (watchEntry.mAutoRefresh != null) + data.Add("AutoRefresh", watchEntry.mAutoRefresh.Value); + } } } } @@ -3017,14 +3073,23 @@ namespace IDE.ui IDEUtils.DeserializeListViewState(data, mListView); for (let itemKey in data.Enumerate("Items")) { - //for (int32 watchIdx = 0; watchIdx < data.Count; watchIdx++) - //for (var watchKV in data) - { - String watchEntry = scope String(); - //data.GetString(watchIdx, watchEntry); - data.GetCurString(watchEntry); - AddWatch(watchEntry); - } + String watchEntry = scope String(); + data.GetCurString(watchEntry); + WatchListViewItem watchItem; + + if (!watchEntry.IsEmpty) + { + watchItem = AddWatch(watchEntry); + } + else + { + data.GetString("EvalStr", watchEntry); + watchItem = AddWatch(watchEntry); + if (data.Contains("AutoRefresh")) + { + watchItem.mWatchEntry.mAutoRefresh = data.GetBool("AutoRefresh"); + } + } } return true; @@ -3622,6 +3687,12 @@ namespace IDE.ui } else if (watch.mEvalStr.Length > 0) { + if (!gApp.mDebugger.IsPaused()) + { + // Keep waiting + return; + } + String evalStr = scope String(1024); if (watch.mStackFrameId != null) { @@ -3637,6 +3708,7 @@ namespace IDE.ui DebugManager.EvalExpressionFlags flags = .AllowStringView; if (watch.mIsNewExpression) flags |= .AllowSideEffects | .AllowCalls; + gApp.DebugEvaluate(null, evalStr, val, -1, watch.mLanguage, flags); watch.mIsNewExpression = false; } @@ -3666,6 +3738,14 @@ namespace IDE.ui if (((!valueSubItem.mFailed) && (watch.mHadValue)) || (hadSideEffects) || (hadPropertyEval)) { watch.mHasValue = true; + + if ((hadSideEffects) && (watch.AutoRefresh)) + { + if (watch.mDebuggerStateIdx != gApp.mDebugger.mStateIdx) + listViewItem.RefreshWatch(); + return; + } + listViewItem.SetDisabled(true, hadSideEffects); return; } @@ -3730,6 +3810,7 @@ namespace IDE.ui watch.mIsStackAlloc = false; watch.mUsedLock = false; watch.mCurStackIdx = -1; + watch.mDebuggerStateIdx = gApp.mDebugger.mStateIdx; watch.mLanguage = .NotSet; DeleteAndNullify!(watch.mEditInitialize); DeleteAndNullify!(watch.mAction); diff --git a/IDE/src/ui/WorkspaceProperties.bf b/IDE/src/ui/WorkspaceProperties.bf index bc117837..949e24ce 100644 --- a/IDE/src/ui/WorkspaceProperties.bf +++ b/IDE/src/ui/WorkspaceProperties.bf @@ -596,7 +596,7 @@ namespace IDE.ui curWorkspaceOptions.mConfigSelections.TryGetValue(project, out setConfigSelection); if (setConfigSelection == null) { - IDEApp.sApp.Fail(StackStringFormat!("Project '{0}' not in workspace", project.mProjectName)); + IDEApp.sApp.Fail(scope String()..AppendF("Project '{0}' not in workspace", project.mProjectName)); return; } setConfigSelection.mEnabled = newConfigSelection.mEnabled; diff --git a/IDE/src/util/ConsoleProvider.bf b/IDE/src/util/ConsoleProvider.bf index 2d6ea730..7bb946b0 100644 --- a/IDE/src/util/ConsoleProvider.bf +++ b/IDE/src/util/ConsoleProvider.bf @@ -903,6 +903,7 @@ class BeefConConsoleProvider : ConsoleProvider public bool mConnected; public Thread mThread ~ delete _; public Monitor mDataMonitor = new .() ~ delete _; + public WaitEvent mDataEvent = new .() ~ delete _; public int mPendingReadClear; public bool mExiting; @@ -949,6 +950,7 @@ class BeefConConsoleProvider : ConsoleProvider using (mDataMonitor.Enter()) { mRecvStream.TryWrite(.(&data, len)); + mDataEvent.Set(true); } case .Err(let err): if ((err == .PipeListening) && (!mConnected)) @@ -1022,7 +1024,7 @@ class BeefConConsoleProvider : ConsoleProvider } } - public Result> ReadMessage(int timeoutMS) + Result> DoReadMessage() { using (mDataMonitor.Enter()) { @@ -1044,6 +1046,19 @@ class BeefConConsoleProvider : ConsoleProvider return .Ok(.(mRecvStream.Memory.Ptr + 4, wantTotalLen - 4)); } } + + public Result> ReadMessage(int timeoutMS) + { + if (timeoutMS != 0) + mDataEvent.WaitFor(timeoutMS); + using (mDataMonitor.Enter()) + { + var result = DoReadMessage(); + if (result case .Err) + mDataEvent.Reset(); + return result; + } + } } public static int sConId = 0; diff --git a/IDE/src/util/GitManager.bf b/IDE/src/util/GitManager.bf index a935f437..dd845c02 100644 --- a/IDE/src/util/GitManager.bf +++ b/IDE/src/util/GitManager.bf @@ -257,7 +257,11 @@ class GitManager if (mProcess.WaitFor(0)) { if (mProcess.ExitCode != 0) + { + if (gApp.mVerbosity >= .Diagnostic) + gApp.OutputLine($"Git failed with Exit Code:{mProcess.ExitCode} Args:{mArgs} Path:{mPath}"); mFailed = true; + } mDone = true; } } diff --git a/IDE/src/util/ResourceGen.bf b/IDE/src/util/ResourceGen.bf index 88dc98be..dcc59fe7 100644 --- a/IDE/src/util/ResourceGen.bf +++ b/IDE/src/util/ResourceGen.bf @@ -1,8 +1,12 @@ +#pragma warning disable 168 + using System; using System.IO; using System.Collections; using System.Text; using System.Diagnostics; +using Beefy.gfx; + namespace IDE.util { class ResourceGen @@ -62,6 +66,60 @@ namespace IDE.util FileStream mOutStream = new FileStream() ~ delete _; + public static Result LoadIcon(String iconFile, int wantSize = -1) + { + FileStream stream = scope FileStream(); + if (stream.Open(iconFile, .Read) case .Err) + { + return .Err; + } + + let iconDir = Try!(stream.Read()); + if ((iconDir.mReserved != 0) || (iconDir.mType != 1) || (iconDir.mCount > 0x100)) + { + return .Err; + } + + var entries = scope List(); + + for (int idx < iconDir.mCount) + { + entries.Add(Try!(stream.Read())); + } + + int bestIdx = iconDir.mCount - 1; + for (int idx < iconDir.mCount) + { + let iconEntry = ref entries[idx]; + if (iconEntry.mWidth >= wantSize) + { + bestIdx = idx; + break; + } + } + + let iconEntry = ref entries[bestIdx]; + + Try!(stream.Seek(iconEntry.mImageOffset)); + + if (iconEntry.mBytesInRes > 1024*1024) + { + return .Err; + } + + uint8* data = new:ScopedAlloc! uint8[iconEntry.mBytesInRes]*; + + Try!(stream.TryRead(.(data, iconEntry.mBytesInRes))); + + String bmpPath = scope $"@{(int)(void*)data:X}:{iconEntry.mBytesInRes}.bmp"; + + var image = Image.LoadFromFile(bmpPath); + if (image != null) + return image; + + return .Err; + } + public Result AddIcon(String iconFile) { if (!iconFile.EndsWith(".ico", .OrdinalIgnoreCase)) diff --git a/IDEHelper/Backend/BeCOFFObject.cpp b/IDEHelper/Backend/BeCOFFObject.cpp index 3daef63b..39043308 100644 --- a/IDEHelper/Backend/BeCOFFObject.cpp +++ b/IDEHelper/Backend/BeCOFFObject.cpp @@ -1953,6 +1953,11 @@ void BeCOFFObject::WriteConst(BeCOFFSection& sect, BeConstant* constVal) sizeLeft -= writeSize; } } + else if (beType->mTypeCode == BeTypeCode_Float) + { + float f = constVal->mDouble; + sect.mData.Write((void*)&f, sizeof(float)); + } else { sect.mData.Write((void*)&constVal->mInt64, beType->mSize); diff --git a/IDEHelper/Backend/BeIRCodeGen.cpp b/IDEHelper/Backend/BeIRCodeGen.cpp index 5fff51e1..8ad1678a 100644 --- a/IDEHelper/Backend/BeIRCodeGen.cpp +++ b/IDEHelper/Backend/BeIRCodeGen.cpp @@ -962,11 +962,21 @@ void BeIRCodeGen::Read(BeValue*& beValue) } else if (constType == BfConstType_TypeOf) { - CMD_PARAM(BeType*, type); + CMD_PARAM(BeType*, type); beValue = mReflectDataMap[type]; BF_ASSERT(beValue != NULL); return; } + else if (constType == BfConstType_TypeOf_Comptime) + { + CMD_PARAM(BeType*, typeType); + CMD_PARAM(int, bfTypeId); + auto beConst = mBeModule->mAlloc.Alloc(); + beConst->mType = typeType; + beConst->mBfTypeId = bfTypeId; + beValue = beConst; + return; + } else if (constType == BfConstType_TypeOf_WithData) { CMD_PARAM(BeType*, type); @@ -1123,7 +1133,13 @@ void BeIRCodeGen::Read(BeMDNode*& llvmMD) } void BeIRCodeGen::HandleNextCmd() -{ +{ + if (mFailed) + { + mStream->SetReadPos(mStream->GetSize()); + return; + } + int curId = mCmdCount; BfIRCmd cmd = (BfIRCmd)mStream->Read(); @@ -1192,6 +1208,11 @@ void BeIRCodeGen::HandleNextCmd() mBeModule->print(outStream, NULL);*/ } break; + case BfIRCmd_Abort: + { + Fail("Stream aborted"); + } + break; case BfIRCmd_SetType: { CMD_PARAM(int, typeId); @@ -1236,11 +1257,31 @@ void BeIRCodeGen::HandleNextCmd() CMD_PARAM(int, instSize); CMD_PARAM(int, instAlign); CMD_PARAM(bool, isPacked); - BF_ASSERT(type->mTypeCode == BeTypeCode_Struct); - auto structType = (BeStructType*)type; - mBeContext->SetStructBody(structType, members, isPacked); - structType->mSize = instSize; - structType->mAlign = instAlign; + + if ((type == NULL) || (type->mTypeCode != BeTypeCode_Struct)) + { + Fail("StructSetBody invalid type"); + break; + } + + bool failed = false; + for (auto member : members) + { + if (member->mSize < 0) + { + Fail("StructSetBody invalid member type"); + failed = true; + } + } + + if (!failed) + { + BF_ASSERT(type->mTypeCode == BeTypeCode_Struct); + auto structType = (BeStructType*)type; + mBeContext->SetStructBody(structType, members, isPacked); + structType->mSize = instSize; + structType->mAlign = instAlign; + } } break; case BfIRCmd_Type: @@ -1699,6 +1740,14 @@ void BeIRCodeGen::HandleNextCmd() CMD_PARAM(int, idx); BF_ASSERT(val->GetType()->IsComposite()); + if (val->GetType()->mTypeCode == BeTypeCode_Struct) + { + auto structType = (BeStructType*)val->GetType(); + if (idx >= structType->mMembers.mSize) + { + FatalError("ExtractValue OOB"); + } + } auto extractValueInst = mBeModule->AllocInst(); extractValueInst->mAggVal = val; @@ -2293,7 +2342,11 @@ void BeIRCodeGen::HandleNextCmd() CMD_PARAM(BeValue*, value); CMD_PARAM(BeBlock*, comingFrom); - BF_ASSERT(phiValue->GetType() == value->GetType()); + if (phiValue->GetType() != value->GetType()) + { + Fail("AddPhiIncoming type mismatch"); + break; + } auto phiIncoming = mBeModule->mAlloc.Alloc(); phiIncoming->mBlock = comingFrom; @@ -3693,6 +3746,18 @@ void BeIRCodeGen::SetConfigConst(int idx, int value) mConfigConsts.Add(value); } +BeValue* BeIRCodeGen::TryGetBeValue(int id) +{ + auto& result = mResults[id]; + if (result.mKind != BeIRCodeGenEntryKind_Value) + return NULL; +#ifdef BE_EXTRA_CHECKS + BF_ASSERT(!result.mBeValue->mLifetimeEnded); + BF_ASSERT(!result.mBeValue->mWasRemoved); +#endif + return result.mBeValue; +} + BeValue* BeIRCodeGen::GetBeValue(int id) { auto& result = mResults[id]; diff --git a/IDEHelper/Backend/BeIRCodeGen.h b/IDEHelper/Backend/BeIRCodeGen.h index 3a2c3dfc..9cf61252 100644 --- a/IDEHelper/Backend/BeIRCodeGen.h +++ b/IDEHelper/Backend/BeIRCodeGen.h @@ -82,7 +82,7 @@ public: BeContext* mBeContext; BeModule* mBeModule; Array mSavedDebugLocs; - bool mHasDebugLoc; + bool mHasDebugLoc; int mCmdCount; Dictionary mResults; @@ -150,6 +150,7 @@ public: virtual void SetConfigConst(int idx, int value) override; BeValue* GetBeValue(int streamId); + BeValue* TryGetBeValue(int streamId); BeType* GetBeType(int streamId); BeBlock* GetBeBlock(int streamId); BeMDNode* GetBeMetadata(int streamId); diff --git a/IDEHelper/Backend/BeMCContext.cpp b/IDEHelper/Backend/BeMCContext.cpp index 54e08070..2f8645b1 100644 --- a/IDEHelper/Backend/BeMCContext.cpp +++ b/IDEHelper/Backend/BeMCContext.cpp @@ -2160,71 +2160,71 @@ BeMCOperand BeMCContext::GetOperand(BeValue* value, bool allowMetaResult, bool a switch (value->GetTypeId()) { case BeGlobalVariable::TypeId: - { - auto globalVar = (BeGlobalVariable*)value; - if ((globalVar->mIsTLS) && (mTLSVRegIdx == -1)) { - auto tlsVReg = AllocVirtualReg(mNativeIntType); - auto vregInfo = GetVRegInfo(tlsVReg); - vregInfo->mMustExist = true; - vregInfo->mForceReg = true; - vregInfo->mDisableR12 = true; - vregInfo->mDisableR13 = true; - mTLSVRegIdx = tlsVReg.mVRegIdx; - } + auto globalVar = (BeGlobalVariable*)value; + if ((globalVar->mIsTLS) && (mTLSVRegIdx == -1)) + { + auto tlsVReg = AllocVirtualReg(mNativeIntType); + auto vregInfo = GetVRegInfo(tlsVReg); + vregInfo->mMustExist = true; + vregInfo->mForceReg = true; + vregInfo->mDisableR12 = true; + vregInfo->mDisableR13 = true; + mTLSVRegIdx = tlsVReg.mVRegIdx; + } - auto sym = mCOFFObject->GetSymbol(globalVar); - if (sym != NULL) - { - BeMCOperand mcOperand; - mcOperand.mKind = BeMCOperandKind_SymbolAddr; - mcOperand.mSymbolIdx = sym->mIdx; - return mcOperand; + auto sym = mCOFFObject->GetSymbol(globalVar); + if (sym != NULL) + { + BeMCOperand mcOperand; + mcOperand.mKind = BeMCOperandKind_SymbolAddr; + mcOperand.mSymbolIdx = sym->mIdx; + return mcOperand; + } } - } - break; + break; case BeCastConstant::TypeId: - { - auto constant = (BeCastConstant*)value; - - BeMCOperand mcOperand; - auto relTo = GetOperand(constant->mTarget); - if (relTo.mKind == BeMCOperandKind_Immediate_Null) { - mcOperand.mKind = BeMCOperandKind_Immediate_Null; - mcOperand.mType = constant->mType; + auto constant = (BeCastConstant*)value; + + BeMCOperand mcOperand; + auto relTo = GetOperand(constant->mTarget); + if (relTo.mKind == BeMCOperandKind_Immediate_Null) + { + mcOperand.mKind = BeMCOperandKind_Immediate_Null; + mcOperand.mType = constant->mType; + return mcOperand; + } + + mcOperand = AllocVirtualReg(constant->mType); + auto vregInfo = GetVRegInfo(mcOperand); + vregInfo->mDefOnFirstUse = true; + vregInfo->mRelTo = relTo; + vregInfo->mIsExpr = true; + return mcOperand; } - - mcOperand = AllocVirtualReg(constant->mType); - auto vregInfo = GetVRegInfo(mcOperand); - vregInfo->mDefOnFirstUse = true; - vregInfo->mRelTo = relTo; - vregInfo->mIsExpr = true; - - return mcOperand; - } - break; + break; case BeConstant::TypeId: - { - auto constant = (BeConstant*)value; - BeMCOperand mcOperand; - switch (constant->mType->mTypeCode) { - case BeTypeCode_Boolean: - case BeTypeCode_Int8: mcOperand.mKind = BeMCOperandKind_Immediate_i8; break; - case BeTypeCode_Int16: mcOperand.mKind = BeMCOperandKind_Immediate_i16; break; - case BeTypeCode_Int32: mcOperand.mKind = BeMCOperandKind_Immediate_i32; break; - case BeTypeCode_Int64: mcOperand.mKind = BeMCOperandKind_Immediate_i64; break; - case BeTypeCode_Float: - mcOperand.mImmF32 = constant->mDouble; - mcOperand.mKind = BeMCOperandKind_Immediate_f32; - return mcOperand; - case BeTypeCode_Double: - mcOperand.mImmF64 = constant->mDouble; - mcOperand.mKind = BeMCOperandKind_Immediate_f64; - return mcOperand; - case BeTypeCode_Pointer: + auto constant = (BeConstant*)value; + BeMCOperand mcOperand; + switch (constant->mType->mTypeCode) + { + case BeTypeCode_Boolean: + case BeTypeCode_Int8: mcOperand.mKind = BeMCOperandKind_Immediate_i8; break; + case BeTypeCode_Int16: mcOperand.mKind = BeMCOperandKind_Immediate_i16; break; + case BeTypeCode_Int32: mcOperand.mKind = BeMCOperandKind_Immediate_i32; break; + case BeTypeCode_Int64: mcOperand.mKind = BeMCOperandKind_Immediate_i64; break; + case BeTypeCode_Float: + mcOperand.mImmF32 = constant->mDouble; + mcOperand.mKind = BeMCOperandKind_Immediate_f32; + return mcOperand; + case BeTypeCode_Double: + mcOperand.mImmF64 = constant->mDouble; + mcOperand.mKind = BeMCOperandKind_Immediate_f64; + return mcOperand; + case BeTypeCode_Pointer: { if (constant->mTarget == NULL) { @@ -2253,140 +2253,151 @@ BeMCOperand BeMCContext::GetOperand(BeValue* value, bool allowMetaResult, bool a } } break; - case BeTypeCode_Struct: - case BeTypeCode_SizedArray: - case BeTypeCode_Vector: + case BeTypeCode_Struct: + case BeTypeCode_SizedArray: + case BeTypeCode_Vector: + mcOperand.mImmediate = constant->mInt64; + mcOperand.mKind = BeMCOperandKind_Immediate_i64; + break; + default: + Fail("Unhandled constant type"); + } mcOperand.mImmediate = constant->mInt64; - mcOperand.mKind = BeMCOperandKind_Immediate_i64; - break; - default: - Fail("Unhandled constant type"); - } - mcOperand.mImmediate = constant->mInt64; - return mcOperand; - } - break; - case BeStructConstant::TypeId: - { - auto structConstant = (BeStructConstant*)value; - - BeMCOperand mcOperand; - mcOperand.mKind = BeMCOperandKind_ConstAgg; - mcOperand.mConstant = structConstant; - - return mcOperand; - } - case BeGEP1Constant::TypeId: - { - auto gepConstant = (BeGEP1Constant*)value; - - auto mcVal = GetOperand(gepConstant->mTarget); - - BePointerType* ptrType = (BePointerType*)GetType(mcVal); - BEMC_ASSERT(ptrType->mTypeCode == BeTypeCode_Pointer); - - auto result = mcVal; - - // We assume we never do both an idx0 and idx1 at once. Fix if we change that. - int byteOffset = 0; - BeType* elementType = ptrType->mElementType; - byteOffset += gepConstant->mIdx0 * ptrType->mElementType->GetStride(); - - result = AllocRelativeVirtualReg(ptrType, result, GetImmediate(byteOffset), 1); - // The def is primary to create a single 'master location' for the GEP vreg to become legalized before use - auto vregInfo = GetVRegInfo(result); - vregInfo->mDefOnFirstUse = true; - result.mKind = BeMCOperandKind_VReg; - - return result; - } - break; - case BeGEP2Constant::TypeId: - { - auto gepConstant = (BeGEP2Constant*)value; - - auto mcVal = GetOperand(gepConstant->mTarget); - - BePointerType* ptrType = (BePointerType*)GetType(mcVal); - BEMC_ASSERT(ptrType->mTypeCode == BeTypeCode_Pointer); - - auto result = mcVal; - - // We assume we never do both an idx0 and idx1 at once. Fix if we change that. - int byteOffset = 0; - BeType* elementType = NULL; - byteOffset += gepConstant->mIdx0 * ptrType->mElementType->GetStride(); - - if (ptrType->mElementType->mTypeCode == BeTypeCode_Struct) - { - BeStructType* structType = (BeStructType*)ptrType->mElementType; - auto& structMember = structType->mMembers[gepConstant->mIdx1]; - elementType = structMember.mType; - byteOffset = structMember.mByteOffset; - } - else if (ptrType->mElementType->mTypeCode == BeTypeCode_SizedArray) - { - BEMC_ASSERT(ptrType->mElementType->mTypeCode == BeTypeCode_SizedArray); - auto arrayType = (BeSizedArrayType*)ptrType->mElementType; - elementType = arrayType->mElementType; - byteOffset = gepConstant->mIdx1 * elementType->GetStride(); - } - else - { - BEMC_ASSERT(ptrType->mElementType->mTypeCode == BeTypeCode_Vector); - auto arrayType = (BeVectorType*)ptrType->mElementType; - elementType = arrayType->mElementType; - byteOffset = gepConstant->mIdx1 * elementType->GetStride(); - } - - auto elementPtrType = mModule->mContext->GetPointerTo(elementType); - result = AllocRelativeVirtualReg(elementPtrType, result, GetImmediate(byteOffset), 1); - // The def is primary to create a single 'master location' for the GEP vreg to become legalized before use - auto vregInfo = GetVRegInfo(result); - vregInfo->mDefOnFirstUse = true; - result.mKind = BeMCOperandKind_VReg; - - return result; - } - break; - case BeExtractValueConstant::TypeId: - { - // Note: this only handles zero-aggregates - auto extractConstant = (BeExtractValueConstant*)value; - auto elementType = extractConstant->GetType(); - - auto mcVal = GetOperand(extractConstant->mTarget); - auto valType = GetType(mcVal); - - BeConstant beConstant; - beConstant.mType = elementType; - beConstant.mUInt64 = 0; - return GetOperand(&beConstant); - } - break; - case BeFunction::TypeId: - { - auto sym = mCOFFObject->GetSymbol(value); - BEMC_ASSERT(sym != NULL); - if (sym != NULL) - { - BeMCOperand mcOperand; - mcOperand.mKind = BeMCOperandKind_SymbolAddr; - mcOperand.mSymbolIdx = sym->mIdx; return mcOperand; } - } - break; + break; + case BeStructConstant::TypeId: + { + auto structConstant = (BeStructConstant*)value; + + BeMCOperand mcOperand; + mcOperand.mKind = BeMCOperandKind_ConstAgg; + mcOperand.mConstant = structConstant; + + return mcOperand; + } + break; + case BeGEP1Constant::TypeId: + { + auto gepConstant = (BeGEP1Constant*)value; + + auto mcVal = GetOperand(gepConstant->mTarget); + + BePointerType* ptrType = (BePointerType*)GetType(mcVal); + BEMC_ASSERT(ptrType->mTypeCode == BeTypeCode_Pointer); + + auto result = mcVal; + + // We assume we never do both an idx0 and idx1 at once. Fix if we change that. + int byteOffset = 0; + BeType* elementType = ptrType->mElementType; + byteOffset += gepConstant->mIdx0 * ptrType->mElementType->GetStride(); + + result = AllocRelativeVirtualReg(ptrType, result, GetImmediate(byteOffset), 1); + // The def is primary to create a single 'master location' for the GEP vreg to become legalized before use + auto vregInfo = GetVRegInfo(result); + vregInfo->mDefOnFirstUse = true; + result.mKind = BeMCOperandKind_VReg; + + return result; + } + break; + case BeGEP2Constant::TypeId: + { + auto gepConstant = (BeGEP2Constant*)value; + + auto mcVal = GetOperand(gepConstant->mTarget); + + BePointerType* ptrType = (BePointerType*)GetType(mcVal); + BEMC_ASSERT(ptrType->mTypeCode == BeTypeCode_Pointer); + + auto result = mcVal; + + // We assume we never do both an idx0 and idx1 at once. Fix if we change that. + int byteOffset = 0; + BeType* elementType = NULL; + byteOffset += gepConstant->mIdx0 * ptrType->mElementType->GetStride(); + + if (ptrType->mElementType->mTypeCode == BeTypeCode_Struct) + { + BeStructType* structType = (BeStructType*)ptrType->mElementType; + auto& structMember = structType->mMembers[gepConstant->mIdx1]; + elementType = structMember.mType; + byteOffset = structMember.mByteOffset; + } + else if (ptrType->mElementType->mTypeCode == BeTypeCode_SizedArray) + { + BEMC_ASSERT(ptrType->mElementType->mTypeCode == BeTypeCode_SizedArray); + auto arrayType = (BeSizedArrayType*)ptrType->mElementType; + elementType = arrayType->mElementType; + byteOffset = gepConstant->mIdx1 * elementType->GetStride(); + } + else + { + BEMC_ASSERT(ptrType->mElementType->mTypeCode == BeTypeCode_Vector); + auto arrayType = (BeVectorType*)ptrType->mElementType; + elementType = arrayType->mElementType; + byteOffset = gepConstant->mIdx1 * elementType->GetStride(); + } + + auto elementPtrType = mModule->mContext->GetPointerTo(elementType); + result = AllocRelativeVirtualReg(elementPtrType, result, GetImmediate(byteOffset), 1); + // The def is primary to create a single 'master location' for the GEP vreg to become legalized before use + auto vregInfo = GetVRegInfo(result); + vregInfo->mDefOnFirstUse = true; + result.mKind = BeMCOperandKind_VReg; + + return result; + } + break; + case BeExtractValueConstant::TypeId: + { + // Note: this only handles zero-aggregates + auto extractConstant = (BeExtractValueConstant*)value; + auto elementType = extractConstant->GetType(); + + auto mcVal = GetOperand(extractConstant->mTarget); + auto valType = GetType(mcVal); + + BeConstant beConstant; + beConstant.mType = elementType; + beConstant.mUInt64 = 0; + return GetOperand(&beConstant); + } + break; + case BeUndefConstant::TypeId: + { + auto undefConstant = (BeUndefConstant*)value; + BeConstant beConstant; + beConstant.mType = undefConstant->mType; + beConstant.mUInt64 = 0; + return GetOperand(&beConstant); + } + break; + case BeFunction::TypeId: + { + auto sym = mCOFFObject->GetSymbol(value); + BEMC_ASSERT(sym != NULL); + if (sym != NULL) + { + BeMCOperand mcOperand; + mcOperand.mKind = BeMCOperandKind_SymbolAddr; + mcOperand.mSymbolIdx = sym->mIdx; + return mcOperand; + } + } + break; case BeCallInst::TypeId: - { - auto callInst = (BeCallInst*)value; - if (callInst->mInlineResult != NULL) - return GetOperand(callInst->mInlineResult); - } - break; + { + auto callInst = (BeCallInst*)value; + if (callInst->mInlineResult != NULL) + return GetOperand(callInst->mInlineResult); + } + break; case BeDbgVariable::TypeId: - { - } + { + } + break; } BeMCOperand* operandPtr = NULL; @@ -2856,7 +2867,7 @@ BeMCOperand BeMCContext::GetCallArgVReg(int argIdx, BeTypeCode typeCode) } } -BeMCOperand BeMCContext::CreateCall(const BeMCOperand &func, const SizedArrayImpl& args, BeType* retType, BfIRCallingConv callingConv, bool structRet, bool noReturn, bool isVarArg) +BeMCOperand BeMCContext::CreateCall(const BeMCOperand &func, const SizedArrayImpl& args, BeType* retType, BfIRCallingConv callingConv, bool structRet, bool noReturn, int varArgStart) { SizedArray opArgs; for (auto itr = args.begin(); itr != args.end(); ++itr) @@ -2864,7 +2875,7 @@ BeMCOperand BeMCContext::CreateCall(const BeMCOperand &func, const SizedArrayImp auto& arg = *itr; opArgs.push_back(GetOperand(arg)); } - return CreateCall(func, opArgs, retType, callingConv, structRet, noReturn, isVarArg); + return CreateCall(func, opArgs, retType, callingConv, structRet, noReturn, varArgStart); } BeMCOperand BeMCContext::CreateLoad(const BeMCOperand& mcTarget) @@ -2878,6 +2889,14 @@ BeMCOperand BeMCContext::CreateLoad(const BeMCOperand& mcTarget) return CreateLoad(fakePtr); } + if (HasImmediateTarget(mcTarget)) + { + BeMCOperand scratchReg = AllocVirtualReg(GetType(mcTarget), 2); + CreateDefineVReg(scratchReg); + AllocInst(BeMCInstKind_Mov, scratchReg, mcTarget); + return CreateLoad(scratchReg); + } + BeMCOperand result; auto loadedTarget = BeMCOperand::ToLoad(mcTarget); @@ -3010,7 +3029,7 @@ void BeMCContext::CreateStore(BeMCInstKind instKind, const BeMCOperand& val, con } } -BeMCOperand BeMCContext::CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType, BfIRCallingConv callingConv, bool structRet, bool noReturn, bool isVarArg) +BeMCOperand BeMCContext::CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType, BfIRCallingConv callingConv, bool structRet, bool noReturn, int varArgStart) { BeMCOperand mcResult; //TODO: Allow user to directly specify ret addr with "sret" attribute @@ -3061,8 +3080,11 @@ BeMCOperand BeMCContext::CreateCall(const BeMCOperand& func, const SizedArrayImp if ((argIdx == 0) && (compositeRetReg == X64Reg_RDX)) argOfs = 0; + bool isVarArg = (varArgStart != -1) && (argIdx >= varArgStart); + auto mcValue = args[argIdx]; - auto argType = GetType(mcValue); + auto argType = GetType(mcValue); + X64CPURegister useReg = X64Reg_None; int useArgIdx = argIdx + argOfs; @@ -3103,7 +3125,7 @@ BeMCOperand BeMCContext::CreateCall(const BeMCOperand& func, const SizedArrayImp } if (isVarArg) - { + { X64CPURegister shadowReg = X64Reg_None; switch (useArgIdx) { @@ -3119,7 +3141,7 @@ BeMCOperand BeMCContext::CreateCall(const BeMCOperand& func, const SizedArrayImp case 3: shadowReg = X64Reg_R9; break; - } + } if ((shadowReg != X64Reg_None) && (useReg != X64Reg_None)) { @@ -6026,6 +6048,18 @@ void BeMCContext::GetRMParams(const BeMCOperand& operand, BeRMParamsInfo& rmInfo } } +bool BeMCContext::HasImmediateTarget(const BeMCOperand& operand) +{ + if (operand.IsImmediate()) + return true; + auto vregInfo = GetVRegInfo(operand); + if (vregInfo == NULL) + return false; + if (vregInfo->mRelTo) + return HasImmediateTarget(vregInfo->mRelTo); + return false; +} + void BeMCContext::DisableRegister(const BeMCOperand& operand, X64CPURegister reg) { auto vregInfo = GetVRegInfo(operand); @@ -10436,7 +10470,7 @@ bool BeMCContext::DoLegalization() bool needSwap = false; // Cmp , is not legal, so we need to swap LHS/RHS, which means also modifying the instruction that uses the result of the cmp - if (inst->mArg0.IsImmediate()) + if (arg0.IsImmediate()) needSwap = true; if (arg0Type->IsFloat()) @@ -17564,7 +17598,7 @@ void BeMCContext::Generate(BeFunction* function) auto castedInst = (BeCallInst*)inst; BeMCOperand mcFunc; BeType* returnType = NULL; - bool isVarArg = false; + int varArgStart = -1; bool useAltArgs = false; SizedArray args; @@ -18156,7 +18190,9 @@ void BeMCContext::Generate(BeFunction* function) auto elementType = ((BePointerType*)funcPtrType)->mElementType; if (elementType->mTypeCode == BeTypeCode_Function) { - isVarArg = ((BeFunctionType*)elementType)->mIsVarArg; + BeFunctionType* funcType = (BeFunctionType*)elementType; + if (funcType->mIsVarArg) + varArgStart = funcType->mParams.mSize; } } @@ -18173,7 +18209,7 @@ void BeMCContext::Generate(BeFunction* function) args.Add(arg.mValue); } - result = CreateCall(mcFunc, args, returnType, castedInst->mCallingConv, castedInst->HasStructRet(), castedInst->mNoReturn, isVarArg); + result = CreateCall(mcFunc, args, returnType, castedInst->mCallingConv, castedInst->HasStructRet(), castedInst->mNoReturn, varArgStart); } } break; diff --git a/IDEHelper/Backend/BeMCContext.h b/IDEHelper/Backend/BeMCContext.h index 01207590..208768c9 100644 --- a/IDEHelper/Backend/BeMCContext.h +++ b/IDEHelper/Backend/BeMCContext.h @@ -1377,8 +1377,8 @@ public: void RemoveInst(BeMCBlock* block, int instIdx, bool needChangesMerged = true, bool removeFromList = true); BeMCOperand AllocBinaryOp(BeMCInstKind instKind, const BeMCOperand & lhs, const BeMCOperand & rhs, BeMCBinIdentityKind identityKind, BeMCOverflowCheckKind overflowCheckKind = BeMCOverflowCheckKind_None); BeMCOperand GetCallArgVReg(int argIdx, BeTypeCode typeCode); - BeMCOperand CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, bool isVarArg = false); - BeMCOperand CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, bool isVarArg = false); + BeMCOperand CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, int varArgStart = -1); + BeMCOperand CreateCall(const BeMCOperand& func, const SizedArrayImpl& args, BeType* retType = NULL, BfIRCallingConv callingConv = BfIRCallingConv_CDecl, bool structRet = false, bool noReturn = false, int varArgStart = -1); BeMCOperand CreateLoad(const BeMCOperand& mcTarget); void CreateStore(BeMCInstKind instKind, const BeMCOperand& val, const BeMCOperand& ptr); void CreateMemSet(const BeMCOperand& addr, uint8 val, int size, int align); @@ -1446,6 +1446,7 @@ public: int GetRegSize(int regNum); void ValidateRMResult(const BeMCOperand& operand, BeRMParamsInfo& rmInfo, bool doValidate = true); void GetRMParams(const BeMCOperand& operand, BeRMParamsInfo& rmInfo, bool doValidate = true); + bool HasImmediateTarget(const BeMCOperand& operand); void DisableRegister(const BeMCOperand& operand, X64CPURegister reg); void MarkInvalidRMRegs(const BeMCOperand& operand); void GetUsedRegs(const BeMCOperand& operand, X64CPURegister& regA, X64CPURegister& regB); // Expands regs diff --git a/IDEHelper/Backend/BeModule.cpp b/IDEHelper/Backend/BeModule.cpp index 39101d4a..c4e821e2 100644 --- a/IDEHelper/Backend/BeModule.cpp +++ b/IDEHelper/Backend/BeModule.cpp @@ -1457,6 +1457,13 @@ void BeDumpContext::ToString(StringImpl& str, BeValue* value, bool showType, boo return; } + if (auto constant = BeValueDynCast(value)) + { + ToString(str, constant->GetType()); + str += StrFormat(" typeof(%d)", constant->mBfTypeId); + return; + } + if (auto constant = BeValueDynCast(value)) { ToString(str, constant->GetType()); diff --git a/IDEHelper/Backend/BeModule.h b/IDEHelper/Backend/BeModule.h index 5aa1e43d..00f41f43 100644 --- a/IDEHelper/Backend/BeModule.h +++ b/IDEHelper/Backend/BeModule.h @@ -439,6 +439,20 @@ public: } }; +class BeTypeOfConstant : public BeConstant +{ +public: + BE_VALUE_TYPE(BeTypeOfConstant, BeConstant); + + int mBfTypeId; + + virtual void HashContent(BeHashContext& hashCtx) override + { + hashCtx.Mixin(TypeId); + hashCtx.Mixin(mBfTypeId); + } +}; + class BeStringConstant : public BeConstant { public: diff --git a/IDEHelper/CMakeLists.txt b/IDEHelper/CMakeLists.txt index 283a36dd..db51f53c 100644 --- a/IDEHelper/CMakeLists.txt +++ b/IDEHelper/CMakeLists.txt @@ -168,7 +168,7 @@ file(GLOB SRC_FILES Backend/BeModule.cpp ) -find_package(LLVM 18.1 CONFIG COMPONENTS) +find_package(LLVM 19.1 CONFIG COMPONENTS) if (LLVM_FOUND) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") @@ -177,7 +177,7 @@ if (LLVM_FOUND) include_directories(${LLVM_INCLUDE_DIRS}) add_definitions(${LLVM_DEFINITIONS}) - string(APPEND TARGET_LIBS_OS "-lLLVM-18 ${LLVM_SYSTEM_LIBS}") + string(APPEND TARGET_LIBS_OS "-lLLVM-19 ${LLVM_SYSTEM_LIBS}") string(STRIP ${TARGET_LIBS_OS} TARGET_LIBS_OS) else() message(FATAL_ERROR "LLVM not found") diff --git a/IDEHelper/COFF.cpp b/IDEHelper/COFF.cpp index a3a19cb9..cba02a7c 100644 --- a/IDEHelper/COFF.cpp +++ b/IDEHelper/COFF.cpp @@ -3451,6 +3451,13 @@ void COFF::ParseCompileUnit_Symbols(DbgCompileUnit* compileUnit, uint8* sectionD _FlushDeferredVariableLocations(); } +static void FixEmitFileName(const char*& fileName) +{ + const char* emitPtr = strstr(fileName, "$Emit$"); + if (emitPtr != NULL) + fileName = emitPtr; +} + CvCompileUnit* COFF::ParseCompileUnit(CvModuleInfo* moduleInfo, CvCompileUnit* compileUnit, uint8* sectionData, int sectionSize) { BP_ZONE("COFF::ParseCompileUnit"); @@ -3648,8 +3655,7 @@ CvCompileUnit* COFF::ParseCompileUnit(CvModuleInfo* moduleInfo, CvCompileUnit* c const char* fileName = mStringTable.mStrTable + fileTableOfs; - if ((fileName[0] == '\\') && (fileName[1] == '$')) - fileName++; + FixEmitFileName(fileName); DbgSrcFile* srcFile = NULL; @@ -4696,8 +4702,7 @@ void COFF::ScanCompileUnit(int compileUnitId) else { const char* fileName = mStringTable.mStrTable + fileTableOfs; - if ((fileName[0] == '\\') && (fileName[1] == '$')) - fileName++; + FixEmitFileName(fileName); srcFile = AddSrcFile(NULL, fileName); mSrcFileDeferredRefs.Add(srcFile); *srcFilePtr = srcFile; diff --git a/IDEHelper/Compiler/BfAst.cpp b/IDEHelper/Compiler/BfAst.cpp index daefb8f7..a4210268 100644 --- a/IDEHelper/Compiler/BfAst.cpp +++ b/IDEHelper/Compiler/BfAst.cpp @@ -236,6 +236,11 @@ void BfStructuralVisitor::Visit(BfTypeReference* typeRef) Visit(typeRef->ToBase()); } +void BfStructuralVisitor::Visit(BfInlineTypeReference* typeRef) +{ + Visit(typeRef->ToBase()); +} + void BfStructuralVisitor::Visit(BfNamedTypeReference* typeRef) { Visit(typeRef->ToBase()); @@ -1233,14 +1238,26 @@ void BfBlock::SetSize(int wantSize) ////////////////////////////////////////////////////////////////////////// +bool BfTypeDeclaration::IsAnonymous() +{ + return (mAnonymousName != NULL); +} + +bool BfTypeDeclaration::IsAnonymousInitializerType() +{ + return (mAnonymousName != NULL) && (mTypeNode == NULL); +} + +////////////////////////////////////////////////////////////////////////// + bool BfTypeReference::IsNamedTypeReference() { - return IsA() || IsA(); + return IsA() || IsA() || IsA(); } bool BfTypeReference::IsTypeDefTypeReference() { - return IsA() || IsA() || IsA(); + return IsA() || IsA() || IsA() || IsA(); } String BfTypeReference::ToCleanAttributeString() @@ -1444,6 +1461,8 @@ const char* Beefy::BfTokenToString(BfToken token) return "namespace"; case BfToken_New: return "new"; + case BfToken_Not: + return "not"; case BfToken_Null: return "null"; case BfToken_Nullable: @@ -1479,9 +1498,7 @@ const char* Beefy::BfTokenToString(BfToken token) case BfToken_Sealed: return "sealed"; case BfToken_SizeOf: - return "sizeof"; - case BfToken_Stack: - return "stack"; + return "sizeof"; case BfToken_Static: return "static"; case BfToken_StrideOf: @@ -1659,6 +1676,11 @@ bool Beefy::BfTokenIsKeyword(BfToken token) return (token >= BfToken_Abstract) && (token <= BfToken_Yield); } +bool Beefy::BfTokenIsTypeDecl(BfToken token) +{ + return (token == BfToken_Struct) || (token == BfToken_Class) || (token == BfToken_Interface) || (token == BfToken_Enum); +} + BfBinaryOp Beefy::BfAssignOpToBinaryOp(BfAssignmentOp assignmentOp) { switch (assignmentOp) diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index 7229c45f..16e70c5c 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -229,6 +229,7 @@ enum BfToken : uint8 BfToken_NameOf, BfToken_Namespace, BfToken_New, + BfToken_Not, BfToken_Null, BfToken_Nullable, BfToken_OffsetOf, @@ -246,8 +247,7 @@ enum BfToken : uint8 BfToken_Return, BfToken_Scope, BfToken_Sealed, - BfToken_SizeOf, - BfToken_Stack, + BfToken_SizeOf, BfToken_Static, BfToken_StrideOf, BfToken_Struct, @@ -359,6 +359,7 @@ class BfAttributedIdentifierNode; class BfQualifiedNameNode; class BfNamespaceDeclaration; class BfTypeDeclaration; +class BfInitializerTypeDeclaration; class BfTypeAliasDeclaration; class BfMethodDeclaration; class BfOperatorDeclaration; @@ -382,6 +383,7 @@ class BfReturnStatement; class BfYieldStatement; class BfUnaryOperatorExpression; class BfBinaryOperatorExpression; +class BfInlineTypeReference; class BfArrayTypeRef; class BfPointerTypeRef; class BfDotTypeReference; @@ -550,6 +552,7 @@ public: virtual void Visit(BfInitializerExpression* collectionInitExpr); virtual void Visit(BfCollectionInitializerExpression* collectionInitExpr); virtual void Visit(BfTypeReference* typeRef); + virtual void Visit(BfInlineTypeReference* typeRef); virtual void Visit(BfNamedTypeReference* typeRef); virtual void Visit(BfQualifiedTypeReference* qualifiedType); virtual void Visit(BfDotTypeReference* typeRef); @@ -844,7 +847,7 @@ public: return (mKind == BfTypedValueKind_NoValue) && (mType != NULL); } - bool IsParams() + bool IsParams() const { return (mKind == BfTypedValueKind_ParamsSplat) || (mKind == BfTypedValueKind_Params); } @@ -1808,6 +1811,7 @@ public: ASTREF(BfTokenNode*) mCloseBrace; //BfDebugArray mChildArr; BfSizedArray mChildArr; + int mParserBlockId; public: using BfAstNode::Init; @@ -2148,6 +2152,7 @@ public: ASTREF(BfTokenNode*) mCtorCloseParen; BfSizedArray mArguments; BfSizedArray mCommas; + bool mIsMultiUse; // For anonymous types and also another use like a field decl ASTREF(BfAttributeDirective*) mNextAttribute; @@ -2280,6 +2285,7 @@ public: BfAstNode* mTarget; BfTokenNode* mOpenBrace; + BfInlineTypeReference* mInlineTypeRef; BfSizedArray mValues; BfSizedArray mCommas; BfTokenNode* mCloseBrace; @@ -2360,6 +2366,7 @@ class BfCaseExpression : public BfExpression public: BF_AST_TYPE(BfCaseExpression, BfExpression); + BfAstNode* mNotToken; BfTokenNode* mCaseToken; BfExpression* mCaseExpression; BfTokenNode* mEqualsNode; @@ -2443,12 +2450,24 @@ public: BfGenericParamsDeclaration* mGenericParams; BfGenericConstraintsDeclaration* mGenericConstraintsDeclaration; bool mIgnoreDeclaration; + char* mAnonymousName; BfTokenNode* mColonToken; BfSizedArray mBaseClasses; BfSizedArray mBaseClassCommas; + BfSizedArray mAnonymousTypes; + + bool IsAnonymous(); + bool IsAnonymousInitializerType(); + }; BF_AST_DECL(BfTypeDeclaration, BfAstNode); +class BfInitializerTypeDeclaration : public BfTypeDeclaration +{ +public: + BF_AST_TYPE(BfInitializerTypeDeclaration, BfTypeDeclaration); +}; BF_AST_DECL(BfInitializerTypeDeclaration, BfTypeDeclaration); + class BfTypeAliasDeclaration : public BfTypeDeclaration { public: @@ -2469,6 +2488,14 @@ public: String ToCleanAttributeString(); }; BF_AST_DECL(BfTypeReference, BfAstNode); +class BfInlineTypeReference : public BfTypeReference +{ +public: + BF_AST_TYPE(BfInlineTypeReference, BfTypeReference); + + BfTypeDeclaration* mTypeDeclaration; +}; BF_AST_DECL(BfInlineTypeReference, BfTypeReference); + class BfDirectTypeReference : public BfTypeReference { public: @@ -2950,6 +2977,14 @@ public: BfSizedArray mCommas; }; BF_AST_DECL(BfObjectCreateExpression, BfMethodBoundExpression); +class BfExtendExpression : public BfExpression +{ +public: + BF_AST_TYPE(BfExtendExpression, BfExpression); + BfAstNode* mTarget; + BfTypeDeclaration* mTypeDecl; +}; BF_AST_DECL(BfExtendExpression, BfExpression); + class BfBoxExpression : public BfExpression { public: @@ -3569,6 +3604,7 @@ public: const char* BfTokenToString(BfToken token); bool BfTokenIsKeyword(BfToken token); +bool BfTokenIsTypeDecl(BfToken token); BfBinaryOp BfAssignOpToBinaryOp(BfAssignmentOp assignmentOp); BfBinaryOp BfGetOppositeBinaryOp(BfBinaryOp origOp); BfBinaryOp BfGetFlippedBinaryOp(BfBinaryOp origOp); diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index fccb5bdf..9af28c5f 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -223,6 +223,7 @@ BfAutoComplete::BfAutoComplete(BfResolveType resolveType, bool doFuzzyAutoComple BfAutoComplete::~BfAutoComplete() { Clear(); + RemoveMethodMatchInfo(); } void BfAutoComplete::SetModule(BfModule* module) @@ -781,6 +782,8 @@ void BfAutoComplete::AddCurrentTypes(BfTypeInstance* typeInst, const StringImpl& void BfAutoComplete::AddField(BfTypeInstance* typeInst, BfFieldDef* fieldDef, BfFieldInstance* fieldInstance, const StringImpl& filter) { + if (fieldDef->mName.IsEmpty()) + return; int wantPrefixCount = 0; const char* filterStr = filter.c_str(); while (filterStr[0] == '@') @@ -920,16 +923,22 @@ void BfAutoComplete::AddTypeMembers(BfTypeInstance* typeInst, bool addStatic, bo addNonStatic = true; auto activeTypeDef = mModule->GetActiveTypeDef(); + +#define CHECK_STATIC(staticVal) ((staticVal && addStatic) || (!staticVal && addNonStatic)) + + mModule->PopulateType(typeInst, BfPopulateType_Data); if ((addStatic) && (mModule->mCurMethodInstance == NULL) && (typeInst->IsEnum()) && (allowImplicitThis)) { AddEntry(AutoCompleteEntry("value", "_"), filter); } -#define CHECK_STATIC(staticVal) ((staticVal && addStatic) || (!staticVal && addNonStatic)) - - mModule->PopulateType(typeInst, BfPopulateType_Data); - + if ((typeInst->IsEnum()) && (typeInst->IsTypedPrimitive())) + { + if (typeInst->IsTypedPrimitive()) + AddEntry(AutoCompleteEntry("valuetype", "UnderlyingType"), filter); + } + BfShow checkShow = (startType == typeInst) ? BfShow_Hide : BfShow_HideIndirect; BfProtectionCheckFlags protectionCheckFlags = BfProtectionCheckFlag_None; @@ -977,8 +986,15 @@ void BfAutoComplete::AddTypeMembers(BfTypeInstance* typeInst, bool addStatic, bo } else { - canUseMethod &= (CHECK_STATIC(methodDef->mIsStatic) && - (mModule->CheckProtection(protectionCheckFlags, typeInst, methodDef->mDeclaringType->mProject, methodDef->mProtection, startType))); + if (typeInst->IsFunction()) + { + canUseMethod = true; + } + else + { + canUseMethod &= (CHECK_STATIC(methodDef->mIsStatic) && + (mModule->CheckProtection(protectionCheckFlags, typeInst, methodDef->mDeclaringType->mProject, methodDef->mProtection, startType))); + } } if (canUseMethod) { @@ -1333,6 +1349,9 @@ void BfAutoComplete::AddExtensionMethods(BfTypeInstance* targetType, BfTypeInsta continue; auto thisType = methodInstance->GetParamType(0); + if (thisType->IsRef()) + thisType = thisType->GetUnderlyingType(); + bool paramValidated = false; if (methodInstance->GetNumGenericParams() > 0) { @@ -1661,6 +1680,8 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress for (auto field : showAttrTypeDef->mFields) { + if (field->mName.IsEmpty()) + continue; if (auto entryAdded = AddEntry(AutoCompleteEntry("field", field->mName + "="), filter)) { auto fieldDeclaration = field->GetFieldDeclaration(); @@ -1832,7 +1853,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress { "alignof", "append", "as", "asm", "base", "break", "case", "catch", "checked", "continue", "const", "default", "defer", "delegate", "delete", "do", "else", "false", "finally", - "fixed", "for", "function", "if", "implicit", "in", "internal", "is", "isconst", "new", "mixin", "null", + "fixed", "for", "function", "if", "implicit", "in", "internal", "is", "isconst", "new", "mixin", "not", "null", "offsetof", "out", "params", "readonly", "ref", "rettype", "return", "sealed", "sizeof", "scope", "static", "strideof", "struct", "switch", /*"this",*/ "try", "true", "typeof", "unchecked", "using", "var", "virtual", "volatile", "where", "while", @@ -1851,7 +1872,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress { const char* tokens[] = { - "abstract", "append", "base", "class", "const", + "abstract", "append", "base", "class", "concrete", "const", "delegate", "extern", "enum", "explicit", "extension", "function", "interface", "in", "implicit", "internal", "mixin", "namespace", "new", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "rettype", "return", @@ -2475,7 +2496,7 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho BfTypeNameFlags nameFlags = (BfTypeNameFlags)(BfTypeNameFlag_ReduceName | BfTypeNameFlag_ResolveGenericParamNames); - if (methodDef->mMethodType == BfMethodType_Normal) + if ((methodDef->mMethodType == BfMethodType_Normal) || (methodDef->mMethodType == BfMethodType_Ctor)) { StringT<128> methodPrefix; StringT<128> methodName; @@ -2483,7 +2504,12 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho bool isAbstract = (methodDef->mIsAbstract) || (isInterface) || (!methodDef->mIsVirtual); - if (isAbstract) + if (methodDef->mMethodType == BfMethodType_Ctor) + { + impString += " : base("; + isAbstract = false; + } + else if (isAbstract) { if (!methodInst->mReturnType->IsVoid()) impString += "return default;"; @@ -2500,6 +2526,9 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho auto methodDeclaration = methodDef->GetMethodDeclaration(); + if (methodDef->HasAppend()) + methodPrefix += "[AllowAppend]\r"; + if (isInterface) { if (!isExplicitInterface) @@ -2507,20 +2536,27 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho } else if (methodDeclaration->mProtectionSpecifier != NULL) methodPrefix += methodDeclaration->mProtectionSpecifier->ToString() + " "; - if (!isInterface) + + if ((!isInterface) && (methodDef->mMethodType != BfMethodType_Ctor)) methodPrefix += "override "; if (methodDef->mIsStatic) methodPrefix += "static "; - methodPrefix += mModule->TypeToString(methodInst->mReturnType, nameFlags); - methodPrefix += " "; + if (methodDef->mMethodType != BfMethodType_Ctor) + { + methodPrefix += mModule->TypeToString(methodInst->mReturnType, nameFlags); + methodPrefix += " "; + } if (isExplicitInterface) { methodName += mModule->TypeToString(methodInst->GetOwner(), nameFlags); methodName += "."; } - methodName += methodDef->mName; + if (methodDef->mMethodType == BfMethodType_Ctor) + methodName += "this"; + else + methodName += methodDef->mName; if (methodInst->GetNumGenericArguments() > 0) { @@ -2536,10 +2572,15 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho methodName += ">"; } + int usedParamIdx = 0; + methodName += "("; for (int paramIdx = 0; paramIdx < (int)methodInst->GetParamCount(); paramIdx++) - { - if (paramIdx > 0) + { + if (methodInst->GetParamKind(paramIdx) == BfParamKind_AppendIdx) + continue; + + if (usedParamIdx > 0) { methodName += ", "; if (!isAbstract) @@ -2577,6 +2618,7 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho impString += methodDef->mParams[paramIdx]->mName; } + usedParamIdx++; } methodName += ")"; @@ -2607,7 +2649,9 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho } } - if (!isAbstract) + if (methodDef->mMethodType == BfMethodType_Ctor) + impString += ")"; + else if (!isAbstract) impString += ");"; if (showString != NULL) @@ -2617,7 +2661,10 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho if (showString == insertString) *insertString += "\t"; - *insertString += methodPrefix + methodName + "\t" + impString; + if (methodDef->mMethodType == BfMethodType_Ctor) + *insertString += methodPrefix + methodName + impString + "\t"; + else + *insertString += methodPrefix + methodName + "\t" + impString; } return true; @@ -2732,7 +2779,7 @@ bool BfAutoComplete::GetMethodInfo(BfMethodInstance* methodInst, StringImpl* sho return false; } -void BfAutoComplete::AddOverrides(const StringImpl& filter) +void BfAutoComplete::AddOverrides(const StringImpl& filter, bool forceAll) { if (!mIsAutoComplete) return; @@ -2743,7 +2790,7 @@ void BfAutoComplete::AddOverrides(const StringImpl& filter) BfTypeInstance* curType = mModule->mCurTypeInstance; while (curType != NULL) - { + { for (auto methodDef : curType->mTypeDef->mMethods) { if (methodDef->mShow >= checkShow) @@ -2753,7 +2800,7 @@ void BfAutoComplete::AddOverrides(const StringImpl& filter) if (curType == mModule->mCurTypeInstance) { // The "normal" case, and only case for types without extensions - if (methodDef->mDeclaringType == activeTypeDef) + if ((methodDef->mDeclaringType == activeTypeDef) && (!forceAll)) continue; if ((methodDef->mDeclaringType->IsExtension()) && (methodDef->mDeclaringType->mProject == activeTypeDef->mProject)) @@ -2785,7 +2832,7 @@ void BfAutoComplete::AddOverrides(const StringImpl& filter) (methodDef->mMethodType != BfMethodType_PropertySetter)) continue; - if ((methodInst->mVirtualTableIdx >= 0) && (methodInst->mVirtualTableIdx < mModule->mCurTypeInstance->mVirtualMethodTable.size())) + if ((methodInst->mVirtualTableIdx >= 0) && (methodInst->mVirtualTableIdx < mModule->mCurTypeInstance->mVirtualMethodTable.size()) && (!forceAll)) { auto& vEntry = mModule->mCurTypeInstance->mVirtualMethodTable[methodInst->mVirtualTableIdx]; if (vEntry.mImplementingMethod.mTypeInstance == mModule->mCurTypeInstance) @@ -2795,8 +2842,8 @@ void BfAutoComplete::AddOverrides(const StringImpl& filter) StringT<512> insertString; GetMethodInfo(methodInst, &insertString, &insertString, true, false); if (insertString.IsEmpty()) - continue; - AddEntry(AutoCompleteEntry("override", insertString), filter); + continue; + AddEntry(AutoCompleteEntry("override", insertString), filter); } if (curType->IsStruct()) @@ -2806,6 +2853,87 @@ void BfAutoComplete::AddOverrides(const StringImpl& filter) } } +void BfAutoComplete::AddCtorPassthroughs() +{ + if (!mIsAutoComplete) + return; + + auto activeTypeDef = mModule->GetActiveTypeDef(); + + BfTypeInstance* curType = mModule->mCurTypeInstance; + auto baseType = curType->mBaseType; + + String totalInsertString; + + Array declMethods; + for (auto methodDef : curType->mTypeDef->mMethods) + { + if (methodDef->mMethodType != BfMethodType_Ctor) + continue; + + auto& methodGroup = curType->mMethodInstanceGroups[methodDef->mIdx]; + auto methodInst = methodGroup.mDefault; + if (methodInst == NULL) + continue; + if (methodDef->mMethodType != BfMethodType_Ctor) + continue; + declMethods.Add(methodInst); + } + + for (auto methodDef : baseType->mTypeDef->mMethods) + { + if (methodDef->mShow != BfShow_Show) + continue; + if (methodDef->mProtection < BfProtection_Protected) + continue; + if (methodDef->mIsStatic) + continue; + + auto& methodGroup = baseType->mMethodInstanceGroups[methodDef->mIdx]; + auto methodInst = methodGroup.mDefault; + if (methodInst == NULL) + continue; + if (methodDef->mMethodType != BfMethodType_Ctor) + continue; + + if (methodInst->GetParamCount() == 0) + continue; + + bool hasDecl = false; + for (auto declMethod : declMethods) + { + if (mModule->CompareMethodSignatures(methodInst, declMethod)) + { + hasDecl = true; + break; + } + } + if (hasDecl) + continue; + + StringT<512> insertString; + GetMethodInfo(methodInst, &insertString, &insertString, true, false); + if (insertString.IsEmpty()) + continue; + AddEntry(AutoCompleteEntry("this", insertString), ""); + + int tabPos = (int)insertString.IndexOf('\t'); + if (tabPos >= 0) + { + if (!totalInsertString.IsEmpty()) + totalInsertString += "\r\r"; + totalInsertString += insertString.Substring(tabPos + 1); + } + } + + if ((!totalInsertString.IsEmpty()) && (mEntriesSet.GetCount() >= 2)) + { + totalInsertString.Replace("\t", "\t\b"); + totalInsertString.Insert(0, "this - all\t"); + auto entry = AddEntry(AutoCompleteEntry("this", totalInsertString), ""); + } +} + void BfAutoComplete::UpdateReplaceData() { } @@ -2857,6 +2985,28 @@ void BfAutoComplete::CheckMethod(BfMethodDeclaration* methodDeclaration, bool is } } + if (auto ctorDeclaration = BfNodeDynCast(methodDeclaration)) + { + if ((ctorDeclaration->mThisToken != NULL) && (ctorDeclaration->mOpenParen == NULL)) + { + auto bfParser = ctorDeclaration->mThisToken->GetSourceData()->ToParser(); + if (bfParser == NULL) + return; + int cursorIdx = bfParser->mCursorIdx; + + if ((IsAutocompleteNode(methodDeclaration, 1)) && (cursorIdx == ctorDeclaration->mThisToken->GetSrcEnd())) + { + if (mIsAutoComplete) + { + mInsertStartIdx = ctorDeclaration->GetSrcStart(); + mInsertEndIdx = ctorDeclaration->mThisToken->GetSrcEnd(); + } + + AddCtorPassthroughs(); + } + } + } + if (methodDeclaration->mReturnType != NULL) CheckTypeRef(methodDeclaration->mReturnType, true, isLocalMethod); } @@ -2957,30 +3107,85 @@ void BfAutoComplete::CheckVarResolution(BfAstNode* varTypeRef, BfType* resolvedT } } -void BfAutoComplete::CheckResult(BfAstNode* node, const BfTypedValue& typedValue) +class BfAutocompleteNodeChecker : public BfStructuralVisitor { +public: + BfAutoComplete* mAutoComplete; + bool mSelected; + +public: + BfAutocompleteNodeChecker(BfAutoComplete* autoComplete) + { + mAutoComplete = autoComplete; + mSelected = false; + } + + virtual void Visit(BfAstNode* bfAstNode) override + { + mSelected = mAutoComplete->IsAutocompleteNode(bfAstNode); + } + + virtual void Visit(BfBinaryOperatorExpression* binOpExpr) override + { + mSelected = mAutoComplete->IsAutocompleteNode(binOpExpr->mOpToken); + } + + virtual void Visit(BfUnaryOperatorExpression* unaryOpExpr) override + { + VisitChild(unaryOpExpr->mExpression); + } + + virtual void Visit(BfCastExpression* castExpr) override + { + VisitChild(castExpr->mExpression); + } + + virtual void Visit(BfLiteralExpression* literalExpr) override + { + mSelected = mAutoComplete->IsAutocompleteNode(literalExpr); + } + + virtual void Visit(BfIdentifierNode* identifierNode) override + { + mSelected = mAutoComplete->IsAutocompleteNode(identifierNode); + } + + virtual void Visit(BfMemberReferenceExpression* memberRefExpr) override + { + mSelected = mAutoComplete->IsAutocompleteNode(memberRefExpr->mMemberName); + } +}; + +void BfAutoComplete::CheckResult(BfAstNode* node, const BfTypedValue& typedValue) +{ if (mResolveType != BfResolveType_GetResultString) return; if (!IsAutocompleteNode(node)) return; - + if (!typedValue.mValue.IsConst()) return; - if (typedValue.mType->IsPointer()) return; if (typedValue.mType->IsObject()) return; - auto constant = mModule->mBfIRBuilder->GetConstant(typedValue.mValue); - if (BfIRConstHolder::IsInt(constant->mTypeCode)) + BfAutocompleteNodeChecker autocompleteNodeChecker(this); + autocompleteNodeChecker.VisitChildNoRef(node); + if (!autocompleteNodeChecker.mSelected) + return; + + String constStr = ConstantToString(mModule->mBfIRBuilder, typedValue); + if (!constStr.IsEmpty()) { - mResultString = StrFormat(":%lld", constant->mInt64); + mResultString = ":"; + mResultString += constStr; + AddResultTypeKind(typedValue.mType); } - else if (BfIRConstHolder::IsFloat(constant->mTypeCode)) + else { - mResultString = StrFormat(":%f", constant->mDouble); + SetResultStringType(typedValue.mType); } } @@ -3041,7 +3246,7 @@ void BfAutoComplete::CheckLocalRef(BfAstNode* identifierNode, BfLocalVariable* v { String constStr; if (varDecl->mConstValue.IsConst()) - constStr = ConstantToString(mModule->mBfIRBuilder, varDecl->mConstValue); + constStr = ConstantToString(mModule->mBfIRBuilder, BfTypedValue(varDecl->mConstValue, varDecl->mResolvedType)); if (!constStr.IsEmpty()) { mResultString = constStr; @@ -3478,7 +3683,7 @@ void BfAutoComplete::FixitAddMember(BfTypeInstance* typeInst, BfType* fieldType, auto parser = typeInst->mTypeDef->GetDefinition()->mSource->ToParser(); if (parser == NULL) return; - + String fullName = typeInst->mTypeDef->mFullName.ToString(); String fieldStr; @@ -3699,11 +3904,21 @@ String BfAutoComplete::FixitGetLocation(BfParserData* parser, int insertPos) return StrFormat("%s|%d:%d", parser->mFileName.c_str(), line, lineChar); } -String BfAutoComplete::ConstantToString(BfIRConstHolder* constHolder, BfIRValue id) +String BfAutoComplete::ConstantToString(BfIRConstHolder* constHolder, BfTypedValue typedValue) { + SetAndRestoreValue prevTypeInst(mModule->mCurTypeInstance, typedValue.mType->ToTypeInstance()); + SetAndRestoreValue prevMethodInst(mModule->mCurMethodInstance, NULL); + + BF_ASSERT(typedValue.mValue.IsConst()); + + String result; + result = "("; + result += mModule->TypeToString(typedValue.mType); + result += ")"; + char str[32]; - int stringId = mModule->GetStringPoolIdx(id, constHolder); + int stringId = mModule->GetStringPoolIdx(typedValue.mValue, constHolder); if (stringId != -1) { BfStringPoolEntry* entry; @@ -3716,49 +3931,52 @@ String BfAutoComplete::ConstantToString(BfIRConstHolder* constHolder, BfIRValue } } - auto constant = constHolder->GetConstant(id); + auto constant = constHolder->GetConstant(typedValue.mValue); switch (constant->mTypeCode) { case BfTypeCode_Boolean: - return StrFormat(":(bool) %s", constant->mBool ? "true" : "false"); - case BfTypeCode_UInt8: - return StrFormat(":(uint8) %llu", constant->mUInt64); - case BfTypeCode_UInt16: - return StrFormat(":(uint16) %llu", constant->mUInt64); - case BfTypeCode_UInt32: - return StrFormat(":(uint32) %llu", constant->mUInt64); - case BfTypeCode_UInt64: - return StrFormat(":(uint64) %llu", constant->mUInt64); - - case BfTypeCode_Int8: - return StrFormat(":(int8) %lld", constant->mInt64); - case BfTypeCode_Int16: - return StrFormat(":(int16) %lld", constant->mInt64); - case BfTypeCode_Int32: - return StrFormat(":(int32) %lld", constant->mInt64); - case BfTypeCode_Int64: - return StrFormat(":(int64) %lld", constant->mInt64); - - case BfTypeCode_Float: - { - ExactMinimalFloatToStr((float)constant->mDouble, str); - String result; - result += str; - result += "f"; - return result; - } - case BfTypeCode_Double: - { - ExactMinimalDoubleToStr(constant->mDouble, str); - String result; - result += str; - return result; - } - default: + result += StrFormat(" %s", constant->mBool ? "true" : "false"); break; + case BfTypeCode_UInt8: + result += StrFormat(" %llu", constant->mUInt64); + break; + case BfTypeCode_UInt16: + result += StrFormat(" %llu", constant->mUInt64); + break; + case BfTypeCode_UInt32: + result += StrFormat(" %llu", constant->mUInt64); + break; + case BfTypeCode_UInt64: + result += StrFormat(" %llu", constant->mUInt64); + break; + case BfTypeCode_Int8: + result += StrFormat(" %lld", constant->mInt64); + break; + case BfTypeCode_Int16: + result += StrFormat(" %lld", constant->mInt64); + break; + case BfTypeCode_Int32: + result += StrFormat(" %lld", constant->mInt64); + break; + case BfTypeCode_Int64: + result += StrFormat(" %lld", constant->mInt64); + break; + case BfTypeCode_Float: + ExactMinimalFloatToStr((float)constant->mDouble, str); + result += " "; + result += str; + result += "f"; + break; + case BfTypeCode_Double: + ExactMinimalDoubleToStr(constant->mDouble, str); + result += " "; + result += str; + break; + default: + return ""; } - return ""; + return result; } void BfAutoComplete::FixitAddMethod(BfTypeInstance* typeInst, const StringImpl& methodName, BfType* returnType, const BfTypeVector& paramTypes, bool wantStatic) @@ -3871,7 +4089,7 @@ void BfAutoComplete::FixitAddConstructor(BfTypeInstance *typeInstance) int insertPos = FixitGetMemberInsertPos(mModule->mCurTypeInstance->mTypeDef); String methodStr = "\f\a"; - if (methodInstance->mMethodDef->mHasAppend) + if (methodInstance->mMethodDef->HasAppend()) methodStr += "[AllowAppend]\r"; methodStr += "public this("; int useParamIdx = 0; @@ -3946,13 +4164,8 @@ void BfAutoComplete::FixitAddConstructor(BfTypeInstance *typeInstance) } } -void BfAutoComplete::SetResultStringType(BfType * type) +void BfAutoComplete::AddResultTypeKind(BfType* type) { - SetAndRestoreValue prevTypeInst(mModule->mCurTypeInstance, type->ToTypeInstance()); - SetAndRestoreValue prevMethodInst(mModule->mCurMethodInstance, NULL); - - mResultString = ":"; - mResultString += mModule->TypeToString(type); if (type->IsObject()) mResultString += "\n:type\tclass"; else if (type->IsInterface()) @@ -3963,6 +4176,16 @@ void BfAutoComplete::SetResultStringType(BfType * type) mResultString += "\n:type\tvaluetype"; } +void BfAutoComplete::SetResultStringType(BfType* type) +{ + SetAndRestoreValue prevTypeInst(mModule->mCurTypeInstance, type->ToTypeInstance()); + SetAndRestoreValue prevMethodInst(mModule->mCurMethodInstance, NULL); + + mResultString = ":"; + mResultString += mModule->TypeToString(type); + AddResultTypeKind(type); +} + void BfAutoComplete::FixitAddFullyQualify(BfAstNode* refNode, const StringImpl& findName, const SizedArrayImpl& foundList) { BfProtectionCheckFlags protectionCheckFlags = BfProtectionCheckFlag_None; diff --git a/IDEHelper/Compiler/BfAutoComplete.h b/IDEHelper/Compiler/BfAutoComplete.h index ff3c1fca..1009ef65 100644 --- a/IDEHelper/Compiler/BfAutoComplete.h +++ b/IDEHelper/Compiler/BfAutoComplete.h @@ -236,7 +236,8 @@ public: void AddExtensionMethods(BfTypeInstance* targetType, BfTypeInstance* extensionContainer, const StringImpl& filter, bool allowProtected, bool allowPrivate); void AddTopLevelNamespaces(BfAstNode* identifierNode); void AddTopLevelTypes(BfAstNode* identifierNode, bool onlyAttribute = false); - void AddOverrides(const StringImpl& filter); + void AddOverrides(const StringImpl& filter, bool forceAll = false); + void AddCtorPassthroughs(); void UpdateReplaceData(); void AddTypeInstanceEntry(BfTypeInstance* typeInst); bool CheckDocumentation(AutoCompleteEntry* entry, BfCommentNode* documentation); @@ -244,7 +245,7 @@ public: void FixitGetParamString(const BfTypeVector& paramTypes, StringImpl& outStr); int FixitGetMemberInsertPos(BfTypeDef* typeDef); String FixitGetLocation(BfParserData* parserData, int insertPos); - String ConstantToString(BfIRConstHolder* constHolder, BfIRValue id); + String ConstantToString(BfIRConstHolder* constHolder, BfTypedValue typedValue); public: BfAutoComplete(BfResolveType resolveType = BfResolveType_Autocomplete, bool doFuzzyAutoComplete = false); @@ -283,6 +284,7 @@ public: void FixitAddConstructor(BfTypeInstance* typeInstance); void FixitAddFullyQualify(BfAstNode* refNode, const StringImpl& findName, const SizedArrayImpl& foundList); + void AddResultTypeKind(BfType* type); void SetResultStringType(BfType* type); }; diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index 5ab7cc2d..aff318a3 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -18,6 +18,7 @@ #include "llvm/IR/DebugInfo.h" #include "llvm/IR/Argument.h" #include "llvm/IR/Constants.h" +#include "llvm/IR/Module.h" #include "BfCompiler.h" #include "BfSystem.h" @@ -355,6 +356,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mHasRequiredTypes = false; mNeedsFullRefresh = false; mFastFinish = false; + mExtraCompileRequested = false; mHasQueuedTypeRebuilds = false; mIsResolveOnly = isResolveOnly; mResolvePassData = NULL; @@ -419,6 +421,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mEnumTypeDef = NULL; mFriendAttributeTypeDef = NULL; mComptimeAttributeTypeDef = NULL; + mIntrinsicAttributeTypeDef = NULL; mConstEvalAttributeTypeDef = NULL; mNoExtensionAttributeTypeDef = NULL; mCheckedAttributeTypeDef = NULL; @@ -474,9 +477,11 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mStringTypeDef = NULL; mStringViewTypeDef = NULL; mThreadStaticAttributeTypeDef = NULL; + mTypeTypeDeclDef = NULL; mTypeTypeDef = NULL; mUnboundAttributeTypeDef = NULL; mValueTypeTypeDef = NULL; + mTupleTypeDef = NULL; mResultTypeDef = NULL; mObsoleteAttributeTypeDef = NULL; mErrorAttributeTypeDef = NULL; @@ -3487,6 +3492,7 @@ void BfCompiler::UpdateRevisedTypes() compositeTypeDef->mTypeCode = rootTypeDef->mTypeCode; compositeTypeDef->mFullName = rootTypeDef->mFullName; compositeTypeDef->mFullNameEx = rootTypeDef->mFullNameEx; + compositeTypeDef->mIsOpaque = rootTypeDef->mIsOpaque; compositeTypeDef->mIsFunction = rootTypeDef->mIsFunction; compositeTypeDef->mIsDelegate = rootTypeDef->mIsDelegate; compositeTypeDef->mIsCombinedPartial = true; @@ -4441,8 +4447,9 @@ void BfCompiler::ProcessAutocompleteTempType() while (actualTypeDefItr) { auto checkTypeDef = *actualTypeDefItr; - if ((!checkTypeDef->mIsPartial) /*&& (checkTypeDef->mTypeCode != BfTypeCode_Extension)*/ && - ((checkTypeDef->mTypeCode == tempTypeDef->mTypeCode) || (tempTypeDef->mTypeCode == BfTypeCode_Extension))) + if ((!checkTypeDef->mIsPartial) && (checkTypeDef->mName == tempTypeDef->mName) && + (checkTypeDef->mIsFunction == tempTypeDef->mIsFunction) && (checkTypeDef->mIsDelegate == tempTypeDef->mIsDelegate) && + ((checkTypeDef->mTypeCode == tempTypeDef->mTypeCode) || (tempTypeDef->mTypeCode == BfTypeCode_Extension) || (tempTypeDef->mTypeCode == BfTypeCode_Inferred))) { if ((checkTypeDef->NameEquals(tempTypeDef)) && (checkTypeDef->mIsCombinedPartial) && (checkTypeDef->mGenericParamDefs.size() == tempTypeDef->mGenericParamDefs.size()) && @@ -4831,8 +4838,8 @@ void BfCompiler::ProcessAutocompleteTempType() { for (auto baseType : checkTypeDef->mBaseTypes) { - autoComplete->CheckTypeRef(baseType, false); - module->ResolveTypeRef(baseType); + autoComplete->CheckTypeRef(BfNodeDynCast(baseType), false); + module->ResolveTypeRef_Ref(baseType, BfPopulateType_Identity); } checkTypeDef = checkTypeDef->mOuterType; } @@ -5055,6 +5062,9 @@ void BfCompiler::ProcessAutocompleteTempType() BfType* BfCompiler::CheckSymbolReferenceTypeRef(BfModule* module, BfTypeReference* typeRef) { + if (typeRef == NULL) + return NULL; + //auto resolvedType = module->ResolveTypeRef(typeRef, BfPopulateType_Declaration, //(BfResolveTypeRefFlags)(BfResolveTypeRefFlag_AllowRef | BfResolveTypeRefFlag_AllowGenericMethodParamConstValue | BfResolveTypeRefFlag_AllowGenericTypeParamConstValue)); auto resolvedType = module->ResolveTypeRef(typeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_AllowRef); @@ -5271,7 +5281,7 @@ void BfCompiler::GetSymbolReferences() SetAndRestoreValue prevTypeState(module->mContext->mCurTypeState, &typeState); for (auto baseTypeRef : checkTypeDef->mBaseTypes) - CheckSymbolReferenceTypeRef(module, baseTypeRef); + CheckSymbolReferenceTypeRef(module, BfNodeDynCast(baseTypeRef)); for (auto genericParam : checkTypeDef->mGenericParamDefs) { @@ -5343,7 +5353,7 @@ void BfCompiler::GetSymbolReferences() if (mResolvePassData->mGetSymbolReferenceKind == BfGetSymbolReferenceKind_Type) { for (auto baseTypeRef : typeDef->mBaseTypes) - CheckSymbolReferenceTypeRef(module, baseTypeRef); + CheckSymbolReferenceTypeRef(module, BfNodeDynCast(baseTypeRef)); } BfTypeState typeState; @@ -5358,7 +5368,8 @@ void BfCompiler::GetSymbolReferences() typeState.mCurTypeDef = propDef->mDeclaringType; module->GetBasePropertyDef(checkPropDef, checkTypeInst); if (auto fieldDecl = propDef->GetFieldDeclaration()) - mResolvePassData->HandlePropertyReference(fieldDecl->mNameNode, checkTypeInst->mTypeDef, checkPropDef); + if (fieldDecl->mNameNode != NULL) + mResolvePassData->HandlePropertyReference(fieldDecl->mNameNode, checkTypeInst->mTypeDef, checkPropDef); } } @@ -5611,6 +5622,12 @@ void BfCompiler::MarkStringPool(BfModule* module) stringPoolEntry.mLastUsedRevision = mRevision; } + for (int stringId : module->mSignatureIdRefs) + { + BfStringPoolEntry& stringPoolEntry = module->mContext->mStringObjectIdMap[stringId]; + stringPoolEntry.mLastUsedRevision = mRevision; + } + /*if (module->mOptModule != NULL) MarkStringPool(module->mOptModule);*/ auto altModule = module->mNextAltModule; @@ -7010,6 +7027,8 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) { BP_ZONE("BfCompiler::Compile"); + uint32 frontendStartTick = BFTickCount(); + if (mSystem->mTypeDefs.mCount == 0) { // No-source bailout @@ -7027,6 +7046,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) String toolsetErrors; for (auto project : mSystem->mProjects) { + project->ClearCache(); if (project->mDisabled) continue; if (project->mCodeGenOptions.mLTOType != BfLTOType_None) @@ -7173,6 +7193,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mOutputDirectory = outputDirectory; mSystem->StartYieldSection(); + mExtraCompileRequested = false; mFastFinish = false; mHasQueuedTypeRebuilds = false; mCanceling = false; @@ -7264,6 +7285,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mFriendAttributeTypeDef = _GetRequiredType("System.FriendAttribute"); mNoStaticCtorAttributeTypeDef = _GetRequiredType("System.NoStaticCtorAttribute"); mComptimeAttributeTypeDef = _GetRequiredType("System.ComptimeAttribute"); + mIntrinsicAttributeTypeDef = _GetRequiredType("System.IntrinsicAttribute"); mConstEvalAttributeTypeDef = _GetRequiredType("System.ConstEvalAttribute"); mNoExtensionAttributeTypeDef = _GetRequiredType("System.NoExtensionAttribute"); mCheckedAttributeTypeDef = _GetRequiredType("System.CheckedAttribute"); @@ -7322,9 +7344,11 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mStringViewTypeDef = _GetRequiredType("System.StringView"); mTestAttributeTypeDef = _GetRequiredType("System.TestAttribute"); mThreadStaticAttributeTypeDef = _GetRequiredType("System.ThreadStaticAttribute"); + mTypeTypeDeclDef = _GetRequiredType("System.TypeDeclaration"); mTypeTypeDef = _GetRequiredType("System.Type"); mUnboundAttributeTypeDef = _GetRequiredType("System.UnboundAttribute"); mValueTypeTypeDef = _GetRequiredType("System.ValueType"); + mTupleTypeDef = _GetRequiredType("System.Tuple"); mObsoleteAttributeTypeDef = _GetRequiredType("System.ObsoleteAttribute"); mErrorAttributeTypeDef = _GetRequiredType("System.ErrorAttribute"); mWarnAttributeTypeDef = _GetRequiredType("System.WarnAttribute"); @@ -7816,6 +7840,8 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) BpLeave(); BpEnter("Compile_Finish"); + int frontendTicks = (int)(BFTickCount() - frontendStartTick); + //TODO:!! //mCanceling = true; @@ -7983,11 +8009,15 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) } mPassInstance->OutputLine(StrFormat(":low %d module%s built, %d object file%s generated", numModulesWritten, (numModulesWritten != 1) ? "s" : "", - numObjFilesWritten, (numObjFilesWritten != 1) ? "s" : "")); + numObjFilesWritten, (numObjFilesWritten != 1) ? "s" : "")); - if ((mCeMachine != NULL) && (!mIsResolveOnly) && (mCeMachine->mRevisionExecuteTime > 0)) + if (!mIsResolveOnly) { - mPassInstance->OutputLine(StrFormat(":med Comptime execution time: %0.2fs", mCeMachine->mRevisionExecuteTime / 1000.0f)); + mPassInstance->OutputLine(StrFormat(":med Frontend time: %0.2fs", frontendTicks / 1000.0f)); + if ((mCeMachine != NULL) && (mCeMachine->mRevisionExecuteTime > 0)) + { + mPassInstance->OutputLine(StrFormat(":med Comptime execution time: %0.2fs", mCeMachine->mRevisionExecuteTime / 1000.0f)); + } } BpLeave(); @@ -8071,7 +8101,23 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) bool BfCompiler::Compile(const StringImpl& outputDirectory) { - bool success = DoCompile(outputDirectory); + int passIdx = 0; + bool success = false; + while (true) + { + auto passState = mPassInstance->GetState(); + + success = DoCompile(outputDirectory); + if (!mExtraCompileRequested) + break; + + mPassInstance->RestoreState(passState); + + if (passIdx == 1) + break; + passIdx++; + } + if (!success) return false; if (mPassInstance->HasFailed()) @@ -8121,6 +8167,11 @@ void BfCompiler::RequestFastFinish() BpEvent("BfCompiler::RequestFastFinish", ""); } +void BfCompiler::RequestExtraCompile() +{ + mExtraCompileRequested = true; +} + //#define WANT_COMPILE_LOG void BfCompiler::CompileLog(const char* fmt ...) @@ -8338,7 +8389,7 @@ void BfCompiler::GenerateAutocompleteInfo() auto methodMatchInfo = autoComplete->mMethodMatchInfo; if ((methodMatchInfo != NULL) && (wantsDocEntry == NULL)) { - if (methodMatchInfo->mInstanceList.size() > 0) + if ((methodMatchInfo->mInstanceList.size() > 0) && (methodMatchInfo->mBestIdx >= 0)) { if (autoComplete->mIdentifierUsed != NULL) { @@ -8595,6 +8646,9 @@ void BfCompiler::GenerateAutocompleteInfo() } methodText += "\x1)"; } + + autoCompleteResultString += "invoke\t" + methodText; + autoCompleteResultString += StrFormat("\t%d", methodEntry.mArgMatchCount); if (methodEntry.mMethodDef != NULL) { @@ -8603,13 +8657,11 @@ void BfCompiler::GenerateAutocompleteInfo() { String docString; methodDeclaration->mDocumentation->GetDocString(docString); - methodText += "\x03"; - methodText += docString; + autoCompleteResultString += "\x03"; + autoCompleteResultString += docString; } } - autoCompleteResultString += "invoke\t" + methodText; - autoCompleteResultString += StrFormat("\t%d", methodEntry.mArgMatchCount); autoCompleteResultString += "\n"; idx++; @@ -10346,6 +10398,10 @@ BF_EXPORT const char* BF_CALLTYPE BfCompiler_GetCollapseRegions(BfCompiler* bfCo emitParser->GetLineCharAtIdx(kv.mValue.mSrcStart, startLine, startLineChar); int srcEnd = kv.mValue.mSrcEnd - 1; + + if (srcEnd >= emitParser->mOrigSrcLength) + continue; + while (srcEnd >= kv.mValue.mSrcStart) { char c = emitParser->mSrc[srcEnd]; diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index 2cdc6f1e..4a0974fd 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -74,6 +74,7 @@ public: int mQueuedTypesProcessed; int mTypesQueued; int mTypesDeleted; + int mTypesDeleted_LastUpdateAfterDeletingTypes; int mMethodsQueued; int mModulesStarted; @@ -334,6 +335,7 @@ public: bool mHasRequiredTypes; bool mNeedsFullRefresh; bool mFastFinish; + bool mExtraCompileRequested; bool mHasQueuedTypeRebuilds; // Infers we had a fast finish that requires a type rebuild bool mHadCancel; bool mWantsDeferMethodDecls; @@ -376,8 +378,10 @@ public: BfTypeDef* mEnumTypeDef; BfTypeDef* mStringTypeDef; BfTypeDef* mStringViewTypeDef; + BfTypeDef* mTypeTypeDeclDef; BfTypeDef* mTypeTypeDef; BfTypeDef* mValueTypeTypeDef; + BfTypeDef* mTupleTypeDef; BfTypeDef* mResultTypeDef; BfTypeDef* mGCTypeDef; BfTypeDef* mGenericIEnumerableTypeDef; @@ -444,6 +448,7 @@ public: BfTypeDef* mFriendAttributeTypeDef; BfTypeDef* mNoStaticCtorAttributeTypeDef; BfTypeDef* mComptimeAttributeTypeDef; + BfTypeDef* mIntrinsicAttributeTypeDef; BfTypeDef* mConstEvalAttributeTypeDef; BfTypeDef* mNoExtensionAttributeTypeDef; BfTypeDef* mCheckedAttributeTypeDef; @@ -543,6 +548,7 @@ public: void GetSymbolReferences(); void Cancel(); void RequestFastFinish(); + void RequestExtraCompile(); String GetTypeDefList(bool includeLocation); String GetGeneratorString(BfTypeDef* typeDef, BfTypeInstance* typeInst, const StringImpl& generatorMethodName, const StringImpl* args); void HandleGeneratorErrors(StringImpl& result); diff --git a/IDEHelper/Compiler/BfConstResolver.cpp b/IDEHelper/Compiler/BfConstResolver.cpp index c6e87b45..96ce869d 100644 --- a/IDEHelper/Compiler/BfConstResolver.cpp +++ b/IDEHelper/Compiler/BfConstResolver.cpp @@ -167,7 +167,10 @@ BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfCo } else { - mResult = mModule->Cast(expr, mResult, wantType, (BfCastFlags)(BfCastFlags_WantsConst | BfCastFlags_NoConversionOperator | (explicitCast ? BfCastFlags_Explicit : BfCastFlags_None))); + BfCastFlags castFlags = (BfCastFlags)(BfCastFlags_WantsConst | (explicitCast ? BfCastFlags_Explicit : BfCastFlags_None)); + if ((flags & BfConstResolveFlag_NoConversionOperator) != 0) + castFlags = (BfCastFlags)(castFlags | BfCastFlags_NoConversionOperator); + mResult = mModule->Cast(expr, mResult, wantType, castFlags); } } @@ -231,7 +234,8 @@ BfTypedValue BfConstResolver::Resolve(BfExpression* expr, BfType* wantType, BfCo if ((flags & BfConstResolveFlag_NoActualizeValues) == 0) { - prevIgnoreWrites.Restore(); + if (mModule->mBfIRBuilder->mHasStarted) + prevIgnoreWrites.Restore(); mModule->FixValueActualization(mResult, !prevIgnoreWrites.mPrevVal || ((flags & BfConstResolveFlag_ActualizeValues) != 0)); } @@ -399,7 +403,7 @@ bool BfConstResolver::PrepareMethodArguments(BfAstNode* targetSrc, BfMethodMatch if (argExpr != NULL) { - argValue = mModule->Cast(argExpr, argValue, wantType, (BfCastFlags)(BfCastFlags_WantsConst | BfCastFlags_NoConversionOperator)); + argValue = mModule->Cast(argExpr, argValue, wantType, (BfCastFlags)(BfCastFlags_WantsConst)); if (!argValue) return false; } diff --git a/IDEHelper/Compiler/BfConstResolver.h b/IDEHelper/Compiler/BfConstResolver.h index 7f5efd60..0e1be30c 100644 --- a/IDEHelper/Compiler/BfConstResolver.h +++ b/IDEHelper/Compiler/BfConstResolver.h @@ -19,6 +19,7 @@ enum BfConstResolveFlags BfConstResolveFlag_NoActualizeValues = 0x10, BfConstResolveFlag_ArrayInitSize = 0x20, BfConstResolveFlag_AllowGlobalVariable = 0x40, + BfConstResolveFlag_NoConversionOperator = 0x80 }; class BfConstResolver : public BfExprEvaluator diff --git a/IDEHelper/Compiler/BfContext.cpp b/IDEHelper/Compiler/BfContext.cpp index 5a54851a..6dca982a 100644 --- a/IDEHelper/Compiler/BfContext.cpp +++ b/IDEHelper/Compiler/BfContext.cpp @@ -1032,6 +1032,12 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild return; } + typeInst->mRebuildFlags = (BfTypeRebuildFlags)(typeInst->mRebuildFlags | BfTypeRebuildFlag_InRebuildType); + defer( + { + typeInst->mRebuildFlags = (BfTypeRebuildFlags)(typeInst->mRebuildFlags & ~BfTypeRebuildFlag_InRebuildType); + }); + if (mCompiler->mCeMachine != NULL) mCompiler->mCeMachine->ClearTypeData(typeInst); @@ -1226,6 +1232,7 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild typeInst->mHasPackingHoles = false; typeInst->mWantsGCMarking = false; typeInst->mHasDeclError = false; + typeInst->mHasAppendWantMark = false; delete typeInst->mTypeInfoEx; typeInst->mTypeInfoEx = NULL; @@ -1293,7 +1300,7 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild typeInst->mHasStaticInitMethod = false; typeInst->mHasStaticMarkMethod = false; typeInst->mHasStaticDtorMethod = false; - typeInst->mHasTLSFindMethod = false; + typeInst->mHasTLSFindMethod = false; typeInst->mBaseType = NULL; delete typeInst->mCustomAttributes; typeInst->mCustomAttributes = NULL; @@ -1369,6 +1376,11 @@ void BfContext::RebuildDependentTypes_MidCompile(BfDependedType* dType, const St } } +bool BfContext::IsRebuilding(BfType* type) +{ + return ((type->mRebuildFlags & BfTypeRebuildFlag_InRebuildType) != 0); +} + bool BfContext::CanRebuild(BfType* type) { if (type->mRevision == mCompiler->mRevision) @@ -1459,7 +1471,7 @@ void BfContext::TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChang if (dependencyFlags & (BfDependencyMap::DependencyFlag_ReadFields | BfDependencyMap::DependencyFlag_ParamOrReturnValue | BfDependencyMap::DependencyFlag_LocalUsage | BfDependencyMap::DependencyFlag_MethodGenericArg | - BfDependencyMap::DependencyFlag_Allocates)) + BfDependencyMap::DependencyFlag_Allocates | BfDependencyMap::DependencyFlag_TypeSignature)) { RebuildType(dependentType); } @@ -2007,6 +2019,10 @@ void BfContext::DeleteType(BfType* type, bool deferDepRebuilds) { RebuildType(dependentType); } + else if (IsRebuilding(dependentType)) + { + // Ignore + } else if (dependentTypeInst != NULL) { mGhostDependencies.Add(type); @@ -2020,9 +2036,10 @@ void BfContext::DeleteType(BfType* type, bool deferDepRebuilds) void BfContext::UpdateAfterDeletingTypes() { BP_ZONE("BfContext::UpdateAfterDeletingTypes"); - BfLogSysM("UpdateAfterDeletingTypes\n"); + BfLogSysM("UpdateAfterDeletingTypes\n"); int graveyardStart = (int)mTypeGraveyard.size(); + while (true) { @@ -2108,6 +2125,8 @@ void BfContext::UpdateAfterDeletingTypes() SaveDeletingType(type); } } + + mCompiler->mStats.mTypesDeleted_LastUpdateAfterDeletingTypes = mCompiler->mStats.mTypesDeleted; } // This happens before the old defs have been injected @@ -2243,6 +2262,8 @@ void BfContext::UpdateRevisedTypes() bool rebuildAllFilesChanged = mCompiler->mRebuildChangedFileSet.Contains("*"); + uint64 projectDepHash = 0; + // Do primary 'rebuild' scan for (auto type : mResolvedTypes) { @@ -2316,6 +2337,14 @@ void BfContext::UpdateRevisedTypes() changed = true; mCompiler->mRebuildFileSet.Add(kv.mKey.mString); } + + if (kv.mKey.mKind == CeRebuildKey::Kind_TypeDeclListHash) + { + if (projectDepHash == 0) + projectDepHash = mSystem->GetTypeDeclListHash(); + if (kv.mValue.mInt != projectDepHash) + changed = true; + } } if (changed) @@ -3399,6 +3428,9 @@ void BfContext::MarkUsedModules(BfProject* project, BfModule* module) { BP_ZONE("BfContext::MarkUsedModules"); + if (module->mIsDeleting) + return; + BF_ASSERT_REL(!module->mIsDeleting); if (module->mIsScratchModule) @@ -3458,6 +3490,13 @@ void BfContext::Cleanup() mCompiler->mCompileState = BfCompiler::CompileState_Cleanup; + if (mCompiler->mStats.mTypesDeleted_LastUpdateAfterDeletingTypes != mCompiler->mStats.mTypesDeleted) + { + // Should only occur for internal compiler errors + BF_ASSERT(mCompiler->mExtraCompileRequested); + UpdateAfterDeletingTypes(); + } + /// { Array survivingLocalMethods; diff --git a/IDEHelper/Compiler/BfContext.h b/IDEHelper/Compiler/BfContext.h index 911387d6..18b9e41a 100644 --- a/IDEHelper/Compiler/BfContext.h +++ b/IDEHelper/Compiler/BfContext.h @@ -160,12 +160,14 @@ public: BfTypeInstance* mCurBaseType; BfTypeReference* mCurAttributeTypeRef; BfFieldDef* mCurFieldDef; + BfMethodDef* mCurMethodDef; BfTypeDef* mCurTypeDef; BfTypeDef* mForceActiveTypeDef; BfProject* mActiveProject; ResolveKind mResolveKind; BfAstNode* mCurVarInitializer; int mArrayInitializerSize; + BfTypeInstance* mInitializerBaseType; public: BfTypeState() @@ -178,6 +180,7 @@ public: mCurBaseTypeRef = NULL; mCurBaseType = NULL; mCurFieldDef = NULL; + mCurMethodDef = NULL; mCurAttributeTypeRef = NULL; mCurTypeDef = NULL; mForceActiveTypeDef = NULL; @@ -185,6 +188,7 @@ public: mCurVarInitializer = NULL; mArrayInitializerSize = -1; mResolveKind = ResolveKind_None; + mInitializerBaseType = NULL; } BfTypeState(BfType* type, BfTypeState* prevState = NULL) @@ -197,12 +201,15 @@ public: mCurBaseTypeRef = NULL; mCurBaseType = NULL; mCurFieldDef = NULL; + mCurMethodDef = NULL; mCurAttributeTypeRef = NULL; mCurTypeDef = NULL; mForceActiveTypeDef = NULL; + mActiveProject = NULL; mCurVarInitializer = NULL; mArrayInitializerSize = -1; mResolveKind = ResolveKind_None; + mInitializerBaseType = NULL; } }; @@ -497,6 +504,7 @@ public: void RebuildDependentTypes(BfDependedType* dType); void QueueMidCompileRebuildDependentTypes(BfDependedType* dType, const String& reason); void RebuildDependentTypes_MidCompile(BfDependedType* dType, const String& reason); + bool IsRebuilding(BfType* type); bool CanRebuild(BfType* type); void TypeDataChanged(BfDependedType* dType, bool isNonStaticDataChange); void TypeMethodSignaturesChanged(BfTypeInstance* typeInst); diff --git a/IDEHelper/Compiler/BfDefBuilder.cpp b/IDEHelper/Compiler/BfDefBuilder.cpp index 79c71ada..3fe4a677 100644 --- a/IDEHelper/Compiler/BfDefBuilder.cpp +++ b/IDEHelper/Compiler/BfDefBuilder.cpp @@ -426,7 +426,14 @@ BfMethodDef* BfDefBuilder::CreateMethodDef(BfMethodDeclaration* methodDeclaratio methodDef->mBody = methodDeclaration->mBody; if ((methodDeclaration->mThisToken != NULL) && (!methodDeclaration->mParams.IsEmpty())) + { methodDef->mMethodType = BfMethodType_Extension; + if (!methodDef->mIsStatic) + { + methodDef->mIsStatic = true; + Fail("Extension methods must be declared 'static'", methodDef->GetRefNode()); + } + } HashContext signatureHashCtx; HashNode(signatureHashCtx, methodDeclaration, methodDef->mBody); @@ -795,10 +802,16 @@ void BfDefBuilder::Visit(BfMethodDeclaration* methodDeclaration) } } -void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef) +void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef, bool checkReturnType) { - while (attributes != NULL) + if (checkReturnType) { + if (auto inlineTypeRef = BfNodeDynCast(methodDef->mReturnTypeRef)) + ParseAttributes(inlineTypeRef->mTypeDeclaration->mAttributes, methodDef, false); + } + + while (attributes != NULL) + { if (attributes->mAttributeTypeRef != NULL) { auto typeRefName = attributes->mAttributeTypeRef->ToCleanAttributeString(); @@ -816,8 +829,24 @@ void BfDefBuilder::ParseAttributes(BfAttributeDirective* attributes, BfMethodDef } else if (typeRefName == "AllowAppend") { - methodDef->mHasAppend = true; - methodDef->mIsNoSplat = true; + methodDef->mAppendKind = BfAllowAppendKind_Yes; + methodDef->mIsNoSplat = true; + if (!attributes->mArguments.IsEmpty()) + { + if (auto assignExpr = BfNodeDynCast(attributes->mArguments[0])) + { + if ((assignExpr->mLeft != NULL) && (assignExpr->mRight != NULL) && + (assignExpr->mLeft->Equals("ZeroGap"))) + { + if (assignExpr->mRight->Equals("true")) + methodDef->mAppendKind = BfAllowAppendKind_ZeroGap; + else if (assignExpr->mRight->Equals("false")) + methodDef->mAppendKind = BfAllowAppendKind_Yes; + else + Fail("Can only use 'true' or 'false' for 'ZeroGap'", assignExpr->mRight); + } + } + } } else if (typeRefName == "Checked") methodDef->mCheckedKind = BfCheckedKind_Checked; @@ -1196,6 +1225,10 @@ void BfDefBuilder::Visit(BfFieldDeclaration* fieldDeclaration) else fieldDef->mUsingProtection = fieldDef->mProtection; } + else if (fieldDeclaration->mNameNode == NULL) + { + fieldDef->mUsingProtection = fieldDef->mProtection; + } fieldDef->mIsStatic = (fieldDeclaration->mStaticSpecifier != NULL) || fieldDef->mIsConst; fieldDef->mIsVolatile = (fieldDeclaration->mVolatileSpecifier != NULL); @@ -1253,6 +1286,7 @@ BfFieldDef* BfDefBuilder::AddField(BfTypeDef* typeDef, BfTypeReference* fieldTyp BfMethodDef* BfDefBuilder::AddMethod(BfTypeDef* typeDef, BfMethodType methodType, BfProtection protection, bool isStatic, const StringImpl& name, bool addedAfterEmit) { BF_ASSERT(typeDef->mTypeCode != BfTypeCode_TypeAlias); + BF_ASSERT(!typeDef->mIsOpaque); auto methodDef = new BfMethodDef(); methodDef->mIdx = (int)typeDef->mMethods.size(); @@ -1321,9 +1355,9 @@ BfMethodDef* BfDefBuilder::AddDtor(BfTypeDef* typeDef) return methodDef; } -void BfDefBuilder::AddDynamicCastMethods(BfTypeDef* typeDef) +void BfDefBuilder::AddDynamicCastMethods(BfTypeDef* typeDef, bool needsDynamicCastMethods) { - // + if (needsDynamicCastMethods) { auto methodDef = new BfMethodDef(); methodDef->mIdx = (int)typeDef->mMethods.size(); @@ -1344,7 +1378,7 @@ void BfDefBuilder::AddDynamicCastMethods(BfTypeDef* typeDef) methodDef->mIsNoReflect = true; } - // + if (needsDynamicCastMethods) { auto methodDef = new BfMethodDef(); methodDef->mIdx = (int)typeDef->mMethods.size(); @@ -1364,6 +1398,27 @@ void BfDefBuilder::AddDynamicCastMethods(BfTypeDef* typeDef) methodDef->mReturnTypeRef = typeDef->mSystem->mDirectObjectTypeRef; methodDef->mIsNoReflect = true; } + + if ((typeDef->mIsDelegate) && (!typeDef->mIsClosure)) + { + auto methodDef = new BfMethodDef(); + methodDef->mIdx = (int)typeDef->mMethods.size(); + typeDef->mMethods.push_back(methodDef); + methodDef->mDeclaringType = typeDef; + methodDef->mName = BF_METHODNAME_DYNAMICCAST_SIGNATURE; + methodDef->mProtection = BfProtection_Protected; + methodDef->mIsStatic = false; + methodDef->mMethodType = BfMethodType_Normal; + methodDef->mIsVirtual = true; + methodDef->mIsOverride = true; + + auto paramDef = new BfParameterDef(); + paramDef->mName = "sig"; + paramDef->mTypeRef = typeDef->mSystem->mDirectInt32TypeRef; + methodDef->mParams.push_back(paramDef); + methodDef->mReturnTypeRef = typeDef->mSystem->mDirectObjectTypeRef; + methodDef->mIsNoReflect = true; + } } void BfDefBuilder::AddParam(BfMethodDef* methodDef, BfTypeReference* typeRef, const StringImpl& paramName) @@ -1434,13 +1489,11 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) } BF_ASSERT(typeDeclaration->GetSourceData() == mCurSource->mSourceData); - - if ((typeDeclaration->mTypeNode != NULL) && (typeDeclaration->mNameNode == NULL)) - return; - + /*if (typeDeclaration->mNameNode != NULL) OutputDebugStrF("Decl: %s\n", typeDeclaration->mNameNode->ToString().c_str());*/ + bool isAnonymous = typeDeclaration->IsAnonymous(); bool isAutoCompleteTempType = false; if (mResolvePassData != NULL) { @@ -1504,17 +1557,25 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) mCurTypeDef->mProject = mCurSource->mProject; mCurTypeDef->mNamespace = mNamespace; mSystem->AddNamespaceUsage(mCurTypeDef->mNamespace, mCurTypeDef->mProject); - if (typeDeclaration->mTypeNode == NULL) + if ((typeDeclaration->mTypeNode == NULL) && (!isAnonymous)) { mCurTypeDef->mIsPartial = true; mCurTypeDef->mIsExplicitPartial = true; } if (typeDeclaration->mNameNode == NULL) { - // Global - mCurTypeDef->mName = mSystem->mGlobalsAtom; - mCurTypeDef->mName->Ref(); - BF_ASSERT(mCurTypeDef->mSystem != NULL); + if (typeDeclaration->mAnonymousName != NULL) + { + mCurTypeDef->mName = mSystem->GetAtom(typeDeclaration->mAnonymousName); + } + + if (mCurTypeDef->mName == NULL) + { + // Global + mCurTypeDef->mName = mSystem->mGlobalsAtom; + mCurTypeDef->mName->Ref(); + BF_ASSERT(mCurTypeDef->mSystem != NULL); + } } else { @@ -1527,7 +1588,7 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) BfLogSys(mCurSource->mSystem, "DefBuilder %p %p TypeDecl:%s\n", mCurTypeDef, mCurSource, mCurTypeDef->mName->ToString().mPtr); - mCurTypeDef->mProtection = (outerTypeDef == NULL) ? BfProtection_Public : BfProtection_Private; + mCurTypeDef->mProtection = ((outerTypeDef == NULL) || (isAnonymous)) ? BfProtection_Public : BfProtection_Private; if (typeDeclaration->mProtectionSpecifier != NULL) { if ((outerTypeDef == NULL) && @@ -1730,7 +1791,7 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) mCurTypeDef->mSource = mCurSource; mCurTypeDef->mSource->mRefCount++; mCurTypeDef->mTypeDeclaration = typeDeclaration; - mCurTypeDef->mIsAbstract = (typeDeclaration->mAbstractSpecifier != NULL) && (typeDeclaration->mAbstractSpecifier->GetToken() == BfToken_Abstract); + mCurTypeDef->mIsAbstract = (typeDeclaration->mAbstractSpecifier != NULL) && (typeDeclaration->mAbstractSpecifier->GetToken() == BfToken_Abstract); mCurTypeDef->mIsStatic = typeDeclaration->mStaticSpecifier != NULL; mCurTypeDef->mIsDelegate = false; mCurTypeDef->mIsFunction = false; @@ -1739,7 +1800,11 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) if (typeDeclaration->mTypeNode != NULL) typeToken = typeDeclaration->mTypeNode->GetToken(); - if (typeDeclaration->mTypeNode == NULL) + if (typeDeclaration->IsAnonymousInitializerType()) + { + mCurTypeDef->mTypeCode = BfTypeCode_Inferred; + } + else if (typeDeclaration->mTypeNode == NULL) { // Globals mCurTypeDef->mTypeCode = BfTypeCode_Struct; @@ -1841,6 +1906,9 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) (checkTypeDef->mIsFunction == mCurTypeDef->mIsFunction) && (checkTypeDef->mOuterType == actualOuterTypeDef); + if ((mCurTypeDef->mTypeCode == BfTypeCode_Inferred) && (checkTypeDef->mTypeDeclaration->IsAnonymousInitializerType())) + isCompatible = true; + if (isCompatible) { if (prevRevisionTypeDef == NULL) @@ -1956,12 +2024,18 @@ void BfDefBuilder::Visit(BfTypeDeclaration* typeDeclaration) } } + for (auto& anonTypeDecl : typeDeclaration->mAnonymousTypes) + { + VisitChildNoRef(anonTypeDecl); + mFullHashCtx->MixinStr(anonTypeDecl->mAnonymousName); + } + FinishTypeDef(mCurTypeDef->mTypeCode == BfTypeCode_Enum); // Map methods into the correct index from previous revision if (prevRevisionTypeDef != NULL) { - BF_ASSERT(mCurTypeDef->mTypeCode == prevRevisionTypeDef->mTypeCode); + BF_ASSERT((mCurTypeDef->mTypeCode == prevRevisionTypeDef->mTypeCode) || (mCurTypeDef->mTypeCode == BfTypeCode_Inferred)); if (mCurTypeDef->mFullHash == prevRevisionTypeDef->mFullHash) { @@ -2014,6 +2088,15 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) BfMethodDef* strictEqualsMethod = NULL; bool needsStaticInit = false; + if (mCurTypeDef->IsExtension()) + needsDefaultCtor = false; + + bool needsDtor = false; + bool needsStaticDtor = false; + bool hasStaticField = false; + bool hasNonStaticField = false; + bool hasThreadStatics = false; + for (int methodIdx = 0; methodIdx < (int)mCurTypeDef->mMethods.size(); methodIdx++) { auto method = mCurTypeDef->mMethods[methodIdx]; @@ -2027,6 +2110,9 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if (method->mMethodType == BfMethodType_Ctor) { + if (method->HasAppend()) + needsDtor = true; + if (method->mIsStatic) { if ((staticCtor != NULL) && (staticCtor->mMethodDeclaration != NULL)) @@ -2053,7 +2139,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) hasDefaultCtor = true; auto ctorDeclaration = (BfConstructorDeclaration*)method->mMethodDeclaration; - if (method->mHasAppend) + if (method->mAppendKind != BfAllowAppendKind_No) { mCurTypeDef->mHasAppendCtor = true; @@ -2068,6 +2154,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) methodDef->mProtection = BfProtection_Public; methodDef->mMethodType = BfMethodType_CtorCalcAppend; methodDef->mIsMutating = method->mIsMutating; + methodDef->mAppendKind = method->mAppendKind; methodDef->mIsNoSplat = true; methodDef->mMethodDeclaration = method->mMethodDeclaration; @@ -2096,7 +2183,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) // Insert a 'appendIdx' BfParameterDef* newParam = new BfParameterDef(); - newParam->mName = "appendIdx"; + newParam->mName = "__appendIdx"; newParam->mTypeRef = mSystem->mDirectRefIntTypeRef; newParam->mParamKind = BfParamKind_AppendIdx; method->mParams.Insert(0, newParam); @@ -2185,15 +2272,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if ((method->mImportKind == BfImportKind_Import_Dynamic) || (method->mImportKind == BfImportKind_Import_Unknown)) needsStaticInit = true; } - - if (mCurTypeDef->IsExtension()) - needsDefaultCtor = false; - - bool needsDtor = false; - bool needsStaticDtor = false; - bool hasStaticField = false; - bool hasNonStaticField = false; - bool hasThreadStatics = false; + for (auto field : mCurTypeDef->mFields) { if (field->mIsStatic) @@ -2264,25 +2343,29 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) needsDynamicCastMethod = false; } - if ((mCurTypeDef->mTypeCode == BfTypeCode_Object) && (!mCurTypeDef->mIsStatic) && (ctorClear == NULL)) + if (!mCurTypeDef->mIsOpaque) { - auto methodDef = AddMethod(mCurTypeDef, BfMethodType_CtorClear, BfProtection_Private, false, "", mIsComptime); - methodDef->mIsMutating = true; - } + if (((mCurTypeDef->mTypeCode == BfTypeCode_Object) || (mCurTypeDef->mTypeCode == BfTypeCode_Inferred)) && + (!mCurTypeDef->mIsStatic) && (ctorClear == NULL)) + { + auto methodDef = AddMethod(mCurTypeDef, BfMethodType_CtorClear, BfProtection_Private, false, "", mIsComptime); + methodDef->mIsMutating = true; + } - if ((needsDtor) && (dtor == NULL)) - { - auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Dtor, BfProtection_Public, false, "", mIsComptime); - } + if ((needsDtor) && (dtor == NULL)) + { + auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Dtor, BfProtection_Public, false, "", mIsComptime); + } - if ((needsStaticDtor) && (staticDtor == NULL)) - { - auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Dtor, BfProtection_Public, true, "", mIsComptime); - } + if ((needsStaticDtor) && (staticDtor == NULL)) + { + auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Dtor, BfProtection_Public, true, "", mIsComptime); + } - if ((needsStaticInit) && (staticCtor == NULL)) - { - auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Ctor, BfProtection_Public, true, "", mIsComptime); + if ((needsStaticInit) && (staticCtor == NULL)) + { + auto methodDef = AddMethod(mCurTypeDef, BfMethodType_Ctor, BfProtection_Public, true, "", mIsComptime); + } } bool makeCtorPrivate = hasCtor; @@ -2292,7 +2375,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if (mCurTypeDef->mTypeCode == BfTypeCode_Extension) needsDefaultCtor = false; - if ((needsDefaultCtor) && ((!hasDefaultCtor))) + if ((needsDefaultCtor) && (!hasDefaultCtor) && (!mCurTypeDef->mIsOpaque)) { BfProtection prot = hasCtor ? BfProtection_Hidden : BfProtection_Public; if (mCurTypeDef->mName == mSystem->mEmptyAtom) @@ -2309,10 +2392,10 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) isAutocomplete = true; //TODO: Don't do this for the autocomplete pass - if ((needsDynamicCastMethod) && (mCurTypeDef->mTypeCode != BfTypeCode_Interface) && (mCurTypeDef->mTypeCode != BfTypeCode_Extension) && - (!mCurTypeDef->mIsStatic) && (!isAutocomplete) && (!isAlias)) + if ((mCurTypeDef->mTypeCode != BfTypeCode_Interface) && (mCurTypeDef->mTypeCode != BfTypeCode_Extension) && + (!mCurTypeDef->mIsStatic) && (!isAutocomplete) && (!isAlias) && (!mCurTypeDef->mIsOpaque)) { - AddDynamicCastMethods(mCurTypeDef); + AddDynamicCastMethods(mCurTypeDef, needsDynamicCastMethod); } bool isPayloadEnum = false; @@ -2331,7 +2414,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if (isPayloadEnum) hasNonStaticField = true; - if (mCurTypeDef->mTypeCode != BfTypeCode_Interface) + if ((mCurTypeDef->mTypeCode != BfTypeCode_Interface) && (!mCurTypeDef->mIsOpaque)) { if ((hasStaticField) && (staticMarkMethod == NULL)) { @@ -2359,7 +2442,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) if (toStringMethod != NULL) wantsToString = false; - if ((mCurTypeDef->mTypeCode == BfTypeCode_Enum) && (!isPayloadEnum) && (getUnderlyingMethod == NULL)) + if ((mCurTypeDef->mTypeCode == BfTypeCode_Enum) && (!isPayloadEnum) && (getUnderlyingMethod == NULL) && (!mCurTypeDef->mIsOpaque)) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); @@ -2417,7 +2500,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) } } - if (wantsToString) + if ((wantsToString) && (!mCurTypeDef->mIsOpaque)) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); @@ -2433,7 +2516,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) methodDef->mAddedAfterEmit = mIsComptime; } - if ((needsEqualsMethod) && (equalsMethod == NULL) && (equalsOpMethod == NULL)) + if ((needsEqualsMethod) && (equalsMethod == NULL) && (equalsOpMethod == NULL) && (!mCurTypeDef->mIsOpaque)) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); @@ -2448,7 +2531,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) methodDef->mAddedAfterEmit = mIsComptime; } - if ((needsEqualsMethod) && (strictEqualsMethod == NULL)) + if ((needsEqualsMethod) && (strictEqualsMethod == NULL) && (!mCurTypeDef->mIsOpaque)) { auto methodDef = new BfMethodDef(); mCurTypeDef->mMethods.push_back(methodDef); @@ -2483,7 +2566,7 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) mSignatureHashCtx->MixinStr(methodDef->mName); if ((methodDef->mAlwaysInline) || - (methodDef->mHasAppend) || + (methodDef->mAppendKind != BfAllowAppendKind_No) || (methodDef->mMethodType == BfMethodType_Mixin)) inlineHashCtx.Mixin(methodDef->mFullHash); diff --git a/IDEHelper/Compiler/BfDefBuilder.h b/IDEHelper/Compiler/BfDefBuilder.h index 15ef96e1..d683bfe1 100644 --- a/IDEHelper/Compiler/BfDefBuilder.h +++ b/IDEHelper/Compiler/BfDefBuilder.h @@ -52,11 +52,11 @@ public: static BfFieldDef* AddField(BfTypeDef* typeDef, BfTypeReference* typeRef, const StringImpl& name); static BfMethodDef* AddMethod(BfTypeDef* typeDef, BfMethodType methodType, BfProtection protection, bool isStatic, const StringImpl& name, bool addedAfterEmit = false); static BfMethodDef* AddDtor(BfTypeDef* typeDef); - static void AddDynamicCastMethods(BfTypeDef* typeDef); + static void AddDynamicCastMethods(BfTypeDef* typeDef, bool needsDynamicCastMethods); static void AddParam(BfMethodDef* methodDef, BfTypeReference* typeRef, const StringImpl& paramName); BfTypeDef* ComparePrevTypeDef(BfTypeDef* prevTypeDef, BfTypeDef* checkTypeDef); void FinishTypeDef(bool wantsToString); - void ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef); + void ParseAttributes(BfAttributeDirective* attributes, BfMethodDef* methodDef, bool checkReturnType = true); void ParseAttributes(BfAttributeDirective* attributes, BfTypeDef* typeDef); BfMethodDef* CreateMethodDef(BfMethodDeclaration* methodDecl, BfMethodDef* outerMethodDef = NULL); BfError* Fail(const StringImpl& errorStr, BfAstNode* refNode); diff --git a/IDEHelper/Compiler/BfDemangler.cpp b/IDEHelper/Compiler/BfDemangler.cpp index 4747d6f1..83a958f9 100644 --- a/IDEHelper/Compiler/BfDemangler.cpp +++ b/IDEHelper/Compiler/BfDemangler.cpp @@ -2137,7 +2137,7 @@ bool MsDemangleScanner::DemangleScopedName() } else { - return Failed(); + // Ignore error } } else diff --git a/IDEHelper/Compiler/BfElementVisitor.cpp b/IDEHelper/Compiler/BfElementVisitor.cpp index 8a18b5e5..79ea34b2 100644 --- a/IDEHelper/Compiler/BfElementVisitor.cpp +++ b/IDEHelper/Compiler/BfElementVisitor.cpp @@ -301,6 +301,7 @@ void BfElementVisitor::Visit(BfInitializerExpression* initExpr) VisitChild(initExpr->mTarget); VisitChild(initExpr->mOpenBrace); + VisitChild(initExpr->mInlineTypeRef); for (auto& val : initExpr->mValues) VisitChild(val); for (auto& val : initExpr->mCommas) @@ -325,6 +326,13 @@ void BfElementVisitor::Visit(BfTypeReference* typeRef) Visit(typeRef->ToBase()); } +void BfElementVisitor::Visit(BfInlineTypeReference* typeRef) +{ + Visit(typeRef->ToBase()); + + VisitChild(typeRef->mTypeDeclaration); +} + void BfElementVisitor::Visit(BfNamedTypeReference* typeRef) { Visit(typeRef->ToBase()); @@ -716,6 +724,7 @@ void BfElementVisitor::Visit(BfCaseExpression* caseExpr) { Visit(caseExpr->ToBase()); + VisitChild(caseExpr->mNotToken); VisitChild(caseExpr->mCaseToken); VisitChild(caseExpr->mCaseExpression); VisitChild(caseExpr->mEqualsNode); diff --git a/IDEHelper/Compiler/BfElementVisitor.h b/IDEHelper/Compiler/BfElementVisitor.h index 948e260d..3802caa8 100644 --- a/IDEHelper/Compiler/BfElementVisitor.h +++ b/IDEHelper/Compiler/BfElementVisitor.h @@ -51,6 +51,7 @@ public: virtual void Visit(BfInitializerExpression* initExpr); virtual void Visit(BfCollectionInitializerExpression* collectionInitExpr); virtual void Visit(BfTypeReference* typeRef); + virtual void Visit(BfInlineTypeReference* typeRef); virtual void Visit(BfNamedTypeReference* typeRef); virtual void Visit(BfQualifiedTypeReference* qualifiedType); virtual void Visit(BfDotTypeReference* typeRef); diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 5770e9b8..8ae94c8d 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -222,6 +222,7 @@ void BfMethodMatcher::Init(const BfMethodGenericArguments& methodGenericArgument mAutoFlushAmbiguityErrors = true; mMethodCheckCount = 0; mCheckedKind = BfCheckedKind_NotSet; + mAllowAppendKind = BfAllowAppendKind_No; mMatchFailKind = MatchFailKind_None; mBfEvalExprFlags = BfEvalExprFlags_None; @@ -233,8 +234,10 @@ void BfMethodMatcher::Init(const BfMethodGenericArguments& methodGenericArgument mHasVarArguments |= bfType->IsVar(); if (bfType->IsGenericParam()) { - auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)bfType); - if ((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Var) != 0) + BfType* typeConstraint = NULL; + BfGenericParamFlags flags = BfGenericParamFlag_None; + mModule->GetMergedGenericParamData(bfType, flags, typeConstraint); + if ((flags & BfGenericParamFlag_Var) != 0) mHasVarArguments = true; } } @@ -806,7 +809,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp bool anyIsExtension = false; int newImplicitParamCount = newMethodInstance->GetImplicitParamCount(); - if (newMethodInstance->mMethodDef->mHasAppend) + if (newMethodInstance->mMethodDef->HasAppend()) newImplicitParamCount++; if (newMethodInstance->mMethodDef->mMethodType == BfMethodType_Extension) { @@ -815,7 +818,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp } int prevImplicitParamCount = prevMethodInstance->GetImplicitParamCount(); - if (prevMethodInstance->mMethodDef->mHasAppend) + if (prevMethodInstance->mMethodDef->HasAppend()) prevImplicitParamCount++; if (prevMethodInstance->mMethodDef->mMethodType == BfMethodType_Extension) { @@ -1279,6 +1282,7 @@ void BfMethodMatcher::CompareMethods(BfMethodInstance* prevMethodInstance, BfTyp } RETURN_BETTER_OR_WORSE(newMethodDef->mCheckedKind == mCheckedKind, prevMethodDef->mCheckedKind == mCheckedKind); + RETURN_BETTER_OR_WORSE(newMethodDef->mAppendKind == mAllowAppendKind, prevMethodDef->mAppendKind == mAllowAppendKind); RETURN_BETTER_OR_WORSE(newMethodDef->mCommutableKind != BfCommutableKind_Reverse, prevMethodDef->mCommutableKind != BfCommutableKind_Reverse); // If one of these methods is local to the current extension then choose that one @@ -1773,7 +1777,7 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst ((!mHadExplicitGenericArguments) || (mHadPartialGenericArguments)); int paramIdx = 0; BfType* paramsElementType = NULL; - if (checkMethod->mHasAppend) + if (checkMethod->HasAppend()) paramIdx++; int uniqueGenericStartIdx = mModule->GetLocalInferrableGenericArgCount(checkMethod); @@ -1885,7 +1889,7 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst int argIdx = 0; int paramIdx = 0; - if (checkMethod->mHasAppend) + if (checkMethod->HasAppend()) paramIdx++; if (checkMethod->mMethodType == BfMethodType_Extension) @@ -2028,6 +2032,39 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst } } } + else if (paramsType->IsParamsType()) + { + paramsType = paramsType->GetUnderlyingType(); + if (paramsType->IsGenericParam()) + { + auto genericParamType = (BfGenericParamType*)paramsType; + if (genericParamType->mGenericParamKind == BfGenericParamKind_Method) + { + auto genericParamInst = methodInstance->mMethodInfoEx->mGenericParams[genericParamType->mGenericParamIdx]; + if ((genericParamInst->mTypeConstraint != NULL) && (genericParamInst->mTypeConstraint->IsInstanceOf(mModule->mCompiler->mTupleTypeDef))) + { + bool isValid = true; + BfTypeVector genericArgs; + + for (int argIdx = methodInstance->mParams.mSize - 1 - inferParamOffset; argIdx < (int)mArguments.size(); argIdx++) + { + BfTypedValue argTypedValue = ResolveArgTypedValue(mArguments[argIdx], NULL, genericArgumentsSubstitute); + if (!argTypedValue) + { + isValid = false; + break; + } + genericArgs.Add(mModule->FixIntUnknown(argTypedValue.mType)); + } + + if (isValid) + { + (*genericArgumentsSubstitute)[genericParamType->mGenericParamIdx] = mModule->CreateTupleType(genericArgs, SubstituteList()); + } + } + } + } + } } if (!deferredArgs.IsEmpty()) @@ -2274,6 +2311,12 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst } else { + if ((checkMethod->mMethodType == BfMethodType_Extension) && (argIdx == -1)) + { + if ((wantType->IsRef()) && (!argTypedValue.mType->IsRef())) + wantType = wantType->GetUnderlyingType(); + } + if ((wantType->IsRef()) && (!argTypedValue.mType->IsRef()) && ((mAllowImplicitRef) || (wantType->IsIn()))) wantType = wantType->GetUnderlyingType(); @@ -2290,7 +2333,11 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst if (!mModule->CanCast(argTypedValue, wantType, castFlags)) { - if ((mAllowImplicitWrap) && (argTypedValue.mType->IsWrappableType()) && (mModule->GetWrappedStructType(argTypedValue.mType) == wantType)) + if ((argIdx == -1) && (wantType->IsWrappableType()) && (mModule->GetWrappedStructType(wantType) == argTypedValue.mType)) + { + // Extension target can be wrapped + } + else if ((mAllowImplicitWrap) && (argTypedValue.mType->IsWrappableType()) && (mModule->GetWrappedStructType(argTypedValue.mType) == wantType)) { // Is wrapped type } @@ -2344,7 +2391,16 @@ bool BfMethodMatcher::CheckMethod(BfTypeInstance* targetTypeInstance, BfTypeInst // If we allowed this then it would allow too many matches (and allow conversion to any type during CastToValue) goto NoMatch; } + + bool doFullTypeResolve = false; + if (returnType->IsUnspecializedTypeVariation()) + { + returnType = typeUnspecMethodInstance->mReturnType; + doFullTypeResolve = true; + } if ((genericArgumentsSubstitute != NULL) && (returnType->IsUnspecializedType())) + doFullTypeResolve = true; + if (doFullTypeResolve) { auto resolvedType = mModule->ResolveGenericType(returnType, typeGenericArguments, genericArgumentsSubstitute, mModule->mCurTypeInstance, false); if (resolvedType == NULL) @@ -2615,7 +2671,7 @@ Done: return mBestMethodDef == checkMethod; } -void BfMethodMatcher::FlushAmbiguityError() +void BfMethodMatcher::FlushAmbiguityError(bool useWarning) { if (!mAmbiguousEntries.empty()) { @@ -2623,9 +2679,20 @@ void BfMethodMatcher::FlushAmbiguityError() { BfError* error; if (!mMethodName.empty()) - error = mModule->Fail(StrFormat("Ambiguous method call for '%s'", mMethodName.c_str()), mTargetSrc); + { + if (useWarning) + error = mModule->Warn(0, StrFormat("Ambiguous method call for '%s'", mMethodName.c_str()), mTargetSrc); + else + error = mModule->Fail(StrFormat("Ambiguous method call for '%s'", mMethodName.c_str()), mTargetSrc); + } else - error = mModule->Fail("Ambiguous method call", mTargetSrc); + { + if (useWarning) + error = mModule->Warn(0, "Ambiguous method call", mTargetSrc); + else + error = mModule->Fail("Ambiguous method call", mTargetSrc); + } + if (error != NULL) { BfMethodInstance* bestMethodInstance = mModule->GetUnspecializedMethodInstance(mBestRawMethodInstance, true); @@ -3289,8 +3356,7 @@ BfExprEvaluator::BfExprEvaluator(BfModule* module) mExpectingType = NULL; mFunctionBindResult = NULL; mExplicitCast = false; - mDeferCallRef = NULL; - mDeferScopeAlloc = NULL; + mDeferCallData = NULL; mPrefixedAttributeState = NULL; mResolveGenericParam = true; mNoBind = false; @@ -3763,26 +3829,8 @@ void BfExprEvaluator::Visit(BfVariableDeclaration* varDecl) mModule->HandleVariableDeclaration(varDecl, this); } -void BfExprEvaluator::Visit(BfCaseExpression* caseExpr) +void BfExprEvaluator::DoCaseExpression(BfTypedValue caseValAddr, BfCaseExpression* caseExpr) { - if (caseExpr->mEqualsNode != NULL) - { - mModule->Warn(0, "Deprecated case syntax", caseExpr->mEqualsNode); - } - - BfTypedValue caseValAddr; - if (caseExpr->mValueExpression != NULL) - caseValAddr = mModule->CreateValueFromExpression(caseExpr->mValueExpression, NULL, (BfEvalExprFlags)(mBfEvalExprFlags & BfEvalExprFlags_InheritFlags)); - - if ((caseValAddr.mType != NULL) && (caseValAddr.mType->IsPointer())) - { - caseValAddr = mModule->LoadValue(caseValAddr); - caseValAddr = BfTypedValue(caseValAddr.mValue, caseValAddr.mType->GetUnderlyingType(), true); - } - - if (caseValAddr.mType != NULL) - mModule->mBfIRBuilder->PopulateType(caseValAddr.mType); - if ((mModule->mCurMethodState != NULL) && (mModule->mCurMethodState->mDeferredLocalAssignData != NULL)) mModule->mCurMethodState->mDeferredLocalAssignData->BreakExtendChain(); @@ -3805,6 +3853,7 @@ void BfExprEvaluator::Visit(BfCaseExpression* caseExpr) } } + auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean); bool isPayloadEnum = (caseValAddr.mType != NULL) && (caseValAddr.mType->IsPayloadEnum()); auto tupleExpr = BfNodeDynCast(caseExpr->mCaseExpression); @@ -3834,14 +3883,14 @@ void BfExprEvaluator::Visit(BfCaseExpression* caseExpr) if (hasVariable) { - CheckVariableDeclaration(caseExpr, false, true, false); + CheckVariableDeclaration(caseExpr, false, false, false); } // We can avoid clearing on mismatch if we can be sure we ONLY enter the true block on a match. // An example of requiring clearing is: if ((result case .Ok(out val)) || (force)) if (hasOut) clearOutOnMismatch = !CheckVariableDeclaration(caseExpr, true, true, true); - + bool hadConditional = false; if (isPayloadEnum) { @@ -3884,10 +3933,9 @@ void BfExprEvaluator::Visit(BfCaseExpression* caseExpr) return; } - auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean); BfTypedValue caseMatch; if (caseExpr->mCaseExpression != NULL) - caseMatch = mModule->CreateValueFromExpression(caseExpr->mCaseExpression, caseValAddr.mType, BfEvalExprFlags_AllowEnumId); + caseMatch = mModule->CreateValueFromExpression(caseExpr->mCaseExpression, caseValAddr.mType, BfEvalExprFlags_AllowEnumId); if ((!caseMatch) || (!caseValAddr)) { mResult = mModule->GetDefaultTypedValue(boolType); @@ -3918,6 +3966,82 @@ void BfExprEvaluator::Visit(BfCaseExpression* caseExpr) PerformBinaryOperation(caseExpr->mCaseExpression, caseExpr->mValueExpression, BfBinaryOp_Equality, caseExpr->mEqualsNode, BfBinOpFlag_None, caseValAddr, caseMatch); } +void BfExprEvaluator::Visit(BfCaseExpression* caseExpr) +{ + if (caseExpr->mEqualsNode != NULL) + { + mModule->Warn(0, "Deprecated case syntax", caseExpr->mEqualsNode); + } + + auto boolType = mModule->GetPrimitiveType(BfTypeCode_Boolean); + BfTypedValue caseValAddr; + if (caseExpr->mValueExpression != NULL) + caseValAddr = mModule->CreateValueFromExpression(caseExpr->mValueExpression, NULL, (BfEvalExprFlags)(mBfEvalExprFlags & BfEvalExprFlags_InheritFlags)); + + if ((caseValAddr.mType != NULL) && (caseValAddr.mType->IsPointer())) + { + caseValAddr = mModule->LoadValue(caseValAddr); + caseValAddr = BfTypedValue(caseValAddr.mValue, caseValAddr.mType->GetUnderlyingType(), true); + } + + BfIRValue hasValueValue; + if (caseValAddr.mType != NULL) + mModule->mBfIRBuilder->PopulateType(caseValAddr.mType); + + if ((caseValAddr.mType != NULL) && (caseValAddr.mType->IsNullable())) + { + auto nullableElementType = caseValAddr.mType->GetUnderlyingType(); + hasValueValue = mModule->ExtractValue(caseValAddr, nullableElementType->IsValuelessType() ? 1 : 2); + + if (!nullableElementType->IsValuelessType()) + caseValAddr = BfTypedValue(mModule->ExtractValue(caseValAddr, 1), nullableElementType); // value + else + caseValAddr = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), nullableElementType); + } + + BfIRBlock nullBB; + BfIRBlock endBB; + + if (hasValueValue) + { + auto caseBB = mModule->mBfIRBuilder->CreateBlock("caseexpr.case"); + endBB = mModule->mBfIRBuilder->CreateBlock("caseexpr.end"); + + mModule->mBfIRBuilder->CreateCondBr(hasValueValue, caseBB, endBB); + nullBB = mModule->mBfIRBuilder->GetInsertBlock(); + + mModule->mBfIRBuilder->AddBlock(caseBB); + mModule->mBfIRBuilder->SetInsertPoint(caseBB); + } + + DoCaseExpression(caseValAddr, caseExpr); + + if (!mResult) + mResult = mModule->GetDefaultTypedValue(boolType); + else + { + BF_ASSERT(mResult.mType == boolType); + } + + if (hasValueValue) + { + auto endCaseBB = mModule->mBfIRBuilder->GetInsertBlock(); + mModule->mBfIRBuilder->CreateBr(endBB); + + mModule->mBfIRBuilder->AddBlock(endBB); + mModule->mBfIRBuilder->SetInsertPoint(endBB); + + auto phiValue = mModule->mBfIRBuilder->CreatePhi(mModule->mBfIRBuilder->MapType(boolType), 2); + mModule->mBfIRBuilder->AddPhiIncoming(phiValue, mModule->GetDefaultValue(boolType), nullBB); + mModule->mBfIRBuilder->AddPhiIncoming(phiValue, mResult.mValue, endCaseBB); + + mResult = BfTypedValue(phiValue, boolType); + } + + if (caseExpr->mNotToken != NULL) + mResult.mValue = mModule->mBfIRBuilder->CreateNot(mResult.mValue); +} + void BfExprEvaluator::Visit(BfTypedValueExpression* typedValueExpr) { mResult = typedValueExpr->mTypedValue; @@ -3980,7 +4104,7 @@ bool BfExprEvaluator::CheckForMethodName(BfAstNode* refNode, BfTypeInstance* typ } bool BfExprEvaluator::IsVar(BfType* type, bool forceIgnoreWrites) -{ +{ if (type->IsVar()) return true; if ((type->IsGenericParam()) && (!forceIgnoreWrites) && (!mModule->mBfIRBuilder->mIgnoreWrites)) @@ -4175,7 +4299,7 @@ void BfExprEvaluator::Visit(BfStringInterpolationExpression* stringInterpolation // { SetAndRestoreValue prevFlags(mBfEvalExprFlags, (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_NoAutoComplete)); - CreateObject(NULL, stringInterpolationExpression->mAllocNode, stringType); + CreateObject(NULL, stringInterpolationExpression->mAllocNode, stringType, NULL); } BfTypedValue newString = mResult; BF_ASSERT(newString); @@ -4268,12 +4392,19 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef) } else if (varDecl->mAddr) { + if ((!mModule->mBfIRBuilder->mIgnoreWrites) && (varDecl->mAddr.IsFake()) && (!varDecl->mResolvedType->IsValuelessType())) + { + // In an ignore case match we can may need to create a fake "out" when someone tries to read it + auto defaultTypedValue = mModule->GetDefaultTypedValue(varDecl->mResolvedType, true, BfDefaultValueKind_Addr); + varDecl->mAddr = defaultTypedValue.mValue; + } + if ((varDecl->mResolvedType->IsRef()) && (!allowRef)) { BfRefType* refType = (BfRefType*)varDecl->mResolvedType; BfType* innerType = refType->mElementType; - if (innerType->IsValuelessType()) + if (innerType->IsValuelessNonOpaqueType()) { if (refType->mRefKind == BfRefType::RefKind_Mut) return BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), innerType, BfTypedValueKind_MutableValue); @@ -4299,7 +4430,11 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef) else localResult = BfTypedValue(varDecl->mAddr, varDecl->mResolvedType, varDecl->mIsReadOnly ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr); } - else if (varDecl->mResolvedType->IsValuelessType()) + else if ((varDecl->mResolvedType->IsModifiedTypeType()) && (((BfModifiedTypeType*)varDecl->mResolvedType)->mModifiedKind == BfToken_Params)) + { + localResult = BfTypedValue(BfIRValue(), varDecl->mResolvedType); + } + else if (varDecl->mResolvedType->IsValuelessNonOpaqueType()) { if ((varDecl->mResolvedType->IsRef()) && (!allowRef)) { @@ -4312,7 +4447,97 @@ BfTypedValue BfExprEvaluator::LoadLocal(BfLocalVariable* varDecl, bool allowRef) } else if (varDecl->mCompositeCount >= 0) { - localResult = BfTypedValue(BfIRValue(), mModule->GetPrimitiveType(BfTypeCode_None)); + if ((mBfEvalExprFlags & BfEvalExprFlags_InParamsExpr) != 0) + { + localResult = BfTypedValue(BfIRValue(), mModule->GetPrimitiveType(BfTypeCode_None)); + } + else if (!varDecl->mAddr) + { + bool isValid = true; + + Array argVals; + + auto methodState = mModule->mCurMethodState->GetMethodStateForLocal(varDecl); + for (int compositeIdx = 0; compositeIdx < varDecl->mCompositeCount; compositeIdx++) + { + BfResolvedArg compositeResolvedArg; + auto compositeLocalVar = methodState->mLocals[varDecl->mLocalVarIdx + compositeIdx + 1]; + auto argValue = LoadLocal(compositeLocalVar, true); + if (argValue) + { + if (!argValue.mType->IsStruct()) + argValue = mModule->LoadValue(argValue, NULL, mIsVolatileReference); + argVals.Add(argValue); + } + else + isValid = false; + } + + if (isValid) + { + BfTypeInstance* tupleType = NULL; + if (varDecl->mResolvedType->IsTuple()) + tupleType = (BfTupleType*)varDecl->mResolvedType; + else if ((varDecl->mResolvedType->IsDelegateOrFunction())) + { + auto invokeFunction = mModule->GetDelegateInvokeMethod(varDecl->mResolvedType->ToTypeInstance()); + if (invokeFunction != NULL) + { + BfTypeVector fieldTypes; + SubstituteList fieldNames; + for (int paramIdx = 0; paramIdx < invokeFunction->GetParamCount(); paramIdx++) + { + fieldNames.Add(invokeFunction->GetParamName(paramIdx)); + fieldTypes.Add(invokeFunction->GetParamType(paramIdx)); + } + tupleType = mModule->CreateTupleType(fieldTypes, fieldNames); + } + } + + if (tupleType == NULL) + { + isValid = false; + } + else if (tupleType->IsValuelessType()) + { + localResult = mModule->GetDefaultTypedValue(tupleType); + } + else + { + BF_ASSERT(tupleType->mFieldInstances.mSize == argVals.mSize); + + auto instAlloca = mModule->CreateAlloca(tupleType); + + for (int i = 0; i < argVals.mSize; i++) + { + auto& fieldInstance = tupleType->mFieldInstances[i]; + if (fieldInstance.mDataIdx >= 0) + { + auto val = mModule->Cast(varDecl->mNameNode, argVals[i], fieldInstance.mResolvedType); + if (val) + { + val = mModule->LoadOrAggregateValue(val); + if (!val.mType->IsValuelessType()) + { + auto elemPtr = mModule->mBfIRBuilder->CreateInBoundsGEP(instAlloca, 0, fieldInstance.mDataIdx); + mModule->mBfIRBuilder->CreateStore(val.mValue, elemPtr); + } + } + } + } + + varDecl->mResolvedType = tupleType; + varDecl->mAddr = instAlloca; + varDecl->mIsReadOnly = true; + localResult = BfTypedValue(varDecl->mAddr, varDecl->mResolvedType, BfTypedValueKind_ReadOnlyAddr); + } + } + + if (!isValid) + { + localResult = mModule->GetDefaultTypedValue(mModule->ResolveTypeDef(mModule->mCompiler->mTupleTypeDef)); + } + } } else { @@ -4450,11 +4675,6 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI if ((closureTypeInst != NULL) && (wantName == "this")) break; - if ((varDecl->mCompositeCount >= 0) && ((mBfEvalExprFlags & BfEvalExprFlags_AllowParamsExpr) == 0)) - { - mModule->Fail("Invalid use of 'params' parameter", refNode); - } - if (varDecl->mResolvedType->IsVoid()) { if ((varDecl->mIsReadOnly) && (varDecl->mParamIdx == -2) && (varDecl->mParamFailed)) @@ -4464,9 +4684,16 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI } } - mModule->SetElementType(identifierNode, (varDecl->IsParam()) ? BfSourceElementType_Parameter : BfSourceElementType_Local); + if (!varDecl->mIsThis) + mModule->SetElementType(identifierNode, (varDecl->IsParam()) ? BfSourceElementType_Parameter : BfSourceElementType_Local); BfTypedValue localResult = LoadLocal(varDecl); + + if ((localResult) && (localResult.mType->IsParamsType()) && ((mBfEvalExprFlags & BfEvalExprFlags_AllowParamsExpr) == 0)) + { + localResult = mModule->LoadOrAggregateValue(localResult); + } + auto autoComplete = GetAutoComplete(); if (identifierNode != NULL) { @@ -4681,7 +4908,7 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI result = LookupField(identifierNode, thisValue, findName); if ((result) || (mPropDef != NULL)) { - mModule->SetElementType(identifierNode, BfSourceElementType_Member); + mModule->SetHighestElementType(identifierNode, BfSourceElementType_Member); return result; } } @@ -4696,7 +4923,7 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI result = LookupField(identifierNode, thisValue, findName); if ((result) || (mPropDef != NULL)) { - mModule->SetElementType(identifierNode, BfSourceElementType_Member); + mModule->SetHighestElementType(identifierNode, BfSourceElementType_Member); return result; } } @@ -4704,7 +4931,7 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI } else { - mModule->SetElementType(identifierNode, BfSourceElementType_Member); + mModule->SetHighestElementType(identifierNode, BfSourceElementType_Member); } if ((!result) && (identifierNode != NULL)) @@ -4756,7 +4983,12 @@ BfTypedValue BfExprEvaluator::LookupIdentifier(BfAstNode* refNode, const StringI { auto thisLocal = mModule->mCurMethodState->mLocals[0]; if (thisLocal->mIsThis) - return BfTypedValue(mModule->mBfIRBuilder->CreateLoad(thisLocal->mAddr), thisLocal->mResolvedType); + { + if (thisLocal->mAddr) + return BfTypedValue(mModule->mBfIRBuilder->CreateLoad(thisLocal->mAddr), thisLocal->mResolvedType); + else + return BfTypedValue(thisLocal->mValue, thisLocal->mResolvedType); + } } } } @@ -5057,8 +5289,11 @@ BfTypedValue BfExprEvaluator::LoadProperty(BfAstNode* targetSrc, BfTypedValue ta } } - SetAndRestoreValue prevResult(mResult, target); - CheckResultForReading(mResult); + if (!mPropDef->mIsStatic) + { + SetAndRestoreValue prevResult(mResult, target); + CheckResultForReading(mResult); + } return BfTypedValue(); } @@ -5172,7 +5407,7 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe if (fieldInstance->mConstIdx != -1) { - String constStr = autoComplete->ConstantToString(typeInstance->mConstHolder, BfIRValue(BfIRValueFlags_Const, fieldInstance->mConstIdx)); + String constStr = autoComplete->ConstantToString(typeInstance->mConstHolder, BfTypedValue(BfIRValue(BfIRValueFlags_Const, fieldInstance->mConstIdx), fieldInstance->mResolvedType)); if (!constStr.IsEmpty()) { autoComplete->mResultString += " = "; @@ -5197,7 +5432,13 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe if (fieldDef->mIsStatic) { - if ((target) && ((flags & BfLookupFieldFlag_IsImplicitThis) == 0) && (!typeInstance->mTypeDef->IsGlobalsContainer())) + if (target) + { + //BF_ASSERT((flags & BfLookupFieldFlag_HasInstance) != 0); + flags = (BfLookupFieldFlags)(flags | BfLookupFieldFlag_HasInstance); + } + + if (((flags & BfLookupFieldFlag_HasInstance) != 0) && ((flags & BfLookupFieldFlag_IsImplicitThis) == 0) && (!typeInstance->mTypeDef->IsGlobalsContainer())) { //CS0176: Member 'Program.sVal' cannot be accessed with an instance reference; qualify it with a type name instead mModule->Fail(StrFormat("Member '%s.%s' cannot be accessed with an instance reference; qualify it with a type name instead", @@ -5283,7 +5524,7 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe (mModule->mCurMethodInstance->mMethodDef->IsCtorOrInit()) && (mModule->mCurMethodInstance->mMethodDef->mIsStatic); - if ((mModule->mCompiler->mOptions.mRuntimeChecks) && (fieldInstance->IsAppendedObject()) && (!mModule->mBfIRBuilder->mIgnoreWrites) && + if ((mModule->mCompiler->mOptions.mRuntimeChecks) && (fieldInstance->IsAppendedObject()) && (!fieldDef->mIsStatic) && (!mModule->mBfIRBuilder->mIgnoreWrites) && (!mModule->IsSkippingExtraResolveChecks())) { auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); @@ -5422,8 +5663,10 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe mModule->EmitObjectAccessCheck(target); if (fieldInstance->mDataIdx < 0) - { + { + mModule->mCompiler->RequestExtraCompile(); mModule->InternalError("LoadField field DataIdx<0 where InstSize>0"); + mModule->DeferRebuildType(typeInstance); return mModule->GetDefaultTypedValue(resolvedFieldType); } @@ -5498,6 +5741,9 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue target, const StringImpl& fieldName, BfLookupFieldFlags flags) { + if (target) + flags = (BfLookupFieldFlags)(flags | BfLookupFieldFlag_HasInstance); + if ((target.mType != NULL && (target.mType->IsGenericParam()))) { auto genericParamType = (BfGenericParamType*)target.mType; @@ -5646,6 +5892,7 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar flags = (BfLookupFieldFlags)(flags | BfLookupFieldFlag_IsFailurePass); bool isBaseLookup = false; + int checkInterfaceIdx = 0; while (curCheckType != NULL) { if (((flags & BfLookupFieldFlag_CheckingOuter) != 0) && ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0)) @@ -5717,7 +5964,7 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar auto checkProtection = field->mProtection; if (checkProtection == BfProtection_Hidden) { - // Allow acessing hidden fields + // Allow accessing hidden fields checkProtection = BfProtection_Private; } @@ -6003,17 +6250,30 @@ BfTypedValue BfExprEvaluator::LookupField(BfAstNode* targetSrc, BfTypedValue tar } } - isBaseLookup = true; - curCheckType = curCheckType->mBaseType; + if ((!isBaseLookup) && (startCheckType->IsInterface() && (checkInterfaceIdx < (int)startCheckType->mInterfaces.size()))) + { + curCheckType = startCheckType->mInterfaces[checkInterfaceIdx].mInterfaceType; + checkInterfaceIdx++; + } + else + { + isBaseLookup = true; + curCheckType = curCheckType->mBaseType; + } } } - + auto outerTypeDef = mModule->GetOuterType(startCheckType); if (outerTypeDef != NULL) { + BfLookupFieldFlags newFlags = BfLookupFieldFlag_CheckingOuter; + if (((flags & BfLookupFieldFlag_HasInstance) != 0) && + ((flags & BfLookupFieldFlag_IsImplicitThis) == 0)) + newFlags = (BfLookupFieldFlags)(newFlags | BfLookupFieldFlag_HasInstance); + // Check statics in outer type - return LookupField(targetSrc, BfTypedValue(outerTypeDef), fieldName, BfLookupFieldFlag_CheckingOuter); - } + return LookupField(targetSrc, BfTypedValue(outerTypeDef), fieldName, newFlags); + } return BfTypedValue(); } @@ -6251,26 +6511,35 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr auto methodState = mModule->mCurMethodState->GetMethodStateForLocal(localVar); if (localVar->mCompositeCount >= 0) { - if ((resolvedArg.mArgFlags & BfArgFlag_ParamsExpr) == 0) - mModule->Warn(0, "'params' token expected", argExpr); - - for (int compositeIdx = 0; compositeIdx < localVar->mCompositeCount; compositeIdx++) + if ((resolvedArg.mArgFlags & BfArgFlag_ParamsExpr) != 0) { - BfResolvedArg compositeResolvedArg; - auto compositeLocalVar = methodState->mLocals[localVar->mLocalVarIdx + compositeIdx + 1]; - auto argValue = exprEvaluator.LoadLocal(compositeLocalVar, true); - if (argValue) + for (int compositeIdx = 0; compositeIdx < localVar->mCompositeCount; compositeIdx++) { - if (!argValue.mType->IsStruct()) - argValue = mModule->LoadValue(argValue, NULL, exprEvaluator.mIsVolatileReference); - } - resolvedArg.mTypedValue = argValue; - resolvedArg.mExpression = argExpr; - resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_FromParamComposite); - resolvedArgs.mResolvedArgs.push_back(resolvedArg); - } + BfResolvedArg compositeResolvedArg; + auto compositeLocalVar = methodState->mLocals[localVar->mLocalVarIdx + compositeIdx + 1]; + auto argValue = exprEvaluator.LoadLocal(compositeLocalVar, true); + if (argValue) + { + if (!argValue.mType->IsStruct()) + argValue = mModule->LoadValue(argValue, NULL, exprEvaluator.mIsVolatileReference); + } + resolvedArg.mTypedValue = argValue; + resolvedArg.mExpression = argExpr; + resolvedArg.mArgFlags = (BfArgFlags)(resolvedArg.mArgFlags | BfArgFlag_FromParamComposite); + resolvedArgs.mResolvedArgs.push_back(resolvedArg); - continue; + exprEvaluator.mIsVolatileReference = false; + } + + if ((localVar->mResolvedType->IsModifiedTypeType()) && (((BfModifiedTypeType*)localVar->mResolvedType)->mModifiedKind == BfToken_Params)) + { + // Is a 'params' + } + else + continue; + } + else + exprEvaluator.mResult = mModule->LoadOrAggregateValue(exprEvaluator.mResult); } } @@ -6329,6 +6598,8 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* // NOP; // } + bool isDelegateThunk = ((callFlags & (BfCreateCallFlags_DelegateThunkNonStatic | BfCreateCallFlags_DelegateThunkStatic)) != 0); + auto methodDef = methodInstance->mMethodDef; BfIRValue funcCallInst = func; @@ -6383,7 +6654,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* mModule->mCurMethodState->mCancelledDeferredCall = true; } - if (methodDef->mIsNoReturn) + if ((methodDef->mIsNoReturn) && ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) == 0)) { mModule->mCurMethodState->SetHadReturn(true); mModule->mCurMethodState->mLeftBlockUncond = true; @@ -6452,7 +6723,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* }; mModule->PopulateType(origReturnType, BfPopulateType_Data); - if (GetStructRetIdx(methodInstance) != -1) + if ((GetStructRetIdx(methodInstance) != -1) && (!isDelegateThunk)) { // We need to ensure that mReceivingValue has the correct type, otherwise it's possible that a conversion operator needs to be applied // This happens for returning Result's with a 'T' value @@ -6683,7 +6954,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* if (methodInstance->mVirtualTableIdx != -1) { - if ((!bypassVirtual) && (mDeferCallRef == NULL)) + if ((!bypassVirtual) && (mDeferCallData == NULL)) { if ((methodDef->mIsOverride) && (mModule->mCurMethodInstance->mIsReified)) { @@ -6770,20 +7041,25 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* else { // Map this new virtual index back to the original index - //vDataIdx += (methodInstance->mVirtualTableIdx - typeInst->GetBaseVTableSize()) + typeInst->GetOrigBaseVTableSize(); - - //vDataIdx = mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, 1 + methodInstance->GetOwner()->GetDynCastVDataCount() + mModule->mCompiler->mMaxInterfaceSlots); + + // Find the type instance that declared the original method + auto declTypeInst = typeInst; + while (declTypeInst->mBaseType != NULL) + { + mModule->PopulateType(declTypeInst->mBaseType, BfPopulateType_DataAndMethods); + if (methodInstance->mVirtualTableIdx >= declTypeInst->mBaseType->mVirtualMethodTableSize) + break; + BF_ASSERT(methodInstance->mMethodDef->mIsOverride); + declTypeInst = declTypeInst->mBaseType; + } vDataIdx = mModule->mBfIRBuilder->GetConfigConst(BfIRConfigConst_VirtualMethodOfs, BfTypeCode_Int32); vDataIdx = mModule->mBfIRBuilder->CreateAdd(vDataIdx, mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, - (methodInstance->mVirtualTableIdx - typeInst->GetImplBaseVTableSize()) + typeInst->GetOrigImplBaseVTableSize())); + (methodInstance->mVirtualTableIdx - declTypeInst->GetImplBaseVTableSize()) + declTypeInst->GetOrigImplBaseVTableSize())); } } else - { - //vDataIdx += methodInstance->mVirtualTableIdx; - - //vDataIdx = mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, 1 + methodInstance->GetOwner()->GetDynCastVDataCount() + mModule->mCompiler->mMaxInterfaceSlots); + { vDataIdx = mModule->mBfIRBuilder->GetConfigConst(BfIRConfigConst_VirtualMethodOfs, BfTypeCode_Int32); vDataIdx = mModule->mBfIRBuilder->CreateAdd(vDataIdx, mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, methodInstance->mVirtualTableIdx)); } @@ -6847,9 +7123,12 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* return _GetDefaultReturnValue(); } - if (mDeferCallRef != NULL) + if (mDeferCallData != NULL) { - mModule->AddDeferredCall(BfModuleMethodInstance(methodInstance, func), irArgs, mDeferScopeAlloc, mDeferCallRef, bypassVirtual); + if (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); return mModule->GetFakeTypedValue(returnType); } @@ -6878,6 +7157,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* expectCallingConvention = BfIRCallingConv_ThisCall; } + if (((callFlags & BfCreateCallFlags_DelegateThunkStatic) != 0) && (expectCallingConvention == BfIRCallingConv_ThisCall)) + expectCallingConvention = BfIRCallingConv_CDecl; + if ((methodInstance->mAlwaysInline) && (mModule->mCompiler->mOptions.mEmitLineInfo)) { // Emit a NOP so we always have a "step over" point @@ -6887,7 +7169,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* if (returnType->IsComposite()) mModule->mBfIRBuilder->PopulateType(returnType); - methodInstance->mMethodInstanceGroup->mHasEmittedReference = true; + methodInstance->mMethodInstanceGroup->mHasEmittedReference = true; BfIRValue callInst; int callIRArgCount = (int)irArgs.size(); @@ -6926,6 +7208,10 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* bool doingThis = methodInstance->HasThis(); int argIdx = 0; + if ((callFlags & BfCreateCallFlags_DelegateThunkStatic) != 0) + doingThis = false; + bool forceThisPtr = ((callFlags & BfCreateCallFlags_DelegateThunkNonStatic) != 0); + if (methodDef->mHasExplicitThis) paramIdx++; @@ -6936,7 +7222,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* if (methodInstance->mIsIntrinsic) break; - if (argIdx == GetStructRetIdx(methodInstance)) + if ((sret != NULL) && (argIdx == GetStructRetIdx(methodInstance))) { mModule->mBfIRBuilder->Call_AddAttribute(callInst, argIdx + 1, BfIRAttribute_StructRet); argIdx++; @@ -7106,6 +7392,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, BfMethodInstance* mModule->mCurMethodState->mMayNeedThisAccessCheck = true; } + if (isDelegateThunk) + return BfTypedValue(callInst, methodInstance->mReturnType); + BfTypedValue result; if (sret != NULL) result = *sret; @@ -7280,7 +7569,10 @@ void BfExprEvaluator::PushArg(BfTypedValue argVal, SizedArrayImpl& ir argVal = mModule->GetDefaultTypedValue(mModule->mContext->mBfObjectType); } - if (argVal.mType->IsValuelessType()) + if (argVal.mType->IsIncomplete()) + mModule->PopulateType(argVal.mType); + + if (argVal.mType->IsValuelessNonOpaqueType()) return; bool wantSplat = false; if ((argVal.mType->IsSplattable()) && (!disableSplat) && (!IsComptime())) @@ -7375,7 +7667,7 @@ void BfExprEvaluator::PushArg(BfTypedValue argVal, SizedArrayImpl& ir void BfExprEvaluator::PushThis(BfAstNode* targetSrc, BfTypedValue argVal, BfMethodInstance* methodInstance, SizedArrayImpl& irArgs, bool skipMutCheck) { - MakeBaseConcrete(argVal); + MakeBaseConcrete(argVal); auto methodDef = methodInstance->mMethodDef; if (methodInstance->IsSkipCall()) @@ -7427,18 +7719,18 @@ void BfExprEvaluator::PushThis(BfAstNode* targetSrc, BfTypedValue argVal, BfMeth } } - if (argVal.mType->IsValuelessType()) + if (argVal.mType->IsValuelessNonOpaqueType()) return; auto owner = methodInstance->GetOwner(); bool allowThisSplatting; if (mModule->mIsComptimeModule) - allowThisSplatting = owner->IsTypedPrimitive() || owner->IsValuelessType(); + allowThisSplatting = owner->IsTypedPrimitive() || owner->IsValuelessNonOpaqueType(); else allowThisSplatting = methodInstance->AllowsSplatting(-1); - if ((!allowThisSplatting) || (methodDef->mIsMutating)) + if ((!allowThisSplatting) || (methodDef->mIsMutating) || (methodInstance->ForcingThisPtr())) { argVal = mModule->MakeAddressable(argVal); irArgs.push_back(argVal.mValue); @@ -7492,6 +7784,10 @@ void BfExprEvaluator::FinishDeferredEvals(BfResolvedArgs& argValues) mModule->CheckVariableDef(localVar); localVar->Init(); mModule->AddLocalVariableDef(localVar, true); + + auto curScope = mModule->mCurMethodState->mCurScope; + if (curScope->mScopeKind == BfScopeKind_StatementTarget) + mModule->MoveLocalToParentScope(localVar); } } } @@ -7597,6 +7893,13 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu auto funcType = mModule->mBfIRBuilder->MapMethod(moduleMethodInstance.mMethodInstance); auto funcPtrType = mModule->mBfIRBuilder->GetPointerTo(funcType); moduleMethodInstance.mFunc = mModule->mBfIRBuilder->CreateIntToPtr(target.mValue, funcPtrType); + + if (mDeferCallData != NULL) + { + mDeferCallData->mFuncAlloca_Orig = moduleMethodInstance.mFunc; + mDeferCallData->mFuncAlloca = mModule->CreateAlloca(funcPtrType, target.mType->mAlign, false, "FuncAlloca"); + mModule->mBfIRBuilder->CreateStore(mDeferCallData->mFuncAlloca_Orig, mDeferCallData->mFuncAlloca); + } } else if (!methodDef->mIsStatic) { @@ -7787,6 +8090,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu BfIRValue expandedParamAlloca; BfTypedValue expandedParamsArray; BfType* expandedParamsElementType = NULL; + bool hadDelegateParamIdx = false; int extendedParamIdx = 0; AddCallDependencies(methodInstance); @@ -7805,7 +8109,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu autoComplete->mIsCapturingMethodMatchInfo = wasCapturingMatchInfo; }); - BfScopeData* boxScopeData = mDeferScopeAlloc; + BfScopeData* boxScopeData = (mDeferCallData != NULL) ? mDeferCallData->mScopeAlloc : NULL; if ((boxScopeData == NULL) && (mModule->mCurMethodState != NULL)) boxScopeData = mModule->mCurMethodState->mCurScope; @@ -7996,7 +8300,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu arrayType, false); BfResolvedArgs resolvedArgs; - MatchConstructor(targetSrc, NULL, expandedParamsArray, arrayType, resolvedArgs, false, BfMethodGenericArguments(), false); + MatchConstructor(targetSrc, NULL, expandedParamsArray, arrayType, resolvedArgs, false, BfMethodGenericArguments(), BfAllowAppendKind_No); //TODO: Assert 'length' var is at slot 1 auto arrayBits = mModule->mBfIRBuilder->CreateBitCast(expandedParamsArray.mValue, mModule->mBfIRBuilder->MapType(arrayType->mBaseType)); @@ -8054,6 +8358,8 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu } } } + else if (paramKind == BfParamKind_DelegateParam) + hadDelegateParamIdx = true; } BfAstNode* arg = NULL; @@ -8065,7 +8371,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu if (argExprIdx < (int)argValues.size()) { arg = argValues[argExprIdx].mExpression; - if (((argValues[argExprIdx].mArgFlags & BfArgFlag_StringInterpolateArg) != 0) && (!expandedParamsArray)) + if (((argValues[argExprIdx].mArgFlags & BfArgFlag_StringInterpolateArg) != 0) && (!expandedParamsArray) && (!hadDelegateParamIdx)) { BfAstNode* errorRef = arg; int checkIdx = argExprIdx - 1; @@ -8150,6 +8456,9 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu } } + if (refNode == NULL) + refNode = methodInstance->GetOwner()->mTypeDef->GetRefNode(); + if ((autoComplete != NULL) && (prevNode != NULL)) autoComplete->CheckEmptyStart(prevNode, wantType); @@ -8547,7 +8856,7 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu } else if (expandedParamAlloca) { - argValue = mModule->LoadValue(argValue); + argValue = mModule->LoadOrAggregateValue(argValue); auto addr = mModule->mBfIRBuilder->CreateInBoundsGEP(expandedParamAlloca, extendedParamIdx); auto storeInst = mModule->mBfIRBuilder->CreateAlignedStore(argValue.mValue, addr, argValue.mType->mAlign); } @@ -8762,15 +9071,24 @@ BfTypedValue BfExprEvaluator::CreateCall(BfAstNode* targetSrc, const BfTypedValu } BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBoundExpression* methodBoundExpr, BfTypedValue target, BfTypeInstance* targetType, BfResolvedArgs& argValues, bool callCtorBodyOnly, - const BfMethodGenericArguments& methodGenericArguments, bool allowAppendAlloc, BfTypedValue* appendIndexValue) + const BfMethodGenericArguments& methodGenericArguments, BfAllowAppendKind allowAppendKind, BfTypedValue* appendIndexValue) { // Temporarily disable so we don't capture calls in params SetAndRestoreValue prevBindResult(mFunctionBindResult, NULL); + auto origAllowAppendKind = allowAppendKind; + + if (allowAppendKind == BfAllowAppendKind_Infer) + { + mModule->PopulateType(targetType); + allowAppendKind = targetType->IsZeroGap() ? BfAllowAppendKind_ZeroGap : BfAllowAppendKind_Yes; + } + static int sCtorCount = 0; sCtorCount++; BfMethodMatcher methodMatcher(targetSrc, mModule, "", argValues.mResolvedArgs, methodGenericArguments); + methodMatcher.mAllowAppendKind = allowAppendKind; methodMatcher.mBfEvalExprFlags = mBfEvalExprFlags; BfTypeVector typeGenericArguments; @@ -8898,17 +9216,29 @@ BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBou } BfConstructorDeclaration* ctorDecl = (BfConstructorDeclaration*)methodMatcher.mBestMethodDef->mMethodDeclaration; - if ((methodMatcher.mBestMethodDef->mHasAppend) && (targetType->IsObject())) - { - if (!allowAppendAlloc) + if ((methodMatcher.mBestMethodDef->HasAppend()) && (targetType->IsObject())) + { + if (allowAppendKind == BfAllowAppendKind_No) { if (mModule->mCurMethodInstance->mMethodDef->mMethodDeclaration == NULL) mModule->Fail("Constructors with append allocations cannot be called from a default constructor. Considering adding an explicit default constructor with the [AllowAppend] specifier.", targetSrc); else mModule->Fail("Constructors with append allocations cannot be called from a constructor without [AllowAppend] specified.", targetSrc); - } + } else { + if ((allowAppendKind == BfAllowAppendKind_Yes) && (methodMatcher.mBestMethodDef->mAppendKind == BfAllowAppendKind_ZeroGap)) + { + BfError* error; + if (origAllowAppendKind == BfAllowAppendKind_Infer) + error = mModule->Fail(StrFormat("Cannot call ZeroGap constructor for type '%s' because of fields added from type extensions", mModule->TypeToString(targetType).c_str()), targetSrc); + else + error = mModule->Fail(StrFormat("Cannot call ZeroGap constructor for type '%s' from here", mModule->TypeToString(targetType).c_str()), targetSrc); + + if ((error != NULL) && (methodMatcher.mBestMethodDef->mMethodDeclaration != NULL)) + mModule->mCompiler->mPassInstance->MoreInfo(StrFormat("See method declaration"), methodMatcher.mBestMethodDef->GetRefNode()); + } + BfResolvedArg resolvedArg; if (appendIndexValue != NULL) { @@ -8931,6 +9261,9 @@ BfTypedValue BfExprEvaluator::MatchConstructor(BfAstNode* targetSrc, BfMethodBou } } + if (methodMatcher.mAutoFlushAmbiguityErrors) + methodMatcher.FlushAmbiguityError(true); + if (isFailurePass) mModule->Fail(StrFormat("'%s' is inaccessible due to its protection level", mModule->MethodToString(moduleMethodInstance.mMethodInstance).c_str()), targetSrc); prevBindResult.Restore(); @@ -8963,9 +9296,9 @@ BfTypedValue BfExprEvaluator::ResolveArgValue(BfResolvedArg& resolvedArg, BfType if ((argValue) && (argValue.mType != wantType) && (wantType != NULL)) { - if ((mDeferScopeAlloc != NULL) && (wantType == mModule->mContext->mBfObjectType)) + if ((mDeferCallData != NULL) && (wantType == mModule->mContext->mBfObjectType)) { - BfAllocTarget allocTarget(mDeferScopeAlloc); + BfAllocTarget allocTarget(mDeferCallData->mScopeAlloc); argValue = mModule->BoxValue(expr, argValue, wantType, allocTarget, ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0) ? BfCastFlags_WantsConst : BfCastFlags_None); } else @@ -9040,47 +9373,7 @@ BfTypedValue BfExprEvaluator::ResolveArgValue(BfResolvedArg& resolvedArg, BfType mModule->CreateValueFromExpression(variableDeclaration->mInitializer, variableType, BfEvalExprFlags_NoCast); } - BfLocalVariable* localVar = new BfLocalVariable(); - if ((variableDeclaration != NULL) && (variableDeclaration->mNameNode != NULL)) - { - localVar->mName = variableDeclaration->mNameNode->ToString(); - localVar->mNameNode = BfNodeDynCast(variableDeclaration->mNameNode); - } - else - { - if (paramNameNode != NULL) - { - localVar->mName = "__"; - paramNameNode->ToString(localVar->mName); - localVar->mName += "_"; - localVar->mName += StrFormat("%d", mModule->mCurMethodState->GetRootMethodState()->mCurLocalVarId); - } - else - localVar->mName = "__" + StrFormat("%d", mModule->mCurMethodState->GetRootMethodState()->mCurLocalVarId); - } - - localVar->mResolvedType = variableType; - mModule->PopulateType(variableType); - if (!variableType->IsValuelessType()) - localVar->mAddr = mModule->CreateAlloca(variableType); - localVar->mIsReadOnly = isLet; - localVar->mReadFromId = 0; - localVar->mWrittenToId = 0; - localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional; - mModule->CheckVariableDef(localVar); - localVar->Init(); - mModule->AddLocalVariableDef(localVar, true); - - CheckVariableDeclaration(resolvedArg.mExpression, false, false, false); - - argValue = BfTypedValue(localVar->mAddr, mModule->CreateRefType(variableType, BfRefType::RefKind_Out)); - - auto curScope = mModule->mCurMethodState->mCurScope; - if (curScope->mScopeKind == BfScopeKind_StatementTarget) - { - // Move this variable into the parent scope - curScope->mLocalVarStart = (int)mModule->mCurMethodState->mLocals.size(); - } + argValue = mModule->CreateOutVariable(resolvedArg.mExpression, variableDeclaration, paramNameNode, variableType, BfTypedValue()); } return argValue; } @@ -9217,8 +9510,14 @@ BfTypedValue BfExprEvaluator::CheckEnumCreation(BfAstNode* targetSrc, BfTypeInst BfTypedValue receivingValue; BfIRValue tupleFieldPtr; - if (tuplePtr) + + mModule->PopulateType(tupleFieldInstance->mResolvedType); + if (tupleFieldInstance->mResolvedType->IsValuelessType()) { + receivingValue = mModule->GetDefaultTypedValue(tupleFieldInstance->mResolvedType); + } + else if (tuplePtr) + { tupleFieldPtr = mModule->mBfIRBuilder->CreateInBoundsGEP(tuplePtr, 0, tupleFieldInstance->mDataIdx); receivingValue = BfTypedValue(tupleFieldPtr, tupleFieldInstance->mResolvedType, true); } @@ -9375,6 +9674,10 @@ bool BfExprEvaluator::CheckGenericCtor(BfGenericParamType* genericParamType, BfR mModule->Fail(StrFormat("Must add 'where %s : struct' constraint to generic parameter to instantiate type without allocator", genericParam->GetGenericParamDef()->mName.c_str()), targetSrc); success = false; } + else if ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0) + { + mModule->Fail(StrFormat("Generic parameter '%s' constructor cannot be guaranteed to be const-evaluable", genericParam->GetGenericParamDef()->mName.c_str()), targetSrc); + } return success; } @@ -10119,6 +10422,8 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp if (resolvedTypeInstance != NULL) { + auto origTypeInstance = resolvedTypeInstance; + if ((mBfEvalExprFlags & BfEvalExprFlags_AppendFieldInitializer) == 0) { if ((!resolvedTypeInstance->IsStruct()) && (!resolvedTypeInstance->IsTypedPrimitive())) @@ -10164,10 +10469,13 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp structInst = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), resolvedTypeInstance, true); } + bool doBind = false; + mResultLocalVar = NULL; mResultFieldInstance = NULL; mResultLocalVarRefNode = NULL; - auto result = MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, BfMethodGenericArguments(), resolvedTypeInstance->IsObject()); + BfTypedValue result = MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, BfMethodGenericArguments(), + resolvedTypeInstance->IsObject() ? BfAllowAppendKind_Infer : BfAllowAppendKind_No); if ((result) && (!result.mType->IsVoid())) return result; mModule->ValidateAllocation(resolvedTypeInstance, targetSrc); @@ -10269,7 +10577,10 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp if ((mModule->mCurMethodState != NULL) && (exprEvaluator.mResultLocalVar != NULL) && (exprEvaluator.mResultLocalVarRefNode != NULL)) { auto localVar = exprEvaluator.mResultLocalVar; - if ((localVar->mCompositeCount >= 0) && (localVar->mResolvedType == fieldVal.mType)) + auto checkType = localVar->mResolvedType; + if (checkType->IsParamsType()) + checkType = checkType->GetUnderlyingType(); + if ((localVar->mCompositeCount >= 0) && (checkType == fieldVal.mType)) { delegateFailed = false; if (mModule->mCurMethodInstance->mIsUnspecialized) @@ -10325,6 +10636,11 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp ((typeConstraint->IsDelegate()) || (typeConstraint->IsFunction()))) { BfMethodInstance* invokeMethodInstance = mModule->GetRawMethodInstanceAtIdx(typeConstraint->ToTypeInstance(), 0, "Invoke"); + if (invokeMethodInstance == NULL) + { + mModule->InternalError("Get Invoke failed", targetSrc); + return BfTypedValue(); + } methodDef = invokeMethodInstance->mMethodDef; methodMatcher.mBestMethodInstance = invokeMethodInstance; @@ -10358,6 +10674,12 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp methodGenericArguments = methodInstance->mMethodInfoEx->mMethodGenericArguments; methodMatcher.mBestMethodInstance = mModule->GetMethodInstance(methodInstance->GetOwner(), methodInstance->mMethodDef, methodGenericArguments); } + + if (methodInstance->mMethodInfoEx != NULL) + { + methodMatcher.mBestMethodGenericArguments = methodInstance->mMethodInfoEx->mMethodGenericArguments; + } + methodMatcher.mBestMethodTypeInstance = methodInstance->GetOwner(); if (methodInstance->HasThis()) { @@ -10552,7 +10874,8 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp callTargetType = moduleMethodInstance.mMethodInstance->GetParamType(0); if ((callTargetType->IsRef()) && (target.IsAddr()) && (!target.IsReadOnly()) && (target.mType->IsValueType())) { - target = BfTypedValue(target.mValue, mModule->CreateRefType(target.mType)); + auto refType = (BfRefType*)callTargetType; + target = BfTypedValue(target.mValue, mModule->CreateRefType(target.mType, refType->mRefKind)); } } @@ -10959,6 +11282,12 @@ void BfExprEvaluator::LookupQualifiedName(BfQualifiedNameNode* nameNode, bool ig else mResult.mKind = BfTypedValueKind_Addr; } + else if (mResult.mType->IsAllocType()) + { + BF_ASSERT(mResult.mValue.IsFake()); + mResult.mType = mResult.mType->GetUnderlyingType(); + } + mIsVolatileReference = false; mIsHeapReference = false; @@ -11288,7 +11617,7 @@ void BfExprEvaluator::LookupQualifiedStaticField(BfQualifiedNameNode* nameNode, { BfType* type = NULL; { - type = mModule->ResolveTypeRef(nameNode->mLeft, NULL, BfPopulateType_Data, BfResolveTypeRefFlag_AllowRef); + type = mModule->ResolveTypeRef(nameNode->mLeft, NULL, BfPopulateType_Data, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_AllowRef | BfResolveTypeRefFlag_IgnoreLookupError)); mModule->CheckTypeRefFixit(nameNode->mLeft); } if (type != NULL) @@ -11607,7 +11936,7 @@ void BfExprEvaluator::Visit(BfMixinExpression* mixinExpr) void BfExprEvaluator::Visit(BfSizedArrayCreateExpression* createExpr) { - auto type = mModule->ResolveTypeRef(createExpr->mTypeRef); + auto type = mModule->ResolveTypeRef(createExpr->mTypeRef, NULL, BfPopulateType_Data, BfResolveTypeRefFlag_AllowInferredSizedArray); if (type == NULL) return; @@ -11633,6 +11962,14 @@ void BfExprEvaluator::Visit(BfSizedArrayCreateExpression* createExpr) return; } + if (type->IsUndefSizedArray()) + { + int arraySize = 0; + if (createExpr->mInitializer != NULL) + arraySize = (int)createExpr->mInitializer->mValues.size(); + type = mModule->CreateSizedArrayType(type->GetUnderlyingType(), arraySize); + } + BfSizedArrayType* arrayType = (BfSizedArrayType*)type; if (createExpr->mInitializer == NULL) @@ -11662,7 +11999,7 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) { if (type->IsValueType()) { - if (mReceivingValue != NULL) + if ((mReceivingValue != NULL) && (mReceivingValue->mType == type) && (mReceivingValue->IsAddr())) { mResult = *mReceivingValue; mReceivingValue = NULL; @@ -11681,6 +12018,10 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) } } } + else if (auto objCreateExpr = BfNodeDynCast(initExpr->mTarget)) + { + CreateObject(objCreateExpr, objCreateExpr->mNewNode, NULL, initExpr->mInlineTypeRef); + } else VisitChild(initExpr->mTarget); if (!mResult) @@ -11700,6 +12041,36 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) initValue = mModule->RemoveRef(initValue, false); bool isFirstAdd = true; + SetAndRestoreValue prevPrivateTypeInstance(mModule->mCurMethodState->mPrivateTypeInstance); + BfScopeData newScope; + + if (initExpr->mInlineTypeRef != NULL) + mModule->mCurMethodState->mPrivateTypeInstance = initValue.mType->ToTypeInstance(); + + newScope.mAllowTargeting = false; + newScope.mInnerIsConditional = true; + newScope.mCloseNode = initExpr->mCloseBrace; + mModule->mCurMethodState->AddScope(&newScope); + mModule->NewScopeState(); + + BfLocalVariable* localDef = new BfLocalVariable(); + localDef->mName = "_"; + localDef->mResolvedType = initValue.mType; + localDef->mAssignedKind = BfLocalVarAssignKind_Unconditional; + if (initValue.IsAddr()) + { + localDef->mAddr = initValue.mValue; + } + else + { + localDef->mValue = initValue.mValue; + localDef->mIsSplat = initValue.IsSplat(); + } + if (!localDef->mResolvedType->IsVar()) + mModule->AddLocalVariableDef(localDef, true, true); + + auto autoComplete = mModule->mCompiler->GetAutoComplete(); + for (auto elementExpr : initExpr->mValues) { if ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0) @@ -11715,6 +12086,19 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) BfTypedValue fieldResult; if (auto identifierNode = BfNodeDynCast(assignExpr->mLeft)) { + if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(identifierNode))) + { + auto type = initValue.mType; + if (type->IsPointer()) + type = type->GetUnderlyingType(); + if (auto typeInst = type->ToTypeInstance()) + { + autoComplete->mInsertStartIdx = identifierNode->GetSrcStart(); + autoComplete->mInsertEndIdx = identifierNode->GetSrcEnd(); + autoComplete->AddTypeMembers(typeInst, false, true, identifierNode->ToString(), typeInst, false, true, false); + } + } + StringT<128> findName; identifierNode->ToString(findName); mResultFieldInstance = NULL; @@ -11780,28 +12164,38 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) } else { - auto autoComplete = GetAutoComplete(); + BfBlock* block = BfNodeDynCast(elementExpr); + if ((autoComplete != NULL) && (autoComplete->IsAutocompleteNode(elementExpr))) { if (auto identiferNode = BfNodeDynCast(elementExpr)) { - auto typeInstance = initValue.mType->ToTypeInstance(); - if (typeInstance != NULL) + auto type = initValue.mType; + if (type->IsPointer()) + type = type->GetUnderlyingType(); + if (auto typeInst = type->ToTypeInstance()) { String filter; identiferNode->ToString(filter); - autoComplete->AddTypeMembers(typeInstance, false, true, filter, typeInstance, false, true, false); + autoComplete->AddTypeMembers(typeInst, false, true, filter, typeInst, false, true, false); } } } - BfExprEvaluator exprEvaluator(mModule); - SizedArray argExprs; - argExprs.push_back(elementExpr); - BfSizedArray sizedArgExprs(argExprs); - BfResolvedArgs argValues(&sizedArgExprs); - exprEvaluator.ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); - exprEvaluator.MatchMethod(elementExpr, NULL, initValue, false, false, "Add", argValues, BfMethodGenericArguments()); + if ((block != NULL) && (!block->IsExpression())) + { + mModule->VisitCodeBlock(block); + } + else + { + BfExprEvaluator exprEvaluator(mModule); + SizedArray argExprs; + argExprs.push_back(elementExpr); + BfSizedArray sizedArgExprs(argExprs); + BfResolvedArgs argValues(&sizedArgExprs); + exprEvaluator.ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); + exprEvaluator.MatchMethod(elementExpr, NULL, initValue, false, false, "Add", argValues, BfMethodGenericArguments()); + } wasValidInitKind = true; } @@ -11811,6 +12205,54 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) mModule->Fail("Invalid initializer member declarator", initExpr); } } + mModule->RestoreScopeState(); + + if (initExpr->mValues.IsEmpty()) + { + // When we are first typing out 'override', we + if (initExpr->mInlineTypeRef != NULL) + { + if (auto defineBlock = BfNodeDynCast(initExpr->mInlineTypeRef->mTypeDeclaration->mDefineNode)) + { + if (defineBlock->mChildArr.mSize == 1) + { + auto lastNode = defineBlock->mChildArr[0]; + if (lastNode->Equals("override")) + { + auto autoComplete = mModule->mCompiler->GetAutoComplete(); + if (autoComplete != NULL) + { + int cursorIdx = autoComplete->GetCursorIdx(lastNode); + if ((autoComplete->IsAutocompleteNode(lastNode, 1)) && (cursorIdx == lastNode->GetSrcEnd())) + { + auto typeInst = initValue.mType->ToTypeInstance(); + if (typeInst != NULL) + { + SetAndRestoreValue prevTypeInst(mModule->mCurTypeInstance, typeInst); + SetAndRestoreValue prevMethodInst(mModule->mCurMethodInstance, NULL); + autoComplete->AddOverrides("", true); + autoComplete->mInsertStartIdx = lastNode->mSrcStart; + autoComplete->mInsertEndIdx = lastNode->mSrcEnd; + } + } + } + } + } + } + } + } + else + { + auto lastNode = initExpr->mValues.back(); + if (auto lastIdentifier = BfNodeDynCast(lastNode)) + { + auto autoComplete = mModule->mCompiler->GetAutoComplete(); + if (autoComplete != NULL) + { + autoComplete->CheckIdentifier(lastIdentifier, false, false); + } + } + } if (unassignedFieldFlags != 0) { @@ -11885,8 +12327,16 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie return false; } - mModule->AddDependency(type, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); - mModule->PopulateType(type); + bool success = true; + + defer( + { + if (success) + mModule->AddDependency(type, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_TypeSignature); + }); + + // We want to try to avoid triggering OnTypeInit for basic info + mModule->PopulateType(type, BfPopulateType_Interfaces_Direct); auto typeInstance = type->ToTypeInstance(); auto _BoolResult = [&](bool val) @@ -11953,214 +12403,223 @@ bool BfExprEvaluator::LookupTypeProp(BfTypeOfExpression* typeOfExpr, BfIdentifie auto genericTypeInst = type->ToGenericTypeInstance(); _Int32Result((genericTypeInst != NULL) ? (int)genericTypeInst->mGenericTypeInfo->mTypeGenericArguments.size() : 0); } - else if (memberName == "Size") - _Int32Result(type->mSize); - else if (memberName == "Align") - _Int32Result(type->mAlign); - else if (memberName == "Stride") - _Int32Result(type->GetStride()); - else if (memberName == "InstanceSize") - _Int32Result((typeInstance != NULL) ? typeInstance->mInstSize : type->mSize); - else if (memberName == "InstanceAlign") - _Int32Result((typeInstance != NULL) ? typeInstance->mInstAlign : type->mSize); - else if (memberName == "InstanceStride") - _Int32Result((typeInstance != NULL) ? typeInstance->GetInstStride() : type->GetStride()); - else if (memberName == "UnderlyingType") + else { - bool handled = false; + // We need full data + mModule->PopulateType(type, BfPopulateType_Data); - auto typeType = mModule->ResolveTypeDef(mModule->mCompiler->mTypeTypeDef); - if (type->IsGenericParam()) + if (memberName == "Size") + _Int32Result(type->mSize); + else if (memberName == "Align") + _Int32Result(type->mAlign); + else if (memberName == "Stride") + _Int32Result(type->GetStride()); + else if (memberName == "InstanceSize") + _Int32Result((typeInstance != NULL) ? typeInstance->mInstSize : type->mSize); + else if (memberName == "InstanceAlign") + _Int32Result((typeInstance != NULL) ? typeInstance->mInstAlign : type->mSize); + else if (memberName == "InstanceStride") + _Int32Result((typeInstance != NULL) ? typeInstance->GetInstStride() : type->GetStride()); + else if (memberName == "UnderlyingType") { - auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)type); - if (genericParamInstance->IsEnum()) + bool handled = false; + + auto typeType = mModule->ResolveTypeDef(mModule->mCompiler->mTypeTypeDef); + if (type->IsGenericParam()) { - handled = true; - mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->MapType(typeType)), typeType); - } - } - else if (type->IsEnum()) - { - if (type->IsDataIncomplete()) - mModule->PopulateType(type); - auto underlyingType = type->GetUnderlyingType(); - if (underlyingType != NULL) - { - handled = true; - mModule->AddDependency(underlyingType, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); - mResult = BfTypedValue(mModule->CreateTypeDataRef(underlyingType), typeType); - } - } - - if (!handled) - mResult = BfTypedValue(mModule->CreateTypeDataRef(mModule->GetPrimitiveType(BfTypeCode_None)), typeType); - } - else if (memberName == "BitSize") - { - auto int32Type = mModule->GetPrimitiveType(BfTypeCode_Int32); - - BfType* checkType = type; - if (checkType->IsTypedPrimitive()) - checkType = checkType->GetUnderlyingType(); - - if (checkType->IsGenericParam()) - { - mResult = mModule->GetDefaultTypedValue(int32Type, false, Beefy::BfDefaultValueKind_Undef); - return true; - } - - if ((typeInstance != NULL) && (typeInstance->IsEnum())) - { - if (typeInstance->mTypeInfoEx != NULL) - { - int64 minValue = typeInstance->mTypeInfoEx->mMinValue; - if (minValue < 0) - minValue = ~minValue; - int64 maxValue = typeInstance->mTypeInfoEx->mMaxValue; - if (maxValue < 0) - maxValue = ~maxValue; - uint64 value = (uint64)minValue | (uint64)maxValue; - - int bitCount = 1; - if (typeInstance->mTypeInfoEx->mMinValue < 0) - bitCount++; - - while (value >>= 1) - bitCount++; - - mModule->AddDependency(typeInstance, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields); - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, bitCount), int32Type); - return true; - } - } - - int bitSize = checkType->mSize * 8; - if (checkType->GetTypeCode() == BfTypeCode_Boolean) - bitSize = 1; - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, bitSize), int32Type); - return true; - } - else if ((memberName == "MinValue") || (memberName == "MaxValue")) - { - bool isMin = memberName == "MinValue"; - bool isBitSize = memberName == "BitSize"; - - BfType* checkType = type; - if (checkType->IsTypedPrimitive()) - checkType = checkType->GetUnderlyingType(); - - if (checkType->IsGenericParam()) - { - bool foundMatch = false; - - auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)checkType); - if (((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Enum) != 0) || - ((genericParamInstance->mTypeConstraint != NULL) && (genericParamInstance->mTypeConstraint->IsInstanceOf(mModule->mCompiler->mEnumTypeDef)))) - foundMatch = true; - - else - { - for (auto constraint : genericParamInstance->mInterfaceConstraints) + auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)type); + if (genericParamInstance->IsEnum()) { - if (constraint->IsInstanceOf(mModule->mCompiler->mIIntegerTypeDef)) - foundMatch = true; + handled = true; + mResult = BfTypedValue(mModule->mBfIRBuilder->GetUndefConstValue(mModule->mBfIRBuilder->MapType(typeType)), typeType); + } + } + else if (type->IsEnum()) + { + if (type->IsDataIncomplete()) + mModule->PopulateType(type); + auto underlyingType = type->GetUnderlyingType(); + if (underlyingType != NULL) + { + handled = true; + mModule->AddDependency(underlyingType, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); + mResult = BfTypedValue(mModule->CreateTypeDataRef(underlyingType), typeType); } } - if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL)) - { - for (int genericParamIdx = (int)mModule->mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); - genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++) - { - genericParamInstance = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; - if (genericParamInstance->mExternType == type) - { - if (((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Enum) != 0) || - ((genericParamInstance->mTypeConstraint != NULL) && (genericParamInstance->mTypeConstraint->IsInstanceOf(mModule->mCompiler->mEnumTypeDef)))) - foundMatch = true; - } - } - } + if (!handled) + mResult = BfTypedValue(mModule->CreateTypeDataRef(mModule->GetPrimitiveType(BfTypeCode_None)), typeType); + } + else if (memberName == "BitSize") + { + auto int32Type = mModule->GetPrimitiveType(BfTypeCode_Int32); - if (foundMatch) + BfType* checkType = type; + if (checkType->IsTypedPrimitive()) + checkType = checkType->GetUnderlyingType(); + + if (checkType->IsGenericParam()) { - mResult = mModule->GetDefaultTypedValue(type, false, Beefy::BfDefaultValueKind_Undef); + mResult = mModule->GetDefaultTypedValue(int32Type, false, Beefy::BfDefaultValueKind_Undef); return true; } - } - - if (checkType->IsPrimitiveType()) - { - auto primType = (BfPrimitiveType*)checkType; if ((typeInstance != NULL) && (typeInstance->IsEnum())) { if (typeInstance->mTypeInfoEx != NULL) { + int64 minValue = typeInstance->mTypeInfoEx->mMinValue; + if (minValue < 0) + minValue = ~minValue; + int64 maxValue = typeInstance->mTypeInfoEx->mMaxValue; + if (maxValue < 0) + maxValue = ~maxValue; + uint64 value = (uint64)minValue | (uint64)maxValue; + + int bitCount = 1; + if (typeInstance->mTypeInfoEx->mMinValue < 0) + bitCount++; + + while (value >>= 1) + bitCount++; + mModule->AddDependency(typeInstance, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields); - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)typeInstance->mTypeInfoEx->mMinValue : (uint64)typeInstance->mTypeInfoEx->mMaxValue), typeInstance); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, bitCount), int32Type); return true; } } + + int bitSize = checkType->mSize * 8; + if (checkType->GetTypeCode() == BfTypeCode_Boolean) + bitSize = 1; + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(BfTypeCode_Int32, bitSize), int32Type); + return true; + } + else if ((memberName == "MinValue") || (memberName == "MaxValue")) + { + bool isMin = memberName == "MinValue"; + bool isBitSize = memberName == "BitSize"; + + BfType* checkType = type; + if (checkType->IsTypedPrimitive()) + checkType = checkType->GetUnderlyingType(); + + if (checkType->IsGenericParam()) + { + bool foundMatch = false; + + auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)checkType); + if (((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Enum) != 0) || + ((genericParamInstance->mTypeConstraint != NULL) && (genericParamInstance->mTypeConstraint->IsInstanceOf(mModule->mCompiler->mEnumTypeDef)))) + foundMatch = true; + + else + { + for (auto constraint : genericParamInstance->mInterfaceConstraints) + { + if (constraint->IsInstanceOf(mModule->mCompiler->mIIntegerTypeDef)) + foundMatch = true; + } + } + + if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized) && (mModule->mCurMethodInstance->mMethodInfoEx != NULL)) + { + for (int genericParamIdx = (int)mModule->mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); + genericParamIdx < mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++) + { + genericParamInstance = mModule->mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; + if (genericParamInstance->mExternType == type) + { + if (((genericParamInstance->mGenericParamFlags & BfGenericParamFlag_Enum) != 0) || + ((genericParamInstance->mTypeConstraint != NULL) && (genericParamInstance->mTypeConstraint->IsInstanceOf(mModule->mCompiler->mEnumTypeDef)))) + foundMatch = true; + } + } + } + + if (foundMatch) + { + mResult = mModule->GetDefaultTypedValue(type, false, Beefy::BfDefaultValueKind_Undef); + return true; + } + } + + if (checkType->IsPrimitiveType()) + { + auto primType = (BfPrimitiveType*)checkType; + + if ((typeInstance != NULL) && (typeInstance->IsEnum())) + { + if (typeInstance->mTypeInfoEx != NULL) + { + mModule->AddDependency(typeInstance, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ReadFields); + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)typeInstance->mTypeInfoEx->mMinValue : (uint64)typeInstance->mTypeInfoEx->mMaxValue), typeInstance); + return true; + } + } + else + { + switch (primType->mTypeDef->mTypeCode) + { + case BfTypeCode_Int8: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x80 : 0x7F), primType); + return true; + case BfTypeCode_Int16: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x8000 : 0x7FFF), primType); + return true; + case BfTypeCode_Int32: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), primType); + return true; + case BfTypeCode_Int64: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), primType); + return true; + case BfTypeCode_UInt8: + case BfTypeCode_Char8: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFF), primType); + return true; + case BfTypeCode_UInt16: + case BfTypeCode_Char16: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFFFF), primType); + return true; + case BfTypeCode_UInt32: + case BfTypeCode_Char32: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), primType); + return true; + case BfTypeCode_UInt64: + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), primType); + return true; + case BfTypeCode_IntPtr: + if (mModule->mSystem->mPtrSize == 8) + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), primType); + else + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), primType); + return true; + case BfTypeCode_UIntPtr: + if (mModule->mSystem->mPtrSize == 8) + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), primType); + else + mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), primType); + return true; + default: break; + } + } + } + + if (type->IsEnum()) + { + mModule->Fail(StrFormat("'MinValue' cannot be used on enum with payload '%s'", mModule->TypeToString(type).c_str()), propName); + } else { - switch (primType->mTypeDef->mTypeCode) - { - case BfTypeCode_Int8: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x80 : 0x7F), primType); - return true; - case BfTypeCode_Int16: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? -0x8000 : 0x7FFF), primType); - return true; - case BfTypeCode_Int32: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), primType); - return true; - case BfTypeCode_Int64: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), primType); - return true; - case BfTypeCode_UInt8: - case BfTypeCode_Char8: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFF), primType); - return true; - case BfTypeCode_UInt16: - case BfTypeCode_Char16: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : 0xFFFF), primType); - return true; - case BfTypeCode_UInt32: - case BfTypeCode_Char32: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), primType); - return true; - case BfTypeCode_UInt64: - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), primType); - return true; - case BfTypeCode_IntPtr: - if (mModule->mSystem->mPtrSize == 8) - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x8000000000000000LL : (uint64)0x7FFFFFFFFFFFFFFFLL), primType); - else - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? (uint64)-0x80000000LL : 0x7FFFFFFF), primType); - return true; - case BfTypeCode_UIntPtr: - if (mModule->mSystem->mPtrSize == 8) - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFFFFFFFFFLL), primType); - else - mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, isMin ? 0 : (uint64)0xFFFFFFFFLL), primType); - return true; - default: break; - } + mModule->Fail(StrFormat("'%s' cannot be used on type '%s'", memberName.c_str(), mModule->TypeToString(type).c_str()), propName); } } - - if (type->IsEnum()) - { - mModule->Fail(StrFormat("'MinValue' cannot be used on enum with payload '%s'", mModule->TypeToString(type).c_str()), propName); - } else { - mModule->Fail(StrFormat("'%s' cannot be used on type '%s'", memberName.c_str(), mModule->TypeToString(type).c_str()), propName); + success = false; + return false; } } - else - return false; if ((type->IsGenericParam()) && (!mModule->mIsComptimeModule)) { @@ -14067,7 +14526,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) } BfResolvedArgs resolvedArgs; - MatchConstructor(delegateBindExpr, delegateBindExpr, mResult, useTypeInstance, resolvedArgs, false, BfMethodGenericArguments(), false); + MatchConstructor(delegateBindExpr, delegateBindExpr, mResult, useTypeInstance, resolvedArgs, false, BfMethodGenericArguments(), BfAllowAppendKind_No); auto baseDelegateType = VerifyBaseDelegateType(delegateTypeInstance->mBaseType); auto baseDelegate = mModule->mBfIRBuilder->CreateBitCast(mResult.mValue, mModule->mBfIRBuilder->MapType(baseDelegateType, BfIRPopulateType_Full)); @@ -14471,30 +14930,19 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam if (invokeMethodInstance != NULL) { - for (int paramIdx = 0; paramIdx < (int)invokeMethodInstance->mMethodDef->mParams.size(); paramIdx++) + for (int paramIdx = 0; paramIdx < (int)invokeMethodInstance->GetParamCount(); paramIdx++) { - auto invokeParamDef = invokeMethodInstance->mMethodDef->mParams[paramIdx]; - - BfParameterDef* paramDef = new BfParameterDef(); - paramDef->mParamDeclaration = tempParamDecls.Alloc(); - BfAstNode::Zero(paramDef->mParamDeclaration); - - BfLocalVariable* localVar = new BfLocalVariable(); + BfLocalVariable* localVar = new BfLocalVariable(); if (paramIdx < (int)lambdaBindExpr->mParams.size()) { localVar->mName = lambdaBindExpr->mParams[paramIdx]->ToString(); - localVar->mNameNode = lambdaBindExpr->mParams[paramIdx]; - paramDef->mParamDeclaration->mNameNode = lambdaBindExpr->mParams[paramIdx]; + localVar->mNameNode = lambdaBindExpr->mParams[paramIdx]; } else { mModule->AssertErrorState(); - localVar->mName = invokeParamDef->mName; - paramDef->mParamDeclaration->mNameNode = NULL; - } - paramDef->mName = localVar->mName; - methodDef->mParams.push_back(paramDef); - + localVar->mName = invokeMethodInstance->GetParamName(paramIdx); + } localVar->mResolvedType = invokeMethodInstance->GetParamType(paramIdx); mModule->PopulateType(localVar->mResolvedType); localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional; @@ -14503,15 +14951,41 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam auto rootMethodState = methodState.GetRootMethodState(); localVar->mLocalVarId = rootMethodState->mCurLocalVarId++; - mModule->DoAddLocalVariable(localVar); + mModule->DoAddLocalVariable(localVar); - if (autoComplete != NULL) - autoComplete->CheckLocalDef(BfNodeDynCast(paramDef->mParamDeclaration->mNameNode), methodState.mLocals.back()); auto resolvePassData = mModule->mCompiler->mResolvePassData; if (resolvePassData != NULL) - resolvePassData->HandleLocalReference(BfNodeDynCast(paramDef->mParamDeclaration->mNameNode), mModule->mCurTypeInstance->mTypeDef, + resolvePassData->HandleLocalReference(BfNodeDynCast(localVar->mNameNode), mModule->mCurTypeInstance->mTypeDef, mModule->mCurMethodInstance->mMethodDef, localVar->mLocalVarId); } + + for (int paramIdx = 0; paramIdx < (int)invokeMethodInstance->mMethodDef->mParams.size(); paramIdx++) + { + auto invokeParamDef = invokeMethodInstance->mMethodDef->mParams[paramIdx]; + + BfParameterDef* paramDef = new BfParameterDef(); + paramDef->mParamDeclaration = tempParamDecls.Alloc(); + BfAstNode::Zero(paramDef->mParamDeclaration); + paramDef->mTypeRef = invokeParamDef->mTypeRef; + paramDef->mParamKind = invokeParamDef->mParamKind; + + if ((paramIdx < (int)lambdaBindExpr->mParams.size()) && (invokeMethodInstance->GetParamKind(paramIdx) != BfParamKind_DelegateParam)) + { + //TODO: Not always correct if we have a 'params' + paramDef->mParamDeclaration->mNameNode = lambdaBindExpr->mParams[paramIdx]; + paramDef->mName = paramDef->mParamDeclaration->mNameNode->ToString(); + } + else + { + paramDef->mParamDeclaration->mNameNode = NULL; + paramDef->mName = invokeParamDef->mName; + } + + methodDef->mParams.push_back(paramDef); + + if (autoComplete != NULL) + autoComplete->CheckLocalDef(BfNodeDynCast(paramDef->mParamDeclaration->mNameNode), methodState.mLocals.back()); + } } bool isAutocomplete = mModule->mCompiler->IsAutocomplete(); @@ -15477,11 +15951,12 @@ void BfExprEvaluator::CheckObjectCreateTypeRef(BfType* expectingType, BfAstNode* auto arrayType = (BfArrayType*)expectingType; expectingType = arrayType->mGenericTypeInfo->mTypeGenericArguments[0]; } - + auto expectingTypeInst = expectingType->ToTypeInstance(); if (expectingTypeInst != NULL) { - autoComplete->AddTypeInstanceEntry(expectingTypeInst); + if (!expectingTypeInst->IsAnonymous()) + autoComplete->AddTypeInstanceEntry(expectingTypeInst); } else autoComplete->mDefaultSelection = mModule->TypeToString(expectingType); @@ -15491,17 +15966,17 @@ void BfExprEvaluator::CheckObjectCreateTypeRef(BfType* expectingType, BfAstNode* void BfExprEvaluator::Visit(BfObjectCreateExpression* objCreateExpr) { - CreateObject(objCreateExpr, objCreateExpr->mNewNode, NULL); + CreateObject(objCreateExpr, objCreateExpr->mNewNode, NULL, NULL); } -void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAstNode* allocNode, BfType* wantAllocType) -{ +void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAstNode* allocNode, BfType* wantAllocType, BfInlineTypeReference* inlineTypeRef) +{ auto autoComplete = GetAutoComplete(); if ((autoComplete != NULL) && (objCreateExpr != NULL) && (objCreateExpr->mTypeRef != NULL)) { autoComplete->CheckTypeRef(objCreateExpr->mTypeRef, false, true); } - + if ((autoComplete != NULL) && (objCreateExpr != NULL) && (objCreateExpr->mOpenToken != NULL) && (objCreateExpr->mCloseToken != NULL) && (objCreateExpr->mOpenToken->mToken == BfToken_LBrace) && (autoComplete->CheckFixit(objCreateExpr->mOpenToken))) { @@ -15531,7 +16006,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs bool isStructAlloc = newToken == NULL; bool isScopeAlloc = (newToken != NULL) && (newToken->GetToken() == BfToken_Scope); bool isAppendAlloc = (newToken != NULL) && (newToken->GetToken() == BfToken_Append); - bool isStackAlloc = ((newToken != NULL) && (newToken->GetToken() == BfToken_Stack)) || (isScopeAlloc) || (isStructAlloc); + bool isStackAlloc = (isScopeAlloc) || (isStructAlloc); bool isArrayAlloc = false;// (objCreateExpr->mArraySizeSpecifier != NULL); bool isRawArrayAlloc = (objCreateExpr != NULL) && (objCreateExpr->mStarToken != NULL); @@ -15735,6 +16210,16 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs resolvedTypeRef = unresolvedTypeRef; } + if (inlineTypeRef != NULL) + { + auto inlineType = mModule->ResolveTypeRef(inlineTypeRef); + if (inlineType != NULL) + { + unresolvedTypeRef = inlineType; + resolvedTypeRef = inlineType; + } + } + if (resolvedTypeRef == NULL) { unresolvedTypeRef = mModule->GetPrimitiveType(BfTypeCode_Var); @@ -15785,7 +16270,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs mModule->Fail("Append allocations are only allowed as local variable declarations in the main method body", allocNode); isAppendAlloc = false; } - else if (!methodDef->mHasAppend) + else if (!methodDef->HasAppend()) { mModule->Fail("Append allocations can only be used on constructors with [AllowAppend] specified", allocNode); isAppendAlloc = false; @@ -16153,11 +16638,11 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs if (rawAutoComplete != NULL) { SetAndRestoreValue prevCapturing(rawAutoComplete->mIsCapturingMethodMatchInfo, false); - MatchConstructor(refNode, objCreateExpr, arrayValue, arrayType, resolvedArgs, false, methodGenericArguments, false); + MatchConstructor(refNode, objCreateExpr, arrayValue, arrayType, resolvedArgs, false, methodGenericArguments, BfAllowAppendKind_No); } else { - MatchConstructor(refNode, objCreateExpr, arrayValue, arrayType, resolvedArgs, false, methodGenericArguments, false); + MatchConstructor(refNode, objCreateExpr, arrayValue, arrayType, resolvedArgs, false, methodGenericArguments, BfAllowAppendKind_No); } //TODO: Assert 'length' var is at slot 1 @@ -16337,7 +16822,10 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs BfAstNode* refNode = objCreateExpr->mTypeRef; if ((objCreateExpr->mCtorExplicit != NULL) && (objCreateExpr->mCtorExplicit->mThisToken != NULL)) refNode = objCreateExpr->mCtorExplicit->mThisToken; - MatchConstructor(refNode, objCreateExpr, emtpyThis, typeInstance, argValues, false, methodGenericArguments, true); + auto checkTypeInst = typeInstance; + if (checkTypeInst->IsAnonymousInitializerType()) + checkTypeInst = checkTypeInst->mBaseType; + MatchConstructor(refNode, objCreateExpr, emtpyThis, checkTypeInst, argValues, false, methodGenericArguments, BfAllowAppendKind_Infer); if ((wasCapturingMethodInfo) && (!autoComplete->mIsCapturingMethodMatchInfo)) { if (autoComplete->mMethodMatchInfo != NULL) @@ -16351,7 +16839,11 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs auto refNode = allocNode; if (objCreateExpr != NULL) refNode = objCreateExpr->mTypeRef; - MatchConstructor(refNode, objCreateExpr, emtpyThis, typeInstance, argValues, false, methodGenericArguments, true); + + auto checkTypeInst = typeInstance; + if (checkTypeInst->IsAnonymousInitializerType()) + checkTypeInst = checkTypeInst->mBaseType; + MatchConstructor(refNode, objCreateExpr, emtpyThis, checkTypeInst, argValues, false, methodGenericArguments, BfAllowAppendKind_Infer); } if (objCreateExpr != NULL) mModule->ValidateAllocation(typeInstance, objCreateExpr->mTypeRef); @@ -16361,9 +16853,10 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs int allocAlign = resolvedTypeRef->mAlign; if (typeInstance != NULL) allocAlign = typeInstance->mInstAlign; - int appendAllocAlign = 0; + int appendAllocAlign = 0; + BfAllocFlags allocFlags = BfAllocFlags_None; - if ((bindResult.mMethodInstance != NULL) && (bindResult.mMethodInstance->mMethodDef->mHasAppend)) + if ((bindResult.mMethodInstance != NULL) && (bindResult.mMethodInstance->mMethodDef->HasAppend())) { if (!bindResult.mFunc) { @@ -16397,6 +16890,9 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs appendSizeValue = appendSizeTypedValue.mValue; allocAlign = BF_MAX(allocAlign, calcAppendMethodModule.mMethodInstance->mAppendAllocAlign); appendAllocAlign = calcAppendMethodModule.mMethodInstance->mAppendAllocAlign; + + if (calcAppendMethodModule.mMethodInstance->mHasAppendWantMark) + allocFlags = (BfAllocFlags)(allocFlags | BfAllocFlags_HasAppendWantMark); } if (appendAllocAlign != 0) @@ -16408,6 +16904,12 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs appendSizeValue = mModule->mBfIRBuilder->CreateAdd(appendSizeValue, mModule->GetConstValue(extraSize)); } } + + if ((allocFlags & BfAllocFlags_HasAppendWantMark) != 0) + { + int markInfoSize = sizeof(intptr) + sizeof(intptr) * 4; // Stack trace, MarkAppendEntry + appendSizeValue = mModule->mBfIRBuilder->CreateAdd(appendSizeValue, mModule->GetConstValue(markInfoSize)); + } } // WTF? I'm not even sure this is correct - add more tests @@ -16427,7 +16929,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs } else { - allocValue = mModule->AllocFromType(resolvedTypeRef, allocTarget, appendSizeValue, BfIRValue(), 0, BfAllocFlags_None, allocAlign); + allocValue = mModule->AllocFromType(resolvedTypeRef, allocTarget, appendSizeValue, BfIRValue(), 0, allocFlags, allocAlign); } if (((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0) && (mModule->mCompiler->mCeMachine != NULL)) { @@ -16462,10 +16964,9 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs } else if (bindResult.mFunc) { - if (typeInstance->IsObject()) + bool hasRealtimeLeakCheck = (mModule->mCompiler->mOptions.mEnableRealtimeLeakCheck) && (!IsComptime()); + if ((typeInstance->IsObject()) || (typeInstance->IsAnonymousInitializerType())) { - bool hasRealtimeLeakCheck = (mModule->mCompiler->mOptions.mEnableRealtimeLeakCheck) && (!IsComptime()); - bool wantsCtorClear = true; if (hasRealtimeLeakCheck) { @@ -16488,7 +16989,10 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs CreateCall(objCreateExpr, ctorClear.mMethodInstance, ctorClear.mFunc, false, irArgs); } } + } + if (typeInstance->IsObject()) + { if ((!mModule->mIsComptimeModule) && (isStackAlloc) && (hasRealtimeLeakCheck)) { BfMethodInstance* markMethod = mModule->GetRawMethodByName(mModule->mContext->mBfObjectType, "GCMarkMembers"); @@ -16500,26 +17004,27 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs { auto impMethodInstance = (BfMethodInstance*)vtableEntry.mImplementingMethod; bool needsCall = false; - if (impMethodInstance != NULL) - { - needsCall = impMethodInstance->mMethodDef->mBody != NULL; - } - else - { + + if (allocFlags & BfAllocFlags_HasAppendWantMark) needsCall = true; - BF_ASSERT(vtableEntry.mImplementingMethod.mKind == BfMethodRefKind_AmbiguousRef); + + if (!needsCall) + { + if (impMethodInstance != NULL) + { + needsCall = impMethodInstance->mMethodDef->mBody != NULL; + } + else + { + needsCall = true; + BF_ASSERT(vtableEntry.mImplementingMethod.mKind == BfMethodRefKind_AmbiguousRef); + } } if (!needsCall) { - for (auto& fieldInst : typeInstance->mFieldInstances) - { - if (fieldInst.IsAppendedObject()) - { - needsCall = true; - break; - } - } + if (typeInstance->HasAppendedField(true)) + needsCall = true; } if (needsCall) @@ -16551,23 +17056,56 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs } } - if ((bindResult.mMethodInstance->mMethodDef->mHasAppend) && (mResult.mType->IsObject())) + auto origThisTypedValue = mResult; + auto thisTypedValue = mResult; + if (inlineTypeRef != NULL) + { + BfType* wantType = bindResult.mMethodInstance->GetOwner(); + if (thisTypedValue.mType->IsPointer()) + wantType = mModule->CreatePointerType(wantType); + thisTypedValue = mModule->Cast(allocNode, thisTypedValue, wantType); + } + + if ((bindResult.mMethodInstance->mMethodDef->HasAppend()) && (mResult.mType->IsObject())) { BF_ASSERT(bindResult.mIRArgs[0].IsFake()); auto typeInst = mResult.mType->ToTypeInstance(); - auto intPtrType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); - auto thisVal = mResult; + auto intPtrType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); BfIRValue intPtrVal = mModule->CreateAlloca(intPtrType); - auto intPtrThisVal = mModule->mBfIRBuilder->CreatePtrToInt(thisVal.mValue, (intPtrType->mSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64); + auto intPtrThisVal = mModule->mBfIRBuilder->CreatePtrToInt(thisTypedValue.mValue, (intPtrType->mSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64); auto curValPtr = mModule->mBfIRBuilder->CreateAdd(intPtrThisVal, mModule->GetConstValue(typeInst->mInstSize, intPtrType)); mModule->mBfIRBuilder->CreateStore(curValPtr, intPtrVal); bindResult.mIRArgs[0] = intPtrVal; } if (!typeInstance->IsValuelessType()) - bindResult.mIRArgs.Insert(0, mResult.mValue); + bindResult.mIRArgs.Insert(0, thisTypedValue.mValue); auto result = CreateCall(objCreateExpr, bindResult.mMethodInstance, bindResult.mFunc, false, bindResult.mIRArgs); if ((result) && (!result.mType->IsVoid())) mResult = result; + + if (origThisTypedValue.mType != thisTypedValue.mType) + { + auto origThisType = origThisTypedValue.mType; + if (origThisType->IsPointer()) + origThisType = origThisType->GetUnderlyingType(); + auto origThisTypeInst = origThisType->ToTypeInstance(); + if (origThisTypeInst != NULL) + { + BF_ASSERT(origThisTypeInst->IsAnonymousInitializerType()); + + auto ctorMethod = mModule->GetMethodByName(origThisTypeInst, "__BfCtor", 0); + if (!ctorMethod) + { + mModule->AssertErrorState(); + } + else if ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) == 0) + { + SizedArray irArgs; + irArgs.push_back(origThisTypedValue.mValue); + CreateCall(objCreateExpr, ctorMethod.mMethodInstance, ctorMethod.mFunc, false, irArgs); + } + } + } } } @@ -16716,12 +17254,7 @@ void BfExprEvaluator::ResolveAllocTarget(BfAllocTarget& allocTarget, BfAstNode* if (mModule->mCurMethodState != NULL) allocTarget.mScopeData = mModule->mCurMethodState->mCurScope->GetTargetable(); } - else if (newToken->GetToken() == BfToken_Stack) - { - if (mModule->mCurMethodState != NULL) - allocTarget.mScopeData = &mModule->mCurMethodState->mHeadScope; - } - + if (attributeDirective != NULL) { auto customAttrs = mModule->GetCustomAttributes(attributeDirective, BfAttributeTargets_Alloc, BfGetCustomAttributesFlags_AllowNonConstArgs, allocTarget.mCaptureInfo); @@ -16802,7 +17335,10 @@ BfTypedValue BfExprEvaluator::MakeCallableTarget(BfAstNode* targetSrc, BfTypedVa else if (target.IsAddr()) { auto ptrType = mModule->CreatePointerType(primStructType); - target = BfTypedValue(mModule->mBfIRBuilder->CreateBitCast(target.mValue, mModule->mBfIRBuilder->MapType(ptrType)), primStructType, true); + if (primStructType->IsValuelessType()) + target = BfTypedValue(target.mValue, primStructType, true); + else + target = BfTypedValue(mModule->mBfIRBuilder->CreateBitCast(target.mValue, mModule->mBfIRBuilder->MapType(ptrType)), primStructType, true); } else if ((primStructType->IsSplattable()) && (target.IsSplat()) && (!IsComptime())) { @@ -17246,7 +17782,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo if (mModule->mCurMethodState == NULL) return; - if (mDeferCallRef != NULL) + if (mDeferCallData != NULL) { mModule->Fail("Mixins cannot be directly deferred. Consider wrapping in a block.", targetSrc); } @@ -17693,6 +18229,7 @@ void BfExprEvaluator::InjectMixin(BfAstNode* targetSrc, BfTypedValue target, boo mModule->mBfIRBuilder->SaveDebugLocation(); SetAndRestoreValue prevMixinState(curMethodState->mMixinState, mixinState); SetAndRestoreValue prevExprEvaluator(curMethodState->mCurScope->mExprEvaluator, NULL); + SetAndRestoreValue prevNullConditional(curMethodState->mPendingNullConditional, NULL); BfGetSymbolReferenceKind prevSymbolRefKind = BfGetSymbolReferenceKind_None; if (mModule->mCompiler->mResolvePassData != NULL) @@ -18338,7 +18875,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m else mResult = BfTypedValue(mModule->CreateAlloca(expectingType), expectingType, BfTypedValueKind_TempAddr); - auto ctorResult = MatchConstructor(target, methodBoundExpr, mResult, expectingType->ToTypeInstance(), argValues, false, BfMethodGenericArguments(), false); + auto ctorResult = MatchConstructor(target, methodBoundExpr, mResult, expectingType->ToTypeInstance(), argValues, false, BfMethodGenericArguments(), BfAllowAppendKind_No); if ((ctorResult) && (!ctorResult.mType->IsVoid())) mResult = ctorResult; mModule->ValidateAllocation(expectingType, invocationExpr->mTarget); @@ -18364,7 +18901,8 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m BfResolveArgsFlags resolveArgsFlags = BfResolveArgsFlag_None; ResolveArgValues(argValues, resolveArgsFlags); - CheckGenericCtor((BfGenericParamType*)expectingType, argValues, invocationExpr->mTarget); + if (!mModule->mCurMethodInstance->mIsUnspecializedVariation) + CheckGenericCtor((BfGenericParamType*)expectingType, argValues, invocationExpr->mTarget); mResult = mModule->GetDefaultTypedValue(expectingType); return; } @@ -18962,6 +19500,9 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m return; } + if (targetFunctionName.StartsWith('@')) + targetFunctionName.Remove(0, 1); + //TODO: We removed this... Messed up with PrimStruct 'this' non-mut errors // We moved this until later in MatchMethod, we want the raw target for the GetType optimization, plus we shouldn't do this until we know we won't do a SkipCall @@ -19107,6 +19648,11 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m } void BfExprEvaluator::Visit(BfInvocationExpression* invocationExpr) +{ + DoInvocation(invocationExpr); +} + +void BfExprEvaluator::DoInvocation(BfInvocationExpression* invocationExpr) { BfAutoParentNodeEntry autoParentNodeEntry(mModule, invocationExpr); @@ -19415,6 +19961,19 @@ BfModuleMethodInstance BfExprEvaluator::GetPropertyMethodInstance(BfMethodDef* m if (bestIFaceEntry != NULL) { + if ((checkTypeInst->IsInterface())) + { + auto propTypeInst = bestIFaceEntry->mInterfaceType->ToTypeInstance(); + if (propTypeInst == NULL) + { + mModule->Fail("INTERNAL ERROR: Invalid property target", mPropSrc); + return BfModuleMethodInstance(); + } + BF_ASSERT(propTypeInst->mTypeDef->mFullNameEx == methodDef->mDeclaringType->mFullNameEx); + + return mModule->GetMethodInstance(propTypeInst, methodDef, BfTypeVector(), mPropGetMethodFlags); + } + auto ifaceMethodEntry = checkTypeInst->mInterfaceMethodTable[bestIFaceEntry->mStartInterfaceTableIdx + methodDef->mIdx]; BfMethodInstance* bestMethodInstance = ifaceMethodEntry.mMethodRef; if (bestMethodInstance != NULL) @@ -19518,7 +20077,7 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp if (!handled) { SetAndRestoreValue prevFunctionBindResult(mFunctionBindResult, NULL); - SetAndRestoreValue prevDeferCallRef(mDeferCallRef, NULL); + SetAndRestoreValue prevDeferCallRef(mDeferCallData, NULL); BfMethodDef* matchedMethod = GetPropertyMethodDef(mPropDef, BfMethodType_PropertyGetter, mPropCheckedKind, mPropTarget); if (matchedMethod == NULL) @@ -19586,7 +20145,16 @@ BfTypedValue BfExprEvaluator::GetResult(bool clearResult, bool resolveGenericTyp mModule->EmitObjectAccessCheck(mPropTarget); } + SetAndRestoreValue prevExprFlags(mBfEvalExprFlags); auto callFlags = mPropDefBypassVirtual ? BfCreateCallFlags_BypassVirtual : BfCreateCallFlags_None; + + auto methodDef = methodInstance.mMethodInstance->mMethodDef; + if ((methodDef->mMethodDeclaration == NULL) && (mModule->mBfIRBuilder->IsConstValue(mPropTarget.mValue)) && + (methodDef->mName == "get__Underlying")) + { + mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_Comptime); + } + mResult = CreateCall(mPropSrc, mPropTarget, mOrigPropTarget, matchedMethod, methodInstance, callFlags, mIndexerValues, NULL); } } @@ -19630,12 +20198,12 @@ void BfExprEvaluator::CheckResultForReading(BfTypedValue& typedValue) int fieldIdx = mResultLocalVarField - 1; auto localVar = mResultLocalVar; - if (localVar->mCompositeCount > 0) + /*if (localVar->mCompositeCount > 0) { mModule->Fail(StrFormat("Cannot read from composite '%s', it can only be used in an argument list", localVar->mName.c_str()), mResultLocalVarRefNode); typedValue = BfTypedValue(); return; - } + }*/ if (localVar->mAssignedKind == BfLocalVarAssignKind_None) { @@ -19777,6 +20345,12 @@ bool BfExprEvaluator::CheckIsBase(BfAstNode* checkNode) bool BfExprEvaluator::CheckModifyResult(BfTypedValue& typedVal, BfAstNode* refNode, const char* modifyType, bool onlyNeedsMut, bool emitWarning, bool skipCopyOnMutate) { + if (typedVal.mType->IsVar()) + { + // Allow without error + return true; + } + BfLocalVariable* localVar = NULL; bool isCapturedLocal = false; if (mResultLocalVar != NULL) @@ -19803,7 +20377,7 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue& typedVal, BfAstNode* refNo { auto methodState = mModule->mCurMethodState->GetNonCaptureState(); localVar = methodState->mLocals[typedVal.mValue.mId]; - } + } if ((typedVal.mKind == BfTypedValueKind_MutableValue) && (onlyNeedsMut)) { @@ -19894,6 +20468,12 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue& typedVal, BfAstNode* refNo mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str(), mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); } + else if (mResultFieldInstance->GetFieldDef()->mIsAppend) + { + error = _Fail(StrFormat("Cannot %s append field '%s.%s' within method '%s'", modifyType, + mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str(), + mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); + } else if (auto propertyDeclaration = BfNodeDynCast(mResultFieldInstance->GetFieldDef()->mFieldDeclaration)) { String propNam; @@ -19980,9 +20560,12 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue& typedVal, BfAstNode* refNo if ((mResultFieldInstance != NULL) && (mResultFieldInstance->GetFieldDef()->mIsReadOnly) && (!canModify)) { - auto error = _Fail(StrFormat("Cannot %s static readonly field '%s.%s' within method '%s'", modifyType, - mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str(), - mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); + if (mModule->mCurMethodInstance != NULL) + { + auto error = _Fail(StrFormat("Cannot %s static readonly field '%s.%s' within method '%s'", modifyType, + mModule->TypeToString(mResultFieldInstance->mOwner).c_str(), mResultFieldInstance->GetFieldDef()->mName.c_str(), + mModule->MethodToString(mModule->mCurMethodInstance).c_str()), refNode); + } return false; } @@ -19990,6 +20573,12 @@ bool BfExprEvaluator::CheckModifyResult(BfTypedValue& typedVal, BfAstNode* refNo if ((!skipCopyOnMutate) && (typedVal.IsCopyOnMutate())) typedVal = mModule->CopyValue(typedVal); + if ((emitWarning) && (typedVal.IsReadOnly())) + { + mModule->Warn(0, StrFormat("Cannot %s read-only variable", modifyType), refNode); + return true; + } + return mModule->CheckModifyValue(typedVal, refNode, modifyType); } @@ -20858,7 +21447,11 @@ void BfExprEvaluator::PerformAssignment(BfAssignmentExpression* assignExpr, bool } else { - if (ptr.mType->IsValuelessType()) + if (ptr.mType->IsOpaque()) + { + mModule->Fail(StrFormat("Unable to assign to opaque type '%s'", mModule->TypeToString(ptr.mType).c_str()), assignExpr); + } + else if (ptr.mType->IsValuelessType()) { mModule->EmitEnsureInstructionAt(); } @@ -21485,6 +22078,10 @@ void BfExprEvaluator::Visit(BfTupleExpression* tupleExpr) BfFieldInstance* fieldInstance = &tupleType->mFieldInstances[fieldIdx]; if (fieldInstance->mDataIdx < 0) continue; + + while (fieldInstance->mDataIdx >= irValues.size()) + irValues.Add(BfIRValue()); + irValues[fieldInstance->mDataIdx] = typedValues[fieldIdx].mValue; } @@ -21600,7 +22197,7 @@ BfTypedValue BfExprEvaluator::SetupNullConditional(BfTypedValue thisValue, BfTok { // Success } - else if ((thisValue.mType->IsPointer()) || (thisValue.mType->IsObjectOrInterface())) + else if ((thisValue.mType->IsPointer()) || (thisValue.mType->IsObjectOrInterface()) || (thisValue.mType->IsFunction())) { // Also good } @@ -21660,6 +22257,10 @@ BfTypedValue BfExprEvaluator::SetupNullConditional(BfTypedValue thisValue, BfTok thisValue = BfTypedValue(valuePtr, elementType, true); } } + else if (thisValue.mType->IsFunction()) + { + isNotNull = mModule->mBfIRBuilder->CreateCmpNE(thisValue.mValue, mModule->GetDefaultValue(thisValue.mType)); + } else isNotNull = mModule->mBfIRBuilder->CreateIsNotNull(thisValue.mValue); BfIRBlock notNullBB = mModule->mBfIRBuilder->CreateBlock("nullCond.notNull"); @@ -21873,7 +22474,62 @@ void BfExprEvaluator::DoMemberReference(BfMemberReferenceExpression* memberRefEx if ((isArrowLookup) && (thisValue)) thisValue = TryArrowLookup(thisValue, memberRefExpr->mDotToken); - mResult = LookupField(nameRefNode, thisValue, findName); + auto nameNode = memberRefExpr->mMemberName; + + if ((thisValue.mType != NULL) && (!thisValue.mType->IsTypeInstance()) && (!thisValue.mType->IsGenericParam())) + { + if (thisValue.mType->IsSizedArray()) + { + if (thisValue.mType->IsValuelessType()) + { + thisValue.mType = mModule->GetWrappedStructType(thisValue.mType); + thisValue.mValue = mModule->mBfIRBuilder->GetFakeVal(); + } + else + { + thisValue = mModule->MakeAddressable(thisValue); + thisValue.mType = mModule->GetWrappedStructType(thisValue.mType); + thisValue.mValue = mModule->mBfIRBuilder->CreateBitCast(thisValue.mValue, mModule->mBfIRBuilder->MapTypeInstPtr(thisValue.mType->ToTypeInstance())); + } + } + else if (thisValue.mType->IsPointer()) + { + // Leave alone + } + else if (thisValue.mType->IsWrappableType()) + { + thisValue.mType = mModule->GetWrappedStructType(thisValue.mType); + } + } + + BfTypedValue lookupVal = thisValue; + if (thisValue.mType != NULL) + { + auto lookupType = BindGenericType(nameNode, thisValue.mType); + if ((lookupType->IsGenericParam()) && (!thisValue.mType->IsGenericParam())) + { + bool prevUseMixinGenerics = false; + if (mModule->mCurMethodState->mMixinState != NULL) + { + prevUseMixinGenerics = mModule->mCurMethodState->mMixinState->mUseMixinGenerics; + mModule->mCurMethodState->mMixinState->mUseMixinGenerics = true; + } + + // Try to lookup from generic binding + mResult = LookupField(nameRight, BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), lookupType), findName, BfLookupFieldFlag_BindOnly); + + if (mModule->mCurMethodState->mMixinState != NULL) + mModule->mCurMethodState->mMixinState->mUseMixinGenerics = prevUseMixinGenerics; + + if (mPropDef != NULL) + { + mOrigPropTarget = lookupVal; + return; + } + } + } + + mResult = LookupField(nameRefNode, lookupVal, findName); if ((!mResult) && (mPropDef == NULL)) { @@ -22304,6 +22960,8 @@ void BfExprEvaluator::HandleIndexerExpression(BfIndexerExpression* indexerExpr, if ((!target.IsAddr()) && (!target.mType->IsSizeAligned())) mModule->MakeAddressable(target); + mResult = BfTypedValue(); + mModule->PopulateType(underlyingType); if ((sizedArrayType->IsUndefSizedArray()) || (isUndefIndex)) { @@ -22320,6 +22978,40 @@ void BfExprEvaluator::HandleIndexerExpression(BfIndexerExpression* indexerExpr, } else if (target.IsAddr()) { + // Handle below + } + else + { + if ((!target.mValue.IsConst()) && (!indexArgument.mValue.IsConst())) + { + target = mModule->MakeAddressable(target); + } + else + { + mModule->mBfIRBuilder->PopulateType(target.mType); + auto gepResult = mModule->mBfIRBuilder->CreateExtractValue(target.mValue, indexArgument.mValue); + + if ((underlyingType->IsString()) || (underlyingType->IsPointer())) + { + auto resultConst = mModule->mBfIRBuilder->GetConstant(gepResult); + if ((resultConst != NULL) && (resultConst->mTypeCode == BfTypeCode_Int32)) + { + int strId = resultConst->mInt32; + const StringImpl& str = mModule->mContext->mStringObjectIdMap[strId].mString; + + if (underlyingType->IsString()) + gepResult = mModule->GetStringObjectValue(str, false); + else + gepResult = mModule->GetStringCharPtr(strId); + } + } + + mResult = BfTypedValue(gepResult, underlyingType, BfTypedValueKind_Value); + } + } + + if ((!mResult) && (target.IsAddr())) + { if (target.mType->IsSizeAligned()) { auto gepResult = mModule->mBfIRBuilder->CreateInBoundsGEP(target.mValue, mModule->GetConstValue(0), indexArgument.mValue); @@ -22331,33 +23023,11 @@ void BfExprEvaluator::HandleIndexerExpression(BfIndexerExpression* indexerExpr, mResult = BfTypedValue(indexResult, underlyingType, target.IsReadOnly() ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr); } } - else + + if (!mResult) { - if ((!target.mValue.IsConst()) && (!indexArgument.mValue.IsConst())) - { - mModule->Fail("Unable to index value", indexerExpr->mTarget); - return; - } - - mModule->mBfIRBuilder->PopulateType(target.mType); - auto gepResult = mModule->mBfIRBuilder->CreateExtractValue(target.mValue, indexArgument.mValue); - - if ((underlyingType->IsString()) || (underlyingType->IsPointer())) - { - auto resultConst = mModule->mBfIRBuilder->GetConstant(gepResult); - if ((resultConst != NULL) && (resultConst->mTypeCode == BfTypeCode_Int32)) - { - int strId = resultConst->mInt32; - const StringImpl& str = mModule->mContext->mStringObjectIdMap[strId].mString; - - if (underlyingType->IsString()) - gepResult = mModule->GetStringObjectValue(str, false); - else - gepResult = mModule->GetStringCharPtr(strId); - } - } - - mResult = BfTypedValue(gepResult, underlyingType, BfTypedValueKind_Value); + mModule->Fail("Unable to index value", indexerExpr->mTarget); + return; } } else @@ -22370,7 +23040,11 @@ void BfExprEvaluator::HandleIndexerExpression(BfIndexerExpression* indexerExpr, auto underlyingType = pointerType->mElementType; mModule->mBfIRBuilder->PopulateType(underlyingType); - if (isUndefIndex) + if (underlyingType->IsOpaque()) + { + mModule->Fail(StrFormat("Unable to index opaque pointer type '%s'", mModule->TypeToString(pointerType).c_str()), indexerExpr); + } + else if (isUndefIndex) { mResult = mModule->GetDefaultTypedValue(underlyingType, false, BfDefaultValueKind_Addr); } @@ -22398,9 +23072,7 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp /// { - // If this is a cast, we don't want the value to be coerced before the unary operator is applied. - // WAIT: Why not? - //SetAndRestoreValue prevExpectingType(mExpectingType, NULL); + SetAndRestoreValue prevFlags(mBfEvalExprFlags); BfType* prevExpedcting = mExpectingType; switch (unaryOp) @@ -22413,7 +23085,8 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp { if (mExpectingType->IsRef()) mExpectingType = mExpectingType->GetUnderlyingType(); - mExpectingType = mModule->CreatePointerType(mExpectingType); + if (!mExpectingType->IsVar()) + mExpectingType = mModule->CreatePointerType(mExpectingType); } break; case BfUnaryOp_Negate: @@ -22423,10 +23096,13 @@ void BfExprEvaluator::PerformUnaryOperation(BfExpression* unaryOpExpr, BfUnaryOp if ((mExpectingType != NULL) && (mExpectingType->IsInteger()) && (mExpectingType->mSize == 8)) mExpectingType = NULL; // Otherwise keep expecting type - break; + break; + case BfUnaryOp_Params: + mBfEvalExprFlags = (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_InParamsExpr); + break; default: mExpectingType = NULL; - } + } VisitChild(unaryOpExpr); mExpectingType = prevExpedcting; } @@ -22586,7 +23262,7 @@ BfTypedValue BfExprEvaluator::PerformUnaryOperation_TryOperator(const BfTypedVal else { SetAndRestoreValue prevFlags(mBfEvalExprFlags, (BfEvalExprFlags)(mBfEvalExprFlags | BfEvalExprFlags_NoAutoComplete)); - SetAndRestoreValue prevDeferCallRef(mDeferCallRef, NULL); + SetAndRestoreValue prevDeferCallRef(mDeferCallData, NULL); result = CreateCall(&methodMatcher, callTarget); } @@ -22806,7 +23482,7 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, mModule->FixIntUnknown(mResult); mModule->PopulateType(mResult.mType); auto ptrType = mModule->CreatePointerType(mResult.mType); - if (mResult.mType->IsValuelessType()) + if ((mResult.mType->IsValuelessType()) && (!mResult.mType->IsOpaque())) { if (!mModule->IsInSpecializedSection()) { @@ -22831,6 +23507,15 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, case BfUnaryOp_Dereference: { CheckResultForReading(mResult); + if (mResult.mType->IsGenericParam()) + { + auto genericParamInstance = mModule->GetGenericParamInstance((BfGenericParamType*)mResult.mType); + if ((genericParamInstance->mTypeConstraint != NULL) && (genericParamInstance->mTypeConstraint->IsPointer())) + { + mResult = mModule->GetDefaultTypedValue(genericParamInstance->mTypeConstraint->GetUnderlyingType(), false, BfDefaultValueKind_Addr); + break; + } + } if (!mResult.mType->IsPointer()) { mResult = BfTypedValue(); @@ -22864,7 +23549,7 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, BfPointerType* pointerType = (BfPointerType*)derefTarget.mType; auto resolvedType = pointerType->mElementType; mModule->PopulateType(resolvedType); - if (resolvedType->IsValuelessType()) + if (resolvedType->IsValuelessNonOpaqueType()) mResult = BfTypedValue(mModule->mBfIRBuilder->GetFakeVal(), resolvedType, true); else mResult = BfTypedValue(derefTarget.mValue, resolvedType, true); @@ -23118,6 +23803,8 @@ void BfExprEvaluator::PerformUnaryOperation_OnResult(BfExpression* unaryOpExpr, if (allowParams) { mResult = mModule->LoadValue(mResult); + if ((mResult.mType != NULL) && (mResult.mType->IsParamsType())) + mResult.mType = mResult.mType->GetUnderlyingType(); if (mResult.IsSplat()) mResult.mKind = BfTypedValueKind_ParamsSplat; else @@ -23654,12 +24341,17 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp rightTypedValueExpr.mRefNode = opToken; auto valueTypeEmpty = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mBaseType->mBaseType), {}); + + SizedArray tupleEmptyMembers; + tupleEmptyMembers.Add(valueTypeEmpty); + auto tupleTypeEmpty = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(mModule->ResolveTypeDef(mModule->mCompiler->mTupleTypeDef)), tupleEmptyMembers); + SizedArray enumMembers; enumMembers.Add(valueTypeEmpty); auto enumValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mBaseType), enumMembers); SizedArray tupleMembers; - tupleMembers.Add(valueTypeEmpty); + tupleMembers.Add(tupleTypeEmpty); tupleMembers.Add(mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 1)); auto tupleValue = mModule->mBfIRBuilder->CreateConstAgg(mModule->mBfIRBuilder->MapType(indexType->mFieldInstances[0].mResolvedType), tupleMembers); @@ -23687,7 +24379,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfExpression* leftExpression, BfExp ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); mResult = BfTypedValue(alloca, allocType, true); - auto result = MatchConstructor(opToken, NULL, mResult, allocType, argValues, true, BfMethodGenericArguments(), false); + auto result = MatchConstructor(opToken, NULL, mResult, allocType, argValues, true, BfMethodGenericArguments(), BfAllowAppendKind_No); if ((result) && (!result.mType->IsVoid())) mResult = result; @@ -23873,6 +24565,7 @@ void BfExprEvaluator::AddStrings(const BfTypedValue& leftValue, const BfTypedVal void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNode* rightExpression, BfBinaryOp binaryOp, BfAstNode* opToken, BfBinOpFlags flags, BfTypedValue leftValue, BfTypedValue rightValue) { bool noClassify = (flags & BfBinOpFlag_NoClassify) != 0; + bool forceRightType = (flags & BfBinOpFlag_ForceRightType) != 0; bool forceLeftType = (flags & BfBinOpFlag_ForceLeftType) != 0; bool deferRight = (flags & BfBinOpFlag_DeferRight) != 0; @@ -23936,7 +24629,11 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod } auto resultType = leftValue.mType; - if (!forceLeftType) + if (forceRightType) + { + resultType = rightValue.mType; + } + else if (!forceLeftType) { bool handled = false; @@ -24639,7 +25336,7 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod AddStrings(leftValue, rightValue, opToken); return; } - + //TODO: Allow all pointer comparisons, but only allow SUBTRACTION between equal pointer types if ((binaryOp == BfBinaryOp_Subtract) || (binaryOp == BfBinaryOp_OverflowSubtract)) { @@ -24760,6 +25457,9 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod if ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality) || (binaryOp == BfBinaryOp_InEquality) || (binaryOp == BfBinaryOp_StrictInEquality)) { + leftValue = mModule->LoadOrAggregateValue(leftValue); + rightValue = mModule->LoadOrAggregateValue(rightValue); + if (resultType->IsInterface()) { // Compare as objects instead @@ -24824,11 +25524,26 @@ void BfExprEvaluator::PerformBinaryOperation(BfAstNode* leftExpression, BfAstNod } if (needsOtherCast) - { - // The only purpose of this cast is to potentially throw a casting error - BfIRValue otherCastResult = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType, explicitCast ? BfCastFlags_Explicit : BfCastFlags_None); - if (!otherCastResult) - return; + { + BfCastFlags castFlags = (BfCastFlags)((explicitCast ? BfCastFlags_Explicit : BfCastFlags_None) | BfCastFlags_SilentFail); + BfIRValue otherCastResult = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType, castFlags); + if (!otherCastResult) + { + // We picked the wrong type, try the other one... + if (mModule->CanCast(*resultTypedValue, otherType)) + { + BfBinOpFlags newFlags = flags; + if (otherTypedValue == &leftValue) + newFlags = (BfBinOpFlags)(flags | BfBinOpFlag_ForceLeftType & ~BfBinOpFlag_ForceRightType & ~BfBinOpFlag_DeferRight); + else + newFlags = (BfBinOpFlags)(flags | BfBinOpFlag_ForceRightType & ~BfBinOpFlag_ForceLeftType & ~BfBinOpFlag_DeferRight); + return PerformBinaryOperation(leftExpression, rightExpression, binaryOp, opToken, newFlags, leftValue, rightValue); + } + + // Do again but with an error + castFlags = (BfCastFlags)(castFlags & ~BfCastFlags_SilentFail); + otherCastResult = mModule->CastToValue(otherTypeSrc, *otherTypedValue, resultType, castFlags); + } } } diff --git a/IDEHelper/Compiler/BfExprEvaluator.h b/IDEHelper/Compiler/BfExprEvaluator.h index 1899a489..8e54a346 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.h +++ b/IDEHelper/Compiler/BfExprEvaluator.h @@ -52,7 +52,9 @@ enum BfCreateCallFlags BfCreateCallFlags_SkipThis = 2, BfCreateCallFlags_AllowImplicitRef = 4, BfCreateCallFlags_TailCall = 8, - BfCreateCallFlags_GenericParamThis = 0x10 + BfCreateCallFlags_GenericParamThis = 0x10, + BfCreateCallFlags_DelegateThunkNonStatic = 0x20, + BfCreateCallFlags_DelegateThunkStatic = 0x40, }; class BfResolvedArg @@ -219,6 +221,7 @@ public: BfType* mCheckReturnType; BfMethodType mMethodType; BfCheckedKind mCheckedKind; + BfAllowAppendKind mAllowAppendKind; Array*>* mUsingLists; bool mHasArgNames; bool mHadExplicitGenericArguments; @@ -261,7 +264,7 @@ public: void CompareMethods(BfMethodInstance* prevMethodInstance, BfTypeVector* prevGenericArgumentsSubstitute, BfMethodInstance* newMethodInstance, BfTypeVector* genericArgumentsSubstitute, bool* outNewIsBetter, bool* outNewIsWorse, bool allowSpecializeFail); - void FlushAmbiguityError(); + void FlushAmbiguityError(bool useWarning = false); bool IsType(BfTypedValue& val, BfType* type); int GetMostSpecificType(BfType* lhs, BfType* rhs); // 0, 1, or -1 @@ -379,7 +382,8 @@ enum BfLookupFieldFlags BfLookupFieldFlag_IgnoreProtection = 8, BfLookupFieldFlag_BindOnly = 0x10, BfLookupFieldFlag_IsFailurePass = 0x20, - BfLookupFieldFlag_IsAnonymous = 0x40 + BfLookupFieldFlag_IsAnonymous = 0x40, + BfLookupFieldFlag_HasInstance = 0x80 }; enum BfUnaryOpFlags @@ -393,9 +397,18 @@ enum BfBinOpFlags BfBinOpFlag_None = 0, BfBinOpFlag_NoClassify = 1, BfBinOpFlag_ForceLeftType = 2, - BfBinOpFlag_IgnoreOperatorWithWrongResult = 4, - BfBinOpFlag_IsConstraintCheck = 8, - BfBinOpFlag_DeferRight = 0x10 + BfBinOpFlag_ForceRightType = 4, + BfBinOpFlag_IgnoreOperatorWithWrongResult = 8, + BfBinOpFlag_IsConstraintCheck = 0x10, + BfBinOpFlag_DeferRight = 0x20 +}; + +struct BfDeferCallData +{ + BfAstNode* mRefNode; + BfScopeData* mScopeAlloc; + BfIRValue mFuncAlloca; // When we need to load + BfIRValue mFuncAlloca_Orig; }; class BfExprEvaluator : public BfStructuralVisitor @@ -419,9 +432,8 @@ public: BfAttributeState* mPrefixedAttributeState; BfTypedValue* mReceivingValue; BfFunctionBindResult* mFunctionBindResult; - SizedArray mIndexerValues; - BfAstNode* mDeferCallRef; - BfScopeData* mDeferScopeAlloc; + SizedArray mIndexerValues; + BfDeferCallData* mDeferCallData; bool mUsedAsStatement; bool mPropDefBypassVirtual; bool mExplicitCast; @@ -497,7 +509,7 @@ public: void PushArg(BfTypedValue argVal, SizedArrayImpl& irArgs, bool disableSplat = false, bool disableLowering = false, bool isIntrinsic = false, bool createCompositeCopy = false); void PushThis(BfAstNode* targetSrc, BfTypedValue callTarget, BfMethodInstance* methodInstance, SizedArrayImpl& irArgs, bool skipMutCheck = false); BfTypedValue MatchConstructor(BfAstNode* targetSrc, BfMethodBoundExpression* methodBoundExpr, BfTypedValue target, BfTypeInstance* targetType, - BfResolvedArgs& argValues, bool callCtorBodyOnly, const BfMethodGenericArguments& methodGenericArguments, bool allowAppendAlloc, BfTypedValue* appendIndexValue = NULL); + BfResolvedArgs& argValues, bool callCtorBodyOnly, const BfMethodGenericArguments& methodGenericArguments, BfAllowAppendKind allowAppendKind, BfTypedValue* appendIndexValue = NULL); BfTypedValue CheckEnumCreation(BfAstNode* targetSrc, BfTypeInstance* enumType, const StringImpl& caseName, BfResolvedArgs& argValues); BfTypedValue MatchMethod(BfAstNode* targetSrc, BfMethodBoundExpression* methodBoundExpr, BfTypedValue target, bool allowImplicitThis, bool bypassVirtual, const StringImpl& name, BfResolvedArgs& argValue, const BfMethodGenericArguments& methodGenericArguments, BfCheckedKind checkedKind = BfCheckedKind_NotSet); @@ -506,7 +518,9 @@ public: BfModuleMethodInstance GetSelectedMethod(BfMethodMatcher& methodMatcher); bool CheckVariableDeclaration(BfAstNode* checkNode, bool requireSimpleIfExpr, bool exprMustBeTrue, bool silentFail); bool HasVariableDeclaration(BfAstNode* checkNode); + void DoInvocation(BfInvocationExpression* invocationExpr); void DoInvocation(BfAstNode* target, BfMethodBoundExpression* methodBoundExpr, const BfSizedArray& args, const BfMethodGenericArguments& methodGenericArgs, BfTypedValue* outCascadeValue = NULL); + void DoCaseExpression(BfTypedValue caseValAddr, BfCaseExpression* caseExpr); int GetMixinVariable(); void CheckLocalMethods(BfAstNode* targetSrc, BfTypeInstance* typeInstance, const StringImpl& methodName, BfMethodMatcher& methodMatcher, BfMethodType methodType); void InjectMixin(BfAstNode* targetSrc, BfTypedValue target, bool allowImplicitThis, const StringImpl& name, const BfSizedArray& arguments, const BfMethodGenericArguments& methodGenericArgs); @@ -538,7 +552,7 @@ public: void InitializedSizedArray(BfSizedArrayType* sizedArrayType, BfTokenNode* openToken, const BfSizedArray& values, const BfSizedArray& commas, BfTokenNode* closeToken, BfTypedValue* receivingValue = NULL); void CheckDotToken(BfTokenNode* tokenNode); void DoMemberReference(BfMemberReferenceExpression* memberRefExpr, BfTypedValue* outCascadeValue); - void CreateObject(BfObjectCreateExpression* objCreateExpr, BfAstNode* allocNode, BfType* allocType); + void CreateObject(BfObjectCreateExpression* objCreateExpr, BfAstNode* allocNode, BfType* allocType, BfInlineTypeReference* inlineTypeRef); void HandleIndexerExpression(BfIndexerExpression* indexerExpr, BfTypedValue target); ////////////////////////////////////////////////////////////////////////// diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index 547c6b84..d8ff8714 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -322,6 +322,10 @@ String BfIRConstHolder::ToString(BfIRValue irValue) { return StrFormat("Constant %lld", constant->mInt64); } + else if (constant->mTypeCode == BfTypeCode_CharPtr) + { + return StrFormat("CharPtr %d", constant->mInt64); + } else if (constant->mTypeCode == BfTypeCode_StringId) { return StrFormat("StringId %d", constant->mInt64); @@ -406,6 +410,11 @@ String BfIRConstHolder::ToString(BfIRValue irValue) auto typeofConst = (BfTypeOf_Const*)constant; return "typeof " + mModule->TypeToString(typeofConst->mType); } + else if (constant->mConstType == BfConstType_TypeOf_Comptime) + { + auto typeofConst = (BfTypeOf_Const*)constant; + return "typeof_comptime " + mModule->TypeToString(typeofConst->mType); + } else if (constant->mConstType == BfConstType_TypeOf_WithData) { auto typeofConst = (BfTypeOf_WithData_Const*)constant; @@ -655,6 +664,8 @@ bool BfIRConstHolder::IsConstValue(BfIRValue value) if (constant->mConstType == BfConstType_GlobalVar) return false; + if (constant->mConstType == BfConstType_Undef) + return false; return true; } @@ -687,8 +698,8 @@ int BfIRConstHolder::CheckConstEquality(BfIRValue lhs, BfIRValue rhs) } } - if (((constLHS->mConstType == BfConstType_TypeOf) || (constLHS->mConstType == BfConstType_TypeOf_WithData)) && - ((constRHS->mConstType == BfConstType_TypeOf) || (constRHS->mConstType == BfConstType_TypeOf_WithData))) + if (((constLHS->mConstType == BfConstType_TypeOf) || (constLHS->mConstType == BfConstType_TypeOf_WithData) || (constLHS->mConstType == BfConstType_TypeOf_Comptime)) && + ((constRHS->mConstType == BfConstType_TypeOf) || (constRHS->mConstType == BfConstType_TypeOf_WithData) || (constRHS->mConstType == BfConstType_TypeOf_Comptime))) { auto typeOfLHS = (BfTypeOf_Const*)constLHS; auto typeOfRHS = (BfTypeOf_Const*)constRHS; @@ -892,6 +903,11 @@ BfIRValue BfIRConstHolder::CreateConst(BfConstant* fromConst, BfIRConstHolder* f auto typeOf = (BfTypeOf_Const*)fromConst; return CreateTypeOf(typeOf->mType); } + else if (fromConst->mConstType == BfConstType_TypeOf_Comptime) + { + auto typeOf = (BfTypeOf_Const*)fromConst; + return CreateTypeOfComptime(typeOf->mType); + } else if (fromConst->mConstType == BfConstType_TypeOf_WithData) { auto typeOf = (BfTypeOf_WithData_Const*)fromConst; @@ -932,7 +948,7 @@ BfIRValue BfIRConstHolder::CreateConst(BfConstant* fromConst, BfIRConstHolder* f { return CreateConst(fromConst->mTypeCode, 0); } - else if ((IsInt(fromConst->mTypeCode)) || (fromConst->mTypeCode == BfTypeCode_Boolean) || (fromConst->mTypeCode == BfTypeCode_StringId)) + else if ((IsInt(fromConst->mTypeCode)) || (fromConst->mTypeCode == BfTypeCode_Boolean) || (fromConst->mTypeCode == BfTypeCode_StringId) || (fromConst->mTypeCode == BfTypeCode_CharPtr)) { return CreateConst(fromConst->mTypeCode, fromConst->mUInt64); } @@ -1039,6 +1055,7 @@ BfIRValue BfIRConstHolder::CreateConstAgg(BfIRType type, const BfSizedArray(); + typeOf->mConstType = BfConstType_TypeOf_Comptime; + typeOf->mType = type; + auto irValue = BfIRValue(BfIRValueFlags_Const, mTempAlloc.GetChunkedId(typeOf)); +#ifdef CHECK_CONSTHOLDER + irValue.mHolder = this; +#endif + return irValue; +} + BfIRValue BfIRConstHolder::CreateTypeOf(BfType* type) { BfTypeOf_Const* typeOf = mTempAlloc.Alloc(); @@ -2217,6 +2246,12 @@ void BfIRBuilder::WriteIR(const StringImpl& fileName) NEW_CMD_INSERTED; } +void BfIRBuilder::AbortStream() +{ + WriteCmd(BfIRCmd_Abort); + NEW_CMD_INSERTED; +} + void BfIRBuilder::Module_SetTargetTriple(const StringImpl& targetTriple, const StringImpl& targetCPU) { WriteCmd(BfIRCmd_Module_SetTargetTriple, targetTriple, targetCPU); @@ -2524,6 +2559,14 @@ void BfIRBuilder::Write(const BfIRValue& irValue) Write(MapType(typeofConst->mType, BfIRPopulateType_Identity)); } break; + case (int)BfConstType_TypeOf_Comptime: + { + auto typeType = mModule->ResolveTypeDef(mModule->mCompiler->mTypeTypeDef); + auto typeofConst = (BfTypeOf_Const*)constant; + Write(MapType(typeType, BfIRPopulateType_Identity)); + Write(typeofConst->mType->mTypeId); + } + break; case (int)BfConstType_Undef: { auto undefConst = (BfConstantUndef*)constant; @@ -2774,7 +2817,11 @@ void BfIRBuilder::CreateTypeDeclaration(BfType* type, bool forceDbgDefine) if (!type->IsDeclared()) populateModule->PopulateType(type, BfPopulateType_Declaration); - BF_ASSERT(type->IsDeclared()); + if (!type->IsDeclared()) + { + AbortStream(); + return; + } #ifdef BFIR_RENTRY_CHECK ReEntryCheck reEntryCheck(&mDeclReentrySet, type); @@ -2794,7 +2841,7 @@ void BfIRBuilder::CreateTypeDeclaration(BfType* type, bool forceDbgDefine) { BfPointerType* pointerType = (BfPointerType*)type; populateModule->PopulateType(pointerType->mElementType, BfPopulateType_Data); - if (pointerType->mElementType->IsValuelessType()) + if (pointerType->mElementType->IsValuelessNonOpaqueType()) { irType = GetPrimitiveType(BfTypeCode_NullPtr); } @@ -2813,7 +2860,7 @@ void BfIRBuilder::CreateTypeDeclaration(BfType* type, bool forceDbgDefine) { BfRefType* refType = (BfRefType*)type; - if (refType->mElementType->IsValuelessType()) + if (refType->mElementType->IsValuelessNonOpaqueType()) irType = GetPrimitiveType(BfTypeCode_NullPtr); else { @@ -3220,265 +3267,282 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type) diFieldTypes.push_back(memberType); } - bool isPayloadEnum = (typeInstance->IsEnum()) && (!typeInstance->IsTypedPrimitive()); - for (int fieldIdx = 0; fieldIdx < typeInstance->mFieldInstances.mSize; fieldIdx++) + std::function _AddFields = [&](BfType* type, int depth, int byteOffset) { - auto fieldInstance = &typeInstance->mFieldInstances[fieldIdx]; - if (!fieldInstance->mFieldIncluded) - continue; - auto fieldDef = fieldInstance->GetFieldDef(); + typeInstance = type->ToTypeInstance(); + if (typeInstance == NULL) + return; - if ((fieldInstance->mResolvedType == NULL) || (typeInstance->IsBoxed())) - continue; - - auto resolvedFieldType = fieldInstance->GetResolvedType(); - mModule->PopulateType(resolvedFieldType, BfPopulateType_Declaration); - BfIRType resolvedFieldIRType = MapType(resolvedFieldType); - BfIRMDNode resolvedFieldDIType; - - if ((fieldDef != NULL) && (!fieldDef->mIsStatic) && (resolvedFieldType->IsStruct())) - PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full); - resolvedFieldDIType = DbgGetType(resolvedFieldType); - - if (fieldInstance->IsAppendedObject()) - resolvedFieldDIType = DbgGetTypeInst(resolvedFieldType->ToTypeInstance()); - - if ((fieldDef == NULL) && (typeInstance->IsPayloadEnum())) + bool isPayloadEnum = (typeInstance->IsEnum()) && (!typeInstance->IsTypedPrimitive()); + for (int fieldIdx = 0; fieldIdx < typeInstance->mFieldInstances.mSize; fieldIdx++) { - orderedFields.push_back(fieldInstance); + auto fieldInstance = &typeInstance->mFieldInstances[fieldIdx]; + if (!fieldInstance->mFieldIncluded) + continue; + auto fieldDef = fieldInstance->GetFieldDef(); - int lineNum = 0; - int flags = llvm::DINode::FlagPublic; - auto fieldType = fieldInstance->mResolvedType; - String fieldName = "__bftag"; - auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, lineNum, - resolvedFieldType->mSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8, - flags, resolvedFieldDIType); - diFieldTypes.push_back(memberType); - } + if ((fieldInstance->mResolvedType == NULL) || (typeInstance->IsBoxed())) + continue; - if ((!typeInstance->IsBoxed()) && (fieldDef != NULL)) - { - if (fieldDef->mIsConst) + auto resolvedFieldType = fieldInstance->GetResolvedType(); + mModule->PopulateType(resolvedFieldType, BfPopulateType_Declaration); + BfIRType resolvedFieldIRType = MapType(resolvedFieldType); + BfIRMDNode resolvedFieldDIType; + + if ((fieldDef != NULL) && (!fieldDef->mIsStatic) && (resolvedFieldType->IsStruct())) + PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full); + resolvedFieldDIType = DbgGetType(resolvedFieldType); + + if ((fieldInstance->IsAppendedObject()) && (!fieldDef->mIsStatic)) + resolvedFieldDIType = DbgGetTypeInst(resolvedFieldType->ToTypeInstance()); + + if ((fieldDef == NULL) && (typeInstance->IsPayloadEnum())) { - if (isDefiningModule) + orderedFields.push_back(fieldInstance); + + int lineNum = 0; + int flags = llvm::DINode::FlagPublic; + auto fieldType = fieldInstance->mResolvedType; + String fieldName = "__bftag"; + auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, lineNum, + resolvedFieldType->mSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8, + flags, resolvedFieldDIType); + diFieldTypes.push_back(memberType); + } + + if ((!typeInstance->IsBoxed()) && (fieldDef != NULL)) + { + if (fieldDef->mIsConst) { - if ((isPayloadEnum) && (fieldDef->IsEnumCaseEntry())) + if ((isDefiningModule) && (depth == 0)) { - auto payloadType = fieldInstance->mResolvedType; - if (payloadType == NULL) - payloadType = mModule->CreateTupleType(BfTypeVector(), Array()); - - String fieldName = StrFormat("_%d_%s", -fieldInstance->mDataIdx - 1, fieldDef->mName.c_str()); - - int flags = 0; - auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, 0, - BF_ALIGN(payloadType->mSize, payloadType->mAlign) * 8, payloadType->mAlign * 8, 0, - flags, DbgGetType(payloadType)); - diFieldTypes.push_back(memberType); - } - else - { - BfConstant* constant = NULL; - BfIRValue staticValue; - - if (fieldInstance->mConstIdx != -1) + if ((isPayloadEnum) && (fieldDef->IsEnumCaseEntry())) { - constant = typeInstance->mConstHolder->GetConstantById(fieldInstance->mConstIdx); - if (!resolvedFieldType->IsValuelessType()) - staticValue = mModule->ConstantToCurrent(constant, typeInstance->mConstHolder, resolvedFieldType); - } + auto payloadType = fieldInstance->mResolvedType; + if (payloadType == NULL) + payloadType = mModule->CreateTupleType(BfTypeVector(), Array()); - if (fieldInstance->mResolvedType->IsComposite()) - PopulateType(fieldInstance->mResolvedType); - - BfIRMDNode constDIType = DbgCreateConstType(resolvedFieldDIType); - if ((fieldDef->mIsExtern) && (resolvedFieldType->IsPointer())) - { - auto underlyingType = resolvedFieldType->GetUnderlyingType(); - auto staticTypedValue = mModule->ReferenceStaticField(fieldInstance); - staticValue = staticTypedValue.mValue; + String fieldName = StrFormat("_%d_%s", -fieldInstance->mDataIdx - 1, fieldDef->mName.c_str()); int flags = 0; - auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldDef->mName, fileDIScope, 0, - constDIType, flags, CreateConst(BfTypeCode_Int32, 0)); - diFieldTypes.push_back(memberType); - - StringT<128> staticVarName; - BfMangler::Mangle(staticVarName, mModule->mCompiler->GetMangleKind(), fieldInstance); - if (!staticVarName.StartsWith("#")) - { - String fieldName = DbgGetStaticFieldName(fieldInstance); - DbgCreateGlobalVariable(diForwardDecl, fieldName, staticVarName, fileDIScope, 0, - constDIType, false, staticValue, memberType); - } - } - else if (resolvedFieldType->IsValuelessType()) - { - // Do nothing - } - else if ((resolvedFieldType->IsObjectOrInterface()) || (resolvedFieldType->IsPointer()) || (resolvedFieldType->IsSizedArray())) - { - bool useIntConstant = false; - - bool wasMadeAddr = false; - - StringT<128> staticVarName; - BfMangler::Mangle(staticVarName, mModule->mCompiler->GetMangleKind(), fieldInstance); - - String fieldName = fieldDef->mName; - BfIRValue intConstant; - if (constant != NULL) - { - // This constant debug info causes linking errors on Linux - if (isOptimized) - continue; - - if ((constant->mConstType == BfConstType_Agg) || - (constant->mConstType == BfConstType_AggZero) || - (constant->mTypeCode == BfTypeCode_NullPtr)) - { - staticValue = ConstToMemory(staticValue); - wasMadeAddr = true; - } - else if (constant->mTypeCode == BfTypeCode_StringId) - { - int stringId = constant->mInt32; - const StringImpl& str = mModule->mContext->mStringObjectIdMap[stringId].mString; - if (resolvedFieldType->IsPointer()) - staticValue = mModule->GetStringCharPtr(str); - else - staticValue = mModule->GetStringObjectValue(str); - } - else - { - // Ignore other types (for now) - continue; - } - } - - if (!useIntConstant) - { - auto useType = resolvedFieldType; - if (wasMadeAddr) - useType = mModule->CreatePointerType(useType); - staticValue = CreateGlobalVariable(mModule->mBfIRBuilder->MapType(useType), true, BfIRLinkageType_Internal, staticValue, staticVarName); - GlobalVar_SetAlignment(staticValue, useType->mAlign); - } - - int flags = 0; - auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldName, fileDIScope, 0, - constDIType, flags, useIntConstant ? intConstant : BfIRValue()); - diFieldTypes.push_back(memberType); - - if (fieldDef->mUsingProtection != BfProtection_Hidden) - { - auto memberType = DbgCreateStaticMemberType(diForwardDecl, "$using$" + fieldName, fileDIScope, 0, - constDIType, flags, useIntConstant ? intConstant : BfIRValue()); - diFieldTypes.push_back(memberType); - } - - if (staticValue) - { - String qualifiedName = DbgGetStaticFieldName(fieldInstance); - DbgCreateGlobalVariable(diForwardDecl, qualifiedName, staticVarName, fileDIScope, 0, - constDIType, false, staticValue, memberType); - } - } - else if (mModule->mCompiler->mOptions.IsCodeView()) - { - int flags = 0; - String fieldName = fieldDef->mName; - if ((constant != NULL) && - ((IsIntable(constant->mTypeCode)) || (IsFloat(constant->mTypeCode)))) - { - int64 writeVal = constant->mInt64; - if (constant->mTypeCode == BfTypeCode_Float) - { - // We need to do this because Singles are stored in mDouble, so we need to reduce here - float floatVal = (float)constant->mDouble; - writeVal = *(uint32*)&floatVal; - } - if (writeVal < 0) - fieldName += StrFormat("$_%llu", -writeVal); - else - fieldName += StrFormat("$%llu", writeVal); - } - auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldName, fileDIScope, 0, - constDIType, flags, staticValue); + auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, 0, + BF_ALIGN(payloadType->mSize, payloadType->mAlign) * 8, payloadType->mAlign * 8, 0, + flags, DbgGetType(payloadType)); diFieldTypes.push_back(memberType); } else { - int flags = 0; - String fieldName = DbgGetStaticFieldName(fieldInstance); - auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldName, fileDIScope, 0, - constDIType, flags, staticValue); + BfConstant* constant = NULL; + BfIRValue staticValue; + + if (fieldInstance->mConstIdx != -1) + { + constant = typeInstance->mConstHolder->GetConstantById(fieldInstance->mConstIdx); + if (!resolvedFieldType->IsValuelessType()) + staticValue = mModule->ConstantToCurrent(constant, typeInstance->mConstHolder, resolvedFieldType); + } + + if (fieldInstance->mResolvedType->IsComposite()) + PopulateType(fieldInstance->mResolvedType); + + BfIRMDNode constDIType = DbgCreateConstType(resolvedFieldDIType); + if ((fieldDef->mIsExtern) && (resolvedFieldType->IsPointer())) + { + auto underlyingType = resolvedFieldType->GetUnderlyingType(); + auto staticTypedValue = mModule->ReferenceStaticField(fieldInstance); + staticValue = staticTypedValue.mValue; + + int flags = 0; + auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldDef->mName, fileDIScope, 0, + constDIType, flags, CreateConst(BfTypeCode_Int32, 0)); + diFieldTypes.push_back(memberType); + + StringT<128> staticVarName; + BfMangler::Mangle(staticVarName, mModule->mCompiler->GetMangleKind(), fieldInstance); + if (!staticVarName.StartsWith("#")) + { + String fieldName = DbgGetStaticFieldName(fieldInstance); + DbgCreateGlobalVariable(diForwardDecl, fieldName, staticVarName, fileDIScope, 0, + constDIType, false, staticValue, memberType); + } + } + else if (resolvedFieldType->IsValuelessType()) + { + // Do nothing + } + else if ((resolvedFieldType->IsObjectOrInterface()) || (resolvedFieldType->IsPointer()) || (resolvedFieldType->IsSizedArray())) + { + bool useIntConstant = false; + + bool wasMadeAddr = false; + + StringT<128> staticVarName; + BfMangler::Mangle(staticVarName, mModule->mCompiler->GetMangleKind(), fieldInstance); + + String fieldName = fieldDef->mName; + BfIRValue intConstant; + if (constant != NULL) + { + // This constant debug info causes linking errors on Linux + if (isOptimized) + continue; + + if ((constant->mConstType == BfConstType_Agg) || + (constant->mConstType == BfConstType_AggZero) || + (constant->mTypeCode == BfTypeCode_NullPtr)) + { + staticValue = ConstToMemory(staticValue); + wasMadeAddr = true; + } + else if (constant->mTypeCode == BfTypeCode_StringId) + { + int stringId = constant->mInt32; + const StringImpl& str = mModule->mContext->mStringObjectIdMap[stringId].mString; + if (resolvedFieldType->IsPointer()) + staticValue = mModule->GetStringCharPtr(str); + else + staticValue = mModule->GetStringObjectValue(str); + } + else + { + // Ignore other types (for now) + continue; + } + } + + if (!useIntConstant) + { + auto useType = resolvedFieldType; + if (wasMadeAddr) + useType = mModule->CreatePointerType(useType); + staticValue = CreateGlobalVariable(mModule->mBfIRBuilder->MapType(useType), true, BfIRLinkageType_Internal, staticValue, staticVarName); + GlobalVar_SetAlignment(staticValue, useType->mAlign); + } + + BfIRMDNode memberType; + int flags = 0; + if (!fieldDef->mName.IsEmpty()) + { + memberType = DbgCreateStaticMemberType(diForwardDecl, fieldName, fileDIScope, 0, + constDIType, flags, useIntConstant ? intConstant : BfIRValue()); + diFieldTypes.push_back(memberType); + } + + if (fieldDef->mUsingProtection != BfProtection_Hidden) + { + auto memberType = DbgCreateStaticMemberType(diForwardDecl, "$using$" + fieldName, fileDIScope, 0, + constDIType, flags, useIntConstant ? intConstant : BfIRValue()); + diFieldTypes.push_back(memberType); + } + + if ((staticValue) && (!fieldDef->mName.IsEmpty())) + { + String qualifiedName = DbgGetStaticFieldName(fieldInstance); + DbgCreateGlobalVariable(diForwardDecl, qualifiedName, staticVarName, fileDIScope, 0, + constDIType, false, staticValue, memberType); + } + } + else if (mModule->mCompiler->mOptions.IsCodeView()) + { + int flags = 0; + String fieldName = fieldDef->mName; + if ((constant != NULL) && + ((IsIntable(constant->mTypeCode)) || (IsFloat(constant->mTypeCode)))) + { + int64 writeVal = constant->mInt64; + if (constant->mTypeCode == BfTypeCode_Float) + { + // We need to do this because Singles are stored in mDouble, so we need to reduce here + float floatVal = (float)constant->mDouble; + writeVal = *(uint32*)&floatVal; + } + if (writeVal < 0) + fieldName += StrFormat("$_%llu", -writeVal); + else + fieldName += StrFormat("$%llu", writeVal); + } + auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldName, fileDIScope, 0, + constDIType, flags, staticValue); + diFieldTypes.push_back(memberType); + } + else + { + int flags = 0; + String fieldName = DbgGetStaticFieldName(fieldInstance); + auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldName, fileDIScope, 0, + constDIType, flags, staticValue); + diFieldTypes.push_back(memberType); + } + } + } + } + else if (fieldDef->mIsStatic) + { + if ((isDefiningModule) && (depth == 0)) + { + BfIRMDNode memberType; + int flags = 0; + if (!fieldDef->mName.IsEmpty()) + { + memberType = DbgCreateStaticMemberType(diForwardDecl, fieldDef->mName, fileDIScope, 0, + resolvedFieldDIType, flags, BfIRValue()); + diFieldTypes.push_back(memberType); + } + + if (fieldDef->mUsingProtection != BfProtection_Hidden) + { + auto memberType = DbgCreateStaticMemberType(diForwardDecl, "$using$" + fieldDef->mName, fileDIScope, 0, + resolvedFieldDIType, flags, BfIRValue()); + diFieldTypes.push_back(memberType); + } + + StringT<128> staticVarName; + BfMangler::Mangle(staticVarName, mModule->mCompiler->GetMangleKind(), fieldInstance); + if ((!staticVarName.StartsWith('#')) && (!fieldDef->mName.IsEmpty())) + { + auto staticValue = mModule->ReferenceStaticField(fieldInstance); + if (staticValue.mValue) + { + String fieldName = DbgGetStaticFieldName(fieldInstance); + DbgCreateGlobalVariable(diForwardDecl, fieldName, staticVarName, fileDIScope, 0, + resolvedFieldDIType, false, staticValue.mValue, memberType); + } + } + } + } + else + { + bool useForUnion = false; + + BF_ASSERT(!fieldInstance->mIsEnumPayloadCase); + + if (wantDIData) + { + if ((fieldDef->mUsingProtection != BfProtection_Hidden) && (depth < 10)) + { + _AddFields(fieldInstance->mResolvedType, depth + 1, byteOffset + fieldInstance->mDataOffset); + } + + int lineNum = 0; + + int flags = 0; + String fieldName = fieldDef->mName; + if (!fieldDef->mName.IsEmpty()) + { + if (fieldDef->mHasMultiDefs) + fieldName += "$" + fieldDef->mDeclaringType->mProject->mName; + auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, lineNum, + fieldInstance->mDataSize * 8, resolvedFieldType->mAlign * 8, (byteOffset + fieldInstance->mDataOffset) * 8, + flags, resolvedFieldDIType); diFieldTypes.push_back(memberType); } } } } - else if (fieldDef->mIsStatic) - { - if (isDefiningModule) - { - int flags = 0; - auto memberType = DbgCreateStaticMemberType(diForwardDecl, fieldDef->mName, fileDIScope, 0, - resolvedFieldDIType, flags, BfIRValue()); - diFieldTypes.push_back(memberType); - - if (fieldDef->mUsingProtection != BfProtection_Hidden) - { - auto memberType = DbgCreateStaticMemberType(diForwardDecl, "$using$" + fieldDef->mName, fileDIScope, 0, - resolvedFieldDIType, flags, BfIRValue()); - diFieldTypes.push_back(memberType); - } - - StringT<128> staticVarName; - BfMangler::Mangle(staticVarName, mModule->mCompiler->GetMangleKind(), fieldInstance); - if (!staticVarName.StartsWith('#')) - { - auto staticValue = mModule->ReferenceStaticField(fieldInstance); - if (staticValue.mValue) - { - String fieldName = DbgGetStaticFieldName(fieldInstance); - DbgCreateGlobalVariable(diForwardDecl, fieldName, staticVarName, fileDIScope, 0, - resolvedFieldDIType, false, staticValue.mValue, memberType); - } - } - } - } - else - { - bool useForUnion = false; - - BF_ASSERT(!fieldInstance->mIsEnumPayloadCase); - - if (wantDIData) - { - int lineNum = 0; - - int flags = 0; - String fieldName = fieldDef->mName; - if (fieldDef->mHasMultiDefs) - fieldName += "$" + fieldDef->mDeclaringType->mProject->mName; - auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, lineNum, - fieldInstance->mDataSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8, - flags, resolvedFieldDIType); - diFieldTypes.push_back(memberType); - - if (fieldDef->mUsingProtection != BfProtection_Hidden) - { - auto memberType = DbgCreateMemberType(diForwardDecl, "$using$" + fieldName, fileDIScope, lineNum, - fieldInstance->mDataSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8, - flags, resolvedFieldDIType); - diFieldTypes.push_back(memberType); - } - } - } } - } + }; + + _AddFields(typeInstance, 0, 0); int dataPos = 0; if (typeInstance->mBaseType != NULL) @@ -3720,7 +3784,14 @@ void BfIRBuilder::CreateTypeDefinition_Data(BfModule* populateModule, BfTypeInst SizedArray irFieldTypes; if ((!typeInstance->IsTypedPrimitive()) && (typeInstance->mBaseType != NULL)) { - irFieldTypes.push_back(MapTypeInst(typeInstance->mBaseType, BfIRPopulateType_Eventually_Full)); + if (typeInstance->mBaseType->IsValuelessCReprType()) + { + // Fake an empty type to avoid having a one-byte base type + auto valueTypeType = mModule->ResolveTypeDef(mModule->mCompiler->mValueTypeTypeDef); + irFieldTypes.push_back(MapType(valueTypeType, BfIRPopulateType_Eventually_Full)); + } + else + irFieldTypes.push_back(MapTypeInst(typeInstance->mBaseType, BfIRPopulateType_Eventually_Full)); } SizedArray diFieldTypes; @@ -5487,10 +5558,13 @@ void BfIRBuilder::SetActiveFunction(BfIRFunction func) if (mActiveFunctionHasBody) mNumFunctionsWithBodies++; - WriteCmd(BfIRCmd_SetActiveFunction, func); mActiveFunction = func; mActiveFunctionHasBody = false; - NEW_CMD_INSERTED; + if (!func.IsFake()) + { + WriteCmd(BfIRCmd_SetActiveFunction, func); + NEW_CMD_INSERTED; + } } BfIRFunction BfIRBuilder::GetActiveFunction() diff --git a/IDEHelper/Compiler/BfIRBuilder.h b/IDEHelper/Compiler/BfIRBuilder.h index 03d69c3c..37f003c5 100644 --- a/IDEHelper/Compiler/BfIRBuilder.h +++ b/IDEHelper/Compiler/BfIRBuilder.h @@ -118,7 +118,8 @@ enum BfTypeCode : uint8 BfTypeCode_Int64X2, BfTypeCode_Int64X3, BfTypeCode_Int64X4, - BfTypeCode_Length + BfTypeCode_Length, + BfTypeCode_Inferred }; enum BfConstType @@ -133,6 +134,7 @@ enum BfConstType BfConstType_IntToPtr, BfConstType_TypeOf, BfConstType_TypeOf_WithData, + BfConstType_TypeOf_Comptime, BfConstType_AggZero, BfConstType_Agg, BfConstType_AggCE, @@ -160,6 +162,7 @@ enum BfIRCmd : uint8 BfIRCmd_Module_SetTargetTriple, BfIRCmd_Module_AddModuleFlag, BfIRCmd_WriteIR, + BfIRCmd_Abort, BfIRCmd_SetType, BfIRCmd_SetInstType, @@ -979,6 +982,7 @@ public: BfIRValue CreateConstBitCast(BfIRValue val, BfIRType type); BfIRValue CreateConstBox(BfIRValue val, BfIRType type); BfIRValue CreateTypeOf(BfType* type); + BfIRValue CreateTypeOfComptime(BfType* type); BfIRValue CreateTypeOf(BfType* type, BfIRValue typeData); BfIRValue GetUndefConstValue(BfIRType type); BfIRValue CreateGlobalVariableConstant(BfIRType varType, bool isConstant, BfIRLinkageType linkageType, BfIRValue initializer, const StringImpl& name, bool isTLS = false); @@ -1198,6 +1202,7 @@ public: void SetBackend(bool isBeefBackend); void RemoveIRCodeGen(); void WriteIR(const StringImpl& fileName); + void AbortStream(); void Module_SetTargetTriple(const StringImpl& targetTriple, const StringImpl& targetCPU); void Module_AddModuleFlag(const StringImpl& flag, int val); diff --git a/IDEHelper/Compiler/BfIRCodeGen.cpp b/IDEHelper/Compiler/BfIRCodeGen.cpp index 5e05a918..0a227cf4 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.cpp +++ b/IDEHelper/Compiler/BfIRCodeGen.cpp @@ -345,7 +345,7 @@ static void AddStdErrCrashInfo() /// BfIRCodeGen::BfIRCodeGen() -{ +{ mStream = NULL; mBfIRBuilder = NULL; mLLVMTargetMachine = NULL; @@ -385,7 +385,7 @@ BfIRCodeGen::~BfIRCodeGen() { mDebugLoc = llvm::DebugLoc(); mSavedDebugLocs.Clear(); - + for (auto typeEx : mIRTypeExs) delete typeEx; @@ -482,7 +482,7 @@ void BfIRCodeGen::PrintFunction() void pte(BfIRTypeEx* typeEx, int indent) { - Beefy::debug_ostream os; + Beefy::debug_ostream os; typeEx->mLLVMType->print(os); os << "\n"; os.flush(); @@ -505,8 +505,8 @@ void pte(BfIRTypeEx* typeEx) } void pve(const BfIRTypedValue& typedValue) -{ - Beefy::debug_ostream os; +{ + Beefy::debug_ostream os; os << "Value: "; typedValue.mValue->print(os); os << "\nType: "; @@ -522,7 +522,7 @@ void pirb(llvm::IRBuilder<>* irBuilder) if (debugLoc.get() == NULL) os << "NULL"; else - debugLoc->print(os); + debugLoc->print(os); os << "\n"; os.flush(); } @@ -726,32 +726,32 @@ BfIRTypeEx* BfIRCodeGen::GetTypeEx(BfTypeCode typeCode, bool& isSigned) { isSigned = false; switch (typeCode) - { - case BfTypeCode_Int8: - case BfTypeCode_Int16: - case BfTypeCode_Int24: - case BfTypeCode_Int32: - case BfTypeCode_Int40: - case BfTypeCode_Int48: - case BfTypeCode_Int56: - case BfTypeCode_Int64: + { + case BfTypeCode_Int8: + case BfTypeCode_Int16: + case BfTypeCode_Int24: + case BfTypeCode_Int32: + case BfTypeCode_Int40: + case BfTypeCode_Int48: + case BfTypeCode_Int56: + case BfTypeCode_Int64: case BfTypeCode_Int128: - isSigned = true; - } + isSigned = true; + } } return *valuePtr; } BfIRTypeEx* BfIRCodeGen::GetTypeEx(llvm::Type* llvmType) -{ +{ BfIRTypeEx** valuePtr = NULL; if (mLLVMTypeExMap.TryAdd(llvmType, NULL, &valuePtr)) { BfIRTypeEx* typeEx = new BfIRTypeEx(); mIRTypeExs.Add(typeEx); - typeEx->mLLVMType = llvmType; + typeEx->mLLVMType = llvmType; *valuePtr = typeEx; - } + } return *valuePtr; } @@ -764,14 +764,14 @@ BfIRTypeEx* BfIRCodeGen::CreateTypeEx(llvm::Type* llvmType) } BfIRTypeEx* BfIRCodeGen::GetPointerTypeEx(BfIRTypeEx* elementType) -{ +{ BF_ASSERT(elementType != NULL); BfIRTypeEx** valuePtr = NULL; if (mPointerTypeExMap.TryAdd(elementType, NULL, &valuePtr)) { BfIRTypeEx* typeEx = new BfIRTypeEx(); mIRTypeExs.Add(typeEx); - typeEx->mLLVMType = llvm::PointerType::get(*mLLVMContext, 0); + typeEx->mLLVMType = llvm::PointerType::get(*mLLVMContext, 0); typeEx->mMembers.Add(elementType); *valuePtr = typeEx; } @@ -807,7 +807,7 @@ BfIRTypeEntry* BfIRCodeGen::GetTypeEntry(BfIRTypeEx* type) } void BfIRCodeGen::SetResult(int id, llvm::Value* value) -{ +{ BF_ASSERT(!value->getType()->isAggregateType()); BF_ASSERT(!value->getType()->isPointerTy()); @@ -969,17 +969,17 @@ void BfIRCodeGen::Read(BfIRTypeEx*& typeEx, BfIRTypeEntry** outTypeEntry) BfIRType::TypeKind typeKind = (BfIRType::TypeKind)mStream->Read(); if (typeKind == BfIRType::TypeKind::TypeKind_None) - return; + return; if (typeKind == BfIRType::TypeKind::TypeKind_Stream) { int streamId = (int)ReadSLEB128(); if (streamId == -1) - { + { typeEx = NULL; return; } - auto& result = mResults[streamId]; + auto& result = mResults[streamId]; BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_TypeEx); typeEx = result.mTypeEx; return; @@ -1000,7 +1000,7 @@ void BfIRCodeGen::Read(BfIRTypeEx*& typeEx, BfIRTypeEntry** outTypeEntry) int typeId = (int)ReadSLEB128(); if (typeKind == BfIRType::TypeKind::TypeKind_TypeCode) - { + { bool isSigned = false; typeEx = GetTypeEx((BfTypeCode)typeId, isSigned); return; @@ -1035,7 +1035,7 @@ void BfIRCodeGen::Read(llvm::FunctionType*& llvmType) auto& result = mResults[streamId]; if (result.mKind == BfIRCodeGenEntryKind_TypeEx) - { + { llvmType = (llvm::FunctionType*)result.mTypeEx->mLLVMType; return; } @@ -1178,7 +1178,7 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt CMD_PARAM(int, idx0); llvm::Value* gepArgs[] = { llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), idx0)}; - + auto compositeType = GetTypeMember(target.mTypeEx, 0); auto constant = llvm::dyn_cast(target.mValue); @@ -1202,21 +1202,21 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt auto constant = llvm::dyn_cast(target.mValue); typedValue.mValue = llvm::ConstantExpr::getInBoundsGetElementPtr(compositeType->mLLVMType, constant, gepArgs); - typedValue.mTypeEx = GetPointerTypeEx(elemType); + typedValue.mTypeEx = GetPointerTypeEx(elemType); return; } else if (constType == BfConstType_ExtractValue) { CMD_PARAM(BfIRTypedValue, target); - CMD_PARAM(int, idx0); - + CMD_PARAM(int, idx0); + auto compositeType = target.mTypeEx; int elemIdx = BF_MIN(idx0, (int)compositeType->mMembers.mSize - 1); auto elemType = GetTypeMember(compositeType, elemIdx); typedValue.mTypeEx = elemType; - if (auto constant = llvm::dyn_cast(target.mValue)) - typedValue.mValue = constant->getAggregateElement(llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), idx0)); + if (auto constant = llvm::dyn_cast(target.mValue)) + typedValue.mValue = constant->getAggregateElement(llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), idx0)); FixTypedValue(typedValue); return; } @@ -1310,7 +1310,7 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt typedValue.mValue = llvm::ConstantStruct::get(alignedType, values); } else - { + { typedValue.mValue = llvm::ConstantStruct::get(structType, values); } } @@ -1331,14 +1331,14 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt { CMD_PARAM(BfIRTypeEx*, type); typedValue.mTypeEx = type; - typedValue.mValue = llvm::UndefValue::get(type->mLLVMType); + typedValue.mValue = llvm::UndefValue::get(type->mLLVMType); return; } else if (constType == BfConstType_TypeOf) { CMD_PARAM(BfIRTypeEx*, type); typedValue = mReflectDataMap[type]; - BF_ASSERT(typedValue.mValue != NULL); + BF_ASSERT(typedValue.mValue != NULL); return; } else if (constType == BfConstType_TypeOf_WithData) @@ -1346,7 +1346,7 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt CMD_PARAM(BfIRTypeEx*, type); CMD_PARAM(BfIRTypedValue, value); mReflectDataMap[type] = value; - typedValue = value; + typedValue = value; return; } @@ -1425,26 +1425,26 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt else { cmdId = mCmdCount - (paramType - BfIRParamType_StreamId_Back1) - 1; - } + } auto& result = mResults[cmdId]; - if ((codeGenEntry != NULL) && (result.mKind != BfIRCodeGenEntryKind_None)) - *codeGenEntry = &result; + if ((codeGenEntry != NULL) && (result.mKind != BfIRCodeGenEntryKind_None)) + *codeGenEntry = &result; if (result.mKind == BfIRCodeGenEntryKind_TypedValue_Aligned) { typedValue = result.mTypedValue; BfIRTypeEx* normalType = NULL; - if (mAlignedTypeToNormalType.TryGetValue(typedValue.mTypeEx, &normalType)) - typedValue.mTypeEx = normalType; + if (mAlignedTypeToNormalType.TryGetValue(typedValue.mTypeEx, &normalType)) + typedValue.mTypeEx = normalType; return; } if (result.mKind == BfIRCodeGenEntryKind_TypedValue) - { + { typedValue = result.mTypedValue; return; } @@ -1467,7 +1467,7 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt llvm::Type* normalType = NULL; //TODO: if (auto ptrType = llvm::dyn_cast(llvmValue->getType())) { - + // if (mAlignedTypeToNormalType.TryGetValue(ptrType->getElementType(), &normalType)) // { // llvmValue = mIRBuilder->CreateBitCast(llvmValue, normalType->getPointerTo()); @@ -1477,7 +1477,7 @@ void BfIRCodeGen::Read(BfIRTypedValue& typedValue, BfIRCodeGenEntry** codeGenEnt } BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue); - typedValue.mValue = result.mLLVMValue; + typedValue.mValue = result.mLLVMValue; } FixTypedValue(typedValue); } @@ -1512,7 +1512,7 @@ void BfIRCodeGen::Read(llvm::Function*& llvmFunc) llvmFunc = NULL; return; } - auto& result = mResults[streamId]; + auto& result = mResults[streamId]; if (result.mKind == BfIRCodeGenEntryKind_TypedValue) { llvmFunc = (llvm::Function*)result.mTypedValue.mValue; @@ -1543,7 +1543,7 @@ void BfIRCodeGen::ReadFunction(BfIRTypedValue& typedValue) BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue); BF_ASSERT(llvm::isa(result.mLLVMValue)); typedValue.mValue = result.mLLVMValue; - FixTypedValue(typedValue); + FixTypedValue(typedValue); } void BfIRCodeGen::Read(llvm::BasicBlock*& llvmBlock) @@ -1594,7 +1594,7 @@ void BfIRCodeGen::AddNop() } llvm::CallInst* callInst = mIRBuilder->CreateCall(mNopInlineAsm); - + callInst->addFnAttr(llvm::Attribute::NoUnwind); } @@ -1603,7 +1603,7 @@ llvm::Value* BfIRCodeGen::TryToVector(const BfIRTypedValue& value) auto valueType = value.mTypeEx->mLLVMType; if (llvm::isa(valueType)) return value.mValue; - + if (auto ptrType = llvm::dyn_cast(valueType)) { auto ptrElemType = GetTypeMember(value.mTypeEx, 0); @@ -1611,11 +1611,11 @@ llvm::Value* BfIRCodeGen::TryToVector(const BfIRTypedValue& value) { auto vecType = llvm::FixedVectorType::get(arrType->getArrayElementType(), (uint)arrType->getArrayNumElements()); auto vecPtrType = vecType->getPointerTo(); - + auto ptrVal0 = mIRBuilder->CreateBitCast(value.mValue, vecPtrType); return mIRBuilder->CreateAlignedLoad(vecType, ptrVal0, llvm::MaybeAlign(1)); } - + if (auto vecType = llvm::dyn_cast(ptrElemType->mLLVMType)) { return mIRBuilder->CreateAlignedLoad(vecType, value.mValue, llvm::MaybeAlign(1)); @@ -1687,7 +1687,7 @@ bool BfIRCodeGen::TryMemCpy(const BfIRTypedValue& ptr, llvm::Value* val) } bool BfIRCodeGen::TryVectorCpy(const BfIRTypedValue& ptr, llvm::Value* val) -{ +{ if (GetTypeMember(ptr.mTypeEx, 0)->mLLVMType == val->getType()) return false; @@ -1733,7 +1733,7 @@ BfIRTypeEx* BfIRCodeGen::GetSizeAlignedType(BfIRTypeEntry* typeEntry) int fillSize = alignSize - typeEntry->mSize; members.push_back(llvm::ArrayType::get(llvm::Type::getInt8Ty(*mLLVMContext), fillSize)); alignType->setBody(members, structType->isPacked()); - + alignTypeEx->mLLVMType = alignType; typeEntry->mAlignType = alignTypeEx; mAlignedTypeToNormalType[alignTypeEx] = typeEntry->mType; @@ -1747,7 +1747,7 @@ BfIRTypeEx* BfIRCodeGen::GetSizeAlignedType(BfIRTypeEntry* typeEntry) } BfIRTypedValue BfIRCodeGen::GetAlignedPtr(const BfIRTypedValue& val) -{ +{ BfIRTypedValue result = val; if (auto ptrType = llvm::dyn_cast(val.mTypeEx->mLLVMType)) @@ -1800,7 +1800,7 @@ llvm::Value* BfIRCodeGen::DoCheckedIntrinsic(llvm::Intrinsic::ID intrin, llvm::V auto callInst = mIRBuilder->CreateCall(trapDecl); callInst->addFnAttr(llvm::Attribute::NoReturn); mIRBuilder->CreateBr(passBB); - + mActiveFunction->insert(mActiveFunction->end(), passBB); mIRBuilder->SetInsertPoint(passBB); } @@ -2029,6 +2029,9 @@ void BfIRCodeGen::InitTarget() void BfIRCodeGen::HandleNextCmd() { + if (mFailed) + return; + int curId = mCmdCount; BfIRCmd cmd = (BfIRCmd)mStream->Read(); @@ -2047,6 +2050,7 @@ void BfIRCodeGen::HandleNextCmd() mPtrSize = ptrSize; mIsOptimized = isOptimized; mLLVMModule = new llvm::Module(moduleName.c_str(), *mLLVMContext); + mLLVMModule->setIsNewDbgInfoFormat(false); mIRBuilder = new llvm::IRBuilder<>(*mLLVMContext); //OutputDebugStrF("-------- Starting Module %s --------\n", moduleName.c_str()); @@ -2058,7 +2062,7 @@ void BfIRCodeGen::HandleNextCmd() CMD_PARAM(String, targetCPU); mTargetTriple.Set(targetTriple); - mTargetCPU = targetCPU; + mTargetCPU = targetCPU; if (targetTriple.IsEmpty()) mLLVMModule->setTargetTriple(llvm::sys::getDefaultTargetTriple()); @@ -2091,10 +2095,13 @@ void BfIRCodeGen::HandleNextCmd() mLLVMModule->print(outStream, NULL); } break; + case BfIRCmd_Abort: + Fail("Stream aborted"); + break; case BfIRCmd_SetType: { CMD_PARAM(int, typeId); - + CMD_PARAM(BfIRTypeEx*, type); //llvm::Type* type; //llvm::Type* elementType; @@ -2123,9 +2130,9 @@ void BfIRCodeGen::HandleNextCmd() case BfIRCmd_CreateAnonymousStruct: { CMD_PARAM(CmdParamVec, members); - + CmdParamVec llvmMembers; - for (auto& memberType : members) + for (auto& memberType : members) llvmMembers.push_back(memberType->mLLVMType); auto structType = llvm::StructType::get(*mLLVMContext, llvmMembers); @@ -2144,7 +2151,7 @@ void BfIRCodeGen::HandleNextCmd() CMD_PARAM(String, typeName); auto structType = llvm::StructType::create(*mLLVMContext, typeName.c_str()); - auto typeEx = CreateTypeEx(structType); + auto typeEx = CreateTypeEx(structType); SetResult(curId, typeEx); } break; @@ -2210,7 +2217,7 @@ void BfIRCodeGen::HandleNextCmd() case BfIRCmd_GetPointerToFuncType: { BfIRTypeEx* funcType = NULL; - ReadFunctionType(funcType); + ReadFunctionType(funcType); SetResult(curId, GetPointerTypeEx(funcType)); } break; @@ -2226,13 +2233,13 @@ void BfIRCodeGen::HandleNextCmd() BfIRTypeEntry* elementTypeEntry = NULL; Read(elementType, &elementTypeEntry); - auto typeEx = new BfIRTypeEx(); + auto typeEx = new BfIRTypeEx(); typeEx->mMembers.Add(elementType); mIRTypeExs.Add(typeEx); - + CMD_PARAM(int, length); if (elementTypeEntry != NULL) - typeEx->mLLVMType = llvm::ArrayType::get(GetSizeAlignedType(elementTypeEntry)->mLLVMType, length); + typeEx->mLLVMType = llvm::ArrayType::get(GetSizeAlignedType(elementTypeEntry)->mLLVMType, length); else typeEx->mLLVMType = llvm::ArrayType::get(elementType->mLLVMType, length); @@ -2338,7 +2345,8 @@ void BfIRCodeGen::HandleNextCmd() { CMD_PARAM_NOTRANS(llvm::Value*, val); CMD_PARAM(String, name); - val->setName(name.c_str()); + if (!val->getType()->isVoidTy()) + val->setName(name.c_str()); } break; case BfIRCmd_CreateUndefValue: @@ -2347,7 +2355,7 @@ void BfIRCodeGen::HandleNextCmd() BfIRTypedValue result; result.mTypeEx = type; result.mValue = llvm::UndefValue::get(type->mLLVMType); - SetResult(curId, result); + SetResult(curId, result); } break; case BfIRCmd_NumericCast: @@ -2361,10 +2369,10 @@ void BfIRCodeGen::HandleNextCmd() llvm::Value* retVal = NULL; - if (BfIRBuilder::IsInt(typeCode)) + if (BfIRBuilder::IsIntable(typeCode)) { // Int -> Int - if ((BfIRBuilder::IsInt(valTypeCode)) || (valTypeCode == BfTypeCode_Boolean)) + if ((BfIRBuilder::IsIntable(valTypeCode)) || (valTypeCode == BfTypeCode_Boolean)) { retVal = mIRBuilder->CreateIntCast(val, toLLVMType, toSigned && valIsSigned); } @@ -2379,7 +2387,7 @@ void BfIRCodeGen::HandleNextCmd() else { // Int -> Float - if ((BfIRBuilder::IsInt(valTypeCode)) || (valTypeCode == BfTypeCode_Boolean)) + if ((BfIRBuilder::IsIntable(valTypeCode)) || (valTypeCode == BfTypeCode_Boolean)) { if (BfIRBuilder::IsSigned(valTypeCode)) retVal = mIRBuilder->CreateSIToFP(val, toLLVMType); @@ -2633,16 +2641,16 @@ void BfIRCodeGen::HandleNextCmd() CMD_PARAM(BfIRTypedValue, val); CMD_PARAM(BfIRTypeEx*, toType); - BfIRTypedValue result; + BfIRTypedValue result; result.mTypeEx = toType; auto fromType = val.mValue->getType(); if ((!fromType->isPointerTy()) || (!toType->mLLVMType->isPointerTy())) { - if (fromType->isIntegerTy()) - result.mValue = mIRBuilder->CreateIntToPtr(val.mValue, toType->mLLVMType); + if (fromType->isIntegerTy()) + result.mValue = mIRBuilder->CreateIntToPtr(val.mValue, toType->mLLVMType); else - result.mValue = mIRBuilder->CreatePtrToInt(val.mValue, toType->mLLVMType); + result.mValue = mIRBuilder->CreatePtrToInt(val.mValue, toType->mLLVMType); } else result.mValue = mIRBuilder->CreateBitCast(val.mValue, toType->mLLVMType); @@ -2653,7 +2661,7 @@ void BfIRCodeGen::HandleNextCmd() { CMD_PARAM(llvm::Value*, val); auto typeCode = (BfTypeCode)mStream->Read(); - bool isSigned; + bool isSigned; BfIRTypedValue result; result.mTypeEx = GetTypeEx(typeCode, isSigned); @@ -2676,14 +2684,14 @@ void BfIRCodeGen::HandleNextCmd() { CMD_PARAM(BfIRTypedValue, val); CMD_PARAM(int, idx0); - + BfIRTypedValue result; result.mTypeEx = val.mTypeEx; auto alignedPtr = GetAlignedPtr(val); auto compositeType = GetTypeMember(alignedPtr.mTypeEx, 0); - result.mValue = mIRBuilder->CreateConstInBoundsGEP1_32(compositeType->mLLVMType, alignedPtr.mValue, idx0); - SetResult(curId, result); + result.mValue = mIRBuilder->CreateConstInBoundsGEP1_32(compositeType->mLLVMType, alignedPtr.mValue, idx0); + SetResult(curId, result); } break; case BfIRCmd_InboundsGEP2_32: @@ -2691,7 +2699,7 @@ void BfIRCodeGen::HandleNextCmd() CMD_PARAM(BfIRTypedValue, val); CMD_PARAM(int, idx0); CMD_PARAM(int, idx1); - + auto compositeType = GetTypeMember(val.mTypeEx, 0); int elemIdx = BF_MIN(idx1, (int)compositeType->mMembers.mSize - 1); BfIRTypeEx* elemType = GetTypeMember(compositeType, elemIdx); @@ -2708,8 +2716,8 @@ void BfIRCodeGen::HandleNextCmd() CMD_PARAM(BfIRTypedValue, val); CMD_PARAM(llvm::Value*, idx0); - BfIRTypedValue result; - auto alignedPtr = GetAlignedPtr(val); + BfIRTypedValue result; + auto alignedPtr = GetAlignedPtr(val); auto compositeType = GetTypeMember(alignedPtr.mTypeEx, 0); FixIndexer(idx0); @@ -2728,14 +2736,14 @@ void BfIRCodeGen::HandleNextCmd() llvm::Value* indices[2] = { idx0, idx1 }; int elemIdx = 0; - if (auto constInt = llvm::dyn_cast(idx1)) + if (auto constInt = llvm::dyn_cast(idx1)) elemIdx = BF_MIN((int)constInt->getSExtValue(), (int)val.mTypeEx->mMembers.mSize - 1); auto compositeType = GetTypeMember(val.mTypeEx, 0); BfIRTypeEx* elemType = GetTypeMember(compositeType, elemIdx); - BfIRTypedValue result; - result.mValue = mIRBuilder->CreateInBoundsGEP(compositeType->mLLVMType, val.mValue, llvm::makeArrayRef(indices)); + BfIRTypedValue result; + result.mValue = mIRBuilder->CreateInBoundsGEP(compositeType->mLLVMType, val.mValue, llvm::ArrayRef(indices)); result.mTypeEx = GetPointerTypeEx(elemType); SetResult(curId, result); } @@ -2756,14 +2764,14 @@ void BfIRCodeGen::HandleNextCmd() { CMD_PARAM(BfIRTypedValue, val); CMD_PARAM(int, idx); - + auto compositeType = val.mTypeEx; int elemIdx = BF_MIN(idx, (int)compositeType->mMembers.mSize - 1); auto elemType = GetTypeMember(compositeType, elemIdx); BfIRTypedValue result; result.mTypeEx = elemType; - result.mValue = mIRBuilder->CreateExtractValue(val.mValue, llvm::makeArrayRef((unsigned)idx)); + result.mValue = mIRBuilder->CreateExtractValue(val.mValue, llvm::ArrayRef((unsigned)idx)); SetResult(curId, result); } break; @@ -2775,7 +2783,7 @@ void BfIRCodeGen::HandleNextCmd() BfIRTypedValue result; result.mTypeEx = agg.mTypeEx; - result.mValue = mIRBuilder->CreateInsertValue(agg.mValue, val.mValue, llvm::makeArrayRef((unsigned)idx)); + result.mValue = mIRBuilder->CreateInsertValue(agg.mValue, val.mValue, llvm::ArrayRef((unsigned)idx)); SetResult(curId, result); } break; @@ -2805,7 +2813,7 @@ void BfIRCodeGen::HandleNextCmd() BfIRTypedValue typedValue; typedValue.mTypeEx = GetPointerTypeEx(type); - + if (origType != type) { typedValue.mValue = mIRBuilder->CreateAlloca(type->mLLVMType, arraySize); @@ -2872,7 +2880,7 @@ void BfIRCodeGen::HandleNextCmd() BF_ASSERT(typedValue.mTypeEx != NULL); CMD_PARAM(int, alignment); CMD_PARAM(bool, isVolatile); - + BfIRTypedValue result; result.mTypeEx = GetTypeMember(typedValue.mTypeEx, 0); result.mValue = mIRBuilder->CreateAlignedLoad(result.mTypeEx->mLLVMType, typedValue.mValue, llvm::MaybeAlign(alignment), isVolatile); @@ -3049,7 +3057,7 @@ void BfIRCodeGen::HandleNextCmd() // CMD_PARAM(llvm::BasicBlock*, startingBlock); // auto& basicBlockList = mActiveFunction->getBasicBlockList(); // int postExitBlockIdx = -1; -// +// // auto itr = basicBlockList.rbegin(); // int blockIdx = (int)basicBlockList.size() - 1; // while (itr != basicBlockList.rend()) @@ -3063,7 +3071,7 @@ void BfIRCodeGen::HandleNextCmd() // } // blockIdx--; // } -// +// // while ((int)basicBlockList.size() > postExitBlockIdx) // { // auto& block = basicBlockList.back(); @@ -3098,9 +3106,19 @@ void BfIRCodeGen::HandleNextCmd() case BfIRCmd_SetInsertPointAtStart: { CMD_PARAM(llvm::BasicBlock*, block); - mIRBuilder->SetInsertPoint(block, block->begin()); + + auto itr = block->begin(); + if (itr != block->end()) + { + // Some instructructions MUST be at start of the block + auto instType = itr->getType(); + if (llvm::PHINode::classof(&*itr)) + ++itr; + } + + mIRBuilder->SetInsertPoint(block, itr); // SetInsertPoint can clear the debug loc so reset it here - mIRBuilder->SetCurrentDebugLocation(mDebugLoc); + mIRBuilder->SetCurrentDebugLocation(mDebugLoc); } break; case BfIRCmd_EraseFromParent: @@ -3177,8 +3195,8 @@ void BfIRCodeGen::HandleNextCmd() case BfIRCmd_CreatePhi: { CMD_PARAM(BfIRTypeEx*, type); - CMD_PARAM(int, incomingCount); - BfIRTypedValue result; + CMD_PARAM(int, incomingCount); + BfIRTypedValue result; result.mTypeEx = type; result.mValue = mIRBuilder->CreatePHI(type->mLLVMType, incomingCount); SetResult(curId, result); @@ -3272,9 +3290,9 @@ void BfIRCodeGen::HandleNextCmd() { llvm::Intrinsic::sin, 0, -1}, { llvm::Intrinsic::sqrt, 0, -1}, { (llvm::Intrinsic::ID)-2, -1}, // sub, - { (llvm::Intrinsic::ID)-2, -1}, // va_arg, - { llvm::Intrinsic::vaend, -1}, // va_end, - { llvm::Intrinsic::vastart, -1}, // va_start, + { (llvm::Intrinsic::ID)-2, 0, 1, 2}, // va_arg, + { llvm::Intrinsic::vaend, 0, -1}, // va_end, + { llvm::Intrinsic::vastart, 0, -1}, // va_start, { (llvm::Intrinsic::ID)-2, -1}, // xgetbv { (llvm::Intrinsic::ID)-2, -1}, // xor }; @@ -3358,7 +3376,7 @@ void BfIRCodeGen::HandleNextCmd() CMD_PARAM(BfIRTypeEx*, resultType); CMD_PARAM(CmdParamVec, paramTypes); CMD_PARAM(bool, isVarArg); - + CmdParamVec llvmTypes; for (auto typeEx : paramTypes) { @@ -3376,7 +3394,7 @@ void BfIRCodeGen::HandleNextCmd() if (typeEx->mMembers.IsEmpty()) { typeEx->mMembers.Add(resultType); - for (auto paramType : paramTypes) + for (auto paramType : paramTypes) typeEx->mMembers.Add(paramType); } @@ -3497,7 +3515,7 @@ void BfIRCodeGen::HandleNextCmd() BfIRTypedValue func; BfIRCodeGenEntry* codeGenEntry = NULL; Read(func, &codeGenEntry); - CMD_PARAM(CmdParamVec, args); + CMD_PARAM(CmdParamVec, args); if ((func.mValue == NULL) && (codeGenEntry != NULL) && (codeGenEntry->mKind == BfIRCodeGenEntryKind_IntrinsicData)) { @@ -3873,7 +3891,7 @@ void BfIRCodeGen::HandleNextCmd() // for (int i = 2; i < 6; i++) // { // llvm::Type* type = args[i]->getType(); -// +// // if (!type->isPointerTy() || !GetPointerElementType(args[1])->isIntegerTy(32)) // FatalError("Intrinsic argument error"); // } @@ -3947,7 +3965,7 @@ void BfIRCodeGen::HandleNextCmd() llvm::Value* gepArgs[] = { llvm::ConstantInt::get(llvm::Type::getInt32Ty(*mLLVMContext), 0), args[1].mValue }; - auto gep = mIRBuilder->CreateInBoundsGEP(GetLLVMPointerElementType(args[0].mTypeEx), args[0].mValue, llvm::makeArrayRef(gepArgs)); + auto gep = mIRBuilder->CreateInBoundsGEP(GetLLVMPointerElementType(args[0].mTypeEx), args[0].mValue, llvm::ArrayRef(gepArgs)); if (args.size() >= 3) mIRBuilder->CreateStore(args[2].mValue, gep); else @@ -4428,7 +4446,7 @@ void BfIRCodeGen::HandleNextCmd() auto funcTypeEx = GetTypeMember(func.mTypeEx, 0); auto returnTypeEx = GetTypeMember(funcTypeEx, 0); - + BfIRTypedValue result; result.mTypeEx = returnTypeEx; result.mValue = mIRBuilder->CreateCall(funcType, func.mValue, llvmArgs); @@ -4495,7 +4513,7 @@ void BfIRCodeGen::HandleNextCmd() BF_ASSERT(inst.mValue == mLastFuncCalled.mValue); - BfIRAttribute attribute = (BfIRAttribute)mStream->Read(); + BfIRAttribute attribute = (BfIRAttribute)mStream->Read(); auto attr = LLVMMapAttribute(attribute); auto callInst = llvm::dyn_cast(inst.mValue); BfIRTypeEx* funcType = mLastFuncCalled.mTypeEx; @@ -4533,7 +4551,7 @@ void BfIRCodeGen::HandleNextCmd() BfIRTypeEx* funcType = mLastFuncCalled.mTypeEx; if (attribute == BfIRAttribute_Dereferencable) - { + { ((llvm::CallInst*)callInst)->addDereferenceableParamAttr(argIdx - 1, arg); } else if (attribute == BfIRAttribute_ByVal) @@ -4552,7 +4570,7 @@ void BfIRCodeGen::HandleNextCmd() { BfIRTypedValue typedValue; ReadFunction(typedValue); - CMD_PARAM(int, argIdx); + CMD_PARAM(int, argIdx); auto func = llvm::dyn_cast(typedValue.mValue); auto funcType = GetTypeMember(typedValue.mTypeEx, 0); @@ -4604,7 +4622,7 @@ void BfIRCodeGen::HandleNextCmd() { auto attr = LLVMMapAttribute(attribute); if (attr == llvm::Attribute::StructRet) - { + { auto elemPtrType = GetTypeMember(funcType, argIdx); auto elemType = GetTypeMember(elemPtrType, 0); llvm::Attribute sret = llvm::Attribute::getWithStructRetType(*mLLVMContext, elemType->mLLVMType); @@ -4625,7 +4643,7 @@ void BfIRCodeGen::HandleNextCmd() break; default: func->addFnAttr(attr); - } + } } else if (argIdx == 0) func->addRetAttr(attr); @@ -4643,7 +4661,7 @@ void BfIRCodeGen::HandleNextCmd() auto func = llvm::dyn_cast(typedValue.mValue); auto funcType = GetTypeMember(typedValue.mTypeEx, 0); - + BfIRAttribute attribute = (BfIRAttribute)mStream->Read(); CMD_PARAM(int, arg); if (attribute == BfIRAttribute_Dereferencable) @@ -4759,7 +4777,7 @@ void BfIRCodeGen::HandleNextCmd() else { inst.setDebugLoc(llvm::DebugLoc()); - } + } } } } @@ -5463,13 +5481,17 @@ void BfIRCodeGen::HandleNextCmd() { if (insertBeforeInst != NULL) { - SetResult(curId, mDIBuilder->insertDeclare(val, (llvm::DILocalVariable*)varInfo, mDIBuilder->createExpression(), - mIRBuilder->getCurrentDebugLocation(), insertBeforeInst)); + auto dbgResult = mDIBuilder->insertDeclare(val, (llvm::DILocalVariable*)varInfo, mDIBuilder->createExpression(), + mIRBuilder->getCurrentDebugLocation(), insertBeforeInst); + auto inst = dbgResult.get(); + SetResult(curId, inst); } else { - SetResult(curId, mDIBuilder->insertDeclare(val, (llvm::DILocalVariable*)varInfo, mDIBuilder->createExpression(), - mIRBuilder->getCurrentDebugLocation(), mIRBuilder->GetInsertBlock())); + auto dbgResult = mDIBuilder->insertDeclare(val, (llvm::DILocalVariable*)varInfo, mDIBuilder->createExpression(), + mIRBuilder->getCurrentDebugLocation(), mIRBuilder->GetInsertBlock()); + auto inst = dbgResult.get(); + SetResult(curId, inst); } } } @@ -5633,7 +5655,7 @@ BfIRTypedValue BfIRCodeGen::GetTypedValue(int id) if (result.mKind == BfIRCodeGenEntryKind_TypedValue) return result.mTypedValue; BF_ASSERT(result.mKind == BfIRCodeGenEntryKind_LLVMValue); - + BfIRTypedValue typedValue; typedValue.mTypeEx = NULL; typedValue.mValue = result.mLLVMValue; @@ -5675,12 +5697,12 @@ llvm::Type* BfIRCodeGen::GetLLVMTypeById(int id) return GetTypeEntry(id).mType->mLLVMType; } -// LLVM/Clang 18.1.4 -static void addSanitizers(const llvm::Triple& TargetTriple, BfCodeGenOptions& CodeGenOpts, llvm::PassBuilder& PB) +// LLVM/Clang 19.1.7 +static void addSanitizers(const llvm::Triple& TargetTriple, BfCodeGenOptions& CodeGenOpts, llvm::PassBuilder& PB) { -#if 0 +#if 0 auto SanitizersCallback = [&](llvm::ModulePassManager& MPM, llvm::OptimizationLevel Level) { - if (CodeGenOpts.hasSanitizeCoverage()) + if (CodeGenOpts.hasSanitizeCoverage()) { auto SancovOpts = getSancovOptsFromCGOpts(CodeGenOpts); MPM.addPass(SanitizerCoveragePass( @@ -5778,8 +5800,8 @@ static void addSanitizers(const llvm::Triple& TargetTriple, BfCodeGenOptions& Co #endif } -// LLVM/Clang 18.1.4 -static void addKCFIPass(const llvm::Triple& TargetTriple, const BfCodeGenOptions& codeGenOpts, llvm::PassBuilder& PB) +// LLVM/Clang 19.1.7 +static void addKCFIPass(const llvm::Triple& TargetTriple, const BfCodeGenOptions& codeGenOpts, llvm::PassBuilder& PB) { #if 0 // If the back-end supports KCFI operand bundle lowering, skip KCFIPass. @@ -5867,13 +5889,13 @@ void BfIRCodeGen::RunOptimizationPipeline(const llvm::Triple& targetTriple) PB.registerFunctionAnalyses(FAM); PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); - + //llvm::ModulePassManager MPM; // Add a verifier pass, before any other passes, to catch CodeGen issues. llvm::ModulePassManager MPM; if (verifyModule) - MPM.addPass(llvm::VerifierPass()); + MPM.addPass(llvm::VerifierPass()); bool disableLLVMPasses = false; if (!disableLLVMPasses) @@ -5899,7 +5921,7 @@ void BfIRCodeGen::RunOptimizationPipeline(const llvm::Triple& targetTriple) case BfOptLevel_O3: Level = llvm::OptimizationLevel::O3; break; - case BfOptLevel_Og: + case BfOptLevel_Og: Level = llvm::OptimizationLevel::O1; break; } @@ -5960,7 +5982,7 @@ void BfIRCodeGen::RunOptimizationPipeline(const llvm::Triple& targetTriple) MPM.addPass(createModuleToFunctionPassAdaptor(MemProfilerPass())); MPM.addPass(ModuleMemProfilerPass()); }); - } + } #endif if (mCodeGenOptions.mLTOType == BfLTOType_Fat) @@ -5969,15 +5991,15 @@ void BfIRCodeGen::RunOptimizationPipeline(const llvm::Triple& targetTriple) Level, PrepareForThinLTO, PrepareForThinLTO || shouldEmitRegularLTOSummary(targetTriple, mCodeGenOptions, PrepareForLTO))); } - else if (PrepareForThinLTO) + else if (PrepareForThinLTO) { MPM.addPass(PB.buildThinLTOPreLinkDefaultPipeline(Level)); } - else if (PrepareForLTO) + else if (PrepareForLTO) { MPM.addPass(PB.buildLTOPreLinkDefaultPipeline(Level)); } - else + else { MPM.addPass(PB.buildPerModuleDefaultPipeline(Level)); } @@ -6058,7 +6080,7 @@ void BfIRCodeGen::RunOptimizationPipeline(const llvm::Triple& targetTriple) // outs() << "\n"; // return; // } -// +// // if (LangOpts.HIPStdPar && !LangOpts.CUDAIsDevice && // LangOpts.HIPStdParInterposeAlloc) // MPM.addPass(HipStdParAllocationInterpositionPass()); @@ -6066,9 +6088,9 @@ void BfIRCodeGen::RunOptimizationPipeline(const llvm::Triple& targetTriple) // Now that we have all of the passes ready, run them. { //PrettyStackTraceString CrashInfo("Optimizer"); - llvm::TimeTraceScope TimeScope("Optimizer"); + //llvm::TimeTraceScope TimeScope("Optimizer"); MPM.run(*mLLVMModule, MAM); - } + } } bool BfIRCodeGen::WriteObjectFile(const StringImpl& outFileName) @@ -6150,10 +6172,10 @@ bool BfIRCodeGen::WriteObjectFile(const StringImpl& outFileName) llvm::AnalysisID StartAfterID = nullptr; llvm::AnalysisID StopAfterID = nullptr; const llvm::PassRegistry *PR = llvm::PassRegistry::getPassRegistry(); - + //WriteBitcode bool noVerify = false; // Option - + if ((!enableLTO) && (!mCodeGenOptions.mWriteBitcode)) { // Ask the target to add backend passes as necessary. @@ -6271,10 +6293,10 @@ int BF_LinuxFixLinkage() llvm::MCContext* ctx = NULL; llvm::raw_pwrite_stream* stream = NULL; - createWasmStreamer(*ctx, NULL, NULL, NULL, false); - createMachOStreamer(*ctx, NULL, NULL, NULL, false, false, false); - createAsmStreamer(*ctx, NULL, false, false, NULL, NULL, NULL, false); - createELFStreamer(*ctx, NULL, NULL, NULL, false); + createWasmStreamer(*ctx, NULL, NULL, NULL); + createMachOStreamer(*ctx, NULL, NULL, NULL, false, false); + createAsmStreamer(*ctx, NULL, NULL, NULL, NULL); + createELFStreamer(*ctx, NULL, NULL, NULL); return 0; } diff --git a/IDEHelper/Compiler/BfIRCodeGen.h b/IDEHelper/Compiler/BfIRCodeGen.h index 05957450..2b20e8f8 100644 --- a/IDEHelper/Compiler/BfIRCodeGen.h +++ b/IDEHelper/Compiler/BfIRCodeGen.h @@ -21,7 +21,7 @@ namespace llvm class Module; class LLVMContext; class TargetMachine; - class Triple; + class Triple; }; NS_BF_BEGIN @@ -196,7 +196,7 @@ public: void SetResult(int id, llvm::Type* value); void SetResult(int id, BfIRTypeEx* typeEx); void SetResult(int id, llvm::BasicBlock* value); - void SetResult(int id, llvm::MDNode* value); + void SetResult(int id, llvm::MDNode* value); void CreateMemSet(llvm::Value* addr, llvm::Value* val, llvm::Value* size, int alignment, bool isVolatile = false); void AddNop(); llvm::Value* TryToVector(const BfIRTypedValue& value); diff --git a/IDEHelper/Compiler/BfMangler.cpp b/IDEHelper/Compiler/BfMangler.cpp index e9f7dd66..aad755bd 100644 --- a/IDEHelper/Compiler/BfMangler.cpp +++ b/IDEHelper/Compiler/BfMangler.cpp @@ -329,6 +329,8 @@ void BfGNUMangler::MangleTypeInst(MangleContext& mangleContext, StringImpl& name name += "__varargs"; continue; } + if (methodDef->mParams[paramIdx]->mParamKind == BfParamKind_Params) + name += "_params_"; typeVec.push_back(BfNodeDynCast(methodDef->mParams[paramIdx]->mTypeRef)->mType); } for (auto type : typeVec) @@ -456,13 +458,13 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType case BfTypeCode_UInt32: name += "j"; return; case BfTypeCode_Int64: - if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8) + if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)) name += "l"; else name += "x"; return; case BfTypeCode_UInt64: - if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8) + if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)) name += "m"; else name += "y"; @@ -470,7 +472,7 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType case BfTypeCode_UIntPtr: if ((mangleContext.mCCompat) || (mangleContext.mInArgs)) { - if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8) + if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)) name += (primType->mSize == 8) ? "m" : "j"; else name += (primType->mSize == 8) ? "y" : "j"; @@ -481,7 +483,7 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType case BfTypeCode_IntPtr: if ((mangleContext.mCCompat) || (mangleContext.mInArgs)) { - if (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8) + if ((mangleContext.mModule == NULL) || (mangleContext.mModule->mCompiler->mOptions.mCLongSize == 8)) name += (primType->mSize == 8) ? "l" : "i"; else name += (primType->mSize == 8) ? "x" : "i"; @@ -576,6 +578,8 @@ void BfGNUMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType name += "U5alloc"; else if (retTypeType->mModifiedKind == BfToken_Nullable) name += "U8nullable"; + else if (retTypeType->mModifiedKind == BfToken_Params) + name += "U6params"; else BF_FATAL("Unhandled"); Mangle(mangleContext, name, retTypeType->mElementType); @@ -1183,10 +1187,10 @@ void BfMSMangler::AddTypeStart(MangleContext& mangleContext, StringImpl& name, B if ((type->IsEnum()) && (type->IsTypedPrimitive())) { auto unreifiedModule = mangleContext.GetUnreifiedModule(); - if (unreifiedModule != NULL) - unreifiedModule->PopulateType(type, BfPopulateType_Data); - - BF_ASSERT(type->mSize >= 0); + if (type->mDefineState >= BfTypeDefineState_Defined) + { + BF_ASSERT(type->mSize >= 0); + } // The enum size is supposed to be encoded, but VC always uses '4' //name += "W"; @@ -1273,6 +1277,8 @@ bool BfMSMangler::FindOrCreateNameSub(MangleContext& mangleContext, StringImpl& name += "__varargs"; continue; } + if (methodDef->mParams[paramIdx]->mParamKind == BfParamKind_Params) + name += "_params_"; typeVec.push_back(BfNodeDynCast(methodDef->mParams[paramIdx]->mTypeRef)->mType); } name += '@'; @@ -1725,6 +1731,8 @@ void BfMSMangler::Mangle(MangleContext& mangleContext, StringImpl& name, BfType* name += "alloc$"; else if (retType->mModifiedKind == BfToken_Nullable) name += "nullable$"; + else if (retType->mModifiedKind == BfToken_Params) + name += "params$"; else BF_FATAL("Unhandled"); Mangle(mangleContext, name, retType->mElementType); diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 4676a821..052888ce 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -475,6 +475,7 @@ public: BfTypedValue mConstAccum; bool mFailed; bool mIsFirstConstPass; + bool mHasAppendWantMark; int mCurAppendAlign; public: @@ -482,6 +483,7 @@ public: { mFailed = false; mIsFirstConstPass = false; + mHasAppendWantMark = false; mCurAppendAlign = 1; } @@ -654,6 +656,9 @@ public: return; } + if (origResolvedTypeRef->WantsGCMarking()) + mHasAppendWantMark = true; + bool isGenericParam = origResolvedTypeRef->IsGenericParam(); auto resolvedTypeRef = origResolvedTypeRef; auto resultType = resolvedTypeRef; @@ -778,13 +783,13 @@ public: if (typeInst != NULL) { exprEvaluator.ResolveArgValues(argValues); - exprEvaluator.MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInst, argValues, false, BfMethodGenericArguments(), true); + exprEvaluator.MatchConstructor(objCreateExpr->mTypeRef, objCreateExpr, emtpyThis, typeInst, argValues, false, BfMethodGenericArguments(), BfAllowAppendKind_Infer); } exprEvaluator.mFunctionBindResult = NULL; if (bindResult.mMethodInstance != NULL) { - if (bindResult.mMethodInstance->mMethodDef->mHasAppend) + if (bindResult.mMethodInstance->mMethodDef->HasAppend()) { auto calcAppendArgs = bindResult.mIRArgs; BF_ASSERT(calcAppendArgs[0].IsFake()); @@ -1105,6 +1110,8 @@ bool BfModule::IsHotCompile() void BfModule::FinishInit() { + BfLogSysM("FinishInit %@ %s\n", this, mModuleName.c_str()); + BF_ASSERT(mAwaitingInitFinish); auto moduleOptions = GetModuleOptions(); @@ -1287,7 +1294,11 @@ void BfModule::SetupIRBuilder(bool dbgVerifyCodeGen) void BfModule::EnsureIRBuilder(bool dbgVerifyCodeGen) { - BF_ASSERT(!mIsDeleting); + if (mIsDeleting) + { + mCompiler->RequestExtraCompile(); + InternalError("EnsureIRBuilder on deleted module"); + } if (mBfIRBuilder == NULL) { @@ -1798,7 +1809,7 @@ int BfModule::GetStringPoolIdx(BfIRValue constantStr, BfIRConstHolder* constHold if (constant == NULL) return -1; - if (constant->mTypeCode == BfTypeCode_StringId) + if ((constant->mTypeCode == BfTypeCode_StringId) || (constant->mTypeCode == BfTypeCode_CharPtr)) { return constant->mInt32; } @@ -1888,7 +1899,16 @@ BfIRValue BfModule::GetStringCharPtr(BfIRValue strValue, bool force) BfIRValue BfModule::GetStringCharPtr(const StringImpl& str, bool force) { - return GetStringCharPtr(GetStringObjectValue(str, force), force); + auto result = GetStringCharPtr(GetStringObjectValue(str, force), force); + + if (result.IsConst()) + { + auto constant = mBfIRBuilder->GetConstant(result); + if ((constant != NULL) && (constant->mTypeCode == BfTypeCode_StringId)) + return mBfIRBuilder->CreateConst(BfTypeCode_CharPtr, constant->mInt32); + } + + return result; } BfIRValue BfModule::GetStringObjectValue(int strId, bool define, bool force) @@ -2064,23 +2084,23 @@ void BfModule::RestoreScopeState() mCurMethodState->mTailScope = mCurMethodState->mCurScope; } -BfIRValue BfModule::CreateAlloca(BfType* type, bool addLifetime, const char* name, BfIRValue arraySize) +BfIRValue BfModule::CreateAlloca(BfIRType irType, int align, bool addLifetime, const char* name, BfIRValue arraySize) { if (mBfIRBuilder->mIgnoreWrites) return mBfIRBuilder->GetFakeVal(); - BF_ASSERT((*(int8*)&addLifetime == 1) || (*(int8*)&addLifetime == 0)); - mBfIRBuilder->PopulateType(type); + BF_ASSERT((*(int8*)&addLifetime == 1) || (*(int8*)&addLifetime == 0)); auto prevInsertBlock = mBfIRBuilder->GetInsertBlock(); if (!mBfIRBuilder->mIgnoreWrites) BF_ASSERT(!prevInsertBlock.IsFake()); mBfIRBuilder->SetInsertPoint(mCurMethodState->mIRHeadBlock); BfIRValue allocaInst; if (arraySize) - allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(type), arraySize); + allocaInst = mBfIRBuilder->CreateAlloca(irType, arraySize); else - allocaInst = mBfIRBuilder->CreateAlloca(mBfIRBuilder->MapType(type)); - mBfIRBuilder->SetAllocaAlignment(allocaInst, type->mAlign); + allocaInst = mBfIRBuilder->CreateAlloca(irType); + if (align > 0) + mBfIRBuilder->SetAllocaAlignment(allocaInst, align); mBfIRBuilder->ClearDebugLocation(allocaInst); if (name != NULL) mBfIRBuilder->SetName(allocaInst, name); @@ -2094,6 +2114,16 @@ BfIRValue BfModule::CreateAlloca(BfType* type, bool addLifetime, const char* nam return allocaInst; } +BfIRValue BfModule::CreateAlloca(BfType* type, bool addLifetime, const char* name, BfIRValue arraySize) +{ + if (mBfIRBuilder->mIgnoreWrites) + return mBfIRBuilder->GetFakeVal(); + + BF_ASSERT((*(int8*)&addLifetime == 1) || (*(int8*)&addLifetime == 0)); + mBfIRBuilder->PopulateType(type); + return CreateAlloca(mBfIRBuilder->MapType(type), type->mAlign, addLifetime, name, arraySize); +} + BfIRValue BfModule::CreateAllocaInst(BfTypeInstance* typeInst, bool addLifetime, const char* name) { if (mBfIRBuilder->mIgnoreWrites) @@ -2292,7 +2322,14 @@ bool BfModule::TryLocalVariableInit(BfLocalVariable* localVar) { if (fieldInstance.mMergedDataIdx != -1) { - int64 checkMask = (int64)1 << fieldInstance.mMergedDataIdx; + int64 checkMask = 1; + if (auto fieldTypeInst = fieldInstance.mResolvedType->ToTypeInstance()) + { + if (fieldTypeInst->IsValueType()) + checkMask = (1 << fieldTypeInst->mMergedFieldDataCount) - 1; + } + checkMask <<= fieldInstance.mMergedDataIdx; + if ((localVar->mUnassignedFieldFlags & checkMask) != 0) { // For fields added in extensions, we automatically initialize those if necessary @@ -2350,7 +2387,7 @@ bool BfModule::TryLocalVariableInit(BfLocalVariable* localVar) void BfModule::LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit) { BfAstNode* localNameNode = localVar->mNameNode; - if (localVar->mIsThis) + if ((localVar->mIsThis) && (mCurMethodInstance != NULL)) { localNameNode = mCurMethodInstance->mMethodDef->GetRefNode(); } @@ -2607,7 +2644,14 @@ BfProjectSet* BfModule::GetVisibleProjectSet() }; if (mCurTypeInstance != NULL) + { _AddType(mCurTypeInstance); + if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mType == mCurTypeInstance)) + { + if (mContext->mCurTypeState->mCurTypeDef != NULL) + _AddProject(mContext->mCurTypeState->mCurTypeDef->mProject); + } + } auto methodState = mCurMethodState; while (methodState != NULL) @@ -2629,6 +2673,23 @@ BfProjectSet* BfModule::GetVisibleProjectSet() return &mCurMethodState->mVisibleProjectSet; } +bool BfModule::IsProjectVisible(BfProject* project) +{ + auto visibleProjectSet = GetVisibleProjectSet(); + if (visibleProjectSet != NULL) + return visibleProjectSet->Contains(project); + + auto activeTypeDef = GetActiveTypeDef(); + if (activeTypeDef != NULL) + { + if (activeTypeDef->mProject == project) + return true; + return activeTypeDef->mProject->HasDependency(project); + } + + return false; +} + BfFileInstance* BfModule::GetFileFromNode(BfAstNode* astNode) { auto bfParser = astNode->GetSourceData()->ToParserData(); @@ -2867,6 +2928,9 @@ void BfModule::GetAccessAllowed(BfTypeInstance* checkType, bool &allowProtected, bool BfModule::CheckProtection(BfProtectionCheckFlags& flags, BfTypeInstance* memberOwner, BfProject* memberProject, BfProtection memberProtection, BfTypeInstance* lookupStartType) { + if ((memberOwner != NULL) && (mCurMethodState != NULL) && (mCurMethodState->mPrivateTypeInstance == memberOwner)) + return true; + if (memberProtection == BfProtection_Hidden) return false; if (memberProtection == BfProtection_Public) @@ -2999,6 +3063,15 @@ void BfModule::SetElementType(BfAstNode* astNode, BfSourceElementType elementTyp } } +void BfModule::SetHighestElementType(BfAstNode* astNode, BfSourceElementType elementType) +{ + if (mCompiler->mResolvePassData != NULL) + { + if (auto sourceClassifier = mCompiler->mResolvePassData->GetSourceClassifier(astNode)) + sourceClassifier->SetHighestElementType(astNode, elementType); + } +} + bool BfModule::PreFail() { if (!mIgnoreErrors) @@ -3576,25 +3649,22 @@ void BfModule::FatalError(const StringImpl& error, const char* file, int line, i BfpSystem_FatalError(fullError.c_str(), "FATAL MODULE ERROR"); } -void BfModule::FatalError(const StringImpl& error, BfAstNode* refNode) +void BfModule::FatalError(const StringImpl& error, BfFailHandleKind failHandleKind) { - if (refNode != NULL) - { - auto parser = refNode->GetParserData(); - if (parser != NULL) - { - int line = -1; - int lineChar = -1; - parser->GetLineCharAtIdx(refNode->mSrcStart, line, lineChar); - if (line != -1) - FatalError(error, parser->mFileName.c_str(), line, lineChar); - } - } - FatalError(error); + if (failHandleKind == BfFailHandleKind_Normal) + FatalError(error); + else if (failHandleKind == BfFailHandleKind_Soft) + InternalError(error); } void BfModule::InternalError(const StringImpl& error, BfAstNode* refNode, const char* file, int line) { + static bool isInside = false; + if (isInside) + return; + + isInside = true; + String fullError = error; if (file != NULL) @@ -3611,6 +3681,8 @@ void BfModule::InternalError(const StringImpl& error, BfAstNode* refNode, const fullError += StrFormat("\nSource Location: %s:%d", mCurFilePosition.mFileInstance->mParser->mFileName.c_str(), mCurFilePosition.mCurLine + 1); Fail(String("INTERNAL ERROR: ") + fullError, refNode); + + isInside = false; } void BfModule::NotImpl(BfAstNode* astNode) @@ -3930,11 +4002,7 @@ void BfModule::PopulateGlobalContainersList(const BfGlobalLookup& globalLookup) BP_ZONE("PopulateGlobalContainersList"); - BfTypeDef* userTypeDef = mContext->mCurTypeState->mCurTypeDef; - if ((userTypeDef == NULL) && (mCurMethodInstance != NULL)) - userTypeDef = mCurMethodInstance->mMethodDef->mDeclaringType; - if (userTypeDef == NULL) - userTypeDef = mCurTypeInstance->mTypeDef; + BfTypeDef* userTypeDef = GetActiveTypeDef(); if (mContext->mCurTypeState->mGlobalContainerCurUserTypeDef != userTypeDef) { @@ -4204,7 +4272,7 @@ void BfModule::CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLo { BfIRType irType; - if (fieldInstance->IsAppendedObject()) + if ((fieldInstance->IsAppendedObject()) && (!field->mIsStatic)) { irType = mBfIRBuilder->MapTypeInst(fieldType->ToTypeInstance(), BfIRPopulateType_Eventually_Full); initValue = mBfIRBuilder->CreateConstAggZero(irType); @@ -4212,19 +4280,22 @@ void BfModule::CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLo else irType = mBfIRBuilder->MapType(fieldType, BfIRPopulateType_Eventually_Full); - BfIRValue globalVar = mBfIRBuilder->CreateGlobalVariable( - irType, - false, - BfIRLinkageType_External, - initValue, - staticVarName, - isThreadLocal); - mBfIRBuilder->GlobalVar_SetAlignment(globalVar, fieldType->mAlign); - if (storageKind != BfIRStorageKind_Normal) - mBfIRBuilder->GlobalVar_SetStorageKind(globalVar, storageKind); + if (fieldType->mSize > 0) + { + BfIRValue globalVar = mBfIRBuilder->CreateGlobalVariable( + irType, + false, + BfIRLinkageType_External, + initValue, + staticVarName, + isThreadLocal); + mBfIRBuilder->GlobalVar_SetAlignment(globalVar, fieldType->mAlign); + if (storageKind != BfIRStorageKind_Normal) + mBfIRBuilder->GlobalVar_SetStorageKind(globalVar, storageKind); - BF_ASSERT(globalVar); - mStaticFieldRefs[fieldInstance] = globalVar; + BF_ASSERT(globalVar); + mStaticFieldRefs[fieldInstance] = globalVar; + } } } } @@ -4663,6 +4734,10 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, // auto-created underlying type and it will cause an 'error flash'. We defer errors until the full resolve for that purpose resolveFlags = BfConstResolveFlag_NoCast; } + + if ((mCurTypeInstance->IsEnum()) && (fieldDef->IsEnumCaseEntry())) + resolveFlags = (BfConstResolveFlags)(resolveFlags | BfConstResolveFlag_NoConversionOperator | BfConstResolveFlag_ExplicitCast); + UpdateSrcPos(initializer); auto result = constResolver.Resolve(initializer, fieldType, resolveFlags); if ((mCompiler->mCeMachine != NULL) && (fieldInstance != NULL)) @@ -4670,6 +4745,10 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, if (mCompiler->mCeMachine->mExecuteId != ceExecuteId) fieldInstance->mHadConstEval = true; } + + if (auto autoComplete = mCompiler->GetAutoComplete()) + autoComplete->CheckResult(initializer, result); + return result; } } @@ -4680,7 +4759,13 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, staticVarRef = ReferenceStaticField(fieldInstance); exprEvaluator.mReceivingValue = &staticVarRef; } - if (fieldType->IsVar()) + + if (fieldType->IsDeleting()) + { + mCompiler->RequestExtraCompile(); + InternalError("Field using deleted type", fieldDef->GetRefNode()); + } + else if (fieldType->IsVar()) result = CreateValueFromExpression(exprEvaluator, initializer, NULL, (BfEvalExprFlags)(BfEvalExprFlags_NoValueAddr | BfEvalExprFlags_FieldInitializer)); else result = CreateValueFromExpression(exprEvaluator, initializer, fieldType, (BfEvalExprFlags)(BfEvalExprFlags_NoValueAddr | BfEvalExprFlags_FieldInitializer)); @@ -4712,6 +4797,99 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, return result; } +bool BfModule::TryGetAppendedObjectInfo(BfFieldInstance* fieldInstance, int& dataSize, int& alignSize) +{ + auto resolvedFieldType = fieldInstance->GetResolvedType(); + auto fieldDef = fieldInstance->GetFieldDef(); + + BfAstNode* nameRefNode = NULL; + if (auto fieldDecl = fieldDef->GetFieldDeclaration()) + nameRefNode = fieldDecl->mNameNode; + else if (auto paramDecl = fieldDef->GetParamDeclaration()) + nameRefNode = paramDecl->mNameNode; + if (nameRefNode == NULL) + nameRefNode = fieldDef->mTypeRef; + + dataSize = resolvedFieldType->mSize; + alignSize = resolvedFieldType->mAlign; + + SetAndRestoreValue prevTypeRef(mContext->mCurTypeState->mCurFieldDef, fieldDef); + SetAndRestoreValue prevResolveKind(mContext->mCurTypeState->mResolveKind, BfTypeState::ResolveKind_FieldType); + + PopulateType(resolvedFieldType, BfPopulateType_Data); + + auto fieldTypeInst = resolvedFieldType->ToTypeInstance(); + dataSize = BF_MAX(fieldTypeInst->mInstSize, 0); + alignSize = BF_MAX(fieldTypeInst->mInstAlign, 1); + + if (fieldTypeInst->mTypeFailed) + { + TypeFailed(fieldTypeInst); + fieldInstance->mResolvedType = GetPrimitiveType(BfTypeCode_Var); + return false; + } + + if ((fieldTypeInst != NULL) && (fieldTypeInst->mTypeDef->mIsAbstract)) + { + Fail("Cannot create an instance of an abstract class", nameRefNode); + } + + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); + BfMethodState methodState; + SetAndRestoreValue prevMethodState(mCurMethodState, &methodState); + methodState.mTempKind = BfMethodState::TempKind_NonStatic; + + BfTypedValue appendIndexValue; + BfExprEvaluator exprEvaluator(this); + + BfResolvedArgs resolvedArgs; + + auto fieldDecl = fieldDef->GetFieldDeclaration(); + if (auto invocationExpr = BfNodeDynCast(fieldDecl->mInitializer)) + { + resolvedArgs.Init(invocationExpr->mOpenParen, &invocationExpr->mArguments, &invocationExpr->mCommas, invocationExpr->mCloseParen); + exprEvaluator.ResolveArgValues(resolvedArgs, BfResolveArgsFlag_DeferParamEval); + } + + BfFunctionBindResult bindResult; + bindResult.mSkipThis = true; + bindResult.mWantsArgs = true; + SetAndRestoreValue prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult); + + BfTypedValue emptyThis(mBfIRBuilder->GetFakeVal(), mCurTypeInstance, mCurTypeInstance->IsStruct()); + + exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_Comptime; + auto ctorResult = exprEvaluator.MatchConstructor(nameRefNode, NULL, emptyThis, fieldTypeInst, resolvedArgs, false, BfMethodGenericArguments(), BfAllowAppendKind_Infer); + + if ((bindResult.mMethodInstance != NULL) && (bindResult.mMethodInstance->mMethodDef->HasAppend())) + { + auto calcAppendMethodModule = GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND); + + SizedArray irArgs; + if (bindResult.mIRArgs.size() > 1) + irArgs.Insert(0, &bindResult.mIRArgs[1], bindResult.mIRArgs.size() - 1); + BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, irArgs, true); + if (appendSizeTypedValue) + { + int appendAlign = calcAppendMethodModule.mMethodInstance->mAppendAllocAlign; + dataSize = BF_ALIGN(dataSize, appendAlign); + alignSize = BF_MAX(alignSize, appendAlign); + + auto constant = mBfIRBuilder->GetConstant(appendSizeTypedValue.mValue); + if (constant != NULL) + { + dataSize += constant->mInt32; + } + } + else + { + Fail(StrFormat("Append constructor '%s' does not result in a constant size", MethodToString(bindResult.mMethodInstance).c_str()), nameRefNode); + } + } + + return true; +} + void BfModule::AppendedObjectInit(BfFieldInstance* fieldInst) { BfExprEvaluator exprEvaluator(this); @@ -4760,7 +4938,26 @@ void BfModule::AppendedObjectInit(BfFieldInstance* fieldInst) BfIRValue fieldAddr; if (fieldDef->mIsStatic) { - fieldAddr = ReferenceStaticField(fieldInst).mValue; + auto fieldTypedValue = ReferenceStaticField(fieldInst); + + int dataSize = 1; + int alignSize = 1; + TryGetAppendedObjectInfo(fieldInst, dataSize, alignSize); + + String dataName; + BfMangler::MangleStaticFieldName(dataName, mCompiler->GetMangleKind(), mCurTypeInstance, fieldDef->mName, fieldInst->mResolvedType); + dataName += "__DATA"; + + auto arrayType = mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8), dataSize); + auto globalVar = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->GetSizedArrayType(mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8), dataSize), false, + BfIRLinkageType_Internal, mBfIRBuilder->CreateConstArrayZero(dataSize), dataName); + mBfIRBuilder->GlobalVar_SetAlignment(globalVar, alignSize); + + auto castedVal = mBfIRBuilder->CreateBitCast(globalVar, mBfIRBuilder->MapType(fieldInst->mResolvedType)); + mBfIRBuilder->CreateStore(castedVal, fieldTypedValue.mValue); + + fieldTypedValue = LoadValue(fieldTypedValue); + fieldAddr = fieldTypedValue.mValue; } else fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx); @@ -4788,7 +4985,7 @@ void BfModule::AppendedObjectInit(BfFieldInstance* fieldInst) mBfIRBuilder->CreateStore(GetConstValue8(BfObjectFlag_AppendAlloc), thisFlagsPtr); } - exprEvaluator.MatchConstructor(fieldDef->GetNameNode(), NULL, thisValue, fieldInst->mResolvedType->ToTypeInstance(), resolvedArgs, false, BfMethodGenericArguments(), true, &indexVal); + exprEvaluator.MatchConstructor(fieldDef->GetNameNode(), NULL, thisValue, fieldInst->mResolvedType->ToTypeInstance(), resolvedArgs, false, BfMethodGenericArguments(), BfAllowAppendKind_Infer, &indexVal); } void BfModule::CheckInterfaceMethod(BfMethodInstance* methodInstance) @@ -4856,91 +5053,106 @@ void BfModule::CreateDynamicCastMethod() } bool isInterfacePass = mCurMethodInstance->mMethodDef->mName == BF_METHODNAME_DYNAMICCAST_INTERFACE; - + bool isSignaturePass = mCurMethodInstance->mMethodDef->mName == BF_METHODNAME_DYNAMICCAST_SIGNATURE; + auto func = mCurMethodState->mIRFunction; auto thisValue = mBfIRBuilder->GetArgument(0); auto typeIdValue = mBfIRBuilder->GetArgument(1); - auto intPtrType = GetPrimitiveType(BfTypeCode_IntPtr); - auto int32Type = GetPrimitiveType(BfTypeCode_Int32); - typeIdValue = CastToValue(NULL, BfTypedValue(typeIdValue, intPtrType), int32Type, (BfCastFlags)(BfCastFlags_Explicit | BfCastFlags_SilentFail)); - auto thisObject = mBfIRBuilder->CreateBitCast(thisValue, mBfIRBuilder->MapType(objType)); auto trueBB = mBfIRBuilder->CreateBlock("check.true"); //auto falseBB = mBfIRBuilder->CreateBlock("check.false"); auto exitBB = mBfIRBuilder->CreateBlock("exit"); - SizedArray typeMatches; - SizedArray exChecks; - FindSubTypes(mCurTypeInstance, &typeMatches, &exChecks, isInterfacePass); - - if ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType())) - { - // Add 'unbound' type id to cast list so things like "List is List<>" work - auto genericTypeInst = mCurTypeInstance->mTypeDef; - BfTypeVector genericArgs; - for (int i = 0; i < (int) genericTypeInst->mGenericParamDefs.size(); i++) - genericArgs.push_back(GetGenericParamType(BfGenericParamKind_Type, i)); - auto unboundType = ResolveTypeDef(mCurTypeInstance->mTypeDef->GetDefinition(), genericArgs, BfPopulateType_Declaration); - typeMatches.push_back(unboundType->mTypeId); - } - - if (mCurTypeInstance->IsBoxed()) - { - BfBoxedType* boxedType = (BfBoxedType*)mCurTypeInstance; - BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance(); - - FindSubTypes(innerType, &typeMatches, &exChecks, isInterfacePass); - - if (innerType->IsTypedPrimitive()) - { - auto underlyingType = innerType->GetUnderlyingType(); - typeMatches.push_back(underlyingType->mTypeId); - } - - auto innerTypeInst = innerType->ToTypeInstance(); - if ((innerTypeInst->IsInstanceOf(mCompiler->mSizedArrayTypeDef)) || - (innerTypeInst->IsInstanceOf(mCompiler->mPointerTTypeDef)) || - (innerTypeInst->IsInstanceOf(mCompiler->mMethodRefTypeDef))) - { - PopulateType(innerTypeInst); - //TODO: What case was this supposed to handle? - //typeMatches.push_back(innerTypeInst->mFieldInstances[0].mResolvedType->mTypeId); - } - } - - auto curBlock = mBfIRBuilder->GetInsertBlock(); - - BfIRValue vDataPtr; - if (!exChecks.empty()) - { - BfType* intPtrType = GetPrimitiveType(BfTypeCode_IntPtr); - auto ptrPtrType = mBfIRBuilder->GetPointerTo(mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapType(intPtrType))); - auto vDataPtrPtr = mBfIRBuilder->CreateBitCast(thisValue, ptrPtrType); - vDataPtr = FixClassVData(mBfIRBuilder->CreateLoad(vDataPtrPtr/*, "vtable"*/)); - } - - auto switchStatement = mBfIRBuilder->CreateSwitch(typeIdValue, exitBB, (int)typeMatches.size() + (int)exChecks.size()); - for (auto typeMatch : typeMatches) - mBfIRBuilder->AddSwitchCase(switchStatement, GetConstValue32(typeMatch), trueBB); - Array incomingFalses; - for (auto ifaceTypeInst : exChecks) + BfIRBlock curBlock; + + if (isSignaturePass) { - BfIRBlock nextBB = mBfIRBuilder->CreateBlock("exCheck", true); - mBfIRBuilder->AddSwitchCase(switchStatement, GetConstValue32(ifaceTypeInst->mTypeId), nextBB); - mBfIRBuilder->SetInsertPoint(nextBB); + //auto falseBB = mBfIRBuilder->CreateBlock("check.false"); + curBlock = mBfIRBuilder->GetInsertBlock(); - BfIRValue slotOfs = GetInterfaceSlotNum(ifaceTypeInst); + auto signatureId = GetDelegateSignatureId(mCurTypeInstance); + auto eqResult = mBfIRBuilder->CreateCmpEQ(typeIdValue, GetConstValue32(signatureId)); + mBfIRBuilder->CreateCondBr(eqResult, trueBB, exitBB); + } + else + { + auto intPtrType = GetPrimitiveType(BfTypeCode_IntPtr); + auto int32Type = GetPrimitiveType(BfTypeCode_Int32); + typeIdValue = CastToValue(NULL, BfTypedValue(typeIdValue, intPtrType), int32Type, (BfCastFlags)(BfCastFlags_Explicit | BfCastFlags_SilentFail)); - auto ifacePtrPtr = mBfIRBuilder->CreateInBoundsGEP(vDataPtr, slotOfs/*, "iface"*/); - auto ifacePtr = mBfIRBuilder->CreateLoad(ifacePtrPtr); + SizedArray typeMatches; + SizedArray exChecks; + FindSubTypes(mCurTypeInstance, &typeMatches, &exChecks, isInterfacePass); - auto cmpResult = mBfIRBuilder->CreateCmpNE(ifacePtr, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0)); - mBfIRBuilder->CreateCondBr(cmpResult, trueBB, exitBB); + if ((mCurTypeInstance->IsGenericTypeInstance()) && (!mCurTypeInstance->IsUnspecializedType())) + { + // Add 'unbound' type id to cast list so things like "List is List<>" work + auto genericTypeInst = mCurTypeInstance->mTypeDef; + BfTypeVector genericArgs; + for (int i = 0; i < (int)genericTypeInst->mGenericParamDefs.size(); i++) + genericArgs.push_back(GetGenericParamType(BfGenericParamKind_Type, i)); + auto unboundType = ResolveTypeDef(mCurTypeInstance->mTypeDef->GetDefinition(), genericArgs, BfPopulateType_Declaration); + typeMatches.push_back(unboundType->mTypeId); + } - incomingFalses.push_back(nextBB); + if (mCurTypeInstance->IsBoxed()) + { + BfBoxedType* boxedType = (BfBoxedType*)mCurTypeInstance; + BfTypeInstance* innerType = boxedType->mElementType->ToTypeInstance(); + + FindSubTypes(innerType, &typeMatches, &exChecks, isInterfacePass); + + if (innerType->IsTypedPrimitive()) + { + auto underlyingType = innerType->GetUnderlyingType(); + typeMatches.push_back(underlyingType->mTypeId); + } + + auto innerTypeInst = innerType->ToTypeInstance(); + if ((innerTypeInst->IsInstanceOf(mCompiler->mSizedArrayTypeDef)) || + (innerTypeInst->IsInstanceOf(mCompiler->mPointerTTypeDef)) || + (innerTypeInst->IsInstanceOf(mCompiler->mMethodRefTypeDef))) + { + PopulateType(innerTypeInst); + //TODO: What case was this supposed to handle? + //typeMatches.push_back(innerTypeInst->mFieldInstances[0].mResolvedType->mTypeId); + } + } + + curBlock = mBfIRBuilder->GetInsertBlock(); + + BfIRValue vDataPtr; + if (!exChecks.empty()) + { + BfType* intPtrType = GetPrimitiveType(BfTypeCode_IntPtr); + auto ptrPtrType = mBfIRBuilder->GetPointerTo(mBfIRBuilder->GetPointerTo(mBfIRBuilder->MapType(intPtrType))); + auto vDataPtrPtr = mBfIRBuilder->CreateBitCast(thisValue, ptrPtrType); + vDataPtr = FixClassVData(mBfIRBuilder->CreateLoad(vDataPtrPtr/*, "vtable"*/)); + } + + auto switchStatement = mBfIRBuilder->CreateSwitch(typeIdValue, exitBB, (int)typeMatches.size() + (int)exChecks.size()); + for (auto typeMatch : typeMatches) + mBfIRBuilder->AddSwitchCase(switchStatement, GetConstValue32(typeMatch), trueBB); + + for (auto ifaceTypeInst : exChecks) + { + BfIRBlock nextBB = mBfIRBuilder->CreateBlock("exCheck", true); + mBfIRBuilder->AddSwitchCase(switchStatement, GetConstValue32(ifaceTypeInst->mTypeId), nextBB); + mBfIRBuilder->SetInsertPoint(nextBB); + + BfIRValue slotOfs = GetInterfaceSlotNum(ifaceTypeInst); + + auto ifacePtrPtr = mBfIRBuilder->CreateInBoundsGEP(vDataPtr, slotOfs/*, "iface"*/); + auto ifacePtr = mBfIRBuilder->CreateLoad(ifacePtrPtr); + + auto cmpResult = mBfIRBuilder->CreateCmpNE(ifacePtr, mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0)); + mBfIRBuilder->CreateCondBr(cmpResult, trueBB, exitBB); + + incomingFalses.push_back(nextBB); + } } mBfIRBuilder->AddBlock(trueBB); @@ -5505,7 +5717,8 @@ BfIRValue BfModule::CreateClassVDataExtGlobal(BfTypeInstance* declTypeInst, BfTy return mBfIRBuilder->GetFakeVal(); int numElements = declTypeInst->GetSelfVTableSize() - declTypeInst->GetOrigSelfVTableSize(); - BF_ASSERT(numElements >= 0); + if (numElements < 0) + InternalError("CreateClassVDataExtGlobal numElements < 0"); if (numElements <= 0) return BfIRValue(); @@ -5600,7 +5813,7 @@ BfIRValue BfModule::CreateClassVDataExtGlobal(BfTypeInstance* declTypeInst, BfTy return globalVariable; } -BfIRValue BfModule::CreateTypeDataRef(BfType* type) +BfIRValue BfModule::CreateTypeDataRef(BfType* type, bool forceConstant) { if (mBfIRBuilder->mIgnoreWrites) { @@ -5609,6 +5822,9 @@ BfIRValue BfModule::CreateTypeDataRef(BfType* type) if (mIsComptimeModule) { + if (forceConstant) + return mBfIRBuilder->CreateTypeOfComptime(type); + auto typeTypeDef = ResolveTypeDef(mCompiler->mTypeTypeDef); auto typeTypeInst = typeTypeDef->ToTypeInstance(); return mBfIRBuilder->Comptime_GetReflectType(type->mTypeId, mBfIRBuilder->MapType(typeTypeInst)); @@ -5984,29 +6200,13 @@ void BfModule::CreateSlotOfs(BfTypeInstance* typeInstance) GetConstValue32(virtSlotIdx), slotVarName); } -BfIRValue BfModule::CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, bool forceReflectFields, bool needsTypeData, bool needsTypeNames, bool needsVData) +BfIRValue BfModule::GetTypeTypeData(BfType* type, BfCreateTypeDataContext& ctx, bool needsTypeData, bool wantsTypeDecl, bool needsTypeNames, int& typeFlags, int& typeCode) { - if ((IsHotCompile()) && (!type->mDirty)) - return BfIRValue(); - - BfIRValue* irValuePtr = NULL; - if (mTypeDataRefs.TryGetValue(type, &irValuePtr)) - { - return *irValuePtr; - } - BfTypeInstance* typeInstance = type->ToTypeInstance(); BfType* typeInstanceType = ResolveTypeDef(mCompiler->mReflectTypeInstanceTypeDef); mBfIRBuilder->PopulateType(typeInstanceType, BfIRPopulateType_Full_ForceDefinition); - if (typeInstanceType == NULL) - { - AssertErrorState(); - return BfIRValue(); - } - - BfIRValue typeTypeData; - int typeFlags = 0; + BfIRValue typeTypeData; if (needsTypeData) { BfTypeInstance* typeInstanceTypeInstance = typeInstanceType->ToTypeInstance(); @@ -6049,10 +6249,15 @@ BfIRValue BfModule::CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, b else typeDataSource = mContext->mBfTypeType; + if (wantsTypeDecl) + { + typeDataSource = ResolveTypeDef(mCompiler->mTypeTypeDeclDef)->ToTypeInstance(); + } + if ((!mTypeDataRefs.ContainsKey(typeDataSource)) && (typeDataSource != type) && (!mIsComptimeModule)) { CreateTypeData(typeDataSource, ctx, false, true, needsTypeNames, true); - } + } typeTypeData = CreateClassVDataGlobal(typeDataSource); @@ -6064,6 +6269,190 @@ BfIRValue BfModule::CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, b typeTypeData = mBfIRBuilder->CreateConstNull(); } + if (typeInstance != NULL) + { + BF_ASSERT((type->mDefineState >= BfTypeDefineState_DefinedAndMethodsSlotted) || mIsComptimeModule); + typeCode = typeInstance->mTypeDef->mTypeCode; + if (typeInstance->IsBoxed()) + typeCode = BfTypeCode_Object; + } + else if (type->IsPrimitiveType()) + { + BfPrimitiveType* primType = (BfPrimitiveType*)type; + typeCode = primType->mTypeDef->mTypeCode; + } + else if (type->IsPointer()) + { + typeCode = BfTypeCode_Pointer; + typeFlags |= BfTypeFlags_Pointer; + } + else if (type->IsRef()) + { + typeCode = BfTypeCode_Pointer; + typeFlags |= BfTypeFlags_Pointer; + } + + if (type->IsObject()) + { + typeFlags |= BfTypeFlags_Object; + if ((!wantsTypeDecl) && (typeInstance->mDefineState >= BfTypeDefineState_DefinedAndMethodsSlotted)) + { + BfMethodInstance* methodInstance = typeInstance->mVirtualMethodTable[mCompiler->GetVTableMethodOffset() + 0].mImplementingMethod; + if ((methodInstance != NULL) && (methodInstance->GetOwner() != mContext->mBfObjectType)) + typeFlags |= BfTypeFlags_HasDestructor; + } + } + if (type->IsStruct()) + typeFlags |= BfTypeFlags_Struct; + if (type->IsInterface()) + typeFlags |= BfTypeFlags_Interface; + if (type->IsBoxed()) + { + typeFlags |= BfTypeFlags_Boxed; + if (((BfBoxedType*)type)->IsBoxedStructPtr()) + typeFlags |= BfTypeFlags_Pointer; + } + if (type->IsPrimitiveType()) + typeFlags |= BfTypeFlags_Primitive; + if (type->IsTypedPrimitive()) + typeFlags |= BfTypeFlags_TypedPrimitive; + if (type->IsTuple()) + typeFlags |= BfTypeFlags_Tuple; + if (type->IsNullable()) + typeFlags |= BfTypeFlags_Nullable; + if (type->IsSizedArray()) + typeFlags |= BfTypeFlags_SizedArray; + if (type->IsConstExprValue()) + typeFlags |= BfTypeFlags_ConstExpr; + if ((!wantsTypeDecl) && (type->IsSplattable())) + typeFlags |= BfTypeFlags_Splattable; + if (type->IsUnion()) + typeFlags |= BfTypeFlags_Union; + if (type->IsDelegate()) + typeFlags |= BfTypeFlags_Delegate; + if (type->IsFunction()) + typeFlags |= BfTypeFlags_Function; + if ((!wantsTypeDecl) && (type->mDefineState != BfTypeDefineState_CETypeInit) && (type->WantsGCMarking())) + typeFlags |= BfTypeFlags_WantsMarking; + + if ((typeInstance != NULL) && (typeInstance->mTypeDef->mIsStatic)) + typeFlags |= BfTypeFlags_Static; + if ((typeInstance != NULL) && (typeInstance->mTypeDef->mIsAbstract)) + typeFlags |= BfTypeFlags_Abstract; + if ((typeInstance != NULL) && (typeInstance->mHasAppendWantMark)) + typeFlags |= BfTypeFlags_HasAppendWantMark; + + return typeTypeData; +} + +BfIRValue BfModule::CreateTypeDeclData(BfType* type, BfProject* curProject) +{ + auto typeDeclType = ResolveTypeDef(mCompiler->mTypeTypeDeclDef)->ToTypeInstance(); + + int typeCode = 0; + int typeFlags = 0; + + BfCreateTypeDataContext createTypeDataCtx; + BfIRValue typeTypeData = GetTypeTypeData(type, createTypeDataCtx, true, true, true, typeFlags, typeCode); + + SizedArray typeValueParams; + GetConstClassValueParam(typeTypeData, typeValueParams); + + BfType* longType = GetPrimitiveType(BfTypeCode_Int64); + BfType* intType = GetPrimitiveType(BfTypeCode_Int32); + BfType* intPtrType = GetPrimitiveType(BfTypeCode_IntPtr); + BfType* shortType = GetPrimitiveType(BfTypeCode_Int16); + BfType* byteType = GetPrimitiveType(BfTypeCode_Int8); + + BfType* typeIdType = intType; + + auto typeInst = type->ToTypeInstance(); + + auto outerType = GetOuterType(type); + BfType* baseType = NULL; + if (typeInst != NULL) + baseType = typeInst->mBaseType; + + enum BfTypeDeclFlags + { + BfTypeDeclFlag_DeclaredInDependency = 1, + BfTypeDeclFlag_DeclaredInDependent = 2, + BfTypeDeclFlag_DeclaredInCurrent = 4, + BfTypeDeclFlag_AlwaysVisible = 8, + BfTypeDeclFlag_SometimesVisible = 0x10 + }; + int flags = 0; + + if (typeInst != NULL) + { + auto declProject = typeInst->mTypeDef->mProject; + + auto depKind = curProject->GetDependencyKind(declProject); + + if (depKind == BfProject::DependencyKind_Identity) + { + flags |= BfTypeDeclFlag_DeclaredInCurrent | BfTypeDeclFlag_AlwaysVisible | BfTypeDeclFlag_SometimesVisible; + } + else if (depKind == BfProject::DependencyKind_Dependency) + { + flags |= BfTypeDeclFlag_DeclaredInDependency | BfTypeDeclFlag_AlwaysVisible | BfTypeDeclFlag_SometimesVisible; + } + else if (depKind == BfProject::DependencyKind_Dependent_Exclusive) + { + flags |= BfTypeDeclFlag_DeclaredInDependent | BfTypeDeclFlag_AlwaysVisible | BfTypeDeclFlag_SometimesVisible; + } + else if (depKind == BfProject::DependencyKind_Dependent_Shared) + { + flags |= BfTypeDeclFlag_DeclaredInDependent | BfTypeDeclFlag_SometimesVisible; + } + } + + BfIRValue objectData = mBfIRBuilder->CreateConstAgg_Value(mBfIRBuilder->MapTypeInst(mContext->mBfObjectType, BfIRPopulateType_Full), typeValueParams); + SizedArray typeDataParams = + { + objectData, + GetConstValue(type->mTypeId, typeIdType), // mTypeId + GetConstValue((outerType != NULL) ? outerType->mTypeId : 0, typeIdType), // mOuterTypeId + GetConstValue(typeFlags, intType), // mTypeFlags + GetConstValue(flags, byteType), // mFlags + GetConstValue(typeCode, byteType), // mTypeCode + }; + FixConstValueParams(typeDeclType, typeDataParams); + auto typeData = mBfIRBuilder->CreateConstAgg_Value(mBfIRBuilder->MapTypeInst(typeDeclType, BfIRPopulateType_Full), typeDataParams); + + String typeDataName = StrFormat("sBfTypeDeclData.%d", type->mTypeId); + BfIRValue typeDataVar = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapTypeInst(typeDeclType), true, + BfIRLinkageType_External, typeData, typeDataName); + mBfIRBuilder->GlobalVar_SetAlignment(typeDataVar, mSystem->mPtrSize); + + return typeDataVar; +} + +BfIRValue BfModule::CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, bool forceReflectFields, bool needsTypeData, bool needsTypeNames, bool needsVData) +{ + if ((IsHotCompile()) && (!type->mDirty)) + return BfIRValue(); + + BfIRValue* irValuePtr = NULL; + if (mTypeDataRefs.TryGetValue(type, &irValuePtr)) + { + return *irValuePtr; + } + + BfTypeInstance* typeInstance = type->ToTypeInstance(); + BfType* typeInstanceType = ResolveTypeDef(mCompiler->mReflectTypeInstanceTypeDef); + mBfIRBuilder->PopulateType(typeInstanceType, BfIRPopulateType_Full_ForceDefinition); + + if (typeInstanceType == NULL) + { + AssertErrorState(); + return BfIRValue(); + } + + int typeCode = BfTypeCode_None; + int typeFlags = 0; + BfIRValue typeTypeData = GetTypeTypeData(type, ctx, needsTypeData, false, needsTypeNames, typeFlags, typeCode); + BfType* longType = GetPrimitiveType(BfTypeCode_Int64); BfType* intType = GetPrimitiveType(BfTypeCode_Int32); BfType* intPtrType = GetPrimitiveType(BfTypeCode_IntPtr); @@ -6095,80 +6484,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, b typeDataName += "sBfTypeData."; BfMangler::Mangle(typeDataName, mCompiler->GetMangleKind(), type, mContext->mScratchModule); } - - int typeCode = BfTypeCode_None; - - if (typeInstance != NULL) - { - BF_ASSERT((type->mDefineState >= BfTypeDefineState_DefinedAndMethodsSlotted) || mIsComptimeModule); - typeCode = typeInstance->mTypeDef->mTypeCode; - if (typeInstance->IsBoxed()) - typeCode = BfTypeCode_Object; - } - else if (type->IsPrimitiveType()) - { - BfPrimitiveType* primType = (BfPrimitiveType*)type; - typeCode = primType->mTypeDef->mTypeCode; - } - else if (type->IsPointer()) - { - typeCode = BfTypeCode_Pointer; - typeFlags |= BfTypeFlags_Pointer; - } - else if (type->IsRef()) - { - typeCode = BfTypeCode_Pointer; - typeFlags |= BfTypeFlags_Pointer; - } - - if (type->IsObject()) - { - typeFlags |= BfTypeFlags_Object; - if (typeInstance->mDefineState >= BfTypeDefineState_DefinedAndMethodsSlotted) - { - BfMethodInstance* methodInstance = typeInstance->mVirtualMethodTable[mCompiler->GetVTableMethodOffset() + 0].mImplementingMethod; - if ((methodInstance != NULL) && (methodInstance->GetOwner() != mContext->mBfObjectType)) - typeFlags |= BfTypeFlags_HasDestructor; - } - } - if (type->IsStruct()) - typeFlags |= BfTypeFlags_Struct; - if (type->IsInterface()) - typeFlags |= BfTypeFlags_Interface; - if (type->IsBoxed()) - { - typeFlags |= BfTypeFlags_Boxed; - if (((BfBoxedType*)type)->IsBoxedStructPtr()) - typeFlags |= BfTypeFlags_Pointer; - } - if (type->IsPrimitiveType()) - typeFlags |= BfTypeFlags_Primitive; - if (type->IsTypedPrimitive()) - typeFlags |= BfTypeFlags_TypedPrimitive; - if (type->IsTuple()) - typeFlags |= BfTypeFlags_Tuple; - if (type->IsNullable()) - typeFlags |= BfTypeFlags_Nullable; - if (type->IsSizedArray()) - typeFlags |= BfTypeFlags_SizedArray; - if (type->IsConstExprValue()) - typeFlags |= BfTypeFlags_ConstExpr; - if (type->IsSplattable()) - typeFlags |= BfTypeFlags_Splattable; - if (type->IsUnion()) - typeFlags |= BfTypeFlags_Union; - if (type->IsDelegate()) - typeFlags |= BfTypeFlags_Delegate; - if (type->IsFunction()) - typeFlags |= BfTypeFlags_Function; - if ((type->mDefineState != BfTypeDefineState_CETypeInit) && (type->WantsGCMarking())) - typeFlags |= BfTypeFlags_WantsMarking; - - if ((typeInstance != NULL) && (typeInstance->mTypeDef->mIsStatic)) - typeFlags |= BfTypeFlags_Static; - if ((typeInstance != NULL) && (typeInstance->mTypeDef->mIsAbstract)) - typeFlags |= BfTypeFlags_Abstract; - + int virtSlotIdx = -1; if ((typeInstance != NULL) && (typeInstance->mSlotNum >= 0)) virtSlotIdx = typeInstance->mSlotNum + mCompiler->GetVDataPrefixDataCount() + mCompiler->GetDynCastVDataCount(); @@ -6657,8 +6973,11 @@ BfIRValue BfModule::CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, b BfMethodInstance* declaringMethodInstance = (BfMethodInstance*)entry.mDeclaringMethod; if ((declaringMethodInstance != NULL) && (declaringMethodInstance->mMethodInstanceGroup->IsImplemented()) && (declaringMethodInstance->mIsReified)) { - BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].IsImplemented()); - BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].mDefault->mIsReified); + if (entry.mImplementingMethod.mMethodNum >= 0) + { + BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].IsImplemented()); + BF_ASSERT(entry.mImplementingMethod.mTypeInstance->mMethodInstanceGroups[entry.mImplementingMethod.mMethodNum].mDefault->mIsReified); + } BfMethodInstance* methodInstance = (BfMethodInstance*)entry.mImplementingMethod; if ((methodInstance != NULL) && (!methodInstance->mMethodDef->mIsAbstract)) { @@ -7589,7 +7908,7 @@ BfIRValue BfModule::CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, b paramsVal, GetConstValue(defaultMethod->mReturnType->mTypeId, typeIdType), GetConstValue((int)paramVals.size(), shortType), - GetConstValue(methodFlags, shortType), + GetConstValue(methodFlags, intType), GetConstValue(methodIdx, intType), GetConstValue(vDataVal, intType), GetConstValue(customAttrIdx, intType), @@ -7960,6 +8279,9 @@ void BfModule::CheckStaticAccess(BfTypeInstance* typeInstance) BfIRFunction BfModule::GetIntrinsic(BfMethodInstance* methodInstance, bool reportFailure) { + if (!methodInstance->mIsIntrinsic) + return BfIRFunction(); + auto methodOwner = methodInstance->GetOwner(); auto methodDef = methodInstance->mMethodDef; auto methodDeclaration = methodDef->GetMethodDeclaration(); @@ -7970,61 +8292,43 @@ BfIRFunction BfModule::GetIntrinsic(BfMethodInstance* methodInstance, bool repor if (methodInstance->GetCustomAttributes() == NULL) return BfIRFunction(); - for (auto& customAttribute : methodInstance->GetCustomAttributes()->mAttributes) + auto customAttribute = methodInstance->GetCustomAttributes()->Get(mCompiler->mIntrinsicAttributeTypeDef); + auto constant = methodOwner->mConstHolder->GetConstant(customAttribute->mCtorArgs[0]); + String error; + + if ((constant != NULL) && (constant->mTypeCode == BfTypeCode_StringId)) { - String typeName = TypeToString(customAttribute.mType); - if ((typeName == "System.IntrinsicAttribute") && (customAttribute.mCtorArgs.size() > 0)) + int stringId = constant->mInt32; + auto entry = mContext->mStringObjectIdMap[stringId]; + String intrinName = entry.mString; + + int intrinId = BfIRCodeGen::GetIntrinsicId(intrinName); + if (intrinId != -1) { - methodInstance->mIsIntrinsic = true; - - auto constant = methodOwner->mConstHolder->GetConstant(customAttribute.mCtorArgs[0]); - String error; - - if ((constant != NULL) && (constant->mTypeCode == BfTypeCode_StringId)) + if (intrinId == BfIRIntrinsic_Malloc) { - int stringId = constant->mInt32; - auto entry = mContext->mStringObjectIdMap[stringId]; - String intrinName = entry.mString; - -// if (intrinName.StartsWith(":")) -// { -// SizedArray paramTypes; -// for (auto& param : methodInstance->mParams) -// paramTypes.push_back(mBfIRBuilder->MapType(param.mResolvedType)); -// return mBfIRBuilder->GetIntrinsic(intrinName.Substring(1), mBfIRBuilder->MapType(methodInstance->mReturnType), paramTypes); -// } -// else - { - int intrinId = BfIRCodeGen::GetIntrinsicId(intrinName); - if (intrinId != -1) - { - if (intrinId == BfIRIntrinsic_Malloc) - { - return GetBuiltInFunc(BfBuiltInFuncType_Malloc); - } - else if (intrinId == BfIRIntrinsic_Free) - { - return GetBuiltInFunc(BfBuiltInFuncType_Free); - } - - SizedArray paramTypes; - for (auto& param : methodInstance->mParams) - paramTypes.push_back(mBfIRBuilder->MapType(param.mResolvedType)); - return mBfIRBuilder->GetIntrinsic(intrinName, intrinId, mBfIRBuilder->MapType(methodInstance->mReturnType), paramTypes); - } - else if (reportFailure) - error = StrFormat("Unable to find intrinsic '%s'", entry.mString.c_str()); - } + return GetBuiltInFunc(BfBuiltInFuncType_Malloc); } - else if (reportFailure) - error = "Intrinsic name must be a constant string"; - - if (reportFailure) + else if (intrinId == BfIRIntrinsic_Free) { - Fail(error, customAttribute.mRef); + return GetBuiltInFunc(BfBuiltInFuncType_Free); } + + SizedArray paramTypes; + for (auto& param : methodInstance->mParams) + paramTypes.push_back(mBfIRBuilder->MapType(param.mResolvedType)); + return mBfIRBuilder->GetIntrinsic(intrinName, intrinId, mBfIRBuilder->MapType(methodInstance->mReturnType), paramTypes); } + else if (reportFailure) + error = StrFormat("Unable to find intrinsic '%s'", entry.mString.c_str()); } + else if (reportFailure) + error = "Intrinsic name must be a constant string"; + + if (reportFailure) + { + Fail(error, customAttribute->mRef); + } return BfIRFunction(); } @@ -8418,6 +8722,9 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS bool ignoreErrors = (errorOut == NULL) || ((genericParamSource.mMethodInstance == NULL) && (genericParamSource.mTypeInstance == NULL)); + if (checkArgType->IsVar()) + return true; + BfType* origCheckArgType = checkArgType; if (origCheckArgType->IsRef()) @@ -8448,6 +8755,20 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if (checkArgType->IsObjectOrInterface()) argIsReferenceType = true; + if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL)) + { + for (int genericParamIdx = (int)mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); + genericParamIdx < mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++) + { + auto genericParamInst = mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; + + if ((genericParamInst->mExternType == checkArgType) && (checkArgType->IsUnspecializedType())) + { + checkGenericParamFlags |= genericParamInst->mGenericParamFlags; + } + } + } + BfTypeInstance* typeConstraintInst = NULL; if (genericParamInst->mTypeConstraint != NULL) typeConstraintInst = genericParamInst->mTypeConstraint->ToTypeInstance(); @@ -8557,17 +8878,18 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS } } - if (checkArgType->IsPointer()) - { - auto ptrType = (BfPointerType*)checkArgType; - checkArgType = ptrType->mElementType; - } + auto checkArgElementType = checkArgType; + if (checkArgElementType->IsPointer()) + { + auto ptrType = (BfPointerType*)checkArgElementType; + checkArgElementType = ptrType->mElementType; + } if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_New) != 0) { bool canAlloc = false; - if (auto checkTypeInst = checkArgType->ToTypeInstance()) + if (auto checkTypeInst = checkArgElementType->ToTypeInstance()) { if (checkTypeInst->IsObjectOrStruct()) { @@ -8608,11 +8930,11 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS canAlloc = TypeIsSubTypeOf(mCurTypeInstance, checkTypeInst, false); } } - else if (checkArgType->IsGenericParam()) + else if (checkArgElementType->IsGenericParam()) { canAlloc = (checkGenericParamFlags & (BfGenericParamFlag_New | BfGenericParamFlag_Var)) != 0; } - else if (checkArgType->IsPrimitiveType()) + else if (checkArgElementType->IsPrimitiveType()) { // Any primitive types and stuff can be allocated canAlloc = true; @@ -8673,7 +8995,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS } if (doError) - { + { if ((!ignoreErrors) && (PreFail())) *errorOut = Fail(StrFormat("Const generic argument '%s', declared with '%s', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(), _TypeToString(constExprValueType).c_str(), _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); @@ -8959,11 +9281,8 @@ BfTypedValue BfModule::FlushNullConditional(BfTypedValue result, bool ignoreNull { auto pendingNullCond = mCurMethodState->mPendingNullConditional; - if ((result) && (!ignoreNullable)) - { - if (result.mType->IsVar()) - return result; - + if ((result) && (!result.mType->IsVar()) && (!ignoreNullable)) + { auto notNullBB = mBfIRBuilder->GetInsertBlock(); //TODO: Make this work, needed for 'void' and such @@ -9240,6 +9559,9 @@ BfTypedValue BfModule::CreateValueFromExpression(BfExprEvaluator& exprEvaluator, if ((typedVal.mType->IsValueType()) && ((flags & BfEvalExprFlags_NoValueAddr) != 0)) typedVal = LoadValue(typedVal, 0, exprEvaluator.mIsVolatileReference); + if (auto autoComplete = mCompiler->GetAutoComplete()) + autoComplete->CheckResult(expr, typedVal); + return typedVal; } @@ -9267,7 +9589,7 @@ BfTypedValue BfModule::GetOrCreateVarAddr(BfExpression* expr) } // Clear memory, set classVData, call init. Actual ctor is called elsewhere. -void BfModule::InitTypeInst(BfTypedValue typedValue, BfScopeData* scopeData, bool zeroMemory, BfIRValue sizeValue) +void BfModule::InitTypeInst(BfTypedValue typedValue, BfScopeData* scopeData, bool zeroMemory, BfIRValue sizeValue, BfAllocFlags allocFlags) { auto typeInstance = typedValue.mType->ToTypeInstance(); if (zeroMemory) @@ -9314,6 +9636,8 @@ void BfModule::InitTypeInst(BfTypedValue typedValue, BfScopeData* scopeData, boo SizedArray llvmArgs; llvmArgs.push_back(objectPtr); llvmArgs.push_back(vDataRef); + llvmArgs.push_back(sizeValue); + llvmArgs.push_back(mBfIRBuilder->CreateConst(BfTypeCode_Int8, allocFlags)); auto objectStackInitMethod = GetInternalMethod("Dbg_ObjectStackInit"); if (objectStackInitMethod) @@ -9946,7 +10270,7 @@ BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget doCondAlloca = !wasDynAlloc && isDynAlloc && mCurMethodState->mInConditionalBlock; AddStackAlloc(typedVal, arraySize, NULL, scopeData, doCondAlloca, true); } - InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue); + InitTypeInst(typedVal, scopeData, zeroMemory, sizeValue, allocFlags); return typedVal.mValue; } @@ -10176,7 +10500,8 @@ BfIRValue BfModule::AllocFromType(BfType* type, const BfAllocTarget& allocTarget llvmArgs.push_back(sizeValue); llvmArgs.push_back(GetConstValue(typeInstance->mAlign)); llvmArgs.push_back(GetConstValue(stackCount)); - auto moduleMethodInstance = GetInternalMethod("Dbg_ObjectAlloc", 4); + llvmArgs.push_back(GetConstValue(allocFlags, GetPrimitiveType(BfTypeCode_Int8))); + auto moduleMethodInstance = GetInternalMethod("Dbg_ObjectAlloc", 5); BfIRValue objectVal = mBfIRBuilder->CreateCall(moduleMethodInstance.mFunc, llvmArgs); result = mBfIRBuilder->CreateBitCast(objectVal, mBfIRBuilder->MapType(typeInstance)); } @@ -10277,7 +10602,7 @@ void BfModule::EmitAppendAlign(int align, int sizeMultiple) else if (mCurMethodInstance->mMethodDef->mMethodType == BfMethodType_Ctor) { auto localVar = mCurMethodState->GetRootMethodState()->mLocals[1]; - BF_ASSERT(localVar->mName == "appendIdx"); + BF_ASSERT(localVar->mName == "__appendIdx"); auto appendIdxVal = BfTypedValue(localVar->mValue, localVar->mResolvedType, true); BfIRValue appendCurIdx = mBfIRBuilder->CreateLoad(appendIdxVal.mValue); if (align > 1) @@ -10312,7 +10637,7 @@ void BfModule::EmitAppendAlign(int align, int sizeMultiple) BfIRValue BfModule::AppendAllocFromType(BfType* type, BfIRValue appendSizeValue, int appendAllocAlign, BfIRValue arraySize, int arrayDim, bool isRawArrayAlloc, bool zeroMemory) { auto localVar = mCurMethodState->GetRootMethodState()->mLocals[1]; - BF_ASSERT(localVar->mName == "appendIdx"); + BF_ASSERT(localVar->mName == "__appendIdx"); BfTypedValue appendIdxVal(localVar->mValue, localVar->mResolvedType, true); BfIRValue retValue; @@ -10380,6 +10705,9 @@ BfIRValue BfModule::AppendAllocFromType(BfType* type, BfIRValue appendSizeValue, BfIRType toType; if (typeInst != NULL) { + mBfIRBuilder->PopulateType(typeInst); + if (typeInst->WantsGCMarking()) + mCurTypeInstance->mHasAppendWantMark = true; EmitAppendAlign(typeInst->mInstAlign, typeInst->mInstSize); sizeValue = GetConstValue(typeInst->mInstSize); toType = mBfIRBuilder->MapTypeInstPtr(typeInst); @@ -10398,6 +10726,35 @@ BfIRValue BfModule::AppendAllocFromType(BfType* type, BfIRValue appendSizeValue, retTypeInstance = typeInst; retValue = mBfIRBuilder->CreateIntToPtr(curIdxVal, toType); + + if ((typeInst != NULL) && (typeInst->WantsGCMarking()) && (mCompiler->mOptions.mDebugAlloc)) + { + auto curThis = GetThis(); + auto thisObj = Cast(mCurMethodInstance->mMethodDef->GetRefNode(), curThis, mContext->mBfObjectType); + + if (typeInst->IsObject()) + { + auto appendedObj = Cast(mCurMethodInstance->mMethodDef->GetRefNode(), BfTypedValue(retValue, typeInst), mContext->mBfObjectType); + BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_ObjectAppended", 2); + SizedArray llvmArgs; + llvmArgs.push_back(thisObj.mValue); + llvmArgs.push_back(appendedObj.mValue); + mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs); + } + else + { + // Raw + BfIRValue allocPtr = mBfIRBuilder->CreateBitCast(retValue, mBfIRBuilder->MapType(voidPtrType)); + BfIRValue allocData = GetDbgRawAllocData(type); + BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_RawAppended", 3); + + SizedArray llvmArgs; + llvmArgs.push_back(thisObj.mValue); + llvmArgs.push_back(allocPtr); + llvmArgs.push_back(allocData); + mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs); + } + } } if ((retTypeInstance != NULL) && (retTypeInstance->IsObject())) @@ -10605,8 +10962,25 @@ void BfModule::EmitDynamicCastCheck(const BfTypedValue& targetValue, BfType* tar auto typeTypeInstance = ResolveTypeDef(mCompiler->mReflectTypeInstanceTypeDef)->ToTypeInstance(); - if (mCompiler->mOptions.mAllowHotSwapping) + if (targetType->IsDelegate()) { + // Delegate signature check + int signatureId = GetDelegateSignatureId(targetType->ToTypeInstance()); + BfExprEvaluator exprEvaluator(this); + + AddBasicBlock(checkBB); + auto objectParam = mBfIRBuilder->CreateBitCast(targetValue.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType)); + auto moduleMethodInstance = GetMethodByName(mContext->mBfObjectType, "DynamicCastToSignature"); + SizedArray irArgs; + irArgs.push_back(objectParam); + irArgs.push_back(GetConstValue32(signatureId)); + auto callResult = exprEvaluator.CreateCall(NULL, moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, false, irArgs); + auto cmpResult = mBfIRBuilder->CreateCmpNE(callResult.mValue, GetDefaultValue(callResult.mType)); + irb->CreateCondBr(cmpResult, trueBlock, falseBlock); + } + else if (mCompiler->mOptions.mAllowHotSwapping) + { + // "Slow" check BfExprEvaluator exprEvaluator(this); AddBasicBlock(checkBB); @@ -10625,7 +10999,7 @@ void BfModule::EmitDynamicCastCheck(const BfTypedValue& targetValue, BfType* tar BfIRValue vDataPtr = irb->CreateBitCast(targetValue.mValue, irb->MapType(intPtrType)); vDataPtr = irb->CreateLoad(vDataPtr); if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule)) - vDataPtr = irb->CreateAnd(vDataPtr, irb->CreateConst(BfTypeCode_IntPtr, (uint64)~0xFFULL)); + vDataPtr = irb->CreateAnd(vDataPtr, irb->CreateConst(BfTypeCode_IntPtr, (uint64)~0xFFULL)); if (targetType->IsInterface()) { @@ -10939,6 +11313,13 @@ bool BfModule::GetBasePropertyDef(BfPropertyDef*& propDef, BfTypeInstance*& type BfMethodInstance* BfModule::GetRawMethodInstanceAtIdx(BfTypeInstance* typeInstance, int methodIdx, const char* assertName) { + if (typeInstance->IsDeleting()) + { + mCompiler->RequestExtraCompile(); + InternalError("GetRawMethodInstanceAtIdx for deleted type", typeInstance->mTypeDef->GetRefNode()); + return NULL; + } + if (!typeInstance->mResolvingVarField) { if (!typeInstance->DefineStateAllowsStaticMethods()) @@ -10962,7 +11343,7 @@ BfMethodInstance* BfModule::GetRawMethodInstanceAtIdx(BfTypeInstance* typeInstan { if (mCompiler->EnsureCeUnpaused(typeInstance)) { - BF_FATAL("OOB in GetRawMethodInstanceAtIdx"); + InternalError("OOB in GetRawMethodInstanceAtIdx"); } return NULL; } @@ -11151,6 +11532,9 @@ BfIRValue BfModule::CreateFunctionFrom(BfMethodInstance* methodInstance, bool tr return BfIRValue(); } + if ((mAwaitingInitFinish) && (!mBfIRBuilder->mIgnoreWrites)) + FinishInit(); + auto methodDef = methodInstance->mMethodDef; StringT<4096> methodName; BfMangler::Mangle(methodName, mCompiler->GetMangleKind(), methodInstance); @@ -11935,6 +12319,8 @@ bool BfModule::HasUnactializedConstant(BfConstant* constant, BfIRConstHolder* co return true; if (constant->mTypeCode == BfTypeCode_StringId) return true; + if (constant->mTypeCode == BfTypeCode_CharPtr) + return true; if (constant->mConstType == BfConstType_Agg) { @@ -11946,6 +12332,46 @@ bool BfModule::HasUnactializedConstant(BfConstant* constant, BfIRConstHolder* co } } + if (constant->mConstType == BfConstType_PtrToInt) + { + auto fromPtrToInt = (BfConstantPtrToInt*)constant; + auto fromTarget = constHolder->GetConstantById(fromPtrToInt->mTarget); + return HasUnactializedConstant(fromTarget, constHolder); + } + + if (constant->mConstType == BfConstType_BitCast) + { + auto bitcast = (BfConstantBitCast*)constant; + auto fromTarget = constHolder->GetConstantById(bitcast->mTarget); + return HasUnactializedConstant(fromTarget, constHolder); + } + + return false; +} + +bool BfModule::HasGlobalVarReference(BfConstant* constant, BfIRConstHolder* constHolder) +{ + if ((constant->mConstType == BfConstType_TypeOf) || (constant->mConstType == BfConstType_TypeOf_WithData)) + return true; + if (constant->mTypeCode == BfTypeCode_StringId) + return true; + if (constant->mConstType == BfConstType_GlobalVar) + return true; + + // NullPtr is stand-in for GlobalVar during autocomplete + if (constant->mTypeCode == BfTypeCode_NullPtr) + return true; + + if (constant->mConstType == BfConstType_Agg) + { + auto constArray = (BfConstantAgg*)constant; + for (auto val : constArray->mValues) + { + if (HasGlobalVarReference(constHolder->GetConstant(val), constHolder)) + return true; + } + } + return false; } @@ -11962,7 +12388,28 @@ BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* con return GetDefaultValue(wantType); } - if (constant->mTypeCode == BfTypeCode_StringId) + if ((constant->mTypeCode == BfTypeCode_StringId) || (constant->mTypeCode == BfTypeCode_CharPtr)) + { + if (!allowUnactualized) + { + if ((wantType == NULL) || + (wantType->IsInstanceOf(mCompiler->mStringTypeDef)) || + ((wantType->IsPointer()) && (wantType->GetUnderlyingType() == GetPrimitiveType(BfTypeCode_Char8)))) + { + const StringImpl& str = mContext->mStringObjectIdMap[constant->mInt32].mString; + BfIRValue stringObjConst = GetStringObjectValue(str, false, true); + + bool wantCharPtr = ((wantType != NULL) && (wantType->IsPointer())); + if ((wantType == NULL) && (constant->mTypeCode == BfTypeCode_CharPtr)) + wantCharPtr = true; + if (wantCharPtr) + return GetStringCharPtr(stringObjConst, true); + return stringObjConst; + } + } + } + + if (constant->mTypeCode == BfTypeCode_CharPtr) { if (!allowUnactualized) { @@ -11984,7 +12431,7 @@ BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* con auto constTypeOf = (BfTypeOf_Const*)constant; if (mCurTypeInstance != NULL) AddDependency(constTypeOf->mType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); - return CreateTypeDataRef(constTypeOf->mType); + return CreateTypeDataRef(constTypeOf->mType, true); } if (constant->mConstType == BfConstType_PtrToInt) @@ -12130,7 +12577,7 @@ BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* con return mBfIRBuilder->CreateConst(constant, constHolder); } -void BfModule::ValidateCustomAttributes(BfCustomAttributes* customAttributes, BfAttributeTargets attrTarget) +void BfModule::ValidateCustomAttributes(BfCustomAttributes* customAttributes, BfAttributeTargets attrTarget, bool force) { if (attrTarget == BfAttributeTargets_SkipValidate) return; @@ -12142,6 +12589,9 @@ void BfModule::ValidateCustomAttributes(BfCustomAttributes* customAttributes, Bf if ((customAttribute.mType->mAttributeData->mAttributeTargets & attrTarget) == 0) { + if ((customAttribute.mIsMultiUse) && (!force)) + continue; + Fail(StrFormat("Attribute '%s' is not valid on this declaration type. It is only valid on %s.", customAttribute.GetRefNode()->ToString().c_str(), GetAttributesTargetListString(customAttribute.mType->mAttributeData->mAttributeTargets).c_str()), customAttribute.mRef->mAttributeTypeRef); // CS0592 } @@ -12215,6 +12665,7 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri customAttribute.mAwaitingValidation = true; customAttribute.mDeclaringType = activeTypeDef; customAttribute.mRef = attributesDirective; + customAttribute.mIsMultiUse = attributesDirective->mIsMultiUse; if (attributesDirective->mAttrOpenToken != NULL) targetOverride = (BfAttributeTargets)0; @@ -12543,6 +12994,7 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri bool success = true; + int methodCheckCount = 0; bool isFailurePass = false; for (int pass = 0; pass < 2; pass++) { @@ -12554,11 +13006,12 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri continue; // Don't match private default ctor if there's a user-defined one if ((checkMethod->mIsStatic) || (checkMethod->mMethodType != BfMethodType_Ctor)) - continue; + continue; if ((!isFailurePass) && (!CheckProtection(checkMethod->mProtection, attrTypeInst->mTypeDef, false, false))) continue; + methodCheckCount++; methodMatcher.CheckMethod(NULL, attrTypeInst, checkMethod, isFailurePass); } @@ -12582,6 +13035,8 @@ void BfModule::GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttri if (methodMatcher.mBestMethodDef == NULL) { + if (methodCheckCount == 0) + Fail(StrFormat("No attribute constructors found for type '%s'", TypeToString(attrTypeInst).c_str()), attributesDirective); AssertErrorState(); continue; } @@ -12988,8 +13443,14 @@ BfVariant BfModule::TypedValueToVariant(BfAstNode* refNode, const BfTypedValue& } } } - else + else if (value.mType->IsConstExprValue()) { + auto constExprType = (BfConstExprValueType*)value.mType; + return constExprType->mValue; + } + else if (value.mType->IsValueType()) + { + PopulateType(value.mType); int allocSize = value.mType->mSize + 4; BfVariant::StructData* structData = (BfVariant::StructData*)(new uint8[allocSize]); memset(structData, 0, allocSize); @@ -13066,7 +13527,7 @@ BfTypedValue BfModule::ToRef(BfTypedValue typedValue, BfRefType* refType) if (refType->mRefKind == BfRefType::RefKind_Mut) refType = CreateRefType(typedValue.mType); - if (!typedValue.mType->IsValuelessType()) + if (!typedValue.mType->IsValuelessNonOpaqueType()) typedValue = MakeAddressable(typedValue, false, true); return BfTypedValue(typedValue.mValue, refType); } @@ -13182,6 +13643,16 @@ BfTypedValue BfModule::LoadOrAggregateValue(BfTypedValue typedValue) return AggregateSplat(typedValue); if (typedValue.IsAddr()) return LoadValue(typedValue); + + if ((typedValue.mType != NULL) && (typedValue.mType->IsParamsType()) && (!typedValue.IsParams())) + { + return GetDefaultTypedValue(ResolveTypeDef(mCompiler->mTupleTypeDef)); + } + else if ((typedValue.IsParams()) && (typedValue.mType->IsGenericParam())) + { + return BfTypedValue(mBfIRBuilder->GetFakeVal(), typedValue.mType); + } + return typedValue; } @@ -13375,11 +13846,14 @@ void BfModule::AggregateSplatIntoAddr(BfTypedValue typedValue, BfIRValue addrVal BfTypedValue BfModule::MakeAddressable(BfTypedValue typedVal, bool forceMutable, bool forceAddressable) { + if (!typedVal) + return typedVal; + bool wasReadOnly = typedVal.IsReadOnly(); if ((forceAddressable) || ((typedVal.mType->IsValueType()) && - (!typedVal.mType->IsValuelessType()))) + (!typedVal.mType->IsValuelessNonOpaqueType()))) { wasReadOnly = true; // Any non-addr is implicitly read-only @@ -13756,6 +14230,8 @@ bool BfModule::CompareMethodSignatures(BfMethodInstance* methodA, BfMethodInstan } else if (methodA->mMethodDef->mName != methodB->mMethodDef->mName) return false; + if (methodA->mMethodDef->mAppendKind != methodB->mMethodDef->mAppendKind) + return false; if (methodA->mMethodDef->mCheckedKind != methodB->mMethodDef->mCheckedKind) return false; if (methodA->mMethodDef->mHasComptime != methodB->mMethodDef->mHasComptime) @@ -14471,6 +14947,15 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM } else { + if (methodDef->mIdx >= typeInst->mMethodInstanceGroups.mSize) + PopulateType(typeInst, BfPopulateType_DataAndMethods); + + if (methodDef->mIdx >= typeInst->mMethodInstanceGroups.mSize) + { + InternalError("GetMethodInstance OOB error", methodDef->GetRefNode()); + return BfModuleMethodInstance(); + } + methodInstGroup = &typeInst->mMethodInstanceGroups[methodDef->mIdx]; } @@ -14665,6 +15150,23 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM } } + auto _CheckDllImport = [&]() + { + auto declModule = methodInstance->mDeclModule; + if ((!methodInstance->mIRFunction) && (declModule->mIsModuleMutable) && (!declModule->mBfIRBuilder->mIgnoreWrites)) + { + auto importKind = methodInstance->GetImportCallKind(); + if (importKind != BfImportCallKind_None) + { + BfLogSysM("DllImportGlobalVar creating %p in module %p from module %p\n", methodInstance, declModule, this); + methodInstance->mIRFunction = mBfIRBuilder->GetFakeVal(); + auto func = declModule->CreateDllImportGlobalVar(methodInstance, true); + BF_ASSERT(func); + declModule->mFuncReferences[methodInstance] = func; + } + } + }; + if ((methodInstance != NULL) && (!doingRedeclare)) { SetMethodDependency(methodInstance); @@ -14767,25 +15269,14 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM if (methodInstance->mDeclModule != this) return ReferenceExternalMethodInstance(methodInstance, flags); - if ((!methodInstance->mIRFunction) && (mIsModuleMutable) && (!mBfIRBuilder->mIgnoreWrites)) - { - auto importKind = methodInstance->GetImportCallKind(); - if (importKind != BfImportCallKind_None) - { - BfLogSysM("DllImportGlobalVar creating %p\n", methodInstance); - methodInstance->mIRFunction = mBfIRBuilder->GetFakeVal(); - auto func = CreateDllImportGlobalVar(methodInstance, true); - BF_ASSERT(func); - mFuncReferences[methodInstance] = func; - } - } + _CheckDllImport(); return BfModuleMethodInstance(methodInstance); } } if (!doingRedeclare) - { + { BfModule* specModule = NULL; if ((!keepInCurrentModule) && (projectList.size() > 0) && (!isUnspecializedPass) && (HasCompiledOutput()) && (!mIsScratchModule)) { @@ -14801,7 +15292,7 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM return specMethodInstance.mMethodInstance; return ReferenceExternalMethodInstance(specMethodInstance.mMethodInstance, flags); } - + if ((!isUnspecializedPass) && (methodGenericArguments.size() != 0) && (methodInstGroup->mDefault == NULL)) { // We are attempting to specialize but we don't have the unspecialized method yet. Generate that first. @@ -14888,6 +15379,11 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM methodInstance->mMangleWithIdx = true; } + if (methodDef->mAppendKind > BfAllowAppendKind_No) + { + methodInstance->mMangleWithIdx = true; + } + BF_ASSERT(typeInst == methodInstance->GetOwner()); auto methodDeclaration = methodDef->GetMethodDeclaration(); @@ -14980,7 +15476,7 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM } SetMethodDependency(methodInstance); - + SetAndRestoreValue prevMethodInstance(declareModule->mCurMethodInstance, methodInstance); SetAndRestoreValue prevTypeInstance(declareModule->mCurTypeInstance, typeInst); SetAndRestoreValue prevFilePos(declareModule->mCurFilePosition); @@ -15013,6 +15509,11 @@ BfModuleMethodInstance BfModule::GetMethodInstance(BfTypeInstance* typeInst, BfM if (methodInstance->mDeclModule != this) return ReferenceExternalMethodInstance(methodInstance, flags); + if (doingRedeclare) + { + _CheckDllImport(); + } + return BfModuleMethodInstance(methodInstance); } @@ -15291,7 +15792,7 @@ BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance) { // Just fake it for the extern and unspecialized modules // We can't do this for compilation because unreified methods with default params need to get actual global variable refs - return BfTypedValue(mBfIRBuilder->CreateConstNull(), fieldInstance->GetResolvedType(), true); + return BfTypedValue(mBfIRBuilder->CreateUndefValue(mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr)), fieldInstance->GetResolvedType(), true); } BfIRValue* globalValuePtr = NULL; @@ -15327,10 +15828,7 @@ BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance) if ((typeType != NULL) && (!typeType->IsValuelessType())) { BfIRType irType = mBfIRBuilder->MapType(typeType); - - if (fieldInstance->IsAppendedObject()) - irType = mBfIRBuilder->MapTypeInst(typeType->ToTypeInstance()); - + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, mBfIRBuilder->mIgnoreWrites || staticVarName.StartsWith('#')); globalValue = mBfIRBuilder->CreateGlobalVariable( @@ -15354,7 +15852,7 @@ BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance) if (fieldDef->mIsVolatile) return BfTypedValue(globalValue, type, BfTypedValueKind_VolatileAddr); - return BfTypedValue(globalValue, type, !fieldDef->mIsConst && !fieldInstance->IsAppendedObject()); + return BfTypedValue(globalValue, type, !fieldDef->mIsConst); } BfFieldInstance* BfModule::GetFieldInstance(BfTypeInstance* typeInst, int fieldIdx, const char* fieldName) @@ -15560,7 +16058,7 @@ BfTypedValue BfModule::GetThis(bool markUsing) auto curMethodOwner = mCurMethodInstance->mMethodInstanceGroup->mOwner; if ((curMethodOwner->IsStruct()) || (curMethodOwner->IsTypedPrimitive())) { - if ((localDef->mResolvedType->IsTypedPrimitive()) && (!mCurMethodInstance->mMethodDef->mIsMutating)) + if ((localDef->mResolvedType->IsTypedPrimitive()) && (!mCurMethodInstance->mMethodDef->mIsMutating) && (!mCurMethodInstance->ForcingThisPtr())) { return BfTypedValue(thisValue, useMethodState->mLocals[0]->mResolvedType, BfTypedValueKind_ReadOnlyThisValue); } @@ -15653,8 +16151,102 @@ BfIRValue BfModule::AllocLocalVariable(BfType* type, const StringImpl& name, boo return allocaInst; } +BfTypedValue BfModule::CreateOutVariable(BfAstNode* refNode, BfVariableDeclaration* variableDeclaration, BfAstNode* paramNameNode, BfType* variableType, BfTypedValue initValue) +{ + bool isRef = false; + bool isLet = (variableDeclaration != NULL) && (variableDeclaration->mTypeRef->IsExact()); + bool isVar = (variableDeclaration == NULL) || (variableDeclaration->mTypeRef->IsExact()); + + if (variableDeclaration != NULL) + { + if (auto varRefTypeReference = BfNodeDynCast(variableDeclaration->mTypeRef)) + { + isRef = true; + isLet = varRefTypeReference->mVarToken->GetToken() == BfToken_Let; + isVar = varRefTypeReference->mVarToken->GetToken() == BfToken_Var; + } + } + + BfLocalVariable* localVar = new BfLocalVariable(); + if ((variableDeclaration != NULL) && (variableDeclaration->mNameNode != NULL)) + { + localVar->mName = variableDeclaration->mNameNode->ToString(); + localVar->mNameNode = BfNodeDynCast(variableDeclaration->mNameNode); + } + else + { + if (paramNameNode != NULL) + { + localVar->mName = "__"; + paramNameNode->ToString(localVar->mName); + localVar->mName += "_"; + localVar->mName += StrFormat("%d", mCurMethodState->GetRootMethodState()->mCurLocalVarId); + } + else + localVar->mName = "__" + StrFormat("%d", mCurMethodState->GetRootMethodState()->mCurLocalVarId); + } + + localVar->mResolvedType = variableType; + if (isRef) + { + localVar->mResolvedType = CreateRefType(localVar->mResolvedType); + } + + PopulateType(variableType); + if (!variableType->IsValuelessType()) + localVar->mAddr = CreateAlloca(localVar->mResolvedType); + localVar->mIsReadOnly = isLet; + localVar->mReadFromId = 0; + localVar->mWrittenToId = 0; + localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional; + CheckVariableDef(localVar); + localVar->Init(); + AddLocalVariableDef(localVar, true); + + BfExprEvaluator exprEvaluator(this); + exprEvaluator.CheckVariableDeclaration(refNode, false, false, false); + + auto argValue = BfTypedValue(localVar->mAddr, CreateRefType(variableType, BfRefType::RefKind_Out)); + + if (isRef) + { + initValue = MakeAddressable(initValue, true, true); + if ((initValue) && (!initValue.mType->IsValuelessType())) + mBfIRBuilder->CreateStore(initValue.mValue, localVar->mAddr); + } + else + { + initValue = LoadOrAggregateValue(initValue); + if ((initValue) && (!initValue.mType->IsValuelessType())) + mBfIRBuilder->CreateStore(initValue.mValue, localVar->mAddr); + } + + auto curScope = mCurMethodState->mCurScope; + if (curScope->mScopeKind == BfScopeKind_StatementTarget) + MoveLocalToParentScope(localVar); + + return argValue; +} + +void BfModule::MoveLocalToParentScope(BfLocalVariable* localVar) +{ + auto curScope = mCurMethodState->mCurScope; + if (mCurMethodState->mLocals.back() != localVar) + return; + + curScope->mLocalVarStart = (int)mCurMethodState->mLocals.size(); + if ((!curScope->mDeferredLifetimeEnds.IsEmpty()) && (curScope->mDeferredLifetimeEnds.back() == localVar->mAddr)) + { + curScope->mDeferredLifetimeEnds.pop_back(); + if (curScope->mPrevScope != NULL) + curScope->mPrevScope->mDeferredLifetimeEnds.push_back(localVar->mAddr); + } +} + void BfModule::DoAddLocalVariable(BfLocalVariable* localVar) { + BF_ASSERT(localVar->mResolvedType != NULL); + while (localVar->mName.StartsWith('@')) { localVar->mNamePrefixCount++; @@ -15928,22 +16520,30 @@ void BfModule::CheckVariableDef(BfLocalVariable* variableDef) auto checkLocal = localVarEntryPtr->mLocalVar; if ((checkLocal->mLocalVarIdx >= mCurMethodState->GetLocalStartIdx()) && (!checkLocal->mIsShadow)) { - BfError* error; + auto _Fail = [&](int warningNum, String str, BfAstNode* refNode) + { + BfError* error = Warn(warningNum, str, refNode); + if ((checkLocal->mNameNode != NULL) && (error != NULL)) + mCompiler->mPassInstance->MoreInfo("Previous declaration", checkLocal->mNameNode); + }; + + auto checkScope = mCurMethodState->mCurScope; + if (checkScope->mScopeKind == BfScopeKind_StatementTarget) + checkScope = checkScope->mPrevScope; + if (checkLocal->mIsImplicitParam) return; // Ignore 'redefinition' if (checkLocal->IsParam()) { if (variableDef->IsParam()) - error = Fail(StrFormat("A parameter named '%s' has already been declared", variableDef->mName.c_str()), variableDef->mNameNode); + _Fail(4200, StrFormat("A parameter named '%s' has already been declared", variableDef->mName.c_str()), variableDef->mNameNode); else - error = Fail(StrFormat("The name '%s' is already used by a parameter. Consider declaring 'var %s;' if you wish to make a mutable copy of that parameter.", variableDef->mName.c_str(), variableDef->mName.c_str()), variableDef->mNameNode); + _Fail(4200, StrFormat("The name '%s' is already used by a parameter. Consider declaring 'var %s;' if you wish to make a mutable copy of that parameter.", variableDef->mName.c_str(), variableDef->mName.c_str()), variableDef->mNameNode); } - else if (checkLocal->mLocalVarIdx < mCurMethodState->mCurScope->mLocalVarStart) - error = Fail(StrFormat("A variable named '%s' has already been declared in this parent's scope", variableDef->mName.c_str()), variableDef->mNameNode); + else if (checkLocal->mLocalVarIdx < checkScope->mLocalVarStart) + _Fail(4200, StrFormat("A variable named '%s' has already been declared in an outer scope", variableDef->mName.c_str()), variableDef->mNameNode); else - error = Fail(StrFormat("A variable named '%s' has already been declared in this scope", variableDef->mName.c_str()), variableDef->mNameNode); - if ((checkLocal->mNameNode != NULL) && (error != NULL)) - mCompiler->mPassInstance->MoreInfo("Previous declaration", checkLocal->mNameNode); + _Fail(4200, StrFormat("A variable named '%s' has already been declared in this scope", variableDef->mName.c_str()), variableDef->mNameNode); return; } } @@ -16007,6 +16607,7 @@ BfScopeData* BfModule::FindScope(BfAstNode* scopeName, BfMixinState* fromMixinSt if (!inMixinDecl) Fail(StrFormat("Unable to locate label '%s'", findLabel.c_str()), scopeName); + return NULL; } if (auto scopeNode = BfNodeDynCast(scopeName)) @@ -16700,7 +17301,7 @@ void BfModule::AssertErrorState() if (mCompiler->mPassInstance->HasFailed()) return; - InternalError("Compiler in invalid state but AssertErrorState failed to prior error"); + InternalError("Compiler in invalid state but AssertErrorState failed to detect prior error"); } void BfModule::AssertParseErrorState() @@ -16722,9 +17323,61 @@ BfType* BfModule::GetDelegateReturnType(BfType* delegateType) BfMethodInstance* BfModule::GetDelegateInvokeMethod(BfTypeInstance* typeInstance) { + if (typeInstance->IsClosure()) + typeInstance = typeInstance->mBaseType; return GetRawMethodInstanceAtIdx(typeInstance, 0, "Invoke"); } +String BfModule::GetDelegateSignatureString(BfTypeInstance* typeInstance) +{ + auto invokeMethod = GetDelegateInvokeMethod(typeInstance); + if (invokeMethod == NULL) + return ""; + + String sigString = ""; + sigString = TypeToString(invokeMethod->mReturnType); + sigString += "("; + for (int paramIdx = 0; paramIdx < invokeMethod->GetParamCount(); paramIdx++) + { + if (paramIdx > 0) + sigString += ", "; + + auto paramKind = invokeMethod->GetParamKind(paramIdx); + + if (paramKind == BfParamKind_Params) + { + sigString += "params "; + } + + auto paramType = invokeMethod->GetParamType(paramIdx); + sigString += TypeToString(paramType); + + if (paramKind == BfParamKind_ExplicitThis) + sigString += " this"; + } + sigString += ")"; + return sigString; +} + +int BfModule::GetSignatureId(const StringImpl& str) +{ + int strId = mContext->GetStringLiteralId(str); + mSignatureIdRefs.Add(strId); + return strId; +} + +int BfModule::GetDelegateSignatureId(BfTypeInstance* typeInstance) +{ + BF_ASSERT(typeInstance->IsDelegate()); + if (typeInstance->mTypeInfoEx == NULL) + { + typeInstance->mTypeInfoEx = new BfTypeInfoEx(); + auto signature = GetDelegateSignatureString(typeInstance); + typeInstance->mTypeInfoEx->mMinValue = GetSignatureId(signature); + } + return (int)typeInstance->mTypeInfoEx->mMinValue; +} + void BfModule::CreateDelegateInvokeMethod() { // Clear out debug loc - otherwise we'll single step onto the delegate type declaration @@ -16779,6 +17432,8 @@ void BfModule::CreateDelegateInvokeMethod() for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++) { + if (mCurMethodState->mLocals[i]->mCompositeCount >= 0) + continue; BfTypedValue localVal = exprEvaluator.LoadLocal(mCurMethodState->mLocals[i], true); exprEvaluator.PushArg(localVal, staticFuncArgs); exprEvaluator.PushArg(localVal, memberFuncArgs); @@ -16835,11 +17490,11 @@ void BfModule::CreateDelegateInvokeMethod() auto fieldPtr = mBfIRBuilder->CreateInBoundsGEP(multicastDelegate, 0, 1); // Load 'delegate.mFuncPtr' auto funcPtrPtr = mBfIRBuilder->CreateBitCast(fieldPtr, memberFuncPtrPtr); auto funcPtr = mBfIRBuilder->CreateAlignedLoad(funcPtrPtr, mSystem->mPtrSize); - nonStaticResult = mBfIRBuilder->CreateCall(funcPtr, memberFuncArgs); - if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() != -1)) - mBfIRBuilder->Call_AddAttribute(nonStaticResult, mCurMethodInstance->GetStructRetIdx() + 1, BfIRAttribute_StructRet); - if (callingConv != BfIRCallingConv_CDecl) - mBfIRBuilder->SetCallCallingConv(nonStaticResult, callingConv); + + BfExprEvaluator exprEvaluator(this); + BfTypedValue nonStaticTypedResult = exprEvaluator.CreateCall(NULL, mCurMethodInstance, funcPtr, true, memberFuncArgs, NULL, BfCreateCallFlags_DelegateThunkNonStatic); + nonStaticResult = nonStaticTypedResult.mValue; + mCurMethodState->SetHadReturn(false); mCurMethodState->mLeftBlockUncond = false; mCurMethodState->mLeftBlockCond = false; @@ -16853,12 +17508,11 @@ void BfModule::CreateDelegateInvokeMethod() auto fieldPtr = mBfIRBuilder->CreateInBoundsGEP(multicastDelegate, 0, 1); // Load 'delegate.mFuncPtr' auto funcPtrPtr = mBfIRBuilder->CreateBitCast(fieldPtr, staticFuncPtrPtr); auto funcPtr = mBfIRBuilder->CreateAlignedLoad(funcPtrPtr, mSystem->mPtrSize); - staticResult = mBfIRBuilder->CreateCall(funcPtr, staticFuncArgs); - if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx(true) != -1)) - { - // Note: since this is a forced static invocation, we know the sret will be the first parameter - mBfIRBuilder->Call_AddAttribute(staticResult, 0 + 1, BfIRAttribute_StructRet); - } + + + BfExprEvaluator exprEvaluator(this); + BfTypedValue staticTypedResult = exprEvaluator.CreateCall(NULL, mCurMethodInstance, funcPtr, true, staticFuncArgs, NULL, BfCreateCallFlags_DelegateThunkStatic); + staticResult = staticTypedResult.mValue; // We had a sret for the non-static but no sret for the static (because we have a lowered return type there) if ((!mIsComptimeModule) && (mCurMethodInstance->GetStructRetIdx() != -1) && (mCurMethodInstance->GetStructRetIdx(true) == -1)) @@ -16868,10 +17522,6 @@ void BfModule::CreateDelegateInvokeMethod() mBfIRBuilder->CreateStore(staticResult, sretCastedPtr); } - if (callingConv == BfIRCallingConv_ThisCall) - callingConv = BfIRCallingConv_CDecl; - if (callingConv != BfIRCallingConv_CDecl) - mBfIRBuilder->SetCallCallingConv(staticResult, callingConv); mCurMethodState->SetHadReturn(false); mCurMethodState->mLeftBlockUncond = false; mCurMethodState->mLeftBlockCond = false; @@ -16894,7 +17544,7 @@ void BfModule::CreateDelegateInvokeMethod() BfIRType loweredIRReturnType; BfTypeCode loweredTypeCode = BfTypeCode_None; BfTypeCode loweredTypeCode2 = BfTypeCode_None; - if ((!mIsComptimeModule) && (mCurMethodInstance->GetLoweredReturnType(&loweredTypeCode, &loweredTypeCode2))) + if ((!mIsComptimeModule) && (mCurMethodInstance->GetLoweredReturnType(&loweredTypeCode, &loweredTypeCode2)) && (loweredTypeCode != BfTypeCode_None)) loweredIRReturnType = GetIRLoweredType(loweredTypeCode, loweredTypeCode2); else loweredIRReturnType = mBfIRBuilder->MapType(mCurMethodInstance->mReturnType); @@ -16938,6 +17588,24 @@ void BfModule::CalcAppendAlign(BfMethodInstance* methodInst) methodInst->mAppendAllocAlign = 1; } +BfAllowAppendKind BfModule::GetBaseAllowAppend(BfMethodInstance* curMethodInstance) +{ + auto typeInstance = curMethodInstance->GetOwner(); + auto methodDef = curMethodInstance->mMethodDef; + + if (methodDef->mAppendKind == BfAllowAppendKind_No) + return BfAllowAppendKind_No; + + if ((typeInstance->mInstSize == typeInstance->mBaseType->mInstSize) && + (typeInstance->IsZeroGap()) && + (typeInstance->mBaseType->IsZeroGap()) && + (methodDef->mAppendKind == BfAllowAppendKind_ZeroGap)) + { + return BfAllowAppendKind_ZeroGap; + } + return BfAllowAppendKind_Yes; +} + BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl& args, bool force) { BP_ZONE("BfModule::TryConstCalcAppend"); @@ -17081,12 +17749,15 @@ BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArr appendAllocVisitor.mFailed = true; if (!appendAllocVisitor.mFailed) constValue = appendAllocVisitor.mConstAccum; + if (appendAllocVisitor.mHasAppendWantMark) + methodInst->mHasAppendWantMark = true; + if (isFirstRun) { mCurMethodInstance->mEndingAppendAllocAlign = appendAllocVisitor.mCurAppendAlign; if (mCurMethodInstance->mAppendAllocAlign <= 0) mCurMethodInstance->mAppendAllocAlign = 1; - } + } if (isFirstRun) { @@ -17118,7 +17789,7 @@ BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArr } BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly) -{ +{ // Any errors should only be shown in the actual CTOR call SetAndRestoreValue prevIgnoreWrites(mIgnoreErrors, true); @@ -17126,6 +17797,8 @@ BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly) BF_ASSERT((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_CtorCalcAppend)); auto ctorDeclaration = (BfConstructorDeclaration*)methodDef->mMethodDeclaration; + BfAllowAppendKind allowAppendKind = GetBaseAllowAppend(mCurMethodInstance); + BfCustomAttributes* customAttributes = NULL; defer(delete customAttributes); BfInvocationExpression* ctorInvocation = NULL; @@ -17168,11 +17841,11 @@ BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly) BfFunctionBindResult bindResult; bindResult.mSkipThis = true; bindResult.mWantsArgs = true; - { + { SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); exprEvaluator.ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); SetAndRestoreValue prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult); - exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, BfMethodGenericArguments(), true); + exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, BfMethodGenericArguments(), allowAppendKind, NULL); } if (bindResult.mMethodInstance == NULL) @@ -17181,7 +17854,7 @@ BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly) return BfTypedValue(); } - if (!bindResult.mMethodInstance->mMethodDef->mHasAppend) + if (!bindResult.mMethodInstance->mMethodDef->HasAppend()) { return BfTypedValue(); } @@ -17219,7 +17892,7 @@ BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly) bindResult.mSkipThis = true; bindResult.mWantsArgs = true; SetAndRestoreValue prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult); - exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, BfMethodGenericArguments(), true); + exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, BfMethodGenericArguments(), allowAppendKind, NULL); BF_ASSERT(bindResult.mIRArgs[0].IsFake()); bindResult.mIRArgs.RemoveAt(0); calcAppendArgs = bindResult.mIRArgs; @@ -17524,7 +18197,7 @@ void BfModule::EmitDtorBody() localDef->mName = "_"; localDef->mResolvedType = fieldInst->mResolvedType; - if (fieldInst->IsAppendedObject()) + if ((fieldInst->IsAppendedObject()) && (!fieldDef->mIsStatic)) { localDef->mValue = mBfIRBuilder->CreateBitCast(value, mBfIRBuilder->MapType(fieldInst->mResolvedType)); } @@ -17549,7 +18222,7 @@ void BfModule::EmitDtorBody() auto defLocalVar = AddLocalVariableDef(localDef, true); - if (!fieldInst->IsAppendedObject()) + if ((!fieldInst->IsAppendedObject()) && (!fieldDef->mIsStatic)) { // Put back so we actually modify the correct value*/ defLocalVar->mResolvedType = fieldInst->mResolvedType; @@ -17592,7 +18265,10 @@ void BfModule::EmitDtorBody() BfTypedValue val; if (fieldDef->mIsStatic) + { val = ReferenceStaticField(fieldInst); + val = LoadValue(val); + } else { auto fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx); @@ -17620,6 +18296,8 @@ void BfModule::EmitDtorBody() PopulateType(val.mType); PopulateType(objectType, BfPopulateType_DataAndMethods); + EmitObjectAccessCheck(val); + if (objectType->mVirtualMethodTable.size() == 0) { if (!mCompiler->IsAutocomplete()) @@ -17755,6 +18433,20 @@ void BfModule::EmitDtorBody() } } } + + // If there are appends then we just need the rootmost append type to do the Dbg_AppendDeleted + if ((!mIsComptimeModule) && (!methodDef->mIsStatic) && (mCompiler->mOptions.mDebugAlloc) && + (mCurTypeInstance->HasAppendCtor()) && (!mCurTypeInstance->BaseHasAppendCtor())) + { + auto thisValue = GetThis(); + auto appendedObj = mBfIRBuilder->CreateBitCast(thisValue.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType)); + BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_AppendDeleted", 2); + SizedArray llvmArgs; + llvmArgs.Add(appendedObj); + llvmArgs.Add(mBfIRBuilder->CreateConst(BfTypeCode_Boolean, mCurMethodState->mDisableChecks ? 0 : 1)); + if (allocMethod) + mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs); + } } BfIRValue BfModule::CreateDllImportGlobalVar(BfMethodInstance* methodInstance, bool define) @@ -18166,7 +18858,7 @@ void BfModule::EmitCtorBody(bool& skipBody) auto methodDeclaration = methodDef->mMethodDeclaration; auto ctorDeclaration = (BfConstructorDeclaration*)methodDef->mMethodDeclaration; auto typeDef = mCurTypeInstance->mTypeDef; - + BfCustomAttributes* customAttributes = NULL; defer(delete customAttributes); BfInvocationExpression* ctorInvocation = NULL; @@ -18202,7 +18894,7 @@ void BfModule::EmitCtorBody(bool& skipBody) } // Zero out memory for default ctor - if ((methodDeclaration == NULL) && (mCurTypeInstance->IsStruct()) && (methodInstance->mChainType != BfMethodChainType_ChainMember) && + if ((methodDeclaration == NULL) && (mCurTypeInstance->IsStruct()) && (!mCurTypeInstance->IsAnonymousInitializerType()) && (methodInstance->mChainType != BfMethodChainType_ChainMember) && (!mCurMethodState->mLocals.IsEmpty())) { if (mCurTypeInstance->IsTypedPrimitive()) @@ -18346,7 +19038,7 @@ void BfModule::EmitCtorBody(bool& skipBody) auto diVariable = mBfIRBuilder->DbgCreateAutoVariable(mCurMethodState->mCurScope->mDIScope, "this", mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, mBfIRBuilder->DbgGetType(thisType)); - // + if (!mCurTypeInstance->IsValuelessType()) { auto loadedThis = GetThis(); @@ -18565,6 +19257,8 @@ void BfModule::EmitCtorBody(bool& skipBody) targetRefNode = ctorDeclaration->mInitializer; if (auto invocationExpr = BfNodeDynCast(targetRefNode)) targetRefNode = invocationExpr->mTarget; + if (targetRefNode == NULL) + targetRefNode = mCurTypeInstance->mTypeDef->GetRefNode(); if (baseCtorNode != NULL) { @@ -18659,13 +19353,13 @@ void BfModule::EmitCtorBody(bool& skipBody) targetType = mCurTypeInstance->mBaseType; if (ctorDeclaration != NULL) targetRefNode = ctorDeclaration->mThisToken; - else if (typeDef->mTypeDeclaration != NULL) + else if ((typeDef->mTypeDeclaration != NULL) && (typeDef->mTypeDeclaration->mNameNode != NULL)) targetRefNode = typeDef->mTypeDeclaration->mNameNode; } } } - if (methodDef->mHasAppend) + if (methodDef->HasAppend()) { mCurMethodState->mCurAppendAlign = methodInstance->mAppendAllocAlign; } @@ -18689,7 +19383,7 @@ void BfModule::EmitCtorBody(bool& skipBody) } auto autoComplete = mCompiler->GetAutoComplete(); - if (targetType != NULL) + if ((targetType != NULL) && (!mCurTypeInstance->IsAnonymousInitializerType())) { BfAstNode* refNode = methodDeclaration; if (refNode == NULL) @@ -18718,16 +19412,18 @@ void BfModule::EmitCtorBody(bool& skipBody) } exprEvaluator.ResolveArgValues(argValues, BfResolveArgsFlag_DeferParamEval); + BfAllowAppendKind allowAppendKind = GetBaseAllowAppend(mCurMethodInstance); + BfTypedValue appendIdxVal; - if (methodDef->mHasAppend) + if (methodDef->HasAppend()) { auto localVar = mCurMethodState->GetRootMethodState()->mLocals[1]; - BF_ASSERT(localVar->mName == "appendIdx"); + BF_ASSERT(localVar->mName == "__appendIdx"); auto intRefType = localVar->mResolvedType; appendIdxVal = BfTypedValue(localVar->mValue, intRefType); mCurMethodState->mCurAppendAlign = 1; // Don't make any assumptions about how the base leaves the alignment } - exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, BfMethodGenericArguments(), methodDef->mHasAppend, &appendIdxVal); + exprEvaluator.MatchConstructor(targetRefNode, NULL, target, targetType, argValues, true, BfMethodGenericArguments(), allowAppendKind, &appendIdxVal); if (autoComplete != NULL) { @@ -18856,7 +19552,7 @@ void BfModule::EmitEnumToStringBody() rawPayload = ExtractValue(GetThis(), NULL, 1); } else - enumVal = mBfIRBuilder->GetArgument(0); + enumVal = LoadValue(GetThis()).mValue; Array paramTypes; paramTypes.Add(stringType); @@ -19030,17 +19726,16 @@ void BfModule::EmitTupleToStringBody() continue; BfTypedValue fieldValue = ExtractValue(thisValue, &fieldInstance, fieldInstance.mDataIdx); - if (fieldValue.mType->IsWrappableType()) { - auto wrappedType = GetWrappedStructType(fieldValue.mType); + auto wrappedType = GetWrappedStructType(fieldValue.mType); if ((wrappedType->IsTypedPrimitive()) || (wrappedType->IsValuelessType())) { fieldValue.mType = wrappedType; } else { - fieldValue = MakeAddressable(fieldValue); + fieldValue = MakeAddressable(fieldValue, false, true); fieldValue.mType = wrappedType; fieldValue.mValue = mBfIRBuilder->CreateBitCast(fieldValue.mValue, mBfIRBuilder->MapTypeInstPtr(fieldValue.mType->ToTypeInstance())); if (!wrappedType->IsValuelessType()) @@ -19388,7 +20083,7 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp if (!thisType->IsTypedPrimitive()) paramVar->mIsSplat = true; } - else if ((!mIsComptimeModule) && (!methodDef->mIsMutating) && (methodInstance->mCallingConvention == BfCallingConvention_Unspecified)) + else if ((!mIsComptimeModule) && (!methodDef->mIsMutating) && (!methodInstance->ForcingThisPtr())) paramVar->mIsLowered = thisType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2) != BfTypeCode_None; auto thisTypeInst = thisType->ToTypeInstance(); @@ -19448,7 +20143,7 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp for (paramIdx = 0; paramIdx < methodInstance->GetParamCount(); paramIdx++) { // We already issues a type error for this param if we had one in declaration processing - SetAndRestoreValue prevIgnoreErrors(mIgnoreErrors, true); + SetAndRestoreValue prevIgnoreErrors(mIgnoreErrors, true); BfLocalVariable* paramVar = rootMethodState->mBumpAlloc.Alloc(); paramVar->mIsBumpAlloc = true; @@ -19601,14 +20296,17 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp { if (compositeVariableIdx == -1) { - compositeVariableIdx = (int)mCurMethodState->mLocals.size(); - - BfLocalVariable* localVar = new BfLocalVariable(); auto paramInst = &methodInstance->mParams[paramIdx]; auto paramDef = methodDef->mParams[paramInst->mParamDefIdx]; + + compositeVariableIdx = (int)mCurMethodState->mLocals.size(); + BfLocalVariable* localVar = new BfLocalVariable(); localVar->mName = paramDef->mName; localVar->mNamePrefixCount = paramDef->mNamePrefixCount; - localVar->mResolvedType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam); + if (paramDef->mTypeRef != NULL) + localVar->mResolvedType = ResolveTypeRef(paramDef->mTypeRef, BfPopulateType_Declaration, BfResolveTypeRefFlag_NoResolveGenericParam); + if (localVar->mResolvedType == NULL) + localVar->mResolvedType = GetPrimitiveType(BfTypeCode_None); localVar->mCompositeCount = 0; DoAddLocalVariable(localVar); } @@ -19616,6 +20314,16 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp paramVar->mIsImplicitParam = true; } + if (methodInstance->GetParamKind(paramIdx) == BfParamKind_Params) + { + if ((paramVar->mResolvedType->IsModifiedTypeType()) && (((BfModifiedTypeType*)paramVar->mResolvedType)->mModifiedKind == BfToken_Params)) + { + paramVar->mValue = BfIRValue(); + paramVar->mCompositeCount = 0; + paramVar->mIsSplat = false; + } + } + if (!mCurTypeInstance->IsDelegateOrFunction()) CheckVariableDef(paramVar); paramVar->Init(); @@ -19657,15 +20365,28 @@ void BfModule::ProcessMethod_SetupParams(BfMethodInstance* methodInstance, BfTyp if (genericParamInstance->mTypeConstraint != NULL) { auto typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance(); - if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || + if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) || ((typeInstConstraint != NULL) && - ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef))))) + ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef))))) { - BfLocalVariable* localVar = new BfLocalVariable(); - localVar->mName = paramDef->mName; - localVar->mResolvedType = paramsType; - localVar->mCompositeCount = 0; - DoAddLocalVariable(localVar); + bool doAdd = true; + + // TODO: Remove + if (!mCurMethodState->mLocals.IsEmpty()) + { + auto checkVar = mCurMethodState->mLocals.back(); + if (checkVar->mName == paramDef->mName) + doAdd = false; + } + + if (doAdd) + { + BfLocalVariable* localVar = new BfLocalVariable(); + localVar->mName = paramDef->mName; + localVar->mResolvedType = paramsType; + localVar->mCompositeCount = 0; + DoAddLocalVariable(localVar); + } } } } @@ -19848,6 +20569,13 @@ void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int m if (!checkType->WantsGCMarking()) return; + if (checkType->IsDeleting()) + { + mCompiler->RequestExtraCompile(); + InternalError("EmitGCMarkValue deleted type"); + return; + } + auto typeInstance = checkType->ToTypeInstance(); bool callMarkMethod = false; @@ -20183,7 +20911,7 @@ void BfModule::EmitGCMarkMembers() if (markVal) { - if (fieldInst.IsAppendedObject()) + if ((fieldInst.IsAppendedObject()) && (!fieldDef->mIsStatic)) EmitGCMarkAppended(markVal); else EmitGCMarkValue(markVal, markFromGCThreadMethodInstance); @@ -20196,6 +20924,19 @@ void BfModule::EmitGCMarkMembers() CallChainedMethods(mCurMethodInstance, false); } } + + // If there are appends then we just need the rootmost append type to do the Dbg_MarkAppended + if ((!mIsComptimeModule) && (!methodDef->mIsStatic) && (mCompiler->mOptions.mDebugAlloc) && + (mCurTypeInstance->HasAppendCtor()) && (!mCurTypeInstance->BaseHasAppendCtor())) + { + auto thisValue = GetThis(); + auto appendedObj = mBfIRBuilder->CreateBitCast(thisValue.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType)); + BfModuleMethodInstance allocMethod = GetInternalMethod("Dbg_MarkAppended", 1); + SizedArray llvmArgs; + llvmArgs.push_back(appendedObj); + if (allocMethod) + mBfIRBuilder->CreateCall(allocMethod.mFunc, llvmArgs); + } } void BfModule::EmitGCFindTLSMembers() @@ -20558,6 +21299,21 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, return; } + auto _SanityCheckType = [&](BfType* type) + { + if ((type != NULL) && (type->IsDeleting())) + { + InternalError("Method references deleted type", methodDef->GetRefNode()); + return false; + } + return true; + }; + if (!_SanityCheckType(methodInstance->mReturnType)) + return; + for (int paramIdx = 0; paramIdx < methodInstance->GetParamCount(); paramIdx++) + if (!_SanityCheckType(methodInstance->GetParamType(paramIdx))) + return; + StringT<512> mangledName; BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), mCurMethodInstance); if (!methodInstance->mIRFunction) @@ -20587,7 +21343,10 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, if ((mCompiler->mIsResolveOnly) && (!mIsComptimeModule) && (methodDef->mBody != NULL) && (!mCurTypeInstance->IsBoxed())) { if (auto sourceClassifier = mCompiler->mResolvePassData->GetSourceClassifier(methodDef->mBody)) + { + SetAndRestoreValue prevSkipAnonTypes(sourceClassifier->mSkipAnonymousTypes, true); sourceClassifier->VisitChildNoRef(methodDef->mBody); + } } } @@ -20854,8 +21613,10 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, if (mDICompileUnit) { + // Note: for comptime we need to ensure we don't force type population with DbgGetTypeInst here, as that + // can generate a CeMachine InitType circular data reference int flags = 0; - BfIRMDNode funcScope = mBfIRBuilder->DbgGetTypeInst(mCurTypeInstance); + BfIRMDNode funcScope = mBfIRBuilder->DbgGetTypeInst(mCurTypeInstance, BfIRPopulateType_Identity); if (methodDef->mProtection == BfProtection_Public) flags = llvm::DINode::FlagPublic; @@ -21020,7 +21781,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, for ( ; argIdx < irParamCount; localIdx++) { bool isThis = ((localIdx == 0) && (!mCurMethodInstance->mMethodDef->mIsStatic)); - if ((isThis) && (thisType->IsValuelessType())) + if ((isThis) && (thisType->IsValuelessNonOpaqueType())) isThis = false; if ((!mIsComptimeModule) && (methodInstance->GetStructRetIdx() == argIdx)) @@ -21033,15 +21794,26 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, BfLocalVariable* paramVar = NULL; while (true) { - BF_ASSERT(localIdx < methodState.mLocals.size()); + if (localIdx >= methodState.mLocals.size()) + { + paramVar = NULL; + break; + } + paramVar = methodState.mLocals[localIdx]; if ((paramVar->mCompositeCount == -1) && (!paramVar->mIsConst) && - ((!paramVar->mResolvedType->IsValuelessType()) || (paramVar->mResolvedType->IsMethodRef()))) + ((!paramVar->mResolvedType->IsValuelessNonOpaqueType()) || (paramVar->mResolvedType->IsMethodRef()))) break; localIdx++; } + if (paramVar == NULL) + { + Fail("Parameter count error", methodDef->GetRefNode()); + break; + } + if ((isThis) && (mCurTypeInstance->IsValueType()) && (methodDef->mMethodType != BfMethodType_Ctor) && (!methodDef->HasNoThisSplat())) { paramVar->mIsReadOnly = true; @@ -21094,7 +21866,8 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, else { bool wantPtr = (thisType->IsComposite()) && (!paramVar->mIsLowered); - if ((thisType->IsTypedPrimitive()) && (methodDef->HasNoThisSplat())) + if ((thisType->IsTypedPrimitive()) && + ((methodDef->HasNoThisSplat()) || (methodInstance->ForcingThisPtr()))) wantPtr = true; if (wantPtr) @@ -21203,7 +21976,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, continue; bool isThis = ((curLocalIdx == 0) && (!mCurMethodInstance->mMethodDef->mIsStatic)); - if ((isThis) && (thisType->IsValuelessType())) + if ((isThis) && (thisType->IsValuelessNonOpaqueType())) isThis = false; if (paramVar->mValue.IsArg()) @@ -21224,7 +21997,8 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, { diType = mBfIRBuilder->DbgGetType(paramVar->mResolvedType); bool wantRef = paramVar->mResolvedType->IsComposite(); - if ((paramVar->mResolvedType->IsTypedPrimitive()) && (methodDef->HasNoThisSplat())) + if ((paramVar->mResolvedType->IsTypedPrimitive()) && + ((methodDef->HasNoThisSplat()) || (methodInstance->ForcingThisPtr()))) wantRef = true; if (wantRef) @@ -21268,7 +22042,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, } } - if (paramVar->mResolvedType->IsValuelessType()) + if (paramVar->mResolvedType->IsValuelessNonOpaqueType()) { diVariable = mBfIRBuilder->DbgCreateAutoVariable(diFunction, paramName, mCurFilePosition.mFileInstance->mDIFile, mCurFilePosition.mCurLine, diType); @@ -21536,7 +22310,12 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, if (loweredTypeCode2 != BfTypeCode_None) argIdx++; } - else if (!paramVar->mResolvedType->IsValuelessType()) + else if ((isThis) && (paramVar->mResolvedType->IsOpaque())) + { + if ((methodDef->mIsMutating) || (methodInstance->ForcingThisPtr())) + argIdx++; + } + else if (!paramVar->mResolvedType->IsValuelessNonOpaqueType()) { argIdx++; } @@ -21599,6 +22378,20 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, if ((methodDef != NULL) && (propertyDeclaration != NULL) && (propertyDeclaration->mExternSpecifier != NULL)) hasExternSpecifier = true; + if (methodDef->mName == BF_METHODNAME_MARKMEMBERS) + { + // We need to be able to mark deleted objects + mCurMethodState->mIgnoreObjectAccessCheck = true; + } + auto customAttributes = methodInstance->GetCustomAttributes(); + if (customAttributes != NULL) + { + if (customAttributes->Contains(mCompiler->mDisableObjectAccessChecksAttributeTypeDef)) + mCurMethodState->mIgnoreObjectAccessCheck = true; + if (customAttributes->Contains(mCompiler->mDisableChecksAttributeTypeDef)) + mCurMethodState->mDisableChecks = true; + } + // Allocate, clear, set classVData if ((methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mIsStatic)) @@ -21642,50 +22435,55 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, else { auto innerMethodDef = innerMethodInstance.mMethodInstance->mMethodDef; - BF_ASSERT(innerMethodDef == methodDef); - - SizedArray innerParams; - BfExprEvaluator exprEvaluator(this); - - if (!innerType->IsValuelessType()) + if ((innerMethodDef == methodDef != NULL) && (!CompareMethodSignatures(methodInstance, innerMethodInstance.mMethodInstance))) { - BfIRValue thisValue = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, 1); - BfTypedValue innerVal(thisValue, innerType, true); - if (boxedType->IsBoxedStructPtr()) - { - innerVal = LoadValue(innerVal); - innerVal = BfTypedValue(innerVal.mValue, innerType, true); - } - - exprEvaluator.PushThis(NULL, innerVal, innerMethodInstance.mMethodInstance, innerParams); - } - - for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++) - { - BfLocalVariable* localVar = mCurMethodState->mLocals[i]; - BfTypedValue localVal = exprEvaluator.LoadLocal(localVar, true); - exprEvaluator.PushArg(localVal, innerParams); - } - if (!innerMethodInstance.mFunc) - { - BF_ASSERT(innerType->IsUnspecializedType()); - } - else if ((!mIsComptimeModule) && (methodInstance->GetStructRetIdx() != -1)) - { - mBfIRBuilder->PopulateType(methodInstance->mReturnType); - auto returnType = BfTypedValue(mBfIRBuilder->GetArgument(methodInstance->GetStructRetIdx()), methodInstance->mReturnType, true); - exprEvaluator.mReceivingValue = &returnType; - auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, BfCreateCallFlags_TailCall); - BF_ASSERT(exprEvaluator.mReceivingValue == NULL); // Ensure it was actually used - mBfIRBuilder->CreateRetVoid(); + InternalError("Boxing method passthrough error"); } else { - mBfIRBuilder->PopulateType(methodInstance->mReturnType); - auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, BfCreateCallFlags_TailCall); - if (mCurMethodInstance->mReturnType->IsValueType()) - retVal = LoadValue(retVal); - CreateReturn(retVal.mValue); + SizedArray innerParams; + BfExprEvaluator exprEvaluator(this); + + if (!innerType->IsValuelessType()) + { + BfIRValue thisValue = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, 1); + BfTypedValue innerVal(thisValue, innerType, true); + if (boxedType->IsBoxedStructPtr()) + { + innerVal = LoadValue(innerVal); + innerVal = BfTypedValue(innerVal.mValue, innerType, true); + } + + exprEvaluator.PushThis(NULL, innerVal, innerMethodInstance.mMethodInstance, innerParams); + } + + for (int i = 1; i < (int)mCurMethodState->mLocals.size(); i++) + { + BfLocalVariable* localVar = mCurMethodState->mLocals[i]; + BfTypedValue localVal = exprEvaluator.LoadLocal(localVar, true); + exprEvaluator.PushArg(localVal, innerParams); + } + if (!innerMethodInstance.mFunc) + { + BF_ASSERT(innerType->IsUnspecializedType()); + } + else if ((!mIsComptimeModule) && (methodInstance->GetStructRetIdx() != -1)) + { + mBfIRBuilder->PopulateType(methodInstance->mReturnType); + auto returnType = BfTypedValue(mBfIRBuilder->GetArgument(methodInstance->GetStructRetIdx()), methodInstance->mReturnType, true); + exprEvaluator.mReceivingValue = &returnType; + auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, BfCreateCallFlags_TailCall); + BF_ASSERT(exprEvaluator.mReceivingValue == NULL); // Ensure it was actually used + mBfIRBuilder->CreateRetVoid(); + } + else + { + mBfIRBuilder->PopulateType(methodInstance->mReturnType); + auto retVal = exprEvaluator.CreateCall(NULL, innerMethodInstance.mMethodInstance, innerMethodInstance.mFunc, true, innerParams, NULL, BfCreateCallFlags_TailCall); + if (mCurMethodInstance->mReturnType->IsValueType()) + retVal = LoadValue(retVal); + CreateReturn(retVal.mValue); + } } } } @@ -21694,13 +22492,13 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, mCurMethodState->mLeftBlockUncond = true; } else if (methodDef->mMethodType == BfMethodType_CtorClear) - { + { SetIllegalSrcPos(); mBfIRBuilder->ClearDebugLocation(); PopulateType(mCurTypeInstance, BfPopulateType_Data); auto thisVal = GetThis(); int prevSize = 0; - if (mContext->mBfObjectType != NULL) + if ((mContext->mBfObjectType != NULL) && (mCurTypeInstance->IsObject())) { prevSize = mContext->mBfObjectType->mInstSize; PopulateType(mContext->mBfObjectType); @@ -21771,21 +22569,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, if ((!mCurTypeInstance->IsBoxed()) && (methodDeclaration != NULL) && (methodDeclaration->mHadYield) && (methodDef->mBody != NULL)) { EmitIteratorBlock(skipBody); - } - - if (methodDef->mName == BF_METHODNAME_MARKMEMBERS) - { - // We need to be able to mark deleted objects - mCurMethodState->mIgnoreObjectAccessCheck = true; - } - auto customAttributes = methodInstance->GetCustomAttributes(); - if (customAttributes != NULL) - { - if (customAttributes->Contains(mCompiler->mDisableObjectAccessChecksAttributeTypeDef)) - mCurMethodState->mIgnoreObjectAccessCheck = true; - if (customAttributes->Contains(mCompiler->mDisableChecksAttributeTypeDef)) - mCurMethodState->mDisableChecks = true; - } + } if ((methodDef->mMethodType == BfMethodType_CtorNoBody) && (!methodDef->mIsStatic) && ((methodInstance->mChainType == BfMethodChainType_ChainHead) || (methodInstance->mChainType == BfMethodChainType_None))) @@ -21844,7 +22628,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, mCurMethodState->mLeftBlockUncond = true; } } - else if ((methodDef->mName == BF_METHODNAME_DYNAMICCAST) || (methodDef->mName == BF_METHODNAME_DYNAMICCAST_INTERFACE)) + else if ((methodDef->mName == BF_METHODNAME_DYNAMICCAST) || (methodDef->mName == BF_METHODNAME_DYNAMICCAST_INTERFACE) || (methodDef->mName == BF_METHODNAME_DYNAMICCAST_SIGNATURE)) { if (mCurTypeInstance->IsObject()) { @@ -21879,8 +22663,9 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, } else { - auto andResult = mBfIRBuilder->CreateAnd(mCurMethodState->mLocals[0]->mValue, mCurMethodState->mLocals[1]->mValue); - auto toBool = mBfIRBuilder->CreateCmpNE(andResult, GetDefaultValue(mCurMethodState->mLocals[0]->mResolvedType)); + BfIRValue thisValue = LoadValue(GetThis()).mValue; + auto andResult = mBfIRBuilder->CreateAnd(thisValue, mCurMethodState->mLocals[1]->mValue); + auto toBool = mBfIRBuilder->CreateCmpEQ(andResult, mCurMethodState->mLocals[1]->mValue); fromBool = mBfIRBuilder->CreateNumericCast(toBool, false, BfTypeCode_Boolean); } mBfIRBuilder->RestoreDebugLocation(); @@ -21904,10 +22689,24 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, mBfIRBuilder->ClearDebugLocation(); BfIRValue fromBool; mBfIRBuilder->RestoreDebugLocation(); - if (!mCurTypeInstance->IsValuelessType()) - ret = mBfIRBuilder->CreateRet(GetThis().mValue); - else - mBfIRBuilder->CreateRetVoid(); + + if ((!mCompiler->mIsResolveOnly) || (mIsComptimeModule)) + { + if (!mCurTypeInstance->IsValuelessType()) + { + auto thisValueOrAddr = GetThis(); + + BfIRValue thisValue; + if (!methodInstance->mReturnType->IsRef()) + thisValue = LoadValue(thisValueOrAddr).mValue; + else + thisValue = thisValueOrAddr.mValue; + + ret = mBfIRBuilder->CreateRet(thisValue); + } + else + mBfIRBuilder->CreateRetVoid(); + } //ExtendLocalLifetimes(0); EmitLifetimeEnds(&mCurMethodState->mHeadScope); @@ -21970,7 +22769,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, if ((propertyDeclaration != NULL) && (!typeDef->HasAutoProperty(propertyDeclaration))) { if ((!mCurTypeInstance->IsInterface()) && (!hasExternSpecifier)) - Fail("Body expected", methodDef->mBody); + Fail("Body expected", methodDef->GetRefNode()); } else if (methodDef->mMethodType == BfMethodType_PropertyGetter) { @@ -22195,6 +22994,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, mCurMethodState->mHeadScope.mAstBlock = bodyBlock; mCurMethodState->mHeadScope.mCloseNode = bodyBlock->mCloseBrace; VisitCodeBlock(bodyBlock); + BF_ASSERT(mCurMethodState->mCurScope == &mCurMethodState->mHeadScope); } else if (auto expressionBody = BfNodeDynCast(methodDef->mBody)) { @@ -23423,7 +24223,7 @@ void BfModule::GetMethodCustomAttributes(BfMethodInstance* methodInstance) auto typeInstance = methodInstance->GetOwner(); if (typeInstance->IsInstanceOf(mCompiler->mValueTypeTypeDef)) - return; + return; BfTypeState typeState(typeInstance); SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); @@ -23451,7 +24251,7 @@ void BfModule::GetMethodCustomAttributes(BfMethodInstance* methodInstance) if ((methodInstance == methodInstance->mMethodInstanceGroup->mDefault) && (methodInstance->mMethodInstanceGroup->mDefaultCustomAttributes != NULL)) { - // Take over prevoiusly-generated custom attributes + // Take over previously-generated custom attributes methodInstance->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes = methodInstance->mMethodInstanceGroup->mDefaultCustomAttributes; methodInstance->mMethodInstanceGroup->mDefaultCustomAttributes = NULL; } @@ -23472,7 +24272,55 @@ void BfModule::GetMethodCustomAttributes(BfMethodInstance* methodInstance) } } - customAttributes = methodInstance->GetCustomAttributes(); + if (methodDeclaration != NULL) + { + if (auto inlineTypeRef = BfNodeDynCast(methodDeclaration->mReturnType)) + { + if (inlineTypeRef->mTypeDeclaration->mAttributes != NULL) + { + // Apply multiuse attributes from anonymous return type + auto returnType = ResolveTypeRef(inlineTypeRef); + if ((returnType != NULL) && (returnType->ToTypeInstance())) + { + auto returnTypeInst = returnType->ToTypeInstance(); + if ((returnTypeInst->IsAnonymous()) && (returnTypeInst->mCustomAttributes != NULL)) + { + bool hasPendingAttributes = false; + for (const auto& customAttribute : returnTypeInst->mCustomAttributes->mAttributes) + { + if (customAttribute.mAwaitingValidation) + { + hasPendingAttributes = true; + break; + } + } + + if (hasPendingAttributes) + { + if (methodInstance->GetMethodInfoEx()->mMethodCustomAttributes == NULL) + methodInstance->mMethodInfoEx->mMethodCustomAttributes = new BfMethodCustomAttributes(); + + if (methodInstance->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes == NULL) + methodInstance->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes = new BfCustomAttributes(); + + for (const auto& customAttribute : returnTypeInst->mCustomAttributes->mAttributes) + { + if (!customAttribute.mAwaitingValidation) + continue; + + BfCustomAttribute copiedCustomAttribute = customAttribute; + copiedCustomAttribute.mIsMultiUse = false; + methodInstance->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes->mAttributes.Add(copiedCustomAttribute); + } + ValidateCustomAttributes(methodInstance->mMethodInfoEx->mMethodCustomAttributes->mCustomAttributes, attrTarget); + } + } + } + } + } + } + + customAttributes = methodInstance->GetCustomAttributes(); if (customAttributes == NULL) { auto owner = methodInstance->GetOwner(); @@ -23481,8 +24329,17 @@ void BfModule::GetMethodCustomAttributes(BfMethodInstance* methodInstance) } methodInstance->mCallingConvention = methodDef->mCallingConvention; + if ((typeInstance->IsValueType()) && (methodDef->mIsOverride) && (methodDef->mName == BF_METHODNAME_MARKMEMBERS)) + { + // Make sure we we pass 'this' as a pointer into GCMarkMembers so it's compatible with the mark function pointer + methodInstance->mCallingConvention = BfCallingConvention_Cdecl; + } + if (customAttributes != NULL) { + if (customAttributes->Contains(mCompiler->mIntrinsicAttributeTypeDef)) + methodInstance->mIsIntrinsic = true; + auto linkNameAttr = customAttributes->Get(mCompiler->mCallingConventionAttributeTypeDef); if (linkNameAttr != NULL) { @@ -23543,7 +24400,7 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man (methodInstance->GetOwner()->mDefineState < BfTypeDefineState_Defined)); mangledName = "autocomplete_tmp"; } - + if (!mBfIRBuilder->mIgnoreWrites) { BfIRFunction prevFunc; @@ -23552,14 +24409,16 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man prevFunc = mBfIRBuilder->GetFunction(mangledName); if (prevFunc) { - if ((methodDef->mIsOverride) && (mCurTypeInstance->mTypeDef->mIsCombinedPartial)) + auto owner = methodInstance->GetOwner(); + + if ((methodDef->mIsOverride) && (owner->mTypeDef->mIsCombinedPartial)) { bool takeover = false; - mCurTypeInstance->mTypeDef->PopulateMemberSets(); + owner->mTypeDef->PopulateMemberSets(); BfMethodDef* nextMethod = NULL; BfMemberSetEntry* entry = NULL; - if (mCurTypeInstance->mTypeDef->mMethodSet.TryGet(BfMemberSetEntry(methodDef), &entry)) + if (owner->mTypeDef->mMethodSet.TryGet(BfMemberSetEntry(methodDef), &entry)) nextMethod = (BfMethodDef*)entry->mMemberDef; while (nextMethod != NULL) { @@ -23570,7 +24429,7 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man continue; if (checkMethod->mIsOverride) { - auto checkMethodInstance = mCurTypeInstance->mMethodInstanceGroups[checkMethod->mIdx].mDefault; + auto checkMethodInstance = owner->mMethodInstanceGroups[checkMethod->mIdx].mDefault; if (checkMethodInstance == NULL) continue; if ((checkMethodInstance->mIRFunction == prevFunc) && @@ -23583,7 +24442,7 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man else if (auto methodDeclaration = methodDef->GetMethodDeclaration()) refNode = methodDeclaration->mVirtualSpecifier; - auto error = Fail(StrFormat("Conflicting extension override method '%s'", MethodToString(mCurMethodInstance).c_str()), refNode); + auto error = Fail(StrFormat("Conflicting extension override method '%s'", MethodToString(methodInstance).c_str()), refNode); if (error != NULL) mCompiler->mPassInstance->MoreInfo("See previous override", checkMethod->GetRefNode()); @@ -23594,19 +24453,25 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man if (!checkMethod->mIsExtern) continue; - auto checkMethodInstance = mCurTypeInstance->mMethodInstanceGroups[checkMethod->mIdx].mDefault; + auto checkMethodInstance = owner->mMethodInstanceGroups[checkMethod->mIdx].mDefault; if (checkMethodInstance == NULL) continue; - if (!CompareMethodSignatures(checkMethodInstance, mCurMethodInstance)) + if (!CompareMethodSignatures(checkMethodInstance, methodInstance)) continue; // Take over function takeover = true; + + BfLogSysM("Method %p setting mHasBeenProcessed due to funcId takeover from method %p\n", checkMethodInstance, prevFunc.mId, methodInstance); + checkMethodInstance->mHasBeenProcessed = true; } if (takeover) - mCurMethodInstance->mIRFunction = prevFunc; + { + BfLogSysM("Method %p takover of previous funcId: %d\n", methodInstance, prevFunc.mId); + methodInstance->mIRFunction = prevFunc; + } - if (!mCurMethodInstance->mIRFunction) + if (!methodInstance->mIRFunction) { BfLogSysM("Function collision from inner override erased prevFunc %p: %d\n", methodInstance, prevFunc.mId); if (!mIsComptimeModule) @@ -23624,9 +24489,9 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man // their constraints, but they should only collide in their unspecialized form // since only one will be chosen for a given concrete type if (!mIsComptimeModule) - mCurMethodInstance->mMangleWithIdx = true; + methodInstance->mMangleWithIdx = true; mangledName.Clear(); - BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), mCurMethodInstance); + BfMangler::Mangle(mangledName, mCompiler->GetMangleKind(), methodInstance); BfLogSysM("Function collision forced mangleWithIdx for %p: %d GenericArgs: %d Unspecialized: %d\n", methodInstance, prevFunc.mId, methodInstance->GetNumGenericArguments(), methodInstance->mIsUnspecialized); } @@ -23645,42 +24510,6 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man GetMethodCustomAttributes(methodInstance); } -// if (prevFunc) -// { -// if (methodDef->mIsExtern) -// { -// BfLogSysM("Reusing function for %p: %d\n", methodInstance, prevFunc.mId); -// methodInstance->mIRFunction = prevFunc; -// } -// else -// { -// if ((methodInstance->IsSpecializedByAutoCompleteMethod()) || -// ((mCompiler->mResolvePassData != NULL) && (mCompiler->mResolvePassData->mResolveType >= BfResolveType_GoToDefinition))) -// { -// // If we're doing re-pass over the methods for something like BfResolveType_ShowFileSymbolReferences, then allow this collision -// BfLogSysM("Ignoring function name collision\n"); -// } -// else -// { -// if (methodDef->mMethodDeclaration == NULL) -// { -// AssertErrorState(); -// } -// else -// { -// BF_ASSERT(mIsSpecialModule); -// if ((mCompiler->mRevision == 1) && (HasCompiledOutput())) -// { -// // We shouldn't have name collisions on the first run! -// AssertErrorState(); -// } -// } -// } -// BfLogSysM("Before erase\n"); -// mBfIRBuilder->Func_EraseFromParent(prevFunc); -// } -// } - bool isIntrinsic = false; if (!methodInstance->mIRFunction) { @@ -23688,12 +24517,6 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man bool wantsLLVMFunc = ((!typeInstance->IsUnspecializedType() || (mIsComptimeModule)) && (!methodDef->IsEmptyPartial())) && (funcType); - /*if (mCurTypeInstance->mTypeDef->mName->ToString() == "ClassA") - { - if (!mIsReified) - wantsLLVMFunc = false; - }*/ - if (wantsLLVMFunc) { func = GetIntrinsic(methodInstance, true); @@ -23707,14 +24530,9 @@ void BfModule::SetupIRFunction(BfMethodInstance* methodInstance, StringImpl& man BfLogSysM("Creating FuncId:%d %s in module %p\n", func.mId, mangledName.c_str(), this); if (methodInstance->mAlwaysInline) mBfIRBuilder->Func_AddAttribute(func, -1, BFIRAttribute_AlwaysInline); - -// if (prevFunc) -// BfLogSysM("Removing Func %d, replacing with %d\n", prevFunc.mId, func.mId); } - methodInstance->mIRFunction = func; - //if (!ignoreWrites) - //BF_ASSERT(!func.IsFake()); + methodInstance->mIRFunction = func; } } @@ -23910,6 +24728,10 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool BF_ASSERT((mCompiler->mCeMachine == NULL) || (!mCompiler->mCeMachine->mDbgPaused)); BP_ZONE("BfModule::DoMethodDeclaration"); + + auto typeInstance = mCurTypeInstance; + auto typeDef = typeInstance->mTypeDef; + auto methodDef = mCurMethodInstance->mMethodDef; // We could trigger a DoMethodDeclaration from a const resolver or other location, so we reset it here // to effectively make mIgnoreWrites method-scoped @@ -23917,6 +24739,8 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool SetAndRestoreValue prevIsCapturingMethodMatchInfo; SetAndRestoreValue prevAllowLockYield(mContext->mAllowLockYield, false); BfTypeState typeState(mCurTypeInstance); + typeState.mPrevState = mContext->mCurTypeState; + typeState.mCurMethodDef = methodDef; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); BfModule* resolveModule = mContext->mUnreifiedModule; @@ -23945,11 +24769,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool // } if ((mAwaitingInitFinish) && (!mBfIRBuilder->mIgnoreWrites)) - FinishInit(); - - auto typeInstance = mCurTypeInstance; - auto typeDef = typeInstance->mTypeDef; - auto methodDef = mCurMethodInstance->mMethodDef; + FinishInit(); if (methodDef->mName.StartsWith('_')) { @@ -24491,10 +25311,45 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool isValid = true; addParams = false; } + else if (resolvedParamType->IsTuple()) + { + auto tupleType = (BfTupleType*)resolvedParamType; + PopulateType(tupleType); + + hadDelegateParams = true; + + // This means we copy the params from a delegate + BfMethodParam methodParam; + methodParam.mResolvedType = resolvedParamType; + methodParam.mParamDefIdx = paramDefIdx; + methodParam.mDelegateParamIdx = 0; + auto invokeMethodInstance = methodParam.GetDelegateParamInvoke(); + for (int delegateParamIdx = 0; delegateParamIdx < tupleType->mFieldInstances.mSize; delegateParamIdx++) + { + auto& fieldInstance = tupleType->mFieldInstances[delegateParamIdx]; + + methodParam.mDelegateParamIdx = delegateParamIdx; + mCurMethodInstance->mParams.Add(methodParam); + + if (!methodInstance->IsSpecializedGenericMethod()) + AddDependency(fieldInstance.mResolvedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ParamOrReturnValue); + } + isValid = true; + addParams = false; + } else if (resolvedParamType->IsGenericParam()) - { - auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType); - if (genericParamInstance->mTypeConstraint != NULL) + { + bool isGenericDelegateParams = false; + bool addDelegateParamsType = false; + + auto genericParamInstance = GetGenericParamInstance((BfGenericParamType*)resolvedParamType, false, BfFailHandleKind_Ignore); + if (genericParamInstance == NULL) + { + // Delegate case with a 'params T'? + isGenericDelegateParams = true; + addDelegateParamsType = true; + } + else if (genericParamInstance->mTypeConstraint != NULL) { auto typeInstConstraint = genericParamInstance->mTypeConstraint->ToTypeInstance(); if ((genericParamInstance->mTypeConstraint->IsArray()) || (genericParamInstance->mTypeConstraint->IsSizedArray())) @@ -24505,13 +25360,32 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool mCurMethodInstance->mParams.Add(methodParam); isValid = true; } - else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || + else if ((genericParamInstance->mTypeConstraint->IsDelegate()) || (genericParamInstance->mTypeConstraint->IsFunction()) || (genericParamInstance->mTypeConstraint->IsTuple()) || ((genericParamInstance != NULL) && (typeInstConstraint != NULL) && - ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef))))) + ((typeInstConstraint->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef))))) { - mCurMethodInstance->mHadGenericDelegateParams = true; - isValid = true; - addParams = false; + isGenericDelegateParams = true; + //addDelegateParamsType = typeInstConstraint->IsInstanceOf(mCompiler->mTupleTypeDef); + addDelegateParamsType = true; + } + } + + if (isGenericDelegateParams) + { + mCurMethodInstance->mHadGenericDelegateParams = true; + isValid = true; + addParams = false; + + if (addDelegateParamsType) + { + BfMethodParam methodParam; + methodParam.mResolvedType = CreateModifiedTypeType(resolvedParamType, BfToken_Params); + methodParam.mParamDefIdx = paramDefIdx; + mCurMethodInstance->mParams.Add(methodParam); + + //TODO: Why do we have this 'if' + if (!methodInstance->IsSpecializedGenericMethod()) + AddDependency(methodParam.mResolvedType, mCurTypeInstance, BfDependencyMap::DependencyFlag_ParamOrReturnValue); } } } @@ -24519,9 +25393,9 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool { auto paramTypeInst = resolvedParamType->ToTypeInstance(); if ((paramTypeInst != NULL) && - ((paramTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (paramTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef)))) + ((paramTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || (paramTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef)) || (paramTypeInst->IsInstanceOf(mCompiler->mTupleTypeDef)))) { - // If we have a 'params T' and 'T' gets specialized with actually 'Delegate' or 'Function' then just ignore it + // If we have a 'params T' and 'T' gets specialized with actually 'Tuple', 'Delegate' or 'Function' then just ignore it isValid = true; addParams = false; } @@ -24529,7 +25403,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool if (!isValid) { - Fail("Parameters with 'params' specifiers can only be used for array, span, delegate, or function types", paramDef->mParamDeclaration->mModToken); + Fail("Parameters with 'params' specifiers can only be used for array, span, tuple, delegate, or function types", paramDef->mParamDeclaration->mModToken); // Failure case, make it an Object[] resolvedParamType = CreateArrayType(mContext->mBfObjectType, 1); } @@ -24581,10 +25455,22 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool { if (usedParamDefIdx[methodParam.mParamDefIdx] > 1) methodParam.mDelegateParamNameCombine = true; - BfMethodInstance* invokeMethodInstance = methodParam.GetDelegateParamInvoke(); - String paramName = invokeMethodInstance->GetParamName(methodParam.mDelegateParamIdx); - if (usedNames.Contains(paramName)) - methodParam.mDelegateParamNameCombine = true; + + if (methodParam.mResolvedType->IsTuple()) + { + auto tupleType = (BfTupleType*)methodParam.mResolvedType; + auto& fieldInstance = tupleType->mFieldInstances[methodParam.mDelegateParamIdx]; + auto paramName = fieldInstance.GetFieldDef()->mName; + if ((!usedNames.Add(paramName)) || (::isdigit((uint8)paramName[0]))) + methodParam.mDelegateParamNameCombine = true; + } + else + { + BfMethodInstance* invokeMethodInstance = methodParam.GetDelegateParamInvoke(); + String paramName = invokeMethodInstance->GetParamName(methodParam.mDelegateParamIdx); + if (!usedNames.Add(paramName)) + methodParam.mDelegateParamNameCombine = true; + } } } } @@ -24601,7 +25487,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool { BfTypeCode loweredTypeCode = BfTypeCode_None; BfTypeCode loweredTypeCode2 = BfTypeCode_None; - if ((!mIsComptimeModule) && (!methodDef->mIsMutating)) + if ((!mIsComptimeModule) && (!methodDef->mIsMutating) && (!methodInstance->ForcingThisPtr())) thisType->GetLoweredType(BfTypeUsage_Parameter, &loweredTypeCode, &loweredTypeCode2); argIdx++; if (loweredTypeCode2 != BfTypeCode_None) @@ -24697,6 +25583,14 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool { isError = false; } + else if (mCurTypeInstance->IsEnum()) + { + if (((methodInstance->mReturnType == typeInstance) && (methodInstance->GetParamType(0)->IsInteger())) || + ((methodInstance->mReturnType->IsInteger()) && (methodInstance->GetParamType(0) == typeInstance))) + { + isError = false; + } + } } } } @@ -25004,7 +25898,7 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool auto checkMethodInstance = typeInstance->mMethodInstanceGroups[checkMethod->mIdx].mDefault; if (checkMethodInstance == NULL) { - if ((methodDef->mDeclaringType->IsExtension()) && (!checkMethod->mDeclaringType->IsExtension())) + if (methodDef->mDeclaringType->IsExtension()) checkMethodInstance = GetRawMethodInstanceAtIdx(typeInstance, checkMethod->mIdx); if (checkMethodInstance == NULL) continue; @@ -25099,6 +25993,12 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool silentlyAllow = true; } + if (checkMethod->mMethodType == BfMethodType_CtorCalcAppend) + { + // Only use the main ctor as the error method + silentlyAllow = true; + } + if (!silentlyAllow) { if ((!methodDef->mName.IsEmpty()) || (checkMethodInstance->mMethodDef->mIsOperator)) @@ -25881,6 +26781,15 @@ bool BfModule::SlotVirtualMethod(BfMethodInstance* methodInstance, BfAmbiguityCo continue; } + if (iMethodIdx >= ifaceInst->mMethodInstanceGroups.mSize) + PopulateType(ifaceInst, BfPopulateType_DataAndMethods); + + if (iMethodIdx >= ifaceInst->mMethodInstanceGroups.mSize) + { + InternalError("Interface slotting error", methodInstance->mMethodDef->GetMutNode()); + break; + } + auto& iMethodGroup = ifaceInst->mMethodInstanceGroups[iMethodIdx]; auto iMethodInst = iMethodGroup.mDefault; if (iMethodInst == NULL) @@ -25933,12 +26842,17 @@ bool BfModule::SlotVirtualMethod(BfMethodInstance* methodInstance, BfAmbiguityCo bool isBetter = false; bool isWorse = false; - isBetter = (methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface != NULL); - isWorse = (prevMethod->mMethodInfoEx != NULL) && (prevMethod->mMethodInfoEx->mExplicitInterface != NULL); + isBetter = methodInstance->mReturnType == iMethodInst->mReturnType; + isWorse = prevMethod->mReturnType == iMethodInst->mReturnType; if (isBetter == isWorse) { - isBetter = methodInstance->mReturnType == iMethodInst->mReturnType; - isWorse = prevMethod->mReturnType == iMethodInst->mReturnType; + isBetter = (methodInstance->mMethodInfoEx != NULL) && (methodInstance->mMethodInfoEx->mExplicitInterface != NULL); + isWorse = (prevMethod->mMethodInfoEx != NULL) && (prevMethod->mMethodInfoEx->mExplicitInterface != NULL); + if ((isBetter) && (isWorse)) + { + isBetter = methodInstance->mMethodInfoEx->mExplicitInterface == ifaceInst; + isWorse = prevMethod->mMethodInfoEx->mExplicitInterface == ifaceInst; + } } if (isBetter == isWorse) diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 374564dd..1d0a905f 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -44,6 +44,7 @@ enum BfPopulateType BfPopulateType_IdentityNoRemapAlias, BfPopulateType_Declaration, BfPopulateType_BaseType, + BfPopulateType_CustomAttributes, BfPopulateType_Interfaces_Direct, BfPopulateType_AllowStaticMethods, BfPopulateType_Interfaces_All, @@ -92,6 +93,7 @@ enum BfEvalExprFlags : int64 BfEvalExprFlags_AppendFieldInitializer = 0x80000000, BfEvalExprFlags_NameOf = 0x100000000LL, BfEvalExprFlags_NameOfSuccess = 0x200000000LL, + BfEvalExprFlags_InParamsExpr = 0x400000000LL, BfEvalExprFlags_InheritFlags = BfEvalExprFlags_NoAutoComplete | BfEvalExprFlags_Comptime | BfEvalExprFlags_DeclType }; @@ -127,10 +129,11 @@ enum BfCastResultFlags : int8 enum BfAllocFlags : int8 { BfAllocFlags_None = 0, - BfAllocFlags_RawArray = 1, - BfAllocFlags_ZeroMemory = 2, - BfAllocFlags_NoDtorCall = 4, - BfAllocFlags_NoDefaultToMalloc = 8 + BfAllocFlags_HasAppendWantMark = 1, + BfAllocFlags_RawArray = 2, + BfAllocFlags_ZeroMemory = 4, + BfAllocFlags_NoDtorCall = 8, + BfAllocFlags_NoDefaultToMalloc = 0x10, }; enum BfProtectionCheckFlags : int8 @@ -320,6 +323,7 @@ public: bool mCastThis; bool mArgsNeedLoad; bool mIgnored; + bool mIsAllocaFunc; SLIList mDynList; BfIRValue mDynCallTail; @@ -1059,12 +1063,13 @@ public: BfConstResolveState* mConstResolveState; BfMethodInstance* mMethodInstance; BfHotDataReferenceBuilder* mHotDataReferenceBuilder; + BfTypeInstance* mPrivateTypeInstance; BfIRFunction mIRFunction; BfIRBlock mIRHeadBlock; BfIRBlock mIRInitBlock; BfIRBlock mIREntryBlock; - Array > mLocals; - HashSet > mLocalVarSet; + Array mLocals; + HashSet mLocalVarSet; Array mLocalMethods; Dictionary mLocalMethodMap; Dictionary mLocalMethodCache; // So any lambda 'capturing' and 'processing' stages use the same local method @@ -1133,6 +1138,7 @@ public: mPrevMethodState = NULL; mConstResolveState = NULL; mHotDataReferenceBuilder = NULL; + mPrivateTypeInstance = NULL; mHeadScope.mIsScopeHead = true; mCurScope = &mHeadScope; mTailScope = &mHeadScope; @@ -1476,6 +1482,7 @@ enum BfDeferredBlockFlags BfDeferredBlockFlag_DoNullChecks = 2, BfDeferredBlockFlag_SkipObjectAccessCheck = 4, BfDeferredBlockFlag_MoveNewBlocksToEnd = 8, + BfDeferredBlockFlag_IsAllocaFunc = 0x10 }; enum BfGetCustomAttributesFlags @@ -1545,6 +1552,7 @@ public: Dictionary mStringCharPtrPool; Array mStringPoolRefs; HashSet mUnreifiedStringPoolRefs; + HashSet mSignatureIdRefs; Array mPrevIRBuilders; // Before extensions BfIRBuilder* mBfIRBuilder; @@ -1613,6 +1621,7 @@ public: public: void FatalError(const StringImpl& error, const char* file = NULL, int line = -1, int column = -1); void FatalError(const StringImpl& error, BfAstNode* refNode); + void FatalError(const StringImpl& error, BfFailHandleKind failHandleKind); void InternalError(const StringImpl& error, BfAstNode* refNode = NULL, const char* file = NULL, int line = -1); void NotImpl(BfAstNode* astNode); void AddMethodReference(const BfMethodRef& methodRef, BfGetMethodInstanceFlags flags = BfGetMethodInstanceFlag_None); @@ -1620,6 +1629,7 @@ public: void GetAccessAllowed(BfTypeInstance* checkType, bool& allowProtected, bool& allowPrivate); bool CheckProtection(BfProtectionCheckFlags& flags, BfTypeInstance* memberOwner, BfProject* memberProject, BfProtection memberProtection, BfTypeInstance* lookupStartType); void SetElementType(BfAstNode* astNode, BfSourceElementType elementType); + void SetHighestElementType(BfAstNode* astNode, BfSourceElementType elementType); bool PreFail(); void SetFail(); void VerifyOnDemandMethods(); @@ -1632,7 +1642,7 @@ public: BfError* Warn(int warningNum, const StringImpl& warning, BfAstNode* refNode = NULL, bool isPersistent = false, bool showInSpecialized = false); void CheckErrorAttributes(BfTypeInstance* typeInstance, BfMethodInstance* methodInstance, BfFieldInstance* fieldInstance, BfCustomAttributes* customAttributes, BfAstNode* targetSrc); void CheckRangeError(BfType* type, BfAstNode* refNode); - bool CheckCircularDataError(bool failTypes = true); + bool CheckCircularDataError(bool failTypes = true, bool forceFail = false); BfFileInstance* GetFileFromNode(BfAstNode* astNode); //void UpdateSrcPos(BfAstNode* astNode, bool setDebugLoc = true, int debugLocOffset = 0, bool force = false); void UpdateSrcPos(BfAstNode* astNode, BfSrcPosFlags flags = BfSrcPosFlag_None, int debugLocOffset = 0); @@ -1674,9 +1684,10 @@ public: void CurrentAddToConstHolder(BfIRValue& irVal); void ClearConstData(); bool HasUnactializedConstant(BfConstant* constant, BfIRConstHolder* constHolder); + bool HasGlobalVarReference(BfConstant* constant, BfIRConstHolder* constHolder); BfTypedValue GetTypedValueFromConstant(BfConstant* constant, BfIRConstHolder* constHolder, BfType* wantType); BfIRValue ConstantToCurrent(BfConstant* constant, BfIRConstHolder* constHolder, BfType* wantType, bool allowUnactualized = false); - void ValidateCustomAttributes(BfCustomAttributes* customAttributes, BfAttributeTargets attrTarget); + void ValidateCustomAttributes(BfCustomAttributes* customAttributes, BfAttributeTargets attrTarget, bool force = false); void GetCustomAttributes(BfCustomAttributes* customAttributes, BfAttributeDirective* attributesDirective, BfAttributeTargets attrType, BfGetCustomAttributesFlags flags = BfGetCustomAttributesFlags_None, BfCaptureInfo* captureInfo = NULL); BfCustomAttributes* GetCustomAttributes(BfAttributeDirective* attributesDirective, BfAttributeTargets attrType, BfGetCustomAttributesFlags flags = BfGetCustomAttributesFlags_None, BfCaptureInfo* captureInfo = NULL); BfCustomAttributes* GetCustomAttributes(BfTypeDef* typeDef); @@ -1688,6 +1699,7 @@ public: BfTypedValue FlushNullConditional(BfTypedValue result, bool ignoreNullable = false); void NewScopeState(bool createLexicalBlock = true, bool flushValueScope = true); // returns prev scope data + BfIRValue CreateAlloca(BfIRType irType, int align, bool addLifetime = true, const char* name = NULL, BfIRValue arraySize = BfIRValue()); BfIRValue CreateAlloca(BfType* type, bool addLifetime = true, const char* name = NULL, BfIRValue arraySize = BfIRValue()); BfIRValue CreateAllocaInst(BfTypeInstance* typeInst, bool addLifetime = true, const char* name = NULL); BfDeferredCallEntry* AddStackAlloc(BfTypedValue val, BfIRValue arraySize, BfAstNode* refNode, BfScopeData* scope, bool condAlloca = false, bool mayEscape = false, BfIRBlock valBlock = BfIRBlock()); @@ -1698,6 +1710,7 @@ public: BfIRValue ValueScopeStart(); void ValueScopeEnd(BfIRValue valueScopeStart); BfProjectSet* GetVisibleProjectSet(); + bool IsProjectVisible(BfProject* project); void AddBasicBlock(BfIRBlock bb, bool activate = true); void VisitEmbeddedStatement(BfAstNode* stmt, BfExprEvaluator* exprEvaluator = NULL, BfEmbeddedStatementFlags flags = BfEmbeddedStatementFlags_None); @@ -1712,7 +1725,7 @@ public: void EmitDeferredCall(BfModuleMethodInstance moduleMethodInstance, SizedArrayImpl& llvmArgs, BfDeferredBlockFlags flags = BfDeferredBlockFlag_None); bool AddDeferredCallEntry(BfDeferredCallEntry* deferredCallEntry, BfScopeData* scope); BfDeferredCallEntry* AddDeferredBlock(BfBlock* block, BfScopeData* scope, Array* captures = NULL); - BfDeferredCallEntry* AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl& llvmArgs, BfScopeData* scope, BfAstNode* srcNode = NULL, bool bypassVirtual = false, bool doNullCheck = false); + BfDeferredCallEntry* AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl& llvmArgs, BfScopeData* scope, BfAstNode* srcNode = NULL, bool bypassVirtual = false, bool doNullCheck = false, bool isAllocaFunc = false); void EmitDeferredCall(BfScopeData* scopeData, BfDeferredCallEntry& deferredCallEntry, bool moveBlocks); void EmitDeferredCallProcessor(BfScopeData* scopeData, SLIList& callEntries, BfIRValue callTail); void EmitDeferredCallProcessorInstances(BfScopeData* scopeData); @@ -1732,7 +1745,7 @@ public: void CleanupFileInstances(); void AssertErrorState(); void AssertParseErrorState(); - void InitTypeInst(BfTypedValue typedValue, BfScopeData* scope, bool zeroMemory, BfIRValue dataSize); + void InitTypeInst(BfTypedValue typedValue, BfScopeData* scope, bool zeroMemory, BfIRValue dataSize, BfAllocFlags allocFlags = BfAllocFlags_None); bool IsAllocatorAligned(); BfIRValue AllocBytes(BfAstNode* refNode, const BfAllocTarget& allocTarget, BfType* type, BfIRValue sizeValue, BfIRValue alignValue, BfAllocFlags allocFlags/*bool zeroMemory, bool defaultToMalloc*/); BfIRValue GetMarkFuncPtr(BfType* type); @@ -1833,6 +1846,7 @@ public: bool AreConstraintsSubset(BfGenericParamInstance* checkInner, BfGenericParamInstance* checkOuter); bool CheckConstraintState(BfAstNode* refNode); void ValidateGenericParams(BfGenericParamKind genericParamKind, Span genericParams); + void SetGenericValidationError(BfTypeInstance* typeInst); bool ShouldAllowMultipleDefinitions(BfTypeInstance* typeInst, BfTypeDef* firstDeclaringTypeDef, BfTypeDef* secondDeclaringTypeDef); void CheckInjectNewRevision(BfTypeInstance* typeInstance); void InitType(BfType* resolvedTypeRef, BfPopulateType populateType); @@ -1883,6 +1897,7 @@ public: void CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLocal = false); void ResolveConstField(BfTypeInstance* typeInst, BfFieldInstance* fieldInstance, BfFieldDef* field, bool forceResolve = false); BfTypedValue GetFieldInitializerValue(BfFieldInstance* fieldInstance, BfExpression* initializer = NULL, BfFieldDef* fieldDef = NULL, BfType* fieldType = NULL, bool doStore = false); + bool TryGetAppendedObjectInfo(BfFieldInstance* fieldInstance, int& dataSize, int& alignSize); void AppendedObjectInit(BfFieldInstance* fieldInstance); void MarkFieldInitialized(BfFieldInstance* fieldInstance); bool IsThreadLocal(BfFieldInstance* fieldInstance); @@ -1919,7 +1934,10 @@ public: String GenericParamSourceToString(const BfGenericParamSource& genericParamSource); bool CheckGenericConstraints(const BfGenericParamSource& genericParamSource, BfType* checkArgType, BfAstNode* checkArgTypeRef, BfGenericParamInstance* genericParamInst, BfTypeVector* methodGenericArgs = NULL, BfError** errorOut = NULL); BfIRValue AllocLocalVariable(BfType* type, const StringImpl& name, bool doLifetimeEnd = true); + BfTypedValue CreateOutVariable(BfAstNode* refNode, BfVariableDeclaration* variableDeclaration, BfAstNode* paramNameNode, BfType* variableType, BfTypedValue initValue); + void MoveLocalToParentScope(BfLocalVariable* localVar); void DoAddLocalVariable(BfLocalVariable* localVar); + void FixLocalVariable(BfLocalVariable* localVar); void DoLocalVariableDebugInfo(BfLocalVariable* localVar, bool doAliasValue = false, BfIRValue declareBefore = BfIRValue(), BfIRInitType initType = BfIRInitType_NotSet); BfLocalVariable* AddLocalVariableDef(BfLocalVariable* localVarDef, bool addDebugInfo = false, bool doAliasValue = false, BfIRValue declareBefore = BfIRValue(), BfIRInitType initType = BfIRInitType_NotSet); bool TryLocalVariableInit(BfLocalVariable* localVar); @@ -1952,10 +1970,10 @@ public: BfType* ResolveGenericType(BfType* unspecializedType, BfTypeVector* typeGenericArguments, BfTypeVector* methodGenericArguments, BfType* selfType, bool allowFail = false); BfType* ResolveSelfType(BfType* type, BfType* selfType); bool IsUnboundGeneric(BfType* type); - BfGenericParamInstance* GetGenericTypeParamInstance(int paramIdx); + BfGenericParamInstance* GetGenericTypeParamInstance(int paramIdx, BfFailHandleKind failHandleKind = BfFailHandleKind_Normal); BfGenericParamInstance* GetGenericParamInstance(BfGenericParamType* type, bool checkMixinBind = false, BfFailHandleKind failHandleKind = BfFailHandleKind_Normal); void GetActiveTypeGenericParamInstances(SizedArray& genericParamInstance); - BfGenericParamInstance* GetMergedGenericParamData(BfGenericParamType* type, BfGenericParamFlags& outFlags, BfType*& outTypeConstraint); + BfGenericParamInstance* GetMergedGenericParamData(BfType* type, BfGenericParamFlags& outFlags, BfType*& outTypeConstraint); BfTypeInstance* GetBaseType(BfTypeInstance* typeInst); void HandleTypeGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, int typeGenericParamIdx); void HandleMethodGenericParamRef(BfAstNode* refNode, BfTypeDef* typeDef, BfMethodDef* methodDef, int typeGenericParamIdx); @@ -1980,6 +1998,7 @@ public: BfType* ResolveTypeRefAllowUnboundGenerics(BfTypeReference* typeRef, BfPopulateType populateType = BfPopulateType_Data, BfResolveTypeRefFlags resolveFlags = (BfResolveTypeRefFlags)0, bool resolveGenericParam = true); BfType* ResolveTypeRef_Type(BfAstNode* astNode, const BfSizedArray* genericArgs, BfPopulateType populateType, BfResolveTypeRefFlags& resolveFlags); BfType* ResolveTypeRef_Ref(BfAstNode* astNode, const BfSizedArray* genericArgs, BfPopulateType populateType, BfResolveTypeRefFlags& resolveFlags); + BfType* ResolveTypeRef_Ref(BfAstNode* astNode, BfPopulateType populateType); BfType* ResolveTypeRef(BfAstNode* astNode, const BfSizedArray* genericArgs, BfPopulateType populateType = BfPopulateType_Data, BfResolveTypeRefFlags resolveFlags = (BfResolveTypeRefFlags)0); BfType* ResolveTypeDef(BfTypeDef* typeDef, BfPopulateType populateType = BfPopulateType_Data, BfResolveTypeRefFlags resolveFlags = BfResolveTypeRefFlag_None); @@ -2002,6 +2021,9 @@ public: void CreateDelegateInvokeMethod(); BfType* GetDelegateReturnType(BfType* delegateType); BfMethodInstance* GetDelegateInvokeMethod(BfTypeInstance* typeInstance); + String GetDelegateSignatureString(BfTypeInstance* typeInstance); + int GetSignatureId(const StringImpl& str); + int GetDelegateSignatureId(BfTypeInstance* typeInstance); String GetLocalMethodName(const StringImpl& baseName, BfAstNode* anchorNode, BfMethodState* declMethodState, BfMixinState* declMixinState); BfMethodDef* GetLocalMethodDef(BfLocalMethod* localMethod); BfModuleMethodInstance GetLocalMethodInstance(BfLocalMethod* localMethod, const BfTypeVector& methodGenericArguments, BfMethodInstance* methodInstance = NULL, bool force = false); @@ -2014,6 +2036,7 @@ public: void AddMethodToWorkList(BfMethodInstance* methodInstance); bool IsInterestedInMethod(BfTypeInstance* typeInstance, BfMethodDef* methodDef); void CalcAppendAlign(BfMethodInstance* methodInst); + BfAllowAppendKind GetBaseAllowAppend(BfMethodInstance* curMethodInstance); BfTypedValue TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl& args, bool force = false); BfTypedValue CallBaseCtorCalc(bool constOnly); void EmitCtorCalcAppend(); @@ -2085,10 +2108,12 @@ public: BfIRValue CreateClassVDataGlobal(BfTypeInstance* typeInstance, int* outNumElements = NULL, String* outMangledName = NULL); BfIRValue GetClassVDataPtr(BfTypeInstance* typeInstance); BfIRValue CreateClassVDataExtGlobal(BfTypeInstance* declTypeInst, BfTypeInstance* implTypeInst, int startVirtIdx); - BfIRValue CreateTypeDataRef(BfType* type); + BfIRValue CreateTypeDataRef(BfType* type, bool forceConstant = false); void EncodeAttributeData(BfTypeInstance* typeInstance, BfType* argType, BfIRValue arg, SizedArrayImpl& data, Dictionary& usedStringIdMap); BfIRValue CreateFieldData(BfFieldInstance* fieldInstance, int customAttrIdx); void CreateSlotOfs(BfTypeInstance* typeInstance); + BfIRValue GetTypeTypeData(BfType* type, BfCreateTypeDataContext& ctx, bool needsTypeData, bool wantsTypeDecl, bool needsTypeNames, int& typeFlags, int& typeCode); + BfIRValue CreateTypeDeclData(BfType* type, BfProject* curProject); BfIRValue CreateTypeData(BfType* type, BfCreateTypeDataContext& ctx, bool forceReflectFields, bool needsTypeData, bool needsTypeNames, bool needsVData); BfIRValue FixClassVData(BfIRValue value); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 017d8807..af827b7a 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -394,6 +394,26 @@ void BfModule::ValidateGenericParams(BfGenericParamKind genericParamKind, SpanmGenericTypeInfo == NULL) || (typeInst->mGenericTypeInfo->mHadValidateErrors)) + return; + typeInst->mGenericTypeInfo->mHadValidateErrors = true; + + for (auto depKV : typeInst->mDependencyMap) + { + auto depType = depKV.mKey; + auto depEntry = depKV.mValue; + if ((depEntry.mFlags & BfDependencyMap::DependencyFlag_TypeGenericArg) != 0) + { + BF_ASSERT(depType->IsGenericTypeInstance()); + + // If A had validate errors then consider B> to have validate errors + SetGenericValidationError(depType->ToTypeInstance()); + } + } +} + bool BfModule::ValidateGenericConstraints(BfAstNode* typeRef, BfTypeInstance* genericTypeInst, bool ignoreErrors) { if ((mCurTypeInstance != NULL) && (mCurTypeInstance->IsTypeAlias()) && (mCurTypeInstance->IsGenericTypeInstance())) @@ -421,7 +441,7 @@ bool BfModule::ValidateGenericConstraints(BfAstNode* typeRef, BfTypeInstance* ge mContext->mUnreifiedModule->PopulateType(underlyingType, BfPopulateType_Declaration); bool result = ValidateGenericConstraints(typeRef, underlyingGenericType, ignoreErrors); if (underlyingGenericType->mGenericTypeInfo->mHadValidateErrors) - genericTypeInst->mGenericTypeInfo->mHadValidateErrors = true; + SetGenericValidationError(genericTypeInst); return result; } return true; @@ -443,7 +463,7 @@ bool BfModule::ValidateGenericConstraints(BfAstNode* typeRef, BfTypeInstance* ge auto outerType = GetOuterType(genericTypeInst); mContext->mUnreifiedModule->PopulateType(outerType, BfPopulateType_Declaration); if ((outerType->mGenericTypeInfo != NULL) && (outerType->mGenericTypeInfo->mHadValidateErrors)) - genericTypeInst->mGenericTypeInfo->mHadValidateErrors = true; + SetGenericValidationError(genericTypeInst); } for (int paramIdx = startGenericParamIdx; paramIdx < (int)genericTypeInst->mGenericTypeInfo->mGenericParams.size(); paramIdx++) @@ -464,7 +484,7 @@ bool BfModule::ValidateGenericConstraints(BfAstNode* typeRef, BfTypeInstance* ge if ((genericArg == NULL) || (!CheckGenericConstraints(BfGenericParamSource(genericTypeInst), genericArg, typeRef, genericParamInstance, NULL, &error))) { if (!genericTypeInst->IsUnspecializedTypeVariation()) - genericTypeInst->mGenericTypeInfo->mHadValidateErrors = true; + SetGenericValidationError(genericTypeInst); return false; } } @@ -739,8 +759,16 @@ void BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType) { auto genericTypeInst = (BfTypeInstance*)resolvedTypeRef; for (auto typeGenericArg : genericTypeInst->mGenericTypeInfo->mTypeGenericArguments) - { - BF_ASSERT((typeGenericArg->mRebuildFlags & BfTypeRebuildFlag_Deleted) == 0); + { + //BF_ASSERT((typeGenericArg->mRebuildFlags & BfTypeRebuildFlag_Deleted) == 0); + if ((typeGenericArg->mRebuildFlags & BfTypeRebuildFlag_Deleted) != 0) + { + mCompiler->RequestExtraCompile(); + InternalError("Using deleted generic type argument in PopulateType"); + TypeFailed(genericTypeInst); + return; + } + if (mIsReified) { // Try to reify any generic args @@ -1040,12 +1068,18 @@ void BfModule::TypeFailed(BfTypeInstance* typeInstance) typeInstance->mAlign = 1; if (typeInstance->mSize == -1) typeInstance->mSize = 1; + if (typeInstance->mContext == NULL) + typeInstance->mContext = mContext; mContext->mFailTypes.TryAdd(typeInstance, BfFailKind_Normal); mHadBuildError = true; } -bool BfModule::CheckCircularDataError(bool failTypes) +bool BfModule::CheckCircularDataError(bool failTypes, bool forceFail) { + // First check to see if the forceFail is necessary + if ((forceFail) && (CheckCircularDataError(failTypes, false))) + return true; + // Find two loops of mCurTypeInstance. Just finding one loop can give some false errors. BfTypeState* circularTypeStateEnd = NULL; @@ -1055,6 +1089,9 @@ bool BfModule::CheckCircularDataError(bool failTypes) bool isPreBaseCheck = checkTypeState->mPopulateType == BfPopulateType_Declaration; while (true) { + if (forceFail) + break; + if (checkTypeState == NULL) return false; @@ -1090,7 +1127,9 @@ bool BfModule::CheckCircularDataError(bool failTypes) } bool hadError = false; - checkTypeState = mContext->mCurTypeState->mPrevState; + checkTypeState = mContext->mCurTypeState; + if (!forceFail) + checkTypeState = checkTypeState->mPrevState; while (true) { if (checkTypeState == NULL) @@ -1106,7 +1145,12 @@ bool BfModule::CheckCircularDataError(bool failTypes) continue; } - if ((checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL) && + if (forceFail) + { + // Go all the way through + NOP; + } + else if ((checkTypeState->mCurAttributeTypeRef == NULL) && (checkTypeState->mCurBaseTypeRef == NULL) && (checkTypeState->mCurFieldDef == NULL) && ((checkTypeState->mType == NULL) || (checkTypeState->mType->IsTypeInstance()))) return hadError; @@ -1130,6 +1174,11 @@ bool BfModule::CheckCircularDataError(bool failTypes) Fail(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(checkTypeState->mType).c_str(), checkTypeState->mCurFieldDef->mName.c_str()), checkTypeState->mCurFieldDef->mTypeRef, true); } + else if ((checkTypeState->mCurMethodDef != NULL) && (checkTypeState->mCurMethodDef->mMethodDeclaration != NULL)) + { + Fail(StrFormat("Method '%s.%s' causes a data cycle", TypeToString(checkTypeState->mType).c_str(), checkTypeState->mCurMethodDef->mName.c_str()), + checkTypeState->mCurMethodDef->GetRefNode(), true); + } else if (checkTypeState->mCurFieldDef != NULL) { BfAstNode* refNode = checkTypeState->mCurFieldDef->GetRefNode(); @@ -1335,6 +1384,17 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType if (mContext->mGhostDependencies.Contains(resolvedTypeRef)) { // Not a nice state, but we should be able to recover + if (resolvedTypeRef->mDefineState < BfTypeDefineState_Defined) + { + resolvedTypeRef->mDefineState = BfTypeDefineState_Defined; + resolvedTypeRef->mSize = 0; + resolvedTypeRef->mAlign = 1; + if (typeInstance != NULL) + { + typeInstance->mInstSize = 0; + typeInstance->mInstAlign = 1; + } + } return; } @@ -1660,6 +1720,7 @@ void BfModule::PopulateType(BfType* resolvedTypeRef, BfPopulateType populateType case BfTypeCode_Interface: case BfTypeCode_Enum: case BfTypeCode_TypeAlias: + case BfTypeCode_Inferred: // Implemented below break; case BfTypeCode_Extension: @@ -2464,194 +2525,224 @@ void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeIn } void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, BfFieldInstance* fieldInstance, BfCustomAttributes* customAttributes, Dictionary& prevAttrInstances, bool underlyingTypeDeferred) -{ +{ for (auto& customAttribute : customAttributes->mAttributes) { - if ((customAttribute.mDeclaringType->IsExtension()) && (typeInstance->IsGenericTypeInstance()) && (!typeInstance->IsUnspecializedTypeVariation())) - { - if (!typeInstance->IsTypeMemberIncluded(customAttribute.mDeclaringType, typeInstance->mTypeDef, this)) - continue; - } - - auto attrType = customAttribute.mType; - - BfMethodInstance* methodInstance = NULL; bool isFieldApply = false; - BfIRValue irValue; - int checkDepth = 0; - auto checkAttrType = attrType; - while (checkAttrType != NULL) + bool hasFieldApply = false; + bool hasTypeApply = false; + + for (int pass = 0; pass < 2; pass++) { - mContext->mUnreifiedModule->PopulateType(checkAttrType, BfPopulateType_DataAndMethods); - if (checkAttrType->mDefineState < BfTypeDefineState_DefinedAndMethodsSlotted) - break; - - for (auto& ifaceEntry : checkAttrType->mInterfaces) + if (pass == 1) { - isFieldApply = false; - isFieldApply = (ceEmitContext != NULL) && (fieldInstance != NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIOnFieldInitTypeDef)); - - if ((isFieldApply) || - ((ceEmitContext != NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIComptimeTypeApply))) || - ((ceEmitContext != NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIOnTypeInitTypeDef))) || - ((ceEmitContext == NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIOnTypeDoneTypeDef)))) + if ((hasFieldApply) && (hasTypeApply)) { - // Passes + // Keep going - do the field apply now } else - continue; - - prevAttrInstances.TryGetValue(checkAttrType, &irValue); - methodInstance = checkAttrType->mInterfaceMethodTable[ifaceEntry.mStartInterfaceTableIdx].mMethodRef; - break; + break; } - if (methodInstance != NULL) - break; - checkAttrType = checkAttrType->mBaseType; - checkDepth++; - } - - if (methodInstance == NULL) - continue; - - SetAndRestoreValue prevEmitContext(mCompiler->mCeMachine->mCurEmitContext, ceEmitContext); - auto ceContext = mCompiler->mCeMachine->AllocContext(); - defer({ mCompiler->mCeMachine->ReleaseContext(ceContext); }); - - BfIRValue attrVal =ceContext->CreateAttribute(customAttribute.mRef, this, typeInstance->mConstHolder, &customAttribute); - for (int baseIdx = 0; baseIdx < checkDepth; baseIdx++) - attrVal = mBfIRBuilder->CreateExtractValue(attrVal, 0); - - SizedArray args; - if (!attrType->IsValuelessType()) - args.Add(attrVal); - if (isFieldApply) - { - auto fieldInfoType = ResolveTypeDef(mCompiler->mReflectFieldInfoTypeDef); - if (fieldInfoType != NULL) + if ((customAttribute.mDeclaringType->IsExtension()) && (typeInstance->IsGenericTypeInstance()) && (!typeInstance->IsUnspecializedTypeVariation())) { - SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); - SizedArray fieldData = - { - mBfIRBuilder->CreateConstAggZero(mBfIRBuilder->MapType(fieldInfoType->ToTypeInstance()->mBaseType, BfIRPopulateType_Identity)), - mBfIRBuilder->CreateTypeOf(mCurTypeInstance), // mTypeInstance - CreateFieldData(fieldInstance, -1) - }; - FixConstValueParams(fieldInfoType->ToTypeInstance(), fieldData); - auto fieldDataAgg = mBfIRBuilder->CreateConstAgg(mBfIRBuilder->MapType(fieldInfoType, BfIRPopulateType_Identity), fieldData); - args.Add(fieldDataAgg); + if (!typeInstance->IsTypeMemberIncluded(customAttribute.mDeclaringType, typeInstance->mTypeDef, this)) + continue; } - } - else - args.Add(mBfIRBuilder->CreateTypeOf(typeInstance)); - if (methodInstance->GetParamCount() > 1) - { - if (irValue) - args.Add(irValue); - else - args.Add(mBfIRBuilder->CreateConstNull()); - } - else - { - // Only allow a single instance - if (irValue) + auto attrType = customAttribute.mType; + + BfMethodInstance* methodInstance = NULL; + BfIRValue irValue; + int checkDepth = 0; + auto checkAttrType = attrType; + while (checkAttrType != NULL) + { + mContext->mUnreifiedModule->PopulateType(checkAttrType, BfPopulateType_DataAndMethods); + if (checkAttrType->mDefineState < BfTypeDefineState_DefinedAndMethodsSlotted) + break; + + for (auto& ifaceEntry : checkAttrType->mInterfaces) + { + isFieldApply = false; + isFieldApply = (ceEmitContext != NULL) && (fieldInstance != NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIOnFieldInitTypeDef)); + + if (((ceEmitContext != NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIComptimeTypeApply))) || + ((ceEmitContext != NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIOnTypeInitTypeDef))) || + ((ceEmitContext == NULL) && (ifaceEntry.mInterfaceType->IsInstanceOf(mCompiler->mIOnTypeDoneTypeDef)))) + { + // Passes + hasTypeApply = true; + if (pass == 1) + { + // Only find field inits now + continue; + } + } + else if (isFieldApply) + { + // Field passes + hasFieldApply = true; + if (methodInstance != NULL) + continue; + } + else + continue; + + prevAttrInstances.TryGetValue(checkAttrType, &irValue); + methodInstance = checkAttrType->mInterfaceMethodTable[ifaceEntry.mStartInterfaceTableIdx].mMethodRef; + if (pass == 1) + break; + } + + if (methodInstance != NULL) + break; + + checkAttrType = checkAttrType->mBaseType; + checkDepth++; + } + + if (methodInstance == NULL) continue; - } - DoPopulateType_CeCheckEnum(typeInstance, underlyingTypeDeferred); - if (fieldInstance != NULL) - mCompiler->mCeMachine->mFieldInstanceSet.Add(fieldInstance); - BfTypedValue result; - /// - { - SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); + SetAndRestoreValue prevEmitContext(mCompiler->mCeMachine->mCurEmitContext, ceEmitContext); + auto ceContext = mCompiler->mCeMachine->AllocContext(); + defer({ mCompiler->mCeMachine->ReleaseContext(ceContext); }); - CeCallSource callSource; - callSource.mRefNode = customAttribute.mRef; + BfIRValue attrVal = ceContext->CreateAttribute(customAttribute.mRef, this, typeInstance->mConstHolder, &customAttribute); + for (int baseIdx = 0; baseIdx < checkDepth; baseIdx++) + attrVal = mBfIRBuilder->CreateExtractValue(attrVal, 0); + + SizedArray args; + if (!attrType->IsValuelessType()) + args.Add(attrVal); if (isFieldApply) { - callSource.mKind = CeCallSource::Kind_FieldInit; - callSource.mFieldInstance = fieldInstance; + auto fieldInfoType = ResolveTypeDef(mCompiler->mReflectFieldInfoTypeDef); + if (fieldInfoType != NULL) + { + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); + SizedArray fieldData = + { + mBfIRBuilder->CreateConstAggZero(mBfIRBuilder->MapType(fieldInfoType->ToTypeInstance()->mBaseType, BfIRPopulateType_Identity)), + mBfIRBuilder->CreateTypeOf(mCurTypeInstance), // mTypeInstance + CreateFieldData(fieldInstance, -1) + }; + FixConstValueParams(fieldInfoType->ToTypeInstance(), fieldData); + auto fieldDataAgg = mBfIRBuilder->CreateConstAgg(mBfIRBuilder->MapType(fieldInfoType, BfIRPopulateType_Identity), fieldData); + args.Add(fieldDataAgg); + } } - else if (ceEmitContext != NULL) + else + args.Add(mBfIRBuilder->CreateTypeOf(typeInstance)); + + if (methodInstance->GetParamCount() > 1) { - callSource.mKind = CeCallSource::Kind_TypeInit; + if (irValue) + args.Add(irValue); + else + args.Add(mBfIRBuilder->CreateConstNull()); } else { - callSource.mKind = CeCallSource::Kind_TypeDone; + // Only allow a single instance + if (irValue) + continue; } - result = ceContext->Call(callSource, this, methodInstance, args, (CeEvalFlags)(CeEvalFlags_ForceReturnThis | CeEvalFlags_IgnoreConstEncodeFailure), NULL); - } - if (fieldInstance != NULL) - mCompiler->mCeMachine->mFieldInstanceSet.Remove(fieldInstance); - if (result.mType == methodInstance->GetOwner()) - prevAttrInstances[methodInstance->GetOwner()] = result.mValue; - - if (ceEmitContext == NULL) - continue; - - if (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotted) - return; - - if (typeInstance->mDefineState != BfTypeDefineState_CETypeInit) - { - // We populated before we could finish - AssertErrorState(); - } - else - { - auto owner = methodInstance->GetOwner(); - int typeId = owner->mTypeId; - if ((!result) && (mCompiler->mFastFinish)) + DoPopulateType_CeCheckEnum(typeInstance, underlyingTypeDeferred); + if (fieldInstance != NULL) + mCompiler->mCeMachine->mFieldInstanceSet.Add(fieldInstance); + BfTypedValue result; + /// { - if ((typeInstance->mCeTypeInfo != NULL) && (typeInstance->mCeTypeInfo->mNext == NULL)) - typeInstance->mCeTypeInfo->mNext = new BfCeTypeInfo(); - if ((typeInstance->mCeTypeInfo != NULL) && (typeInstance->mCeTypeInfo->mNext != NULL)) - typeInstance->mCeTypeInfo->mNext->mFastFinished = true; - if (typeInstance->mCeTypeInfo != NULL) + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); + + CeCallSource callSource; + callSource.mRefNode = customAttribute.mRef; + if (isFieldApply) { - BfCeTypeEmitEntry* entry = NULL; - if (typeInstance->mCeTypeInfo->mTypeIFaceMap.TryGetValue(typeId, &entry)) + callSource.mKind = CeCallSource::Kind_FieldInit; + callSource.mFieldInstance = fieldInstance; + } + else if (ceEmitContext != NULL) + { + callSource.mKind = CeCallSource::Kind_TypeInit; + } + else + { + callSource.mKind = CeCallSource::Kind_TypeDone; + } + + result = ceContext->Call(callSource, this, methodInstance, args, (CeEvalFlags)(CeEvalFlags_ForceReturnThis | CeEvalFlags_IgnoreConstEncodeFailure), NULL); + } + if (fieldInstance != NULL) + mCompiler->mCeMachine->mFieldInstanceSet.Remove(fieldInstance); + if (result.mType == methodInstance->GetOwner()) + prevAttrInstances[methodInstance->GetOwner()] = result.mValue; + + if (ceEmitContext == NULL) + continue; + + if (typeInstance->mDefineState == BfTypeDefineState_DefinedAndMethodsSlotted) + return; + + if (typeInstance->mDefineState != BfTypeDefineState_CETypeInit) + { + // We populated before we could finish + AssertErrorState(); + } + else + { + auto owner = methodInstance->GetOwner(); + int typeId = owner->mTypeId; + if ((!result) && (mCompiler->mFastFinish)) + { + if ((typeInstance->mCeTypeInfo != NULL) && (typeInstance->mCeTypeInfo->mNext == NULL)) + typeInstance->mCeTypeInfo->mNext = new BfCeTypeInfo(); + if ((typeInstance->mCeTypeInfo != NULL) && (typeInstance->mCeTypeInfo->mNext != NULL)) + typeInstance->mCeTypeInfo->mNext->mFastFinished = true; + if (typeInstance->mCeTypeInfo != NULL) { - ceEmitContext->mEmitData = entry->mEmitData; + BfCeTypeEmitEntry* entry = NULL; + if (typeInstance->mCeTypeInfo->mTypeIFaceMap.TryGetValue(typeId, &entry)) + { + ceEmitContext->mEmitData = entry->mEmitData; + } } } - } - else - { - if (ceEmitContext->HasEmissions()) + else { - if (typeInstance->mCeTypeInfo == NULL) - typeInstance->mCeTypeInfo = new BfCeTypeInfo(); - if (typeInstance->mCeTypeInfo->mNext == NULL) - typeInstance->mCeTypeInfo->mNext = new BfCeTypeInfo(); + if (ceEmitContext->HasEmissions()) + { + if (typeInstance->mCeTypeInfo == NULL) + typeInstance->mCeTypeInfo = new BfCeTypeInfo(); + if (typeInstance->mCeTypeInfo->mNext == NULL) + typeInstance->mCeTypeInfo->mNext = new BfCeTypeInfo(); - BfCeTypeEmitEntry entry; - entry.mEmitData = ceEmitContext->mEmitData; - typeInstance->mCeTypeInfo->mNext->mTypeIFaceMap[typeId] = entry; - typeInstance->mCeTypeInfo->mNext->mAlign = BF_MAX(typeInstance->mCeTypeInfo->mNext->mAlign, ceEmitContext->mAlign); + BfCeTypeEmitEntry entry; + entry.mEmitData = ceEmitContext->mEmitData; + typeInstance->mCeTypeInfo->mNext->mTypeIFaceMap[typeId] = entry; + typeInstance->mCeTypeInfo->mNext->mAlign = BF_MAX(typeInstance->mCeTypeInfo->mNext->mAlign, ceEmitContext->mAlign); + } + + if ((ceEmitContext->mFailed) && (typeInstance->mCeTypeInfo != NULL)) + typeInstance->mCeTypeInfo->mFailed = true; } - if ((ceEmitContext->mFailed) && (typeInstance->mCeTypeInfo != NULL)) - typeInstance->mCeTypeInfo->mFailed = true; - } + if ((ceEmitContext->HasEmissions()) && (!mCompiler->mFastFinish)) + { + String ctxStr = "comptime "; + ctxStr += methodInstance->mMethodDef->mName; + ctxStr += " of "; + ctxStr += TypeToString(attrType); + ctxStr += " to "; + ctxStr += TypeToString(typeInstance); + ctxStr += " "; + ctxStr += customAttribute.mRef->LocationToString(); - if ((ceEmitContext->HasEmissions()) && (!mCompiler->mFastFinish)) - { - String ctxStr = "comptime "; - ctxStr += methodInstance->mMethodDef->mName; - ctxStr += " of "; - ctxStr += TypeToString(attrType); - ctxStr += " to "; - ctxStr += TypeToString(typeInstance); - ctxStr += " "; - ctxStr += customAttribute.mRef->LocationToString(); - - UpdateCEEmit(ceEmitContext, typeInstance, customAttribute.mDeclaringType, ctxStr, customAttribute.mRef, BfCeTypeEmitSourceKind_Type); + UpdateCEEmit(ceEmitContext, typeInstance, customAttribute.mDeclaringType, ctxStr, customAttribute.mRef, BfCeTypeEmitSourceKind_Type); + } } } } @@ -2716,7 +2807,7 @@ void BfModule::ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* if (typeInstance->mCustomAttributes != NULL) HandleCEAttributes(ceEmitContext, typeInstance, NULL, typeInstance->mCustomAttributes, prevAttrInstances, underlyingTypeDeferred); - if (ceEmitContext != NULL) + if ((ceEmitContext != NULL) || (onCompileKind == BfCEOnCompileKind_TypeDone)) { for (auto& fieldInstance : typeInstance->mFieldInstances) { @@ -2784,6 +2875,7 @@ void BfModule::ExecuteCEOnCompile(CeEmitContext* ceEmitContext, BfTypeInstance* BfTypeState typeState; typeState.mPrevState = mContext->mCurTypeState; + typeState.mType = typeInstance; typeState.mForceActiveTypeDef = methodDef->mDeclaringType; SetAndRestoreValue prevTypeState(mContext->mCurTypeState, &typeState); @@ -3255,9 +3347,12 @@ void BfModule::PopulateUsingFieldData(BfTypeInstance* typeInstance) } auto fieldInstance = &usingType->mFieldInstances[fieldDef->mIdx]; - auto fieldTypeInst = fieldInstance->mResolvedType->ToTypeInstance(); - if (fieldTypeInst != NULL) - _CheckType(fieldTypeInst, fieldDef->mIsStatic); + if (fieldInstance->mResolvedType != NULL) + { + auto fieldTypeInst = fieldInstance->mResolvedType->ToTypeInstance(); + if (fieldTypeInst != NULL) + _CheckType(fieldTypeInst, fieldDef->mIsStatic); + } } for (auto propDef : usingType->mTypeDef->mProperties) @@ -3472,6 +3567,9 @@ void BfModule::DoPopulateType_TypeAlias(BfTypeAliasType* typeAlias) void BfModule::DoPopulateType_InitSearches(BfTypeInstance* typeInstance) { + if (typeInstance->IsBoxed()) + return; + auto typeDef = typeInstance->mTypeDef; auto _AddStaticSearch = [&](BfTypeDef* typeDef) @@ -3559,7 +3657,13 @@ void BfModule::DoPopulateType_InitSearches(BfTypeInstance* typeInstance) } void BfModule::DoPopulateType_FinishEnum(BfTypeInstance* typeInstance, bool underlyingTypeDeferred, HashContext* dataMemberHashCtx, BfType* unionInnerType) -{ +{ + if (typeInstance->mDefineState >= BfTypeDefineState_DefinedAndMethodsSlotting) + { + // Already locked + return; + } + if (typeInstance->IsEnum()) { int64 min = 0; @@ -4172,10 +4276,16 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { baseType = ResolveTypeDef(mCompiler->mPointerTTypeDef, BfPopulateType_Data); } + else if (resolvedTypeRef->IsTuple()) + { + baseType = ResolveTypeDef(mCompiler->mTupleTypeDef, BfPopulateType_Data); + } else if ((resolvedTypeRef->IsValueType()) && (typeDef != mCompiler->mValueTypeTypeDef)) { baseType = ResolveTypeDef(mCompiler->mValueTypeTypeDef, BfPopulateType_Data)->ToTypeInstance(); } + else if (typeDef->mTypeCode == BfTypeCode_Inferred) + baseType = mContext->mBfObjectType; if (baseType != NULL) defaultBaseTypeInst = baseType->ToTypeInstance(); @@ -4190,7 +4300,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy bool wantPopulateInterfaces = false; - BfTypeReference* baseTypeRef = NULL; + BfAstNode* baseTypeRef = NULL; if ((typeDef->mIsDelegate) && (!typeInstance->IsClosure())) { if (mCompiler->mDelegateTypeDef == NULL) @@ -4226,12 +4336,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy SetAndRestoreValue prevDefineState(typeInstance->mDefineState, BfTypeDefineState_ResolvingBaseType); bool populateBase = !typeInstance->mTypeFailed; - BfType* checkType = checkType = ResolveTypeRef(checkTypeRef, BfPopulateType_Declaration); + BfType* checkType = checkType = ResolveTypeRef_Ref(checkTypeRef, BfPopulateType_Declaration); if ((checkType != NULL) && (!checkType->IsInterface()) && (populateBase)) { SetAndRestoreValue prevBaseType(mContext->mCurTypeState->mCurBaseType, checkType->ToTypeInstance()); - PopulateType(checkType, (populateType <= BfPopulateType_BaseType) ? BfPopulateType_BaseType : BfPopulateType_Data); + PopulateType(checkType, BfPopulateType_Declaration); } if (typeInstance->mDefineState >= BfTypeDefineState_Defined) @@ -4412,6 +4522,8 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if (baseType != NULL) { baseTypeInst = baseType->ToTypeInstance(); + if ((baseTypeInst != NULL) && (typeDef->mTypeCode == BfTypeCode_Inferred)) + typeDef->mTypeCode = baseTypeInst->mTypeDef->mTypeCode; } if (typeInstance->mBaseType != NULL) @@ -4455,7 +4567,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy baseTypeInst = ResolveTypeDef(mCompiler->mBfObjectTypeDef)->ToTypeInstance(); } } - PopulateType(baseTypeInst, BfPopulateType_Data); + + if (populateType > BfPopulateType_CustomAttributes) + PopulateType(baseTypeInst, BfPopulateType_Data); typeInstance->mBaseTypeMayBeIncomplete = false; @@ -4501,7 +4615,15 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy typeInstance->mInstSize = baseTypeInst->mInstSize; typeInstance->mInstAlign = baseTypeInst->mInstAlign; typeInstance->mAlign = baseTypeInst->mAlign; - typeInstance->mSize = baseTypeInst->mSize; + typeInstance->mSize = baseTypeInst->mSize; + + if (baseTypeInst->IsValuelessCReprType()) + { + typeInstance->mInstSize = 0; + if (typeInstance->IsValueType()) + typeInstance->mSize = 0; + } + typeInstance->mHasPackingHoles = baseTypeInst->mHasPackingHoles; if (baseTypeInst->mIsTypedPrimitive) typeInstance->mIsTypedPrimitive = true; @@ -4735,6 +4857,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } + if (typeInstance->mDefineState < BfTypeDefineState_HasCustomAttributes) + typeInstance->mDefineState = BfTypeDefineState_HasCustomAttributes; + if (typeInstance->mTypeOptionsIdx == -2) { SetTypeOptions(typeInstance); @@ -4958,7 +5083,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy // For 'let', make read-only } else - { + { BfResolveTypeRefFlags resolveFlags = BfResolveTypeRefFlag_NoResolveGenericParam; if (initializer != NULL) resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_AllowInferredSizedArray); @@ -5077,10 +5202,13 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy // Handled elsewhere } else - { + { SetAndRestoreValue prevTypeRef(mContext->mCurTypeState->mCurFieldDef, fieldDef); + fieldInstance->mCustomAttributes = GetCustomAttributes(fieldDef->GetFieldDeclaration()->mAttributes, fieldDef->mIsStatic ? BfAttributeTargets_StaticField : BfAttributeTargets_Field); + } - fieldInstance->mCustomAttributes = GetCustomAttributes(fieldDef->GetFieldDeclaration()->mAttributes, fieldDef->mIsStatic ? BfAttributeTargets_StaticField : BfAttributeTargets_Field); + if (fieldInstance->mCustomAttributes != NULL) + { for (auto customAttr : fieldInstance->mCustomAttributes->mAttributes) { if (TypeToString(customAttr.mType) == "System.ThreadStaticAttribute") @@ -5094,6 +5222,38 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } + if ((fieldInstance->mResolvedType != NULL) && (fieldInstance->mResolvedType->IsTypeInstance()) && (fieldInstance->mResolvedType->ToTypeInstance()->IsAnonymous())) + { + auto fieldTypeInst = fieldInstance->mResolvedType->ToTypeInstance(); + if ((fieldTypeInst->IsAnonymous()) && (fieldTypeInst->mCustomAttributes != NULL)) + { + bool hasPendingAttributes = false; + for (const auto& customAttribute : fieldTypeInst->mCustomAttributes->mAttributes) + { + if (customAttribute.mAwaitingValidation) + { + hasPendingAttributes = true; + break; + } + } + + if (hasPendingAttributes) + { + fieldInstance->mCustomAttributes = new BfCustomAttributes(); + for (const auto& customAttribute : fieldTypeInst->mCustomAttributes->mAttributes) + { + if (!customAttribute.mAwaitingValidation) + continue; + + BfCustomAttribute copiedCustomAttribute = customAttribute; + copiedCustomAttribute.mIsMultiUse = false; + fieldInstance->mCustomAttributes->mAttributes.Add(copiedCustomAttribute); + } + ValidateCustomAttributes(fieldInstance->mCustomAttributes, fieldDef->mIsStatic ? BfAttributeTargets_StaticField : BfAttributeTargets_Field); + } + } + } + if (resolvedFieldType == NULL) { if ((underlyingType != NULL) || (typeInstance->IsPayloadEnum())) @@ -5166,21 +5326,80 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if ((foundTypeCount >= 2) || (typeInstance->mTypeDef->IsEmitted())) { String error = "OnCompile const evaluation creates a data dependency during TypeInit"; - if (mCompiler->mCeMachine->mCurBuilder != NULL) - { - error += StrFormat(" during const-eval generation of '%s'", MethodToString(mCompiler->mCeMachine->mCurBuilder->mCeFunction->mMethodInstance).c_str()); - } + + +// if (mCompiler->mCeMachine->mCurBuilder != NULL) +// { +// error += StrFormat(" during const-eval generation of '%s'", MethodToString(mCompiler->mCeMachine->mCurBuilder->mCeFunction->mMethodInstance).c_str()); +// } + + BfError* bfError = NULL; auto refNode = typeDef->GetRefNode(); - Fail(error, refNode); + //Fail(error, refNode); + mCompiler->mCeMachine->FailCurrent(this, error, refNode); + if ((mCompiler->mCeMachine->mCurContext != NULL) && (mCompiler->mCeMachine->mCurContext->mCurFrame != NULL)) - mCompiler->mCeMachine->mCurContext->Fail(*mCompiler->mCeMachine->mCurContext->mCurFrame, error); + bfError = mCompiler->mCeMachine->mCurContext->Fail(*mCompiler->mCeMachine->mCurContext->mCurFrame, error); else if (mCompiler->mCeMachine->mCurContext != NULL) - mCompiler->mCeMachine->mCurContext->Fail(error); + bfError = mCompiler->mCeMachine->mCurContext->Fail(error); tryCE = false; + + if (bfError != NULL) + { + auto passInstance = mCompiler->mPassInstance; + + int foundTypeCount = 0; + auto typeState = mContext->mCurTypeState; + while (typeState != NULL) + { + if (typeState->mCurAttributeTypeRef != NULL) + { + passInstance->MoreInfo(StrFormat("Attribute type '%s' causes a data cycle", BfTypeUtils::TypeToString(typeState->mCurAttributeTypeRef).c_str()), typeState->mCurAttributeTypeRef); + } + else if (typeState->mCurBaseTypeRef != NULL) + { + passInstance->MoreInfo(StrFormat("Base type '%s' causes a data cycle", BfTypeUtils::TypeToString(typeState->mCurBaseTypeRef).c_str()), typeState->mCurBaseTypeRef); + } + else if ((typeState->mCurFieldDef != NULL) && (typeState->mCurFieldDef->mFieldDeclaration != NULL)) + { + passInstance->MoreInfo(StrFormat("Field '%s.%s' causes a data cycle", TypeToString(typeState->mType).c_str(), typeState->mCurFieldDef->mName.c_str()), + typeState->mCurFieldDef->mTypeRef); + } + else if ((typeState->mCurMethodDef != NULL) && (typeState->mCurMethodDef->mMethodDeclaration != NULL)) + { + passInstance->MoreInfo(StrFormat("Method '%s.%s' causes a data cycle", TypeToString(typeState->mType).c_str(), typeState->mCurMethodDef->mName.c_str()), + typeState->mCurMethodDef->GetRefNode()); + } + else + { + BfAstNode* refNode = NULL; + if (typeState->mCurTypeDef != NULL) + refNode = typeState->mCurTypeDef->GetRefNode(); + passInstance->MoreInfo(StrFormat("Type '%s' causes a data cycle", TypeToString(typeState->mType).c_str()), refNode); + } + + if (typeState->mType == typeInstance) + { + foundTypeCount++; + if (foundTypeCount == 2) + break; + } + typeState = typeState->mPrevState; + } + } } } + if ((typeInstance->mDefineState == BfTypeDefineState_CETypeInit) && (tryCE)) + { + if (!CheckCircularDataError()) + { + Fail(StrFormat("Unexpected comptime circular data error detected in type '%s'", TypeToString(typeInstance).c_str()), typeDef->GetRefNode()); + CheckCircularDataError(true, true); + } + } + if ((typeInstance->mDefineState < BfTypeDefineState_CEPostTypeInit) && (tryCE)) { BF_ASSERT(!typeInstance->mTypeDef->IsEmitted()); @@ -5430,8 +5649,21 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy auto resolvedFieldType = fieldInstance->GetResolvedType(); if ((!typeInstance->IsBoxed()) && (fieldDef != NULL)) { - if ((fieldDef->mUsingProtection != BfProtection_Hidden) && (!resolvedFieldType->IsGenericParam()) && (!resolvedFieldType->IsObject()) && (!resolvedFieldType->IsStruct())) - Warn(0, StrFormat("Field type '%s' is not applicable for 'using'", TypeToString(resolvedFieldType).c_str()), fieldDef->GetFieldDeclaration()->mConstSpecifier); + if (fieldDef->mUsingProtection != BfProtection_Hidden) + { + auto fieldDecl = fieldDef->GetFieldDeclaration(); + BfAstNode* refNode = fieldDecl->mConstSpecifier; + if (refNode == NULL) + refNode = fieldDef->GetRefNode(); + if ((!resolvedFieldType->IsGenericParam()) && (!resolvedFieldType->IsObject()) && (!resolvedFieldType->IsStruct())) + { + Warn(0, StrFormat("Field type '%s' is not applicable for 'using'", TypeToString(resolvedFieldType).c_str()), refNode); + } + else if ((fieldDecl->mConstSpecifier == NULL) && (!BfNodeIsA(fieldDecl->mTypeRef))) + { + Warn(0, "Field needs either a name or a 'using' declaration", refNode); + } + } if (fieldInstance->mIsEnumPayloadCase) { @@ -5442,6 +5674,21 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if ((!fieldDef->mIsConst) && (!fieldDef->mIsStatic)) { + BfAstNode* nameRefNode = NULL; + if (auto fieldDecl = fieldDef->GetFieldDeclaration()) + nameRefNode = fieldDecl->mNameNode; + else if (auto paramDecl = fieldDef->GetParamDeclaration()) + nameRefNode = paramDecl->mNameNode; + if (nameRefNode == NULL) + nameRefNode = fieldDef->mTypeRef; + + if ((!resolvedFieldType->IsValuelessType()) && (typeDef->mIsOpaque)) + { + Fail(StrFormat("Opaque type '%s' attempted to declare non-static field '%s'", TypeToString(typeInstance).c_str(), fieldDef->mName.c_str()), nameRefNode, true); + resolvedFieldType = GetPrimitiveType(BfTypeCode_None); + fieldInstance->mResolvedType = resolvedFieldType; + } + PopulateType(resolvedFieldType, resolvedFieldType->IsValueType() ? BfPopulateType_Data : BfPopulateType_Declaration); if (resolvedFieldType->WantsGCMarking()) typeInstance->mWantsGCMarking = true; @@ -5458,15 +5705,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if (fieldDef->mIsExtern) { Fail("Cannot declare instance member as 'extern'", fieldDef->GetFieldDeclaration()->mExternSpecifier, true); - } - - BfAstNode* nameRefNode = NULL; - if (auto fieldDecl = fieldDef->GetFieldDeclaration()) - nameRefNode = fieldDecl->mNameNode; - else if (auto paramDecl = fieldDef->GetParamDeclaration()) - nameRefNode = paramDecl->mNameNode; - if (nameRefNode == NULL) - nameRefNode = fieldDef->mTypeRef; + } if (!allowInstanceFields) { @@ -5503,79 +5742,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if (fieldInstance->IsAppendedObject()) { - SetAndRestoreValue prevTypeRef(mContext->mCurTypeState->mCurFieldDef, fieldDef); - SetAndRestoreValue prevResolveKind(mContext->mCurTypeState->mResolveKind, BfTypeState::ResolveKind_FieldType); - - PopulateType(resolvedFieldType, BfPopulateType_Data); - - auto fieldTypeInst = resolvedFieldType->ToTypeInstance(); - dataSize = BF_MAX(fieldTypeInst->mInstSize, 0); - alignSize = BF_MAX(fieldTypeInst->mInstAlign, 1); - - if (fieldTypeInst->mTypeFailed) - { - TypeFailed(fieldTypeInst); - fieldInstance->mResolvedType = GetPrimitiveType(BfTypeCode_Var); - continue; - } - - if ((typeInstance != NULL) && (fieldTypeInst->mTypeDef->mIsAbstract)) - { - Fail("Cannot create an instance of an abstract class", nameRefNode); - } - - SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); - BfMethodState methodState; - SetAndRestoreValue prevMethodState(mCurMethodState, &methodState); - methodState.mTempKind = BfMethodState::TempKind_NonStatic; - - BfTypedValue appendIndexValue; - BfExprEvaluator exprEvaluator(this); - - BfResolvedArgs resolvedArgs; - - auto fieldDecl = fieldDef->GetFieldDeclaration(); - if (auto invocationExpr = BfNodeDynCast(fieldDecl->mInitializer)) - { - resolvedArgs.Init(invocationExpr->mOpenParen, &invocationExpr->mArguments, &invocationExpr->mCommas, invocationExpr->mCloseParen); - exprEvaluator.ResolveArgValues(resolvedArgs, BfResolveArgsFlag_DeferParamEval); - } - - BfFunctionBindResult bindResult; - bindResult.mSkipThis = true; - bindResult.mWantsArgs = true; - SetAndRestoreValue prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult); - - BfTypedValue emptyThis(mBfIRBuilder->GetFakeVal(), resolvedTypeRef, resolvedTypeRef->IsStruct()); - - exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_Comptime; - auto ctorResult = exprEvaluator.MatchConstructor(nameRefNode, NULL, emptyThis, fieldTypeInst, resolvedArgs, false, BfMethodGenericArguments(), true); - - if ((bindResult.mMethodInstance != NULL) && (bindResult.mMethodInstance->mMethodDef->mHasAppend)) - { - auto calcAppendMethodModule = GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND); - - SizedArray irArgs; - if (bindResult.mIRArgs.size() > 1) - irArgs.Insert(0, &bindResult.mIRArgs[1], bindResult.mIRArgs.size() - 1); - BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, irArgs, true); - if (appendSizeTypedValue) - { - int appendAlign = calcAppendMethodModule.mMethodInstance->mAppendAllocAlign; - dataSize = BF_ALIGN(dataSize, appendAlign); - alignSize = BF_MAX(alignSize, appendAlign); - - auto constant = mBfIRBuilder->GetConstant(appendSizeTypedValue.mValue); - if (constant != NULL) - { - dataSize += constant->mInt32; - } - } - else - { - Fail(StrFormat("Append constructor '%s' does not result in a constant size", MethodToString(bindResult.mMethodInstance).c_str()), nameRefNode); - } - } + TryGetAppendedObjectInfo(fieldInstance, dataSize, alignSize); } else if (fieldDef->mIsAppend) { @@ -5596,7 +5763,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } else if (!resolvedFieldType->IsObject()) Fail("Append fields must be classes", nameRefNode, true); - } + } BF_ASSERT(dataSize >= 0); fieldInstance->mDataSize = dataSize; @@ -5684,6 +5851,11 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } + if ((resolvedFieldType->IsOpaque()) && (!IsInSpecializedGeneric())) + Fail(StrFormat("Invalid use of opaque type '%s' in field '%s.%s'", + TypeToString(resolvedFieldType).c_str(), TypeToString(mCurTypeInstance).c_str(), fieldDef->mName.c_str()), + fieldDef->mTypeRef, true); + if ((!typeInstance->IsSpecializedType()) && (!typeInstance->IsOnDemand()) && (fieldDef != NULL) && (!CheckDefineMemberProtection(fieldDef->mProtection, resolvedFieldType))) { //CS0052 @@ -5825,6 +5997,11 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy // Align size to alignment if (alignSize >= 1) typeInstance->mInstSize = (dataPos + (alignSize - 1)) & ~(alignSize - 1); + if (typeInstance->mInstSize == 0) + { + // CRepr doesn't allow valueless types + typeInstance->mInstSize = 1; + } typeInstance->mIsCRepr = true; } else @@ -5947,6 +6124,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy std::function splatIterate; splatIterate = [&](BfType* checkType) { + if (hadNonSplattable) + return; + if (checkType->IsValueType()) PopulateType(checkType, BfPopulateType_Data); @@ -5983,6 +6163,14 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy for (int fieldIdx = 0; fieldIdx < (int)checkTypeInstance->mFieldInstances.size(); fieldIdx++) { auto fieldInstance = (BfFieldInstance*)&checkTypeInstance->mFieldInstances[fieldIdx]; + + if ((fieldInstance->mResolvedType != NULL) && + ((fieldInstance->mResolvedType->IsVar()) || (fieldInstance->mResolvedType->IsLet()))) + { + //TODO: allow splattables with var/let field types + hadNonSplattable = true; + } + if (fieldInstance->mDataIdx >= 0) splatIterate(fieldInstance->GetResolvedType()); } @@ -6058,7 +6246,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy typeInstance->mModule->ResolveConstField(typeInstance, fieldInstance, fieldDef); // Check enum cases for duplicates - if (mCurTypeInstance->IsEnum()) + if ((mCurTypeInstance->IsEnum()) && (!mCurTypeInstance->IsUnspecializedTypeVariation())) { auto underlyingType = fieldInstance->mResolvedType->GetUnderlyingType(); if ((fieldDef->IsEnumCaseEntry()) && (fieldInstance->mConstIdx != -1) && (underlyingType->IsIntegral())) @@ -6603,6 +6791,12 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) if (methodDef->mMethodType == BfMethodType_CtorNoBody) declRequired = true; + if ((methodDef->mMethodType == BfMethodType_Ctor) && (methodDef->mIsOverride)) + { + // From extension + implRequired = true; + } + if ((methodDef->mIsStatic) && ((methodDef->mMethodType == BfMethodType_Dtor) || (methodDef->mMethodType == BfMethodType_Ctor))) { @@ -7333,12 +7527,18 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) matchedMethodString.c_str()), matchedMethod->mMethodDef->GetMutNode()); mCompiler->mPassInstance->MoreInfo(StrFormat("Declare the interface method as 'mut' to allow matching 'mut' implementations"), ifaceMethodInst->mMethodDef->mMethodDeclaration); } - else + else if (!matchedMethod->mReturnType->IsVar()) { mCompiler->mPassInstance->MoreInfo(StrFormat("'%s' cannot match because it does not have the return type '%s'", matchedMethodString.c_str(), TypeToString(ifaceMethodInst->mReturnType).c_str()), matchedMethod->mMethodDef->mReturnTypeRef); if ((ifaceMethodInst->mVirtualTableIdx != -1) && (ifaceMethodInst->mReturnType->IsInterface())) - mCompiler->mPassInstance->MoreInfo("Declare the interface method as 'concrete' to allow matching concrete return values", ifaceMethodInst->mMethodDef->GetMethodDeclaration()->mVirtualSpecifier); + { + BfAstNode* refNode = ifaceMethodInst->mMethodDef->GetRefNode(); + auto methodDecl = ifaceMethodInst->mMethodDef->GetMethodDeclaration(); + if ((methodDecl != NULL) && (methodDecl->mVirtualSpecifier != NULL)) + refNode = methodDecl->mVirtualSpecifier; + mCompiler->mPassInstance->MoreInfo("Declare the interface method as 'concrete' to allow matching concrete return values", refNode); + } } } } @@ -7630,22 +7830,35 @@ BfUnknownSizedArrayType* BfModule::CreateUnknownSizedArrayType(BfType* resolvedT BfPointerType* BfModule::CreatePointerType(BfType* resolvedType) { BF_ASSERT(!resolvedType->IsVar()); - BF_ASSERT_REL(!resolvedType->IsDeleting()); - + auto pointerType = mContext->mPointerTypePool.Get(); pointerType->mContext = mContext; pointerType->mElementType = resolvedType; auto resolvedPointerType = (BfPointerType*)ResolveType(pointerType); if (resolvedPointerType != pointerType) + { mContext->mPointerTypePool.GiveBack(pointerType); + } + else + { + if (resolvedType->IsDeleting()) + { + mCompiler->RequestExtraCompile(); + InternalError("CreatePointerType using deleted type"); + mContext->DeleteType(resolvedPointerType); + } + } BF_ASSERT(resolvedPointerType->mElementType == resolvedType); - + return resolvedPointerType; } BfConstExprValueType* BfModule::CreateConstExprValueType(const BfTypedValue& typedValue, bool allowCreate) { + if (typedValue.mType->IsConstExprValue()) + return (BfConstExprValueType*)typedValue.mType; + BfPopulateType populateType = allowCreate ? BfPopulateType_Data : BfPopulateType_Identity; BfResolveTypeRefFlags resolveFlags = allowCreate ? BfResolveTypeRefFlag_None : BfResolveTypeRefFlag_NoCreate; @@ -8445,6 +8658,7 @@ BfType* BfModule::ResolveInnerType(BfType* outerType, BfAstNode* typeRef, BfPopu BfNamedTypeReference* namedTypeRef = NULL; BfGenericInstanceTypeRef* genericTypeRef = NULL; BfDirectStrTypeReference* directStrTypeRef = NULL; + BfInlineTypeReference* inlineTypeRef = NULL; BfIdentifierNode* identifierNode = NULL; if ((namedTypeRef = BfNodeDynCast(typeRef))) { @@ -8463,17 +8677,24 @@ BfType* BfModule::ResolveInnerType(BfType* outerType, BfAstNode* typeRef, BfPopu { // } + else if ((inlineTypeRef = BfNodeDynCastExact(typeRef))) + { + // + } - BF_ASSERT((identifierNode != NULL) || (namedTypeRef != NULL) || (directStrTypeRef != NULL)); + BF_ASSERT((identifierNode != NULL) || (namedTypeRef != NULL) || (directStrTypeRef != NULL) || (inlineTypeRef != NULL)); auto usedOuterType = outerType; if (nestedTypeDef == NULL) { + String tempStr; StringView findName; if (namedTypeRef != NULL) findName = namedTypeRef->mNameNode->ToStringView(); else if (identifierNode != NULL) findName = identifierNode->ToStringView(); + else if (inlineTypeRef != NULL) + findName = inlineTypeRef->mTypeDeclaration->mAnonymousName; else findName = directStrTypeRef->mTypeName; @@ -8501,13 +8722,9 @@ BfType* BfModule::ResolveInnerType(BfType* outerType, BfAstNode* typeRef, BfPopu if ((!isFailurePass) && ((resolveFlags & BfResolveTypeRefFlag_IgnoreProtection) == 0) && (!CheckProtection(latestCheckType->mProtection, latestCheckType, allowProtected, allowPrivate))) continue; - - if (checkType->mProject != checkOuterType->mTypeDef->mProject) - { - auto visibleProjectSet = GetVisibleProjectSet(); - if ((visibleProjectSet == NULL) || (!visibleProjectSet->Contains(checkType->mProject))) - continue; - } + + if ((checkType->mProject != checkOuterType->mTypeDef->mProject) && (!IsProjectVisible(checkType->mProject))) + continue; if ((checkType->mName->mString == findName) && (checkType->GetSelfGenericParamCount() == numGenericArgs)) { @@ -9331,7 +9548,7 @@ BfType* BfModule::ResolveGenericType(BfType* unspecializedType, BfTypeVector* ty if (typeDef->mIsDelegate) { BfDefBuilder::AddMethod(typeDef, BfMethodType_Ctor, BfProtection_Public, false, ""); - BfDefBuilder::AddDynamicCastMethods(typeDef); + BfDefBuilder::AddDynamicCastMethods(typeDef, true); } delegateType->mContext = mContext; @@ -9395,7 +9612,10 @@ BfType* BfModule::ResolveSelfType(BfType* type, BfType* selfType) { if (!type->IsUnspecializedTypeVariation()) return type; - return ResolveGenericType(type, NULL, NULL, selfType); + BfType* resolvedType = ResolveGenericType(type, NULL, NULL, selfType); + if (resolvedType != NULL) + return resolvedType; + return type; } BfType* BfModule::ResolveType(BfType* lookupType, BfPopulateType populateType, BfResolveTypeRefFlags resolveFlags) @@ -9440,7 +9660,7 @@ bool BfModule::IsUnboundGeneric(BfType* type) return (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0; } -BfGenericParamInstance* BfModule::GetGenericTypeParamInstance(int genericParamIdx) +BfGenericParamInstance* BfModule::GetGenericTypeParamInstance(int genericParamIdx, BfFailHandleKind failHandleKind) { // When we're evaluating a method, make sure the params refer back to that method context auto curTypeInstance = mCurTypeInstance; @@ -9449,6 +9669,13 @@ BfGenericParamInstance* BfModule::GetGenericTypeParamInstance(int genericParamId // curTypeInstance = mCurMethodInstance->mMethodInstanceGroup->mOwner; BfTypeInstance* genericTypeInst = curTypeInstance->ToGenericTypeInstance(); + + if (genericTypeInst == NULL) + { + FatalError("Invalid mCurTypeInstance for GetGenericTypeParamInstance", failHandleKind); + return NULL; + } + if ((genericTypeInst->IsIncomplete()) && (genericTypeInst->mGenericTypeInfo->mGenericParams.size() == 0)) { // Set this to NULL so we don't recurse infinitely @@ -9513,7 +9740,9 @@ void BfModule::GetActiveTypeGenericParamInstances(SizedArraymGenericTypeInfo->mGenericExtensionInfo != NULL) { - auto activeTypeDef = GetActiveTypeDef(NULL, true); + // Note: original version had useMixinDecl set. Was there a reason for that? Causes issue 2118 + auto activeTypeDef = GetActiveTypeDef(NULL); + if ((activeTypeDef->mTypeDeclaration != genericTypeInst->mTypeDef->mTypeDeclaration) && (activeTypeDef->IsExtension())) { BfTypeDef* lookupTypeDef = activeTypeDef; @@ -9548,11 +9777,39 @@ void BfModule::GetActiveTypeGenericParamInstances(SizedArraymGenericParamFlags; - outTypeConstraint = genericParam->mTypeConstraint; + BfGenericParamType* genericParamType = NULL; + if (type->IsGenericParam()) + genericParamType = (BfGenericParamType*)type; + + BfGenericParamInstance* genericParam = NULL; + if (genericParamType != NULL) + { + genericParam = GetGenericParamInstance(genericParamType); + outFlags = (BfGenericParamFlags)(outFlags | genericParam->mGenericParamFlags); + if (genericParam->mTypeConstraint != NULL) + outTypeConstraint = genericParam->mTypeConstraint; + } + else + { + outFlags = BfGenericParamFlag_None; + outTypeConstraint = NULL; + + if ((mCurTypeInstance != NULL) && (mCurTypeInstance->mGenericTypeInfo != NULL)) + { + for (int genericIdx = mCurTypeInstance->mTypeDef->mGenericParamDefs.mSize; genericIdx < mCurTypeInstance->mGenericTypeInfo->mGenericParams.mSize; genericIdx++) + { + auto genericParam = mCurTypeInstance->mGenericTypeInfo->mGenericParams[genericIdx]; + if (genericParam->mExternType == type) + { + outFlags = (BfGenericParamFlags)(outFlags | genericParam->mGenericParamFlags); + if (genericParam->mTypeConstraint != NULL) + outTypeConstraint = genericParam->mTypeConstraint; + } + } + } + } // Check method generic constraints if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL)) @@ -9594,7 +9851,7 @@ BfGenericParamInstance* BfModule::GetGenericParamInstance(BfGenericParamType* ty return curGenericMethodInstance->mMethodInfoEx->mGenericParams[type->mGenericParamIdx]; } - return GetGenericTypeParamInstance(type->mGenericParamIdx); + return GetGenericTypeParamInstance(type->mGenericParamIdx, failHandleKind); } bool BfModule::ResolveTypeResult_Validate(BfAstNode* typeRef, BfType* resolvedTypeRef) @@ -10115,8 +10372,16 @@ BfTypeDef* BfModule::GetActiveTypeDef(BfTypeInstance* typeInstanceOverride, bool { BfTypeDef* useTypeDef = NULL; BfTypeInstance* typeInstance = (typeInstanceOverride != NULL) ? typeInstanceOverride : mCurTypeInstance; - if ((mContext->mCurTypeState != NULL) && (mContext->mCurTypeState->mForceActiveTypeDef != NULL)) - return mContext->mCurTypeState->mForceActiveTypeDef; + + auto curTypeState = mContext->mCurTypeState; + if (curTypeState != NULL) + { + if ((curTypeState->mType != NULL) && (curTypeState->mType != typeInstance)) + curTypeState = NULL; + } + + if ((curTypeState != NULL) && (curTypeState->mForceActiveTypeDef != NULL)) + return curTypeState->mForceActiveTypeDef; if (typeInstance != NULL) useTypeDef = typeInstance->mTypeDef->GetDefinition(); if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL) && (useMixinDecl)) @@ -10139,12 +10404,12 @@ BfTypeDef* BfModule::GetActiveTypeDef(BfTypeInstance* typeInstanceOverride, bool } } } - else if (mContext->mCurTypeState != NULL) + else if (curTypeState != NULL) { - if ((mContext->mCurTypeState->mCurFieldDef != NULL) && (mContext->mCurTypeState->mCurFieldDef->mDeclaringType != NULL)) - useTypeDef = mContext->mCurTypeState->mCurFieldDef->mDeclaringType->GetDefinition(true); - else if (mContext->mCurTypeState->mCurTypeDef != NULL) - useTypeDef = mContext->mCurTypeState->mCurTypeDef->GetDefinition(true); + if ((curTypeState->mCurFieldDef != NULL) && (curTypeState->mCurFieldDef->mDeclaringType != NULL)) + useTypeDef = curTypeState->mCurFieldDef->mDeclaringType->GetDefinition(true); + else if (curTypeState->mCurTypeDef != NULL) + useTypeDef = curTypeState->mCurTypeDef->GetDefinition(true); } return useTypeDef; @@ -10468,17 +10733,18 @@ BfTypeDef* BfModule::FindTypeDef(BfTypeReference* typeRef, BfTypeInstance* typeI if (auto elementedType = BfNodeDynCast(typeRef)) return FindTypeDef(elementedType->mElementType, typeInstanceOverride, error); - BF_ASSERT(typeRef->IsA() || typeRef->IsA() || typeRef->IsA()); + BF_ASSERT(typeRef->IsA() || typeRef->IsA() || typeRef->IsA() || typeRef->IsA()); auto namedTypeRef = BfNodeDynCast(typeRef); StringView findNameStr; if (namedTypeRef != NULL) findNameStr = namedTypeRef->mNameNode->ToStringView(); else - { - auto directStrTypeDef = BfNodeDynCastExact(typeRef); - if (directStrTypeDef != NULL) + { + if (auto directStrTypeDef = BfNodeDynCastExact(typeRef)) findNameStr = directStrTypeDef->mTypeName; + else if (auto inlineTypeRef = BfNodeDynCastExact(typeRef)) + findNameStr = inlineTypeRef->mTypeDeclaration->mAnonymousName; else BFMODULE_FATAL(this, "Error?"); } @@ -10943,7 +11209,7 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po { Fail("Invalid use of 'var ref'. Generally references are generated with a 'var' declaration with 'ref' applied to the initializer", typeRef); return NULL; - } + } if (mNoResolveGenericParams) resolveFlags = (BfResolveTypeRefFlags)(resolveFlags | BfResolveTypeRefFlag_NoResolveGenericParam); @@ -10989,13 +11255,16 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po // Check generics first auto namedTypeRef = BfNodeDynCastExact(typeRef); auto directStrTypeRef = BfNodeDynCastExact(typeRef); - if (((namedTypeRef != NULL) && (namedTypeRef->mNameNode != NULL)) || (directStrTypeRef != NULL)) + auto inlineStrTypeRef = BfNodeDynCastExact(typeRef); + if (((namedTypeRef != NULL) && (namedTypeRef->mNameNode != NULL)) || (directStrTypeRef != NULL) || (inlineStrTypeRef != NULL)) { StringView findName; if (namedTypeRef != NULL) findName = namedTypeRef->mNameNode->ToStringView(); - else + else if (directStrTypeRef != NULL) findName = directStrTypeRef->mTypeName; + else + findName = inlineStrTypeRef->mTypeDeclaration->mAnonymousName; if (findName == "Self") { BfType* selfType = mCurTypeInstance; @@ -11685,10 +11954,15 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po } static int sCallIdx = 0; - int callIdx = sCallIdx++; - if (callIdx == 0x00006CA4) + int callIdx = 0; + + if (!mCompiler->mIsResolveOnly) { - NOP; + callIdx = sCallIdx++; + if (callIdx == 0x0000A224) + { + NOP; + } } BfResolvedTypeSet::LookupContext lookupCtx; @@ -11759,6 +12033,12 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po if ((outerTypeInstance != NULL) && (typeDef->mGenericParamDefs.size() != 0)) { // Try to inherit generic params from current parent + if (outerTypeInstance->IsDeleting()) + { + mCompiler->RequestExtraCompile(); + InternalError("ResolveTypeRef with deleted outer type"); + return ResolveTypeResult(typeRef, NULL, populateType, resolveFlags); + } BfTypeDef* outerType = mSystem->GetCombinedPartial(typeDef->mOuterType); BF_ASSERT(!outerType->mIsPartial); @@ -12414,6 +12694,13 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po } } + if (auto refTypeRef = BfNodeDynCast(param->mTypeRef)) + { + // This catches `ref Foo*` cases (which generate warnings) + if ((refTypeRef->mRefToken != NULL) && (refTypeRef->mRefToken->mToken == BfToken_Mut)) + hasMutSpecifier = true; + } + auto paramType = ResolveTypeRef(param->mTypeRef, BfPopulateType_Declaration, resolveTypeFlags); if (paramType == NULL) { @@ -12438,6 +12725,13 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po hasMutSpecifier = true; functionThisType = refType->mElementType; } + + if ((functionThisType != NULL) && (functionThisType->IsPointer())) + { + // We should have already warned against pointer types during hashing + functionThisType = functionThisType->GetUnderlyingType(); + } + paramTypes.Add(functionThisType); _CheckType(functionThisType); } @@ -12570,6 +12864,9 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po if (paramType == NULL) paramType = GetPrimitiveType(BfTypeCode_Var); + if ((param->mModToken != NULL) && (param->mModToken->mToken == BfToken_Params)) + delegateInfo->mHasParams = true; + String paramName; if (param->mNameNode != NULL) paramName = param->mNameNode->ToString(); @@ -12638,7 +12935,7 @@ BfType* BfModule::ResolveTypeRef_Ref(BfTypeReference* typeRef, BfPopulateType po if (typeDef->mIsDelegate) { BfDefBuilder::AddMethod(typeDef, BfMethodType_Ctor, BfProtection_Public, false, ""); - BfDefBuilder::AddDynamicCastMethods(typeDef); + BfDefBuilder::AddDynamicCastMethods(typeDef, true); } delegateType->mContext = mContext; @@ -12916,6 +13213,9 @@ BfType* BfModule::ResolveTypeRef_Ref(BfAstNode* astNode, const BfSizedArray(astNode)) return ResolveTypeRef_Ref(typeRef, populateType, resolveFlags, 0); + if (astNode->IsTemporary()) + return ResolveTypeRef((BfTypeReference*)astNode, populateType, resolveFlags); + if ((resolveFlags & BfResolveTypeRefFlag_AllowImplicitConstExpr) != 0) { if (auto expr = BfNodeDynCast(astNode)) @@ -12942,6 +13242,12 @@ BfType* BfModule::ResolveTypeRef_Ref(BfAstNode* astNode, const BfSizedArrayToTypeInstance(), toInner->ToTypeInstance())) { - if (toInner->IsValuelessType()) + if (toInner->IsValuelessNonOpaqueType()) return mBfIRBuilder->GetFakeVal(); // Is this valid? typedVal = MakeAddressable(typedVal); @@ -13809,6 +14115,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp if (!typedVal.IsAddr()) { auto srcAlloca = CreateAllocaInst(fromNullableType); + typedVal = LoadOrAggregateValue(typedVal); mBfIRBuilder->CreateStore(typedVal.mValue, srcAlloca); srcPtr = srcAlloca; } @@ -14483,16 +14790,20 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp { auto fromType = typedVal.mType; - // Handle the typedPrim<->underlying part implicitly if (fromType->IsTypedPrimitive()) { typedVal = LoadValue(typedVal); auto convTypedValue = BfTypedValue(typedVal.mValue, fromType->GetUnderlyingType()); - return CastToValue(srcNode, convTypedValue, toType, (BfCastFlags)(castFlags & ~BfCastFlags_Explicit), NULL); + if ((fromType->IsEnum()) && (convTypedValue.mType->IsVoid()) && (methodMatcher.mBestRawMethodInstance != NULL)) + { + if (methodMatcher.mBestRawMethodInstance) + convTypedValue = GetDefaultTypedValue(methodMatcher.mBestRawMethodInstance->mReturnType); + } + return CastToValue(srcNode, convTypedValue, toType, castFlags, NULL); } else if (toType->IsTypedPrimitive()) { - auto castedVal = CastToValue(srcNode, typedVal, toType->GetUnderlyingType(), (BfCastFlags)(castFlags & ~BfCastFlags_Explicit), NULL); + auto castedVal = CastToValue(srcNode, typedVal, toType->GetUnderlyingType(), castFlags, NULL); return castedVal; } } @@ -14920,6 +15231,7 @@ BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, Bf BfIRValue curTupleValue = CreateAlloca(tupleType); auto loadedVal = LoadValue(typedVal); + FixValueActualization(loadedVal); mBfIRBuilder->CreateStore(loadedVal.mValue, mBfIRBuilder->CreateBitCast(curTupleValue, mBfIRBuilder->MapTypeInstPtr(fromTupleType))); return BfTypedValue(curTupleValue, tupleType, BfTypedValueKind_TempAddr); } @@ -15079,6 +15391,13 @@ BfTypedValue BfModule::Cast(BfAstNode* srcNode, const BfTypedValue& typedVal, Bf }*/ BfCastResultFlags castResultFlags = BfCastResultFlags_None; + + if ((typedVal.IsParams()) && (toType->IsParamsType())) + { + if (typedVal.mType == toType->GetUnderlyingType()) + return BfTypedValue(mBfIRBuilder->GetFakeVal(), toType); + } + auto castedValue = CastToValue(srcNode, typedVal, toType, castFlags, &castResultFlags); if (!castedValue) return BfTypedValue(); @@ -15760,6 +16079,18 @@ void BfModule::DoTypeToString(StringImpl& str, BfType* resolvedType, BfTypeNameF { BP_ZONE("BfModule::DoTypeToString"); + if (resolvedType == NULL) + { + str += "NULL"; + return; + } + + if (resolvedType->mContext == NULL) + { + str += "*UNINITIALIZED TYPE*"; + return; + } + if ((typeNameFlags & BfTypeNameFlag_AddProjectName) != 0) { BfProject* defProject = NULL; diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index c9e2751f..f20fd95f 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -105,6 +105,40 @@ return 0; ////////////////////////////////////////////////////////////////////////// +static CritSect gParseFileDataCrit; +static Array gFreeIds; +static int gCurFreeId; + +int BfParseFileData::GetUniqueId(int idx) +{ + AutoCrit autoCrit(gParseFileDataCrit); + + int* valuePtr = NULL; + if (mUniqueIDList.TryAdd(idx, NULL, &valuePtr)) + { + if (!gFreeIds.IsEmpty()) + { + *valuePtr = gFreeIds.back(); + gFreeIds.pop_back(); + } + else + *valuePtr = gCurFreeId++; + } + return *valuePtr; +} + +BfParseFileData::~BfParseFileData() +{ + if (!mUniqueIDList.IsEmpty()) + { + AutoCrit autoCrit(gParseFileDataCrit); + for (auto kv : mUniqueIDList) + gFreeIds.Add(kv.mValue); + } +} + +////////////////////////////////////////////////////////////////////////// + BfParserCache* Beefy::gBfParserCache = NULL; bool BfParserCache::DataEntry::operator==(const LookupEntry& lookup) const @@ -201,14 +235,39 @@ BfParserData::BfParserData() mCharIdData = NULL; mUniqueParser = NULL; mDidReduce = false; + mParseFileData = NULL; } BfParserData::~BfParserData() { + if (mParseFileData != NULL) + { + BF_ASSERT(mParseFileData->mRefCount >= 0); + mParseFileData->mRefCount--; + if (mParseFileData->mRefCount == 0) + { + delete mParseFileData; + gBfParserCache->mParseFileDataMap.Remove(mFileName); + } + } + delete[] mJumpTable; delete[] mCharIdData; } +void BfParserData::InitFileData() +{ + BF_ASSERT(mParseFileData == NULL); + + BfParseFileData** valuePtr = NULL; + if (gBfParserCache->mParseFileDataMap.TryAdd(mFileName, NULL, &valuePtr)) + { + *valuePtr = new BfParseFileData(); + } + mParseFileData = *valuePtr; + mParseFileData->mRefCount++; +} + int BfParserData::GetCharIdAtIndex(int findIndex) { if (mCharIdData == NULL) @@ -304,21 +363,18 @@ bool BfParserData::IsUnwarnedAt(BfAstNode* node) bool BfParserData::IsWarningEnabledAtSrcIndex(int warningNumber, int srcIdx) { int enabled = 1; //CDH TODO if/when we add warning level support, this default will change based on the warning number and the general project warning level setting - int lastUnwarnPos = 0; for (const auto& it : mWarningEnabledChanges) { if (it.mKey > srcIdx) break; - if (it.mValue.mWarningNumber == warningNumber) + if ((it.mValue.mWarningNumber == warningNumber) || (it.mValue.mWarningNumber == -1)) { if (it.mValue.mEnable) enabled++; else enabled--; - } - if (it.mValue.mWarningNumber == -1) - lastUnwarnPos = -1; + } } return enabled > 0; } @@ -328,7 +384,7 @@ void BfParserData::Deref() mRefCount--; BF_ASSERT(mRefCount >= 0); if (mRefCount == 0) - { + { AutoCrit autoCrit(gBfParserCache->mCritSect); BfParserCache::DataEntry dataEntry; dataEntry.mParserData = this; @@ -385,6 +441,7 @@ BfParser::BfParser(BfSystem* bfSystem, BfProject* bfProject) : BfSource(bfSystem mTriviaStart = 0; mParsingFailed = false; mInAsmBlock = false; + mCurBlockId = 0; mPreprocessorIgnoredSectionNode = NULL; mPreprocessorIgnoreDepth = 0; mAddedDependsDefines = false; @@ -413,6 +470,8 @@ BfParser::~BfParser() } else if (mParserData->mRefCount == 0) { + + // Just never got added to the cache delete mParserData; } @@ -510,6 +569,7 @@ void BfParser::Init(uint64 cacheHash) mParserData = new BfParserData(); mSourceData = mParserData; mParserData->mFileName = mFileName; + mParserData->InitFileData(); if (mDataId != -1) mParserData->mDataId = mDataId; else @@ -796,7 +856,10 @@ BfBlock* BfParser::ParseInlineBlock(int spaceIdx, int endIdx) if (startNode == NULL) startNode = childNode; if (block == NULL) + { block = mAlloc->Alloc(); + block->mParserBlockId = ++mCurBlockId; + } block->Add(childNode); childArr.push_back(childNode); //block->mChildArr.Add(childNode, &mAlloc); @@ -857,10 +920,14 @@ void BfParser::HandlePragma(const StringImpl& pragma, BfBlock* block) mPassInstance->FailAt("Expected \"disable\" or \"restore\" after \"warning\"", mSourceData, iterNode->GetSrcStart(), iterNode->GetSrcLength()); } - //iterNode = parentNode->mChildArr.GetAs(++curIdx); + int srcStart = iterNode->GetSrcStart(); + + int nodeCount = 0; + iterNode = itr.Get(); while (iterNode) { + ++nodeCount; ++itr; auto tokenStr = iterNode->ToString(); if (tokenStr != ",") // commas allowed between warning numbers but not required; we just ignore them @@ -875,11 +942,14 @@ void BfParser::HandlePragma(const StringImpl& pragma, BfBlock* block) break; } } - if (isNum) + + int warningNum = atoi(tokenStr.c_str()); + + if ((isNum) && (warningNum > 0)) { BfParserWarningEnabledChange wec; wec.mEnable = enable; - wec.mWarningNumber = atoi(tokenStr.c_str()); + wec.mWarningNumber = warningNum; mParserData->mWarningEnabledChanges[iterNode->GetSrcStart()] = wec; } else @@ -887,10 +957,17 @@ void BfParser::HandlePragma(const StringImpl& pragma, BfBlock* block) mPassInstance->FailAt("Expected decimal warning number", mSourceData, iterNode->GetSrcStart(), iterNode->GetSrcLength()); } } - - //iterNode = parentNode->mChildArr.Get(++curIdx); + iterNode = itr.Get(); } + + if (nodeCount == 0) + { + BfParserWarningEnabledChange wec; + wec.mEnable = enable; + wec.mWarningNumber = -1; + mParserData->mWarningEnabledChanges[srcStart] = wec; + } } else { @@ -2246,6 +2323,11 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate, bool disablePrepro { for (int i = 0; i < braceCount - 1; i++) strLiteral += '}'; + + if ((((isClosingBrace) && (braceCount > 1) && (braceCount % 2 == 0)) || ((!isClosingBrace)) && (braceCount % 2 == 1))) + { + mPassInstance->FailAt("Unpaired closing brace.", mSourceData, mSrcIdx - 1, 1); + } } else { @@ -3308,11 +3390,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate, bool disablePrepro case TOKEN_HASH('s', 'i', 'z', 'e'): if (SrcPtrHasToken("sizeof")) mToken = BfToken_SizeOf; - break; - case TOKEN_HASH('s', 't', 'a', 'c'): - if ((!mCompatMode) && (SrcPtrHasToken("stack"))) - mToken = BfToken_Stack; - break; + break; case TOKEN_HASH('s', 't', 'a', 't'): if (SrcPtrHasToken("static")) mToken = BfToken_Static; @@ -3588,6 +3666,7 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth, bool isInterpolate) else*/ { genBlock = mAlloc->Alloc(); + genBlock->mParserBlockId = ++mCurBlockId; genBlock->mOpenBrace = (BfTokenNode*)CreateNode(); newBlock = genBlock; } @@ -3617,6 +3696,26 @@ void BfParser::ParseBlock(BfBlock* astNode, int depth, bool isInterpolate) Fail("Unexpected ending brace"); break; } + else if (mToken == BfToken_Case) + { + if (childArr.mSize > 0) + { + auto prevNode = childArr[childArr.mSize - 1]; + if (auto prevIdentifier = BfNodeDynCastExact(prevNode)) + { + if (prevIdentifier->Equals("not")) + { + auto bfTokenNode = mAlloc->Alloc(); + bfTokenNode->Init(prevIdentifier->mTriviaStart, prevIdentifier->mSrcStart, prevIdentifier->mSrcEnd); + bfTokenNode->SetToken(BfToken_Not); + childArr[childArr.mSize - 1] = bfTokenNode; + } + } + } + + astNode->Add(childNode); + childArr.Add(childNode); + } else { if (mToken == BfToken_LParen) diff --git a/IDEHelper/Compiler/BfParser.h b/IDEHelper/Compiler/BfParser.h index cab5bb04..5974d00f 100644 --- a/IDEHelper/Compiler/BfParser.h +++ b/IDEHelper/Compiler/BfParser.h @@ -62,6 +62,22 @@ enum MaybeBool MaybeBool_True = 1, }; +class BfParseFileData +{ +public: + Dictionary mUniqueIDList; + int mRefCount; + +public: + BfParseFileData() + { + mRefCount = 0; + } + ~BfParseFileData(); + + int GetUniqueId(int idx); +}; + class BfParserData : public BfSourceData { public: @@ -81,8 +97,9 @@ public: OwnedVector mStringLiterals; Dictionary mWarningEnabledChanges; std::set mUnwarns; + BfParseFileData* mParseFileData; bool mFailed; // Don't cache if there's a warning or an error - bool mDidReduce; + bool mDidReduce; public: BfParserData(); @@ -94,6 +111,7 @@ public: return this; } + void InitFileData(); virtual BfParser* ToParser() override; int GetCharIdAtIndex(int findIndex); void GetLineCharAtIdx(int idx, int& line, int& lineChar); @@ -130,6 +148,7 @@ public: int mRefCount; BfAstAllocManager mAstAllocManager; HashSet mEntries; + Dictionary mParseFileDataMap; public: BfParserCache(); @@ -179,6 +198,7 @@ public: int mLineStart; int mLineNum; bool mInAsmBlock; + int mCurBlockId; BfParserFlag mParserFlags; int mCursorIdx; diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index 36c9bc60..c66c1ede 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -1337,6 +1337,8 @@ void BfPrinter::Visit(BfLiteralExpression* literalExpr) { int srcLineStart = 0; + bool startsOnEmptyLine = true; + int checkIdx = literalExpr->GetSrcStart() - 1; while (checkIdx >= 0) { @@ -1346,12 +1348,19 @@ void BfPrinter::Visit(BfLiteralExpression* literalExpr) srcLineStart = checkIdx + 1; break; } + if ((c != '\t') && (c != ' ')) + startsOnEmptyLine = false; checkIdx--; } - + int queuedSpaceCount = mQueuedSpaceCount; FlushIndent(); + if (!startsOnEmptyLine) + { + queuedSpaceCount = mCurIndentLevel * mTabSize; + } + for (int i = literalExpr->GetSrcStart(); i < (int)literalExpr->GetSrcEnd(); i++) { char c = sourceData->mSrc[i]; @@ -1379,6 +1388,9 @@ void BfPrinter::Visit(BfLiteralExpression* literalExpr) } } +// if (!startsOnEmptyLine) +// mCurIndentLevel--; + return; } } @@ -1716,12 +1728,17 @@ void BfPrinter::Visit(BfPointerTypeRef* ptrType) void BfPrinter::Visit(BfNullableTypeRef* ptrType) { - Visit((BfAstNode*) ptrType); + Visit((BfAstNode*)ptrType); VisitChild(ptrType->mElementType); VisitChild(ptrType->mQuestionToken); } +void BfPrinter::Visit(BfInlineTypeReference* typeRef) +{ + VisitChild(typeRef->mTypeDeclaration); +} + void BfPrinter::Visit(BfVariableDeclaration* varDecl) { //Visit(varDecl->ToBase()); @@ -2068,6 +2085,8 @@ void BfPrinter::Visit(BfCaseExpression* caseExpr) { VisitChild(caseExpr->mValueExpression); ExpectSpace(); + VisitChild(caseExpr->mNotToken); + ExpectSpace(); VisitChild(caseExpr->mCaseToken); BF_ASSERT(caseExpr->mEqualsNode == NULL); ExpectSpace(); @@ -2820,6 +2839,7 @@ void BfPrinter::Visit(BfPropertyDeclaration* propertyDeclaration) for (auto method : propertyDeclaration->mMethods) { QueueVisitChild(method->mBody); + QueueVisitChild(method->mEndSemicolon); } } @@ -2887,6 +2907,7 @@ void BfPrinter::Visit(BfFieldDeclaration* fieldDeclaration) ExpectSpace(); if (isEnumDecl) mNextStateModify.mExpectingSpace = false; + QueueVisitChild(fieldDeclaration->mTypeRef); ExpectSpace(); QueueVisitChild(fieldDeclaration->mNameNode); diff --git a/IDEHelper/Compiler/BfPrinter.h b/IDEHelper/Compiler/BfPrinter.h index d37f9dc6..0ee568b6 100644 --- a/IDEHelper/Compiler/BfPrinter.h +++ b/IDEHelper/Compiler/BfPrinter.h @@ -171,6 +171,7 @@ public: virtual void Visit(BfDelegateTypeRef* typeRef) override; virtual void Visit(BfPointerTypeRef* typeRef) override; virtual void Visit(BfNullableTypeRef* typeRef) override; + virtual void Visit(BfInlineTypeReference* typeRef) override; virtual void Visit(BfVariableDeclaration* varDecl) override; virtual void Visit(BfParameterDeclaration* paramDecl) override; virtual void Visit(BfTypeOfExpression* typeOfExpr) override; diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index 09b11340..653e5367 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -46,9 +46,11 @@ BfReducer::BfReducer() mAssertCurrentNodeIdx = 0; mSystem = NULL; mResolvePassData = NULL; - mMethodDepth = 0; + mMethodDepth = 0; mDocumentCheckIdx = 0; mTypeMemberNodeStart = NULL; + mCurTypeState = NULL; + mLastErrorSrcEnd = -1; } bool BfReducer::IsSemicolon(BfAstNode* node) @@ -83,7 +85,7 @@ void BfReducer::AssertCurrentNode(BfAstNode* node) auto currentNode = mVisitorPos.GetCurrent(); if (currentNode == NULL) return; - if (!node->LocationEndEquals(currentNode)) + if ((!node->LocationEndEquals(currentNode)) && (mLastErrorSrcEnd != currentNode->mSrcEnd)) { const char* lastCPtr = &node->GetSourceData()->mSrc[node->GetSrcEnd() - 1]; // We have an "exceptional" case where breaking a double chevron will look like a position error @@ -109,6 +111,20 @@ bool BfReducer::IsNodeRelevant(BfAstNode* astNode) return false; } +bool BfReducer::IsCursorInside(BfAstNode* astNode) +{ + BfParser* bfParser = astNode->GetSourceData()->ToParser(); + if (bfParser == NULL) + return false; + int cursorPos = bfParser->mCursorIdx; + if (cursorPos == -1) + return false; + if (astNode->Contains(cursorPos, 1, 0)) + return true; + BF_ASSERT(bfParser->mParserData->mRefCount == -1); + return false; +} + bool BfReducer::IsNodeRelevant(BfAstNode* startNode, BfAstNode* endNode) { if (startNode == NULL) @@ -131,15 +147,15 @@ void BfReducer::MoveNode(BfAstNode* srcNode, BfAstNode* newOwner) #ifdef BF_AST_HAS_PARENT_MEMBER srcNode->mParent = newOwner; #endif - int srcStart = srcNode->GetSrcStart(); - int srcEnd = srcNode->GetSrcEnd(); - if (srcStart < newOwner->GetSrcStart()) - newOwner->SetSrcStart(srcStart); - if (srcEnd > newOwner->GetSrcEnd()) - newOwner->SetSrcEnd(srcEnd); + int srcStart = srcNode->mSrcStart; + int srcEnd = srcNode->mSrcEnd; + if (srcStart < newOwner->mSrcStart) + newOwner->mSrcStart = srcStart; + if (srcEnd > newOwner->mSrcEnd) + newOwner->mSrcEnd = srcEnd; } -// Replaces prevNode with new node and adds prevNode to newNode's childrenj +// Replaces prevNode with new node and adds prevNode to newNode's children // It can be considered that newNode encapsulated prevNode. void BfReducer::ReplaceNode(BfAstNode* prevNode, BfAstNode* newNode) { @@ -222,6 +238,7 @@ void BfReducer::AddErrorNode(BfAstNode* astNode, bool removeNode) mSource->AddErrorNode(astNode); if (removeNode) astNode->RemoveSelf(); + mLastErrorSrcEnd = astNode->mSrcEnd; } bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int endNode, int* retryNode, int* outEndNode, bool* couldBeExpr, bool* isGenericType, bool* isTuple) @@ -403,6 +420,39 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int mVisitorPos.mReadPos = startNode; return true; } + else if (BfTokenIsTypeDecl(checkToken)) + { + checkIdx++; + auto nextNode = mVisitorPos.Get(checkIdx); + if (auto block = BfNodeDynCast(nextNode)) + { + if (outEndNode != NULL) + *outEndNode = checkIdx; + return true; + } + + if (auto tokenNode = BfNodeDynCast(nextNode)) + { + if (tokenNode->mToken == BfToken_Colon) + { + while (true) + { + checkIdx++; + auto checkNode = mVisitorPos.Get(checkIdx); + if (checkNode == NULL) + return false; + if (auto block = BfNodeDynCast(checkNode)) + { + if (outEndNode != NULL) + *outEndNode = checkIdx; + return true; + } + } + } + } + + return false; + } else return false; } @@ -937,7 +987,7 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int // Ignore } else if ((checkNode->IsA()) || (checkNode->IsA())) - { + { // Identifier is always allowed in tuple (parenDepth == 0), because it's potentially the field name // (successToken == BfToken_RParen) infers we are already checking inside parentheses, such as // when we see a potential cast expression @@ -954,11 +1004,11 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int hadUnexpectedIdentifier = true; } -// if (checkNode->Equals("tag")) -// { -// // Keep looking for tag name -// } -// else + // if (checkNode->Equals("tag")) + // { + // // Keep looking for tag name + // } + // else { hadIdentifier = true; identifierExpected = false; @@ -1018,13 +1068,13 @@ bool BfReducer::IsTypeReference(BfAstNode* checkNode, BfToken successToken, int if ((retryNode != -1) && (successToken == BfToken_None)) { - int newEndNode = -1; - if (IsTypeReference(checkNode, successToken, retryNode, &retryNode, &newEndNode, couldBeExpr, isGenericType, isTuple)) - { - if (outEndNode != NULL) - *outEndNode = newEndNode; - return true; - } + int newEndNode = -1; + if (IsTypeReference(checkNode, successToken, retryNode, &retryNode, &newEndNode, couldBeExpr, isGenericType, isTuple)) + { + if (outEndNode != NULL) + *outEndNode = newEndNode; + return true; + } } return false; } @@ -1548,9 +1598,9 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat { BfExpression* result = NULL; if (!stackHelper.Execute([&]() - { - result = CreateExpression(node, createExprFlags); - })) + { + result = CreateExpression(node, createExprFlags); + })) { Fail("Expression too complex to parse", node); } @@ -1803,6 +1853,45 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat exprLeft = TryCreateInitializerExpression(typeRef); } } + + if (auto tokenNode = BfNodeDynCast(mVisitorPos.Get(endNodeIdx))) + { + if (tokenNode->mToken == BfToken_LParen) + { + int openCount = 1; + int checkIdx = endNodeIdx + 1; + while (true) + { + auto checkNode = mVisitorPos.Get(checkIdx); + if (checkNode == NULL) + break; + if (auto checkTokenNode = BfNodeDynCast(checkNode)) + { + if (checkTokenNode->mToken == BfToken_LParen) + openCount++; + if (checkTokenNode->mToken == BfToken_RParen) + { + openCount--; + if (openCount == 0) + break; + } + } + checkIdx++; + } + + if (auto blockNode = BfNodeDynCast(mVisitorPos.Get(checkIdx + 1))) + { + // If we have 'A() { ... }', that can either be an invocation with an initializer block or a + // create expression with an inline type declaration + initializer block + if (InitializerBlockHasInlineTypeDecl(blockNode)) + { + exprLeft = CreateObjectCreateExpression(NULL, exprLeft); + if (auto initExpr = TryCreateInitializerExpression(exprLeft)) + exprLeft = initExpr; + } + } + } + } } } @@ -1810,12 +1899,28 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat { if (auto tokenNode = BfNodeDynCast(node)) { + BfAstNode* notNode = NULL; + if (tokenNode->mToken == BfToken_Not) + { + auto nextNode = mVisitorPos.GetNext(); + if (auto nextTokenNode = BfNodeDynCast(nextNode)) + { + if (nextTokenNode->mToken == BfToken_Case) + { + mVisitorPos.MoveNext(); + notNode = tokenNode; + node = nextNode; + tokenNode = nextTokenNode; + } + } + } + BfToken token = tokenNode->GetToken(); auto nextNode = mVisitorPos.GetNext(); if ((nextNode != NULL) && ((token == BfToken_Checked) || - (token == BfToken_Unchecked))) + (token == BfToken_Unchecked))) { mVisitorPos.MoveNext(); auto nextExpr = CreateExpression(nextNode); @@ -1825,12 +1930,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat } else if ((token == BfToken_New) || (token == BfToken_Scope) || - (token == BfToken_Stack) || (token == BfToken_Append)) { - if (token == BfToken_Stack) - Fail("'Stack' not supported. Use 'scope::' instead.", tokenNode); - auto allocNode = CreateAllocNode(tokenNode); bool isDelegateBind = false; @@ -2027,8 +2128,18 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat else if (token == BfToken_Case) { auto caseExpr = mAlloc->Alloc(); - ReplaceNode(tokenNode, caseExpr); - caseExpr->mCaseToken = tokenNode; + + if (notNode != NULL) + { + ReplaceNode(notNode, caseExpr); + caseExpr->mNotToken = notNode; + MEMBER_SET(caseExpr, mCaseToken, tokenNode); + } + else + { + ReplaceNode(tokenNode, caseExpr); + caseExpr->mCaseToken = tokenNode; + } if (auto bindToken = BfNodeDynCast(mVisitorPos.GetNext())) { @@ -2058,7 +2169,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat return caseExpr; } else if (token == BfToken_Dot) // Abbreviated dot syntax ".EnumVal" - { + { bool handled = false; if (auto nextTokenNode = BfNodeDynCast(mVisitorPos.GetNext())) { @@ -2067,11 +2178,11 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat auto invocationExpr = CreateObjectCreateExpression(NULL, tokenNode); if (invocationExpr == NULL) return exprLeft; - exprLeft = invocationExpr; + exprLeft = invocationExpr; handled = true; } } - + if (auto blockNode = BfNodeDynCast(mVisitorPos.GetNext())) { // Initializer ".{ x = 1, y = 2 }" @@ -2395,8 +2506,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat bool isTypeRef = IsTypeReference(mVisitorPos.GetCurrent(), BfToken_RParen, -1, &outEndNode); mVisitorPos.mReadPos--; - if ((isTypeRef) && (outEndNode > 0)) - { + if ((isTypeRef) && (outEndNode > 0)) + { if (auto tokenNode = BfNodeDynCast(mVisitorPos.Get(outEndNode - 1))) { if ((tokenNode->mToken == BfToken_RChevron) || (tokenNode->mToken == BfToken_RDblChevron)) @@ -2406,7 +2517,7 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat MEMBER_SET_CHECKED(nameOfExpr, mTarget, typeRef); } } - } + } if (nameOfExpr->mTarget == NULL) { @@ -2609,7 +2720,8 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat continue; } - if ((token == BfToken_Case) && ((createExprFlags & CreateStmtFlags_NoCaseExpr) == 0)) + if (((token == BfToken_Case) || (token == BfToken_Not)) + && ((createExprFlags & CreateStmtFlags_NoCaseExpr) == 0)) { if ((createExprFlags & CreateExprFlags_EarlyExit) != 0) return exprLeft; @@ -2625,8 +2737,20 @@ BfExpression* BfReducer::CreateExpression(BfAstNode* node, CreateExprFlags creat auto caseExpr = mAlloc->Alloc(); ReplaceNode(exprLeft, caseExpr); caseExpr->mValueExpression = exprLeft; - MEMBER_SET(caseExpr, mCaseToken, tokenNode); - mVisitorPos.MoveNext(); + + if (token == BfToken_Not) + { + MEMBER_SET(caseExpr, mNotToken, tokenNode); + mVisitorPos.MoveNext(); + tokenNode = ExpectTokenAfter(caseExpr, BfToken_Case); + MEMBER_SET(caseExpr, mCaseToken, tokenNode); + } + else + { + MEMBER_SET(caseExpr, mCaseToken, tokenNode); + mVisitorPos.MoveNext(); + } + exprLeft = caseExpr; if (auto bindTokenNode = BfNodeDynCast(mVisitorPos.GetNext())) @@ -3921,9 +4045,56 @@ BfAstNode* BfReducer::DoCreateStatement(BfAstNode* node, CreateStmtFlags createS } else if (token == BfToken_Do) { + bool isRepeat = false; + auto checkNode = mVisitorPos.Get(mVisitorPos.mReadPos + 2); auto checkToken = BfNodeDynCast(checkNode); - if ((checkToken != NULL) && (checkToken->GetToken() == BfToken_While)) + + if ((checkToken != NULL) && (checkToken->mToken == BfToken_While)) + { + // Check to see if it's a 'do {} while (...);' or an indepent 'while' statement + int checkIdx = mVisitorPos.mReadPos + 3; + int openCount = 0; + + while (true) + { + auto checkNode = mVisitorPos.Get(checkIdx); + if (checkNode == NULL) + break; + + if (auto tokenNode = BfNodeDynCast(checkNode)) + { + if (tokenNode->mToken == BfToken_LParen) + { + if ((openCount == 0) && (checkIdx != mVisitorPos.mReadPos + 3)) + break; + openCount++; + } + else if (tokenNode->mToken == BfToken_RParen) + { + openCount--; + if (openCount < 0) + break; + } + else if (tokenNode->mToken == BfToken_Semicolon) + { + isRepeat = true; + break; + } + else if (openCount == 0) + break; + } + else + { + if (openCount == 0) + break; + } + + checkIdx++; + } + } + + if (isRepeat) return CreateRepeatStatement(node); else return CreateDoStatement(node); @@ -4082,7 +4253,7 @@ BfAstNode* BfReducer::DoCreateStatement(BfAstNode* node, CreateStmtFlags createS MEMBER_SET(deferStmt, mOpenParen, nextTokenNode); mVisitorPos.MoveNext(); - nextTokenNode = ExpectTokenAfter(deferStmt, BfToken_Scope, BfToken_Stack); + nextTokenNode = ExpectTokenAfter(deferStmt, BfToken_Scope); MEMBER_SET_CHECKED(deferStmt, mScopeToken, nextTokenNode); nextTokenNode = ExpectTokenAfter(deferStmt, BfToken_RParen); @@ -4364,6 +4535,7 @@ BfAstNode* BfReducer::DoCreateStatement(BfAstNode* node, CreateStmtFlags createS ReplaceNode(typeRef, methodDecl); methodDecl->mDocumentation = FindDocumentation(methodDecl); methodDecl->mReturnType = typeRef; + CheckMultiuseAttributeTypeRef(methodDecl->mReturnType); BfDeferredAstSizedArray params(methodDecl->mParams, mAlloc); BfDeferredAstSizedArray commas(methodDecl->mCommas, mAlloc); @@ -4598,7 +4770,6 @@ bool BfReducer::IsTerminatingExpression(BfAstNode* node) case BfToken_Append: case BfToken_Default: case BfToken_Is: - case BfToken_Stack: case BfToken_Scope: case BfToken_New: case BfToken_RetType: @@ -4714,7 +4885,7 @@ BfAstNode* BfReducer::CreateStatement(BfAstNode* node, CreateStmtFlags createStm { Fail("Statement too complex to parse", node); } - return result; + return result; } } @@ -5110,6 +5281,59 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefF if ((createTypeRefFlags & CreateTypeRefFlags_EarlyExit) != 0) return delegateTypeRef; } + else if ((BfTokenIsTypeDecl(token)) || (token == BfToken_LBracket)) + { + BfAttributeDirective* attributes = NULL; + if (token == BfToken_LBracket) + { + attributes = CreateAttributeDirective(tokenNode); + if (attributes == NULL) + return NULL; + mVisitorPos.MoveNext(); + bool isValid = false; + + auto nextNode = mVisitorPos.GetCurrent(); + tokenNode = BfNodeDynCast(nextNode); + if (tokenNode != NULL) + { + token = tokenNode->mToken; + if (BfTokenIsTypeDecl(token)) + isValid = true; + } + if (!isValid) + { + AddErrorNode(attributes); + return NULL; + } + } + + bool attribsApply = false; + if ((mTypeMemberNodeStart != NULL) && (mTypeMemberNodeStart->mSrcEnd == tokenNode->mTriviaStart)) + attribsApply = true; + auto typeDeclNode = CreateTopLevelObject(tokenNode, attributes, attribsApply ? mTypeMemberNodeStart : NULL, true); + if (typeDeclNode == NULL) + { + if (attributes != NULL) + AddErrorNode(attributes); + return NULL; + } + + auto typeDecl = BfNodeDynCast(typeDeclNode); + if (typeDecl == NULL) + { + if (attributes != NULL) + AddErrorNode(attributes); + AddErrorNode(typeDeclNode); + return NULL; + } + + InitAnonymousType(typeDecl); + + auto typeRef = mAlloc->Alloc(); + ReplaceNode(typeDecl, typeRef); + typeRef->mTypeDeclaration = typeDecl; + return typeRef; + } else if ((token == BfToken_Comptype) || (token == BfToken_Decltype)) { auto declTypeRef = mAlloc->Alloc(); @@ -5202,18 +5426,18 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefF BfTypeReference* typeRef = BfNodeDynCast(firstNode); if (typeRef == NULL) { -// if (identifierNode->Equals("tag")) -// { -// auto rightIdentifer = ExpectIdentifierAfter(identifierNode); -// if (rightIdentifer != NULL) -// { -// auto tagTypeRef = mAlloc->Alloc(); -// ReplaceNode(identifierNode, tagTypeRef); -// tagTypeRef->mTagNode = identifierNode; -// MEMBER_SET(tagTypeRef, mNameNode, rightIdentifer); -// return tagTypeRef; -// } -// } + // if (identifierNode->Equals("tag")) + // { + // auto rightIdentifer = ExpectIdentifierAfter(identifierNode); + // if (rightIdentifer != NULL) + // { + // auto tagTypeRef = mAlloc->Alloc(); + // ReplaceNode(identifierNode, tagTypeRef); + // tagTypeRef->mTagNode = identifierNode; + // MEMBER_SET(tagTypeRef, mNameNode, rightIdentifer); + // return tagTypeRef; + // } + // } typeRef = DoCreateNamedTypeRef(identifierNode); } @@ -5270,7 +5494,7 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefF { auto rightIdentifer = ExpectIdentifierAfter(qualifiedTypeRef); if (rightIdentifer == NULL) - return qualifiedTypeRef; + return qualifiedTypeRef; auto namedTypeRef = mAlloc->Alloc(); namedTypeRef->mNameNode = rightIdentifer; @@ -5473,7 +5697,7 @@ BfTypeReference* BfReducer::DoCreateTypeRef(BfAstNode* firstNode, CreateTypeRefF token = tokenNode->GetToken(); if ((tokenNode != NULL) && ((token == BfToken_Const) || - (token == BfToken_Ref) || + (token == BfToken_Ref) || (token == BfToken_Mut) || (token == BfToken_LParen) || (token == BfToken_Delegate) || @@ -5603,7 +5827,14 @@ BfTypeReference* BfReducer::CreateTypeRefAfter(BfAstNode* astNode, CreateTypeRef BfTypeReference* typeRef = CreateTypeRef(nextNode, createTypeRefFlags); if (typeRef == NULL) { - BF_ASSERT(mVisitorPos.mReadPos == startPos); + if (mLastErrorSrcEnd > startPos) + { + // We added an error node and made progress + } + else + { + BF_ASSERT(mVisitorPos.mReadPos == startPos); + } mVisitorPos.mReadPos--; } return typeRef; @@ -5677,8 +5908,8 @@ BfIdentifierNode* BfReducer::CompactQualifiedName(BfAstNode* leftNode) auto prevNodeToken = BfNodeDynCast(prevNode); if ((prevNodeToken != NULL) && ((prevNodeToken->GetToken() == BfToken_Dot) || - (prevNodeToken->GetToken() == BfToken_QuestionDot) || - (prevNodeToken->GetToken() == BfToken_Arrow))) + (prevNodeToken->GetToken() == BfToken_QuestionDot) || + (prevNodeToken->GetToken() == BfToken_Arrow))) return leftIdentifier; mVisitorPos.MoveNext(); // past . @@ -5880,12 +6111,18 @@ BfStatement* BfReducer::CreateAttributedStatement(BfTokenNode* tokenNode, Create if (auto exprStatement = BfNodeDynCast(checkNode)) checkNode = exprStatement->mExpression; - if ((checkNode->IsA()) || - (checkNode->IsA()) || - (checkNode->IsA()) || + if ((checkNode->IsA()) || + (checkNode->IsA()) || + (checkNode->IsA()) || (checkNode->IsA()) || - (checkNode->IsA())) + (checkNode->IsA())) { + if (auto varDecl = BfNodeDynCast(checkNode)) + { + if (CheckInlineTypeRefAttribute(varDecl->mTypeRef, attrib)) + return BfNodeDynCast(stmt); + } + BfAttributedStatement* attribStmt = mAlloc->Alloc(); ReplaceNode(attrib, attribStmt); attribStmt->mAttributes = attrib; @@ -6028,7 +6265,7 @@ BfTokenNode* BfReducer::ReadArguments(BfAstNode* parentNode, BfAstNode* afterNod ReplaceNode(identifierNode, namedExpr); MEMBER_SET(namedExpr, mNameNode, identifierNode); MEMBER_SET(namedExpr, mColonToken, nextNextToken) - mVisitorPos.MoveNext(); + mVisitorPos.MoveNext(); mVisitorPos.MoveNext(); auto innerExpr = CreateExpressionAfter(namedExpr, CreateExprFlags_AllowVariableDecl); @@ -6168,12 +6405,16 @@ BfFieldDeclaration* BfReducer::CreateFieldDeclaration(BfTokenNode* tokenNode, Bf else { ReplaceNode(typeRef, fieldDeclaration); - fieldDeclaration->mTypeRef = typeRef; - fieldDeclaration->mNameNode = nameIdentifier; + fieldDeclaration->mTypeRef = typeRef; fieldDeclaration->mInitializer = NULL; - MoveNode(fieldDeclaration->mNameNode, fieldDeclaration); - //mVisitorPos.MoveNext(); + if (nameIdentifier != NULL) + { + fieldDeclaration->mNameNode = nameIdentifier; + MoveNode(fieldDeclaration->mNameNode, fieldDeclaration); + } } + CheckMultiuseAttributeTypeRef(fieldDeclaration->mTypeRef); + BfToken token = tokenNode->GetToken(); if (token == BfToken_AssignEquals) { @@ -6279,6 +6520,26 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i return memberNode; } + memberNode->mTriviaStart = attributes->mTriviaStart; + memberNode->mSrcStart = attributes->mSrcStart; + + if (auto fieldDecl = BfNodeDynCast(member)) + { + if (CheckInlineTypeRefAttribute(fieldDecl->mTypeRef, attributes)) + { + return member; + } + } + + if (auto methodDecl = BfNodeDynCast(member)) + { + if (CheckInlineTypeRefAttribute(methodDecl->mReturnType, attributes)) + { + return member; + } + } + + ReplaceNode(attributes, member); member->mAttributes = attributes; return member; @@ -6296,6 +6557,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i if (typeRef == NULL) return operatorDecl; MEMBER_SET_CHECKED(operatorDecl, mReturnType, typeRef); + CheckMultiuseAttributeTypeRef(operatorDecl->mReturnType); operatorDecl->mIsConvOperator = true; ParseMethod(operatorDecl, ¶ms, &commas); @@ -6769,13 +7031,13 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i if (token == BfToken_Extern) { if ((fieldDecl->mExternSpecifier != NULL) && (fieldDecl->mExternSpecifier->mToken == BfToken_Append)) - { - Fail("Extern cannot be used with 'append' specified", tokenNode); - } - else if (fieldDecl->mExternSpecifier != NULL) - { + { + Fail("Extern cannot be used with 'append' specified", tokenNode); + } + else if (fieldDecl->mExternSpecifier != NULL) + { Fail("Extern already specified", tokenNode); - } + } MEMBER_SET(fieldDecl, mExternSpecifier, tokenNode); handled = true; @@ -7043,9 +7305,9 @@ void BfReducer::ReadPropertyBlock(BfPropertyDeclaration* propertyDeclaration, Bf BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int depth, BfAstNode* deferredHeadNode) { -// SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, node, false); -// if (depth == 0) -// prevTypeMemberNodeStart.Set(); + // SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, node, false); + // if (depth == 0) + // prevTypeMemberNodeStart.Set(); if (mCurTypeDecl != NULL) AssertCurrentNode(node); @@ -7066,10 +7328,19 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept } } + if (BfTokenIsTypeDecl(token)) + { + int endNodeIdx = -1; + if (IsTypeReference(node, BfToken_None, -1, &endNodeIdx)) + { + isTypeRef = true; + } + } + if ((token == BfToken_LBracket) && (depth > 0)) { - Fail("Unexpected custom attribute", node); - return NULL; + // The only valid option is an attributed type reference + isTypeRef = true; } if (isTypeRef) @@ -7223,6 +7494,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept ReplaceNode(nameIdentifier, methodDeclaration); methodDeclaration->mDocumentation = FindDocumentation(mTypeMemberNodeStart); MEMBER_SET(methodDeclaration, mReturnType, typeRef); + CheckMultiuseAttributeTypeRef(methodDeclaration->mReturnType); MEMBER_SET(methodDeclaration, mExplicitInterface, explicitInterface); MEMBER_SET(methodDeclaration, mExplicitInterfaceDotToken, explicitInterfaceDot); return methodDeclaration; @@ -7233,13 +7505,14 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept } else if (auto nextToken = BfNodeDynCast(nextNode)) { - if (nextToken->GetToken() == BfToken_Operator) + if (nextToken->mToken == BfToken_Operator) { auto operatorDecl = mAlloc->Alloc(); BfDeferredAstSizedArray params(operatorDecl->mParams, mAlloc); BfDeferredAstSizedArray commas(operatorDecl->mCommas, mAlloc); ReplaceNode(typeRef, operatorDecl); operatorDecl->mReturnType = typeRef; + CheckMultiuseAttributeTypeRef(operatorDecl->mReturnType); mVisitorPos.MoveNext(); MEMBER_SET(operatorDecl, mOperatorToken, nextToken); @@ -7318,7 +7591,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept return operatorDecl; } - else if (nextToken->GetToken() == BfToken_LParen) + else if (nextToken->mToken == BfToken_LParen) { Fail("Method return type expected", node); @@ -7330,6 +7603,10 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept typeRef = NULL; } } + else if (nextToken->mToken == BfToken_Semicolon) + { + forceIsMethod = true; + } } if ((nameIdentifier != NULL) || (forceIsMethod) || (indexerThisToken != NULL)) @@ -7399,6 +7676,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept ReplaceNode(typeRef, propDecl); propDecl->mDocumentation = FindDocumentation(mTypeMemberNodeStart); propDecl->mTypeRef = typeRef; + CheckMultiuseAttributeTypeRef(propDecl->mTypeRef); if (explicitInterface != NULL) { @@ -7491,6 +7769,10 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept { MEMBER_SET(method, mBody, expr); propertyDeclaration->SetSrcEnd(expr->GetSrcEnd()); + + auto endSemicolon = ExpectTokenAfter(expr, BfToken_Semicolon); + if (endSemicolon != NULL) + MEMBER_SET(method, mEndSemicolon, endSemicolon); } methods.Add(method); @@ -7558,6 +7840,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept ReplaceNode(typeRef, fieldDecl); fieldDecl->mDocumentation = FindDocumentation(mTypeMemberNodeStart); fieldDecl->mTypeRef = typeRef; + CheckMultiuseAttributeTypeRef(fieldDecl->mTypeRef); return fieldDecl; } } @@ -7589,6 +7872,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfAstNode* node, bool declStarted, int dept } methodDeclaration->mReturnType = typeRef; + CheckMultiuseAttributeTypeRef(methodDeclaration->mReturnType); MEMBER_SET_CHECKED(methodDeclaration, mNameNode, nameIdentifier); mCurMethodDecl = methodDeclaration; ParseMethod(methodDeclaration, ¶ms, &commas); @@ -7683,11 +7967,16 @@ BfInvocationExpression* BfReducer::CreateInvocationExpression(BfAstNode* target, } BfInitializerExpression* BfReducer::TryCreateInitializerExpression(BfAstNode* target) -{ +{ auto block = BfNodeDynCast(mVisitorPos.GetNext()); if (block == NULL) return NULL; + bool allowInitializerStatement = false; + auto objectCreateExpr = BfNodeDynCast(target); + if (objectCreateExpr != NULL) + allowInitializerStatement = true; + mVisitorPos.MoveNext(); auto initializerExpr = mAlloc->Alloc(); @@ -7702,12 +7991,47 @@ BfInitializerExpression* BfReducer::TryCreateInitializerExpression(BfAstNode* ta BfDeferredAstNodeSizedArray values(initializerExpr, initializerExpr->mValues, mAlloc); BfDeferredAstNodeSizedArray commas(initializerExpr, initializerExpr->mCommas, mAlloc); + int initializerStartIdx = 0; + int minTypeDeclEnd = 0; + BfAstNode* nextNode = NULL; while (!isDone) { BfAstNode* node = mVisitorPos.GetCurrent(); + if (node == NULL) + break; initializerExpr->mSrcEnd = node->mSrcEnd; + if ((values.IsEmpty()) && (initializerExpr->mInlineTypeRef == NULL)) + { + if ((allowInitializerStatement) && (!IsInitializerStatement(node))) + { + auto defBlock = mAlloc->Alloc(); + ReplaceNode(block, defBlock); + *defBlock = *block; + + auto typeDecl = mAlloc->Alloc(); + ReplaceNode(node, typeDecl); + defBlock->mOpenBrace = NULL; + MEMBER_SET(typeDecl, mDefineNode, defBlock); + InitAnonymousType(typeDecl); + HandleTypeDeclaration(typeDecl, NULL, NULL, true); + initializerStartIdx = mVisitorPos.mWritePos; + auto typeRef = mAlloc->Alloc(); + ReplaceNode(typeDecl, typeRef); + typeRef->mTypeDeclaration = typeDecl; + MEMBER_SET(initializerExpr, mInlineTypeRef, typeRef); + + if (objectCreateExpr != NULL) + { + BfDeferredAstSizedArray baseClasses(typeDecl->mBaseClasses, mAlloc); + baseClasses.Add(objectCreateExpr->mTypeRef); + } + + continue; + } + } + auto expr = CreateExpression(node, BfReducer::CreateExprFlags_AllowAnonymousIndexer); isDone = !mVisitorPos.MoveNext(); if (expr != NULL) @@ -7740,6 +8064,24 @@ BfInitializerExpression* BfReducer::TryCreateInitializerExpression(BfAstNode* ta mVisitorPos.Trim(); + if (initializerExpr->mInlineTypeRef != NULL) + { + auto typeDecl = initializerExpr->mInlineTypeRef->mTypeDeclaration; + + if (initializerStartIdx != 0) + { + block->SetSize(initializerStartIdx); + int srcEnd = block->mSrcEnd; + if (initializerStartIdx > 0) + srcEnd = block->mChildArr[initializerStartIdx - 1]->mSrcEnd; + typeDecl->mDefineNode->mSrcEnd = srcEnd; + typeDecl->mSrcEnd = srcEnd; + } + + typeDecl->mSrcEnd = BF_MAX(typeDecl->mSrcEnd, minTypeDeclEnd); + initializerExpr->mInlineTypeRef->mSrcEnd = typeDecl->mSrcEnd; + } + if (block->mCloseBrace != NULL) MEMBER_SET(initializerExpr, mCloseBrace, block->mCloseBrace); @@ -7800,7 +8142,7 @@ BfLambdaBindExpression* BfReducer::CreateLambdaBindExpression(BfAstNode* allocNo if (auto tokenNode = BfNodeDynCast(nextNode)) isRParen = tokenNode->GetToken() == BfToken_RParen; if (!isRParen) - { + { auto nameIdentifier = ExpectIdentifierAfter(lambdaBindExpr, "parameter name"); if (nameIdentifier == NULL) return lambdaBindExpr; @@ -7898,7 +8240,7 @@ BfCollectionInitializerExpression* BfReducer::CreateCollectionInitializerExpress return arrayInitializerExpression; } -BfCollectionInitializerExpression * BfReducer::CreateCollectionInitializerExpression(BfTokenNode* openToken) +BfCollectionInitializerExpression* BfReducer::CreateCollectionInitializerExpression(BfTokenNode* openToken) { auto arrayInitializerExpression = mAlloc->Alloc(); BfDeferredAstSizedArray values(arrayInitializerExpression->mValues, mAlloc); @@ -7995,6 +8337,91 @@ BfScopedInvocationTarget* BfReducer::CreateScopedInvocationTarget(BfAstNode*& ta return scopedInvocationTarget; } +void BfReducer::InitAnonymousType(BfTypeDeclaration* typeDecl) +{ + int blockId = 0; + if (auto block = BfNodeDynCast(typeDecl->mDefineNode)) + { + blockId = block->mParserBlockId; + } + else + { + auto parser = mSource->ToParser(); + if (parser != NULL) + blockId = parser->mCurBlockId + typeDecl->mSrcStart; + } + + String name; + auto parserData = typeDecl->GetParserData(); + name = "_Anon_"; + + auto parseFileData = parserData->mParseFileData; + int uniqueId = parseFileData->GetUniqueId(blockId); + name += StrFormat("%d", uniqueId); + + int len = (int)name.length() + 1; + typeDecl->mAnonymousName = (char*)mAlloc->AllocBytes(len); + memcpy(typeDecl->mAnonymousName, name.c_str(), len); + + auto parser = typeDecl->GetParser(); + if ((parser != NULL) && (parser->mIsEmitted)) + { + Fail("Type declarations are not allowed in emitted code", typeDecl); + } + else if (mCurTypeState != NULL) + { + mCurTypeState->mAnonymousTypeDecls.Add(typeDecl); + } + else + { + Fail("Invalid use of anonymous type declaration", typeDecl); + } +} + +bool BfReducer::CheckInlineTypeRefAttribute(BfAstNode* typeRef, BfAttributeDirective* attributes) +{ + if (attributes == NULL) + return false; + + if (auto inlineTypeRef = BfNodeDynCast(typeRef)) + { + auto checkAttribute = attributes; + while (checkAttribute != NULL) + { + checkAttribute->mIsMultiUse = true; + checkAttribute = checkAttribute->mNextAttribute; + } + + auto typeDecl = inlineTypeRef->mTypeDeclaration; + typeDecl->mSrcStart = attributes->mSrcStart; + typeDecl->mTriviaStart = typeDecl->mSrcStart; + typeDecl->mAttributes = attributes; + + inlineTypeRef->mTriviaStart = attributes->mTriviaStart; + inlineTypeRef->mSrcStart = attributes->mSrcStart; + + if ((typeDecl->mIgnoreDeclaration) && (IsNodeRelevant(typeDecl))) + typeDecl->mIgnoreDeclaration = false; + + return true; + } + + return false; +} + +void BfReducer::CheckMultiuseAttributeTypeRef(BfAstNode* typeRef) +{ + if (auto inlineTypeRef = BfNodeDynCast(typeRef)) + { + auto checkAttribute = inlineTypeRef->mTypeDeclaration->mAttributes; + while (checkAttribute != NULL) + { + checkAttribute->mIsMultiUse = true; + checkAttribute = checkAttribute->mNextAttribute; + } + } +} + bool BfReducer::SetProtection(BfAstNode* parentNode, BfAstNode*& protectionNodeRef, BfTokenNode* tokenNode) { bool failed = false; @@ -8231,16 +8658,6 @@ BfObjectCreateExpression* BfReducer::CreateObjectCreateExpression(BfAstNode* all BfTokenNode* tokenNode; - // Why did we want this syntax? - // if (tokenNode = BfNodeDynCast(mVisitorPos.GetNext())) - // { - // if (tokenNode->GetToken() == BfToken_Star) - // { - // MEMBER_SET(objectCreateExpr, mStarToken, tokenNode); - // mVisitorPos.MoveNext(); - // } - // } - if (typeRef == NULL) typeRef = CreateTypeRefAfter(objectCreateExpr); if (typeRef == NULL) @@ -8308,7 +8725,7 @@ BfObjectCreateExpression* BfReducer::CreateObjectCreateExpression(BfAstNode* all if ((nextToken != NULL) && (nextToken->mToken == BfToken_Dot) && (nextNextToken != NULL) && (nextNextToken->mToken == BfToken_This)) { - auto ctorExplicitNode = mAlloc->Alloc(); + auto ctorExplicitNode = mAlloc->Alloc(); ReplaceNode(nextToken, ctorExplicitNode); ctorExplicitNode->mDotToken = nextToken; MEMBER_SET(ctorExplicitNode, mThisToken, nextNextToken); @@ -8320,7 +8737,7 @@ BfObjectCreateExpression* BfReducer::CreateObjectCreateExpression(BfAstNode* all { auto ctorExplicitNode = mAlloc->Alloc(); ReplaceNode(nextToken, ctorExplicitNode); - ctorExplicitNode->mThisToken = nextToken; + ctorExplicitNode->mThisToken = nextToken; mVisitorPos.MoveNext(); MEMBER_SET(objectCreateExpr, mCtorExplicit, ctorExplicitNode); } @@ -8447,7 +8864,7 @@ BfMemberReferenceExpression* BfReducer::CreateMemberReferenceExpression(BfAstNod if (tokenNode->GetToken() == BfToken_This) { mVisitorPos.MoveNext(); - MEMBER_SET(memberReferenceExpr, mMemberName, tokenNode); + MEMBER_SET(memberReferenceExpr, mMemberName, tokenNode); } } @@ -8653,7 +9070,7 @@ BfAstNode* BfReducer::HandleTopLevel(BfBlock* node) return node; } -BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode) +BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode, bool isAnonymous) { AssertCurrentNode(tokenNode); @@ -8999,7 +9416,10 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi case BfToken_Function: { auto typeDeclaration = mAlloc->Alloc(); + SetAndRestoreValue prevTypeDecl(mCurTypeDecl, typeDeclaration); + CurTypeState curTypeState(typeDeclaration, mAlloc); + SetAndRestoreValue prevTypeState(mCurTypeState, &curTypeState); ReplaceNode(tokenNode, typeDeclaration); typeDeclaration->mDocumentation = FindDocumentation(typeDeclaration); @@ -9011,6 +9431,7 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi auto methodDecl = mAlloc->Alloc(); MEMBER_SET(methodDecl, mReturnType, retType); + CheckMultiuseAttributeTypeRef(methodDecl->mReturnType); BfDeferredAstSizedArray params(methodDecl->mParams, mAlloc); BfDeferredAstSizedArray commas(methodDecl->mCommas, mAlloc); methodDecl->mDocumentation = FindDocumentation(methodDecl); @@ -9126,11 +9547,15 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi if ((tokenNode->GetToken() == BfToken_Enum) && (isSimpleEnum)) break; - auto identifierNode = ExpectIdentifierAfter(tokenNode); - if (identifierNode == NULL) + BfIdentifierNode* identifierNode = NULL; + if (!isAnonymous) { - AddErrorNode(tokenNode); - return NULL; + identifierNode = ExpectIdentifierAfter(tokenNode); + if (identifierNode == NULL) + { + AddErrorNode(tokenNode); + return NULL; + } } // We put extra effort in here to continue after failure, since 'return NULL' failure @@ -9143,7 +9568,8 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi typeDeclaration->mTypeNode = tokenNode; typeDeclaration->mNameNode = identifierNode; ReplaceNode(tokenNode, typeDeclaration); - MoveNode(identifierNode, typeDeclaration); + if (identifierNode != NULL) + MoveNode(identifierNode, typeDeclaration); typeDeclaration->mDocumentation = FindDocumentation(mTypeMemberNodeStart); auto nextNode = mVisitorPos.GetNext(); @@ -9172,12 +9598,20 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi if (baseTypeIdx > 0) { + bool hasComma = false; + if (auto tokenNode = BfNodeDynCast(nextNode)) { - if (tokenNode->mToken == BfToken_Semicolon) - { - break; - } + if ((tokenNode->mToken == BfToken_Semicolon) && (!isAnonymous)) + break; + if (tokenNode->mToken == BfToken_Comma) + hasComma = true; + } + + if ((!hasComma) && (isAnonymous)) + { + // End type declaration + break; } BfTokenNode* commaToken = NULL; @@ -9259,7 +9693,7 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi if (tokenNode != NULL) { - if (tokenNode->GetToken() == BfToken_Semicolon) + if ((tokenNode->GetToken() == BfToken_Semicolon) && (!isAnonymous)) { typeDeclaration->mDefineNode = tokenNode; MoveNode(tokenNode, typeDeclaration); @@ -9268,6 +9702,9 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi } } + if ((isAnonymous) && (!BfNodeIsA(nextNode))) + return typeDeclaration; + auto blockNode = ExpectBlockAfter(typeDeclaration); if (blockNode != NULL) { @@ -9284,9 +9721,13 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi if (isSimpleEnum) { - auto identifierNode = ExpectIdentifierAfter(tokenNode, "enum name"); - if (identifierNode == NULL) - return NULL; + BfIdentifierNode* identifierNode = NULL; + if (!isAnonymous) + { + identifierNode = ExpectIdentifierAfter(tokenNode, "enum name"); + if (identifierNode == NULL) + return NULL; + } // We put extra effort in here to continue after failure, since 'return NULL' failure // means we don't parse members inside type (messes up colorization and such) @@ -9298,7 +9739,8 @@ BfAstNode* BfReducer::CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDi typeDeclaration->mTypeNode = tokenNode; typeDeclaration->mNameNode = identifierNode; ReplaceNode(tokenNode, typeDeclaration); - MoveNode(identifierNode, typeDeclaration); + if (identifierNode != NULL) + MoveNode(identifierNode, typeDeclaration); typeDeclaration->mDocumentation = FindDocumentation(typeDeclaration); auto nextNode = mVisitorPos.GetNext(); @@ -9550,7 +9992,7 @@ BfTokenNode* BfReducer::BreakQuestionLBracket(BfTokenNode* tokenNode) return firstToken; } -BfCommentNode * BfReducer::FindDocumentation(BfAstNode* defNodeHead, BfAstNode* defNodeEnd, bool checkDocAfter) +BfCommentNode* BfReducer::FindDocumentation(BfAstNode* defNodeHead, BfAstNode* defNodeEnd, bool checkDocAfter) { if (defNodeEnd == NULL) defNodeEnd = defNodeHead; @@ -9675,7 +10117,8 @@ BfTokenNode* BfReducer::ParseMethodParams(BfAstNode* node, SizedArrayImpl(mVisitorPos.GetNext())) -// { -// if (nextToken->mToken == BfToken_LParen) -// { -// BfGenericConstraintExpression* genericConstraint = mAlloc->Alloc(); -// ReplaceNode(tokenNode, genericConstraint); -// genericConstraint->mWhereToken = tokenNode; -// constraintsDeclaration->mHasExpressions = true; -// -// genericConstraintsArr.push_back(genericConstraint); -// -// auto expr = CreateExpressionAfter(genericConstraint, CreateExprFlags_EarlyExit); -// if (expr == NULL) -// break; -// -// MEMBER_SET(genericConstraint, mExpression, expr); -// -// BfTokenNode* nextWhereToken = NULL; -// if (auto checkToken = BfNodeDynCast(mVisitorPos.GetNext())) -// { -// if (checkToken->mToken != BfToken_Where) -// nextWhereToken = checkToken; -// } -// -// auto nextNode = mVisitorPos.GetNext(); -// if (BfNodeDynCast(nextNode)) -// break; -// -// bool handled = false; -// if (auto tokenNode = BfNodeDynCast(nextNode)) -// { -// if (tokenNode->mToken == BfToken_FatArrow) -// break; -// } -// -// tokenNode = ExpectTokenAfter(genericConstraint, BfToken_LBrace, BfToken_Where, BfToken_Semicolon); -// if (tokenNode == NULL) -// break; -// -// BfToken token = tokenNode->GetToken(); -// if (token != BfToken_Where) -// { -// mVisitorPos.mReadPos--; -// break; -// } -// -// continue; -// } -// } + // if (auto nextToken = BfNodeDynCast(mVisitorPos.GetNext())) + // { + // if (nextToken->mToken == BfToken_LParen) + // { + // BfGenericConstraintExpression* genericConstraint = mAlloc->Alloc(); + // ReplaceNode(tokenNode, genericConstraint); + // genericConstraint->mWhereToken = tokenNode; + // constraintsDeclaration->mHasExpressions = true; + // + // genericConstraintsArr.push_back(genericConstraint); + // + // auto expr = CreateExpressionAfter(genericConstraint, CreateExprFlags_EarlyExit); + // if (expr == NULL) + // break; + // + // MEMBER_SET(genericConstraint, mExpression, expr); + // + // BfTokenNode* nextWhereToken = NULL; + // if (auto checkToken = BfNodeDynCast(mVisitorPos.GetNext())) + // { + // if (checkToken->mToken != BfToken_Where) + // nextWhereToken = checkToken; + // } + // + // auto nextNode = mVisitorPos.GetNext(); + // if (BfNodeDynCast(nextNode)) + // break; + // + // bool handled = false; + // if (auto tokenNode = BfNodeDynCast(nextNode)) + // { + // if (tokenNode->mToken == BfToken_FatArrow) + // break; + // } + // + // tokenNode = ExpectTokenAfter(genericConstraint, BfToken_LBrace, BfToken_Where, BfToken_Semicolon); + // if (tokenNode == NULL) + // break; + // + // BfToken token = tokenNode->GetToken(); + // if (token != BfToken_Where) + // { + // mVisitorPos.mReadPos--; + // break; + // } + // + // continue; + // } + // } BfGenericConstraint* genericConstraint = mAlloc->Alloc(); BfDeferredAstSizedArray constraintTypes(genericConstraint->mConstraintTypes, mAlloc); @@ -10647,17 +11090,152 @@ void BfReducer::HandleBlock(BfBlock* block, bool allowEndingExpression) mVisitorPos.Trim(); } -void BfReducer::HandleTypeDeclaration(BfTypeDeclaration* typeDecl, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode) +bool BfReducer::IsInitializerStatement(int checkIdx) +{ + SetAndRestoreValue prevReadPos(mVisitorPos.mReadPos); + + int nodeCount = 0; + while (true) + { + mVisitorPos.mReadPos = checkIdx; + auto checkNode = mVisitorPos.GetCurrent(); + if (checkNode == NULL) + return false; + + if (auto checkToken = BfNodeDynCast(checkNode)) + { + switch (checkToken->mToken) + { + case BfToken_Dot: + if (nodeCount == 0) + return true; + break; + case BfToken_Public: + case BfToken_Private: + case BfToken_Internal: + case BfToken_Protected: + case BfToken_This: + case BfToken_Semicolon: + case BfToken_Case: + case BfToken_Const: + case BfToken_Static: + case BfToken_TypeAlias: + case BfToken_Mixin: + case BfToken_Class: + case BfToken_Struct: + case BfToken_Enum: + case BfToken_Interface: + case BfToken_Override: + return false; + case BfToken_AssignEquals: + if (nodeCount > 0) + return true; + break; + case BfToken_Comma: + if (nodeCount == 0) + return false; + else + return true; + } + } + else if (auto literalExpr = BfNodeDynCast(checkNode)) + { + return true; + } + else if (auto block = BfNodeDynCast(checkNode)) + { + if (nodeCount == 0) + return true; + } + + int endNode = -1; + bool coundBeExpr = false; + if (IsTypeReference(checkNode, BfToken_None, -1, &endNode, &coundBeExpr)) + { + auto nextNode = mVisitorPos.Get(endNode); + if (nextNode == NULL) + { + // At end + return true; + } + + if (BfNodeIsA(nextNode)) + return false; + + if (auto nextToken = BfNodeDynCast(nextNode)) + { + switch (nextToken->mToken) + { + case BfToken_This: + return false; + } + } + + nodeCount++; + checkIdx = BF_MAX(checkIdx + 1, endNode); + continue; + } + else if (endNode != -1) + { + auto nextNode = mVisitorPos.Get(endNode); + if (auto tokenNode = BfNodeDynCast(nextNode)) + { + if (tokenNode->mToken == BfToken_LParen) + { + int checkEndNode = -1; + if (IsTypeReference(checkNode, tokenNode->mToken, -1, &checkEndNode, &coundBeExpr)) + { + if (checkEndNode == endNode) + { + // Is method call + return true; + } + } + } + } + + + } + + nodeCount++; + checkIdx++; + } + + return false; +} + +bool BfReducer::IsInitializerStatement(BfAstNode* node) +{ + AssertCurrentNode(node); + return IsInitializerStatement(mVisitorPos.mReadPos); +} + +bool BfReducer::InitializerBlockHasInlineTypeDecl(BfBlock* block) +{ + if (block->mChildArr.mSize == 0) + return false; + + SetAndRestoreValue prevVisitorPos(mVisitorPos, BfVisitorPos(block)); + return !IsInitializerStatement(0); +} + +void BfReducer::HandleTypeDeclaration(BfTypeDeclaration* typeDecl, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode, bool findInitializer) { SetAndRestoreValue prevTypeDecl(mCurTypeDecl, typeDecl); + CurTypeState curTypeState(typeDecl, mAlloc); + SetAndRestoreValue prevTypeState(mCurTypeState, &curTypeState); SetAndRestoreValue prevVisitorPos(mVisitorPos, BfVisitorPos(BfNodeDynCast(typeDecl->mDefineNode))); + if (findInitializer) + prevVisitorPos.CancelRestore(); + if (attributes != NULL) { MEMBER_SET(typeDecl, mAttributes, attributes); + typeDecl->mTriviaStart = BF_MIN(typeDecl->mAttributes->mTriviaStart, attributes->mTriviaStart); } - if ((!IsNodeRelevant(deferredHeadNode, typeDecl)) && (!typeDecl->IsTemporary())) + if ((!IsNodeRelevant(deferredHeadNode, typeDecl)) && (!typeDecl->IsTemporary()) && (!findInitializer)) { typeDecl->mIgnoreDeclaration = true; return; @@ -10679,6 +11257,13 @@ void BfReducer::HandleTypeDeclaration(BfTypeDeclaration* typeDecl, BfAttributeDi BfAstNode* typeMember = BfNodeDynCast(node); if (typeMember == NULL) { + if (findInitializer) + { + bool isInitializerStatement = IsInitializerStatement(node); + if (isInitializerStatement) + return; + } + SetAndRestoreValue prevTypeMemberNodeStart(mTypeMemberNodeStart, node); typeMember = ReadTypeMember(node); } @@ -10731,439 +11316,439 @@ BfInlineAsmStatement* BfReducer::CreateInlineAsmStatement(BfAstNode* asmNode) { auto processInstrNodes = [&](const Array& nodes) -> BfInlineAsmInstruction* - { - int nodeCount = (int)nodes.size(); - int curNodeIdx = 0; - - auto instNode = mAlloc->Alloc(); - //instNode->mSource = asmStatement->mSource; - int srcStart = nodes.front()->GetSrcStart(); - int srcEnd = nodes.back()->GetSrcEnd(); - instNode->Init(srcStart, srcStart, srcEnd); - - auto replaceWithLower = [](String& s) { - std::transform(s.begin(), s.end(), s.begin(), ::tolower); - }; + int nodeCount = (int)nodes.size(); + int curNodeIdx = 0; - auto readIdent = [&](String& outStr, bool forceLowerCase, const StringImpl& errorExpectation, bool peekOnly) -> bool - { - if (curNodeIdx >= nodeCount) - { - if (!peekOnly) - Fail(StrFormat("Expected %s", errorExpectation.c_str()), instNode); - return false; - } - BfAstNode* curNode = nodes[curNodeIdx]; - if (!peekOnly) - ++curNodeIdx; - if (!BfNodeDynCast(curNode)) - { - if (!peekOnly) - Fail(StrFormat("Found \"%s\", expected %s", NodeToString(curNode).c_str(), errorExpectation.c_str()), instNode); - return false; - } + auto instNode = mAlloc->Alloc(); + //instNode->mSource = asmStatement->mSource; + int srcStart = nodes.front()->GetSrcStart(); + int srcEnd = nodes.back()->GetSrcEnd(); + instNode->Init(srcStart, srcStart, srcEnd); - outStr = NodeToString(curNode); - if (forceLowerCase) - replaceWithLower(outStr); - return true; - }; - auto readToken = [&](BfToken tokenType, const StringImpl& errorExpectation, bool peekOnly) -> bool - { - if (curNodeIdx >= nodeCount) - { - if (!peekOnly) - Fail(StrFormat("Expected %s", errorExpectation.c_str()), instNode); - return false; - } - BfAstNode* curNode = nodes[curNodeIdx]; - if (!peekOnly) - ++curNodeIdx; - auto tokenNode = BfNodeDynCast(curNode); - if (!tokenNode || tokenNode->GetToken() != tokenType) - { - if (!peekOnly) - Fail(StrFormat("Found \"%s\", expected %s", NodeToString(curNode).c_str(), errorExpectation.c_str()), instNode); - return false; - } - - return true; - }; - auto readInteger = [&](int& outInt, const StringImpl& errorExpectation, bool peekOnly, int& outAdvanceTokenCount) -> bool - { - int origCurNodeIdx = curNodeIdx; - outAdvanceTokenCount = 0; - - bool negate = false; - if (readToken(BfToken_Minus, "", true)) - { - ++curNodeIdx; - ++outAdvanceTokenCount; - negate = true; - } - - if (curNodeIdx >= nodeCount) - { - if (!peekOnly) - Fail(StrFormat("Expected %s", errorExpectation.c_str()), instNode); - else - curNodeIdx = origCurNodeIdx; - - return false; - } - - BfAstNode* curNode = nodes[curNodeIdx]; - ++curNodeIdx; - ++outAdvanceTokenCount; - auto litNode = BfNodeDynCast(curNode); - if (!litNode || litNode->mValue.mTypeCode != BfTypeCode_Int32) - { - if (!peekOnly) - Fail(StrFormat("Found \"%s\", expected %s", NodeToString(curNode).c_str(), errorExpectation.c_str()), instNode); - else - curNodeIdx = origCurNodeIdx; - - return false; - } - - outInt = litNode->mValue.mInt32; - if (negate) - outInt = -outInt; - if (peekOnly) - curNodeIdx = origCurNodeIdx; - - return true; - }; - - auto readArgMemPrimaryExpr = [&](BfInlineAsmInstruction::AsmArg& outArg) -> bool - { - String primaryIdent; - int primaryInt; - int advanceTokenCount = 0; - - if (readIdent(primaryIdent, false, "", true)) - { - outArg.mMemFlags = BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; - outArg.mReg = primaryIdent; - ++curNodeIdx; - return true; - } - else if (readInteger(primaryInt, "", true, advanceTokenCount)) - { - outArg.mMemFlags = BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp; - outArg.mInt = primaryInt; - curNodeIdx += advanceTokenCount; - return true; - } - else - { - Fail(StrFormat("Found \"%s\", expected integer or identifier", NodeToString(nodes[curNodeIdx]).c_str()), instNode); - return false; - } - }; - std::function readArgMemMulExpr = [&](BfInlineAsmInstruction::AsmArg& outArg) -> bool - { - BfInlineAsmInstruction::AsmArg exprArgLeft, exprArgRight; - - if (!readArgMemPrimaryExpr(exprArgLeft)) - return false; - - if (!readToken(BfToken_Star, "", true)) - { - outArg = exprArgLeft; - return true; - } - - ++curNodeIdx; - if (!readArgMemMulExpr(exprArgRight)) - return false; - - bool leftIdent = (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; - bool rightIdent = (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; - if (leftIdent && rightIdent) - { - Fail(StrFormat("Memory expressions can only scale by an integer", NodeToString(nodes[curNodeIdx]).c_str()), instNode); - return false; - } - else if (leftIdent || rightIdent) - { - if (leftIdent) + auto replaceWithLower = [](String& s) { - outArg = exprArgLeft; - outArg.mAdjRegScalar = exprArgRight.mInt; - } - else + std::transform(s.begin(), s.end(), s.begin(), ::tolower); + }; + + auto readIdent = [&](String& outStr, bool forceLowerCase, const StringImpl& errorExpectation, bool peekOnly) -> bool { - outArg = exprArgRight; - outArg.mAdjRegScalar = exprArgLeft.mInt; - } - - outArg.mMemFlags &= ~BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; - outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg; - outArg.mAdjReg = outArg.mReg; - outArg.mReg.clear(); - } - else - { - outArg = exprArgLeft; - outArg.mInt = exprArgLeft.mInt * exprArgRight.mInt; - } - - return true; - }; - std::function readArgMemAddExpr = [&](BfInlineAsmInstruction::AsmArg& outArg, bool subtractLeft) -> bool // can't use 'auto' here since it's recursive - { - BfInlineAsmInstruction::AsmArg exprArgLeft, exprArgRight; - - if (!readArgMemMulExpr(exprArgLeft)) - return false; - - if (subtractLeft) - { - if (exprArgLeft.mMemFlags != BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) - { - Fail("Memory expressions can only subtract by an integer", instNode); - return false; - } - - exprArgLeft.mInt = -exprArgLeft.mInt; - } - - bool subtract = false; - if (!readToken(BfToken_Plus, "", true)) - { - if (!readToken(BfToken_Minus, "", true)) - { - outArg = exprArgLeft; - return true; - } - else - subtract = true; - } - - ++curNodeIdx; - if (!readArgMemAddExpr(exprArgRight, subtract)) - return false; - - bool leftScaling = (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg) != 0; - bool rightScaling = (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg) != 0; - if (leftScaling && rightScaling) - { - Fail("Memory expressions can only have one scaling register and one non-scaling register", instNode); - return false; - } - BfInlineAsmInstruction::AsmArg* scaledArg = leftScaling ? &exprArgLeft : (rightScaling ? &exprArgRight : nullptr); - - if (scaledArg) - { - BfInlineAsmInstruction::AsmArg* otherArg = leftScaling ? &exprArgRight : &exprArgLeft; - - outArg = *scaledArg; - outArg.mMemFlags |= otherArg->mMemFlags; - if (otherArg->mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) - { - if (scaledArg->mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) + if (curNodeIdx >= nodeCount) { - Fail("Memory expressions can involve at most two registers", instNode); + if (!peekOnly) + Fail(StrFormat("Expected %s", errorExpectation.c_str()), instNode); + return false; + } + BfAstNode* curNode = nodes[curNodeIdx]; + if (!peekOnly) + ++curNodeIdx; + if (!BfNodeDynCast(curNode)) + { + if (!peekOnly) + Fail(StrFormat("Found \"%s\", expected %s", NodeToString(curNode).c_str(), errorExpectation.c_str()), instNode); return false; } - outArg.mReg = otherArg->mReg; - } - if (otherArg->mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) - { - outArg.mInt += otherArg->mInt; - } - } - else - { - outArg.mInt = 0; - if (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) - { - outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp; - outArg.mInt += exprArgLeft.mInt; - } - if (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) - { - outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp; - outArg.mInt += exprArgRight.mInt; - } - bool leftIdent = (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; - bool rightIdent = (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; + outStr = NodeToString(curNode); + if (forceLowerCase) + replaceWithLower(outStr); + return true; + }; + auto readToken = [&](BfToken tokenType, const StringImpl& errorExpectation, bool peekOnly) -> bool + { + if (curNodeIdx >= nodeCount) + { + if (!peekOnly) + Fail(StrFormat("Expected %s", errorExpectation.c_str()), instNode); + return false; + } + BfAstNode* curNode = nodes[curNodeIdx]; + if (!peekOnly) + ++curNodeIdx; + auto tokenNode = BfNodeDynCast(curNode); + if (!tokenNode || tokenNode->GetToken() != tokenType) + { + if (!peekOnly) + Fail(StrFormat("Found \"%s\", expected %s", NodeToString(curNode).c_str(), errorExpectation.c_str()), instNode); + return false; + } - if (leftIdent && rightIdent) + return true; + }; + auto readInteger = [&](int& outInt, const StringImpl& errorExpectation, bool peekOnly, int& outAdvanceTokenCount) -> bool { - outArg.mMemFlags |= (BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg | BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg); - outArg.mReg = exprArgLeft.mReg; - outArg.mAdjReg = exprArgRight.mReg; - outArg.mAdjRegScalar = 1; - } - else if (leftIdent) - { - outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; - outArg.mReg = exprArgLeft.mReg; - } - else if (rightIdent) - { - outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; - outArg.mReg = exprArgRight.mReg; - } - } + int origCurNodeIdx = curNodeIdx; + outAdvanceTokenCount = 0; - return true; - }; + bool negate = false; + if (readToken(BfToken_Minus, "", true)) + { + ++curNodeIdx; + ++outAdvanceTokenCount; + negate = true; + } - auto parseArg = [&](BfInlineAsmInstruction::AsmArg& outArg) -> bool - { - bool keepGoing = true; - while (keepGoing) - { - keepGoing = false; + if (curNodeIdx >= nodeCount) + { + if (!peekOnly) + Fail(StrFormat("Expected %s", errorExpectation.c_str()), instNode); + else + curNodeIdx = origCurNodeIdx; - int peekInt; - String peekStr; - int advanceTokenCount; - if (readInteger(peekInt, "", true, advanceTokenCount)) - { - outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_Immediate; - outArg.mInt = peekInt; - curNodeIdx += advanceTokenCount; - } - else if (readIdent(peekStr, false, "", true)) - { + return false; + } + + BfAstNode* curNode = nodes[curNodeIdx]; ++curNodeIdx; - String s(peekStr); - replaceWithLower(s); + ++outAdvanceTokenCount; + auto litNode = BfNodeDynCast(curNode); + if (!litNode || litNode->mValue.mTypeCode != BfTypeCode_Int32) + { + if (!peekOnly) + Fail(StrFormat("Found \"%s\", expected %s", NodeToString(curNode).c_str(), errorExpectation.c_str()), instNode); + else + curNodeIdx = origCurNodeIdx; - String tempIdent; + return false; + } - if ((s == "cs" || s == "ds" || s == "es" || s == "fs" || s == "gs" || s == "ss") && readToken(BfToken_Colon, "", true)) + outInt = litNode->mValue.mInt32; + if (negate) + outInt = -outInt; + if (peekOnly) + curNodeIdx = origCurNodeIdx; + + return true; + }; + + auto readArgMemPrimaryExpr = [&](BfInlineAsmInstruction::AsmArg& outArg) -> bool + { + String primaryIdent; + int primaryInt; + int advanceTokenCount = 0; + + if (readIdent(primaryIdent, false, "", true)) { + outArg.mMemFlags = BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; + outArg.mReg = primaryIdent; ++curNodeIdx; - outArg.mSegPrefix = s; - keepGoing = true; + return true; } - else if (s == "st" && readToken(BfToken_LParen, "", true)) + else if (readInteger(primaryInt, "", true, advanceTokenCount)) { - ++curNodeIdx; - outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_FloatReg; - if (!readInteger(peekInt, "integer floating-point register number", false, advanceTokenCount)) - return false; - outArg.mInt = peekInt; - if (!readToken(BfToken_RParen, "')'", false)) - return false; - } - else if ((s == "byte" || s == "word" || s == "dword" || s == "qword" || s == "xword" || s == "xmmword" || s == "opaque") && readIdent(tempIdent, true, "", true)) - { - if (tempIdent != "ptr") - { - Fail(StrFormat("Found \"%s\", expected \"ptr\"", NodeToString(nodes[curNodeIdx]).c_str()), instNode); - return false; - } - ++curNodeIdx; - outArg.mSizePrefix = s; - keepGoing = true; + outArg.mMemFlags = BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp; + outArg.mInt = primaryInt; + curNodeIdx += advanceTokenCount; + return true; } else { - outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_IntReg; - outArg.mReg = peekStr; + Fail(StrFormat("Found \"%s\", expected integer or identifier", NodeToString(nodes[curNodeIdx]).c_str()), instNode); + return false; } - } - else if (readToken(BfToken_LBracket, "", true)) + }; + std::function readArgMemMulExpr = [&](BfInlineAsmInstruction::AsmArg& outArg) -> bool { - ++curNodeIdx; + BfInlineAsmInstruction::AsmArg exprArgLeft, exprArgRight; - BfInlineAsmInstruction::AsmArg exprArgLeft; - if (!readArgMemAddExpr(exprArgLeft, false)) + if (!readArgMemPrimaryExpr(exprArgLeft)) return false; - if (!readToken(BfToken_RBracket, "']'", false)) - return false; - if (readToken(BfToken_Dot, "", true)) + + if (!readToken(BfToken_Star, "", true)) { - ++curNodeIdx; - if (!readIdent(outArg.mMemberSuffix, false, "struct member suffix identifier", false)) + outArg = exprArgLeft; + return true; + } + + ++curNodeIdx; + if (!readArgMemMulExpr(exprArgRight)) + return false; + + bool leftIdent = (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; + bool rightIdent = (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; + if (leftIdent && rightIdent) + { + Fail(StrFormat("Memory expressions can only scale by an integer", NodeToString(nodes[curNodeIdx]).c_str()), instNode); + return false; + } + else if (leftIdent || rightIdent) + { + if (leftIdent) + { + outArg = exprArgLeft; + outArg.mAdjRegScalar = exprArgRight.mInt; + } + else + { + outArg = exprArgRight; + outArg.mAdjRegScalar = exprArgLeft.mInt; + } + + outArg.mMemFlags &= ~BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; + outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg; + outArg.mAdjReg = outArg.mReg; + outArg.mReg.clear(); + } + else + { + outArg = exprArgLeft; + outArg.mInt = exprArgLeft.mInt * exprArgRight.mInt; + } + + return true; + }; + std::function readArgMemAddExpr = [&](BfInlineAsmInstruction::AsmArg& outArg, bool subtractLeft) -> bool // can't use 'auto' here since it's recursive + { + BfInlineAsmInstruction::AsmArg exprArgLeft, exprArgRight; + + if (!readArgMemMulExpr(exprArgLeft)) + return false; + + if (subtractLeft) + { + if (exprArgLeft.mMemFlags != BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) + { + Fail("Memory expressions can only subtract by an integer", instNode); + return false; + } + + exprArgLeft.mInt = -exprArgLeft.mInt; + } + + bool subtract = false; + if (!readToken(BfToken_Plus, "", true)) + { + if (!readToken(BfToken_Minus, "", true)) + { + outArg = exprArgLeft; + return true; + } + else + subtract = true; + } + + ++curNodeIdx; + if (!readArgMemAddExpr(exprArgRight, subtract)) + return false; + + bool leftScaling = (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg) != 0; + bool rightScaling = (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg) != 0; + if (leftScaling && rightScaling) + { + Fail("Memory expressions can only have one scaling register and one non-scaling register", instNode); + return false; + } + BfInlineAsmInstruction::AsmArg* scaledArg = leftScaling ? &exprArgLeft : (rightScaling ? &exprArgRight : nullptr); + + if (scaledArg) + { + BfInlineAsmInstruction::AsmArg* otherArg = leftScaling ? &exprArgRight : &exprArgLeft; + + outArg = *scaledArg; + outArg.mMemFlags |= otherArg->mMemFlags; + if (otherArg->mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) + { + if (scaledArg->mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) + { + Fail("Memory expressions can involve at most two registers", instNode); + return false; + } + outArg.mReg = otherArg->mReg; + } + if (otherArg->mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) + { + outArg.mInt += otherArg->mInt; + } + } + else + { + outArg.mInt = 0; + if (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) + { + outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp; + outArg.mInt += exprArgLeft.mInt; + } + if (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp) + { + outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_ImmediateDisp; + outArg.mInt += exprArgRight.mInt; + } + + bool leftIdent = (exprArgLeft.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; + bool rightIdent = (exprArgRight.mMemFlags & BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg) != 0; + + if (leftIdent && rightIdent) + { + outArg.mMemFlags |= (BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg | BfInlineAsmInstruction::AsmArg::ARGMEMF_AdjReg); + outArg.mReg = exprArgLeft.mReg; + outArg.mAdjReg = exprArgRight.mReg; + outArg.mAdjRegScalar = 1; + } + else if (leftIdent) + { + outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; + outArg.mReg = exprArgLeft.mReg; + } + else if (rightIdent) + { + outArg.mMemFlags |= BfInlineAsmInstruction::AsmArg::ARGMEMF_BaseReg; + outArg.mReg = exprArgRight.mReg; + } + } + + return true; + }; + + auto parseArg = [&](BfInlineAsmInstruction::AsmArg& outArg) -> bool + { + bool keepGoing = true; + while (keepGoing) + { + keepGoing = false; + + int peekInt; + String peekStr; + int advanceTokenCount; + if (readInteger(peekInt, "", true, advanceTokenCount)) + { + outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_Immediate; + outArg.mInt = peekInt; + curNodeIdx += advanceTokenCount; + } + else if (readIdent(peekStr, false, "", true)) + { + ++curNodeIdx; + String s(peekStr); + replaceWithLower(s); + + String tempIdent; + + if ((s == "cs" || s == "ds" || s == "es" || s == "fs" || s == "gs" || s == "ss") && readToken(BfToken_Colon, "", true)) + { + ++curNodeIdx; + outArg.mSegPrefix = s; + keepGoing = true; + } + else if (s == "st" && readToken(BfToken_LParen, "", true)) + { + ++curNodeIdx; + outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_FloatReg; + if (!readInteger(peekInt, "integer floating-point register number", false, advanceTokenCount)) + return false; + outArg.mInt = peekInt; + if (!readToken(BfToken_RParen, "')'", false)) + return false; + } + else if ((s == "byte" || s == "word" || s == "dword" || s == "qword" || s == "xword" || s == "xmmword" || s == "opaque") && readIdent(tempIdent, true, "", true)) + { + if (tempIdent != "ptr") + { + Fail(StrFormat("Found \"%s\", expected \"ptr\"", NodeToString(nodes[curNodeIdx]).c_str()), instNode); + return false; + } + ++curNodeIdx; + outArg.mSizePrefix = s; + keepGoing = true; + } + else + { + outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_IntReg; + outArg.mReg = peekStr; + } + } + else if (readToken(BfToken_LBracket, "", true)) + { + ++curNodeIdx; + + BfInlineAsmInstruction::AsmArg exprArgLeft; + if (!readArgMemAddExpr(exprArgLeft, false)) + return false; + if (!readToken(BfToken_RBracket, "']'", false)) + return false; + if (readToken(BfToken_Dot, "", true)) + { + ++curNodeIdx; + if (!readIdent(outArg.mMemberSuffix, false, "struct member suffix identifier", false)) + return false; + } + + outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_Memory; + outArg.mMemFlags = exprArgLeft.mMemFlags; + //outArg.mSegPrefix = already_set_leave_me_alone; + //outArg.mSizePrefix = already_set_leave_me_alone; + outArg.mInt = exprArgLeft.mInt; + outArg.mReg = exprArgLeft.mReg; + outArg.mAdjReg = exprArgLeft.mAdjReg; + outArg.mAdjRegScalar = exprArgLeft.mAdjRegScalar; + //outArg.mMemberSuffix = already_set_leave_me_alone; + + return true; + } + else return false; } - outArg.mType = BfInlineAsmInstruction::AsmArg::ARGTYPE_Memory; - outArg.mMemFlags = exprArgLeft.mMemFlags; - //outArg.mSegPrefix = already_set_leave_me_alone; - //outArg.mSizePrefix = already_set_leave_me_alone; - outArg.mInt = exprArgLeft.mInt; - outArg.mReg = exprArgLeft.mReg; - outArg.mAdjReg = exprArgLeft.mAdjReg; - outArg.mAdjRegScalar = exprArgLeft.mAdjRegScalar; - //outArg.mMemberSuffix = already_set_leave_me_alone; - return true; - } - else - return false; + }; + + BfInlineAsmInstruction::AsmInst& outInst = instNode->mAsmInst; + + // instruction / instruction prefix / label + String opStr; + if (!readIdent(opStr, false, "instruction, instruction prefix, or label", false)) + return nullptr; + + if (readToken(BfToken_Colon, "", true)) + { + ++curNodeIdx; + outInst.mLabel = opStr; + if (curNodeIdx >= nodeCount) + return instNode; + + if (!readIdent(opStr, false, "instruction or instruction prefix", false)) + return nullptr; } - return true; + replaceWithLower(opStr); + + // check for instruction prefix(s) + while (opStr == "lock" || opStr == "rep" || opStr == "repe" || opStr == "repne" || opStr == "repz" || opStr == "repnz") + { + if (curNodeIdx >= nodeCount) // in case prefix is listed like a separate instruction + break; + outInst.mOpPrefixes.push_back(opStr); + + if (!readIdent(opStr, true, "instruction or instruction prefix", false)) + return nullptr; + } + + outInst.mOpCode = opStr; + + BfInlineAsmInstruction::AsmArg asmArg; + while (parseArg(asmArg)) + { + outInst.mArgs.push_back(asmArg); + asmArg = BfInlineAsmInstruction::AsmArg(); + + if (!readToken(BfToken_Comma, "", true)) + break; + ++curNodeIdx; + } + + if (curNodeIdx < nodeCount) + return (BfInlineAsmInstruction*)Fail(StrFormat("Found unexpected \"%s\"", NodeToString(nodes[curNodeIdx]).c_str()), instNode); + + //String testStr = outInst.ToString(); + + int unusedLineChar = 0; + auto bfParser = instNode->GetSourceData()->ToParserData(); + if (bfParser != NULL) + bfParser->GetLineCharAtIdx(instNode->GetSrcStart(), outInst.mDebugLine, unusedLineChar); + //return (BfInlineAsmInstruction*)Fail(StrFormat("Line %d\n", outInst.mDebugLine), instNode); + + return instNode; }; - BfInlineAsmInstruction::AsmInst& outInst = instNode->mAsmInst; - - // instruction / instruction prefix / label - String opStr; - if (!readIdent(opStr, false, "instruction, instruction prefix, or label", false)) - return nullptr; - - if (readToken(BfToken_Colon, "", true)) - { - ++curNodeIdx; - outInst.mLabel = opStr; - if (curNodeIdx >= nodeCount) - return instNode; - - if (!readIdent(opStr, false, "instruction or instruction prefix", false)) - return nullptr; - } - - replaceWithLower(opStr); - - // check for instruction prefix(s) - while (opStr == "lock" || opStr == "rep" || opStr == "repe" || opStr == "repne" || opStr == "repz" || opStr == "repnz") - { - if (curNodeIdx >= nodeCount) // in case prefix is listed like a separate instruction - break; - outInst.mOpPrefixes.push_back(opStr); - - if (!readIdent(opStr, true, "instruction or instruction prefix", false)) - return nullptr; - } - - outInst.mOpCode = opStr; - - BfInlineAsmInstruction::AsmArg asmArg; - while (parseArg(asmArg)) - { - outInst.mArgs.push_back(asmArg); - asmArg = BfInlineAsmInstruction::AsmArg(); - - if (!readToken(BfToken_Comma, "", true)) - break; - ++curNodeIdx; - } - - if (curNodeIdx < nodeCount) - return (BfInlineAsmInstruction*)Fail(StrFormat("Found unexpected \"%s\"", NodeToString(nodes[curNodeIdx]).c_str()), instNode); - - //String testStr = outInst.ToString(); - - int unusedLineChar = 0; - auto bfParser = instNode->GetSourceData()->ToParserData(); - if (bfParser != NULL) - bfParser->GetLineCharAtIdx(instNode->GetSrcStart(), outInst.mDebugLine, unusedLineChar); - //return (BfInlineAsmInstruction*)Fail(StrFormat("Line %d\n", outInst.mDebugLine), instNode); - - return instNode; - }; - // split nodes by newlines into individual instructions, skipping empty lines Array instrNodes; @@ -11202,7 +11787,7 @@ BfInlineAsmStatement* BfReducer::CreateInlineAsmStatement(BfAstNode* asmNode) instrNodes.Clear(); } - for (auto & instNode : dstInstructions) + for (auto& instNode : dstInstructions) MoveNode(instNode, asmStatement); asmStatement->mInstructions = std::move(dstInstructions); } diff --git a/IDEHelper/Compiler/BfReducer.h b/IDEHelper/Compiler/BfReducer.h index 4a6a88eb..c5349549 100644 --- a/IDEHelper/Compiler/BfReducer.h +++ b/IDEHelper/Compiler/BfReducer.h @@ -129,6 +129,39 @@ public: } }; + struct CurTypeState + { + public: + BfTypeDeclaration* mTypeDeclaration; + BfAstAllocator* mAlloc; + Array mAnonymousTypeDecls; + + public: + CurTypeState() + { + mTypeDeclaration = NULL; + mAlloc = NULL; + } + + CurTypeState(BfTypeDeclaration* typeDecl, BfAstAllocator* alloc) + { + mTypeDeclaration = typeDecl; + mAlloc = alloc; + } + + ~CurTypeState() + { + if ((mTypeDeclaration != NULL) && (mAnonymousTypeDecls.mSize > 0)) + { + BF_ASSERT(mTypeDeclaration->mAnonymousTypes.mSize == 0); + mTypeDeclaration->mAnonymousTypes.mSize = (int)mAnonymousTypeDecls.size(); + mTypeDeclaration->mAnonymousTypes.mVals = (BfTypeDeclaration**)mAlloc->AllocBytes(mAnonymousTypeDecls.mSize * sizeof(BfTypeDeclaration*), sizeof(BfTypeDeclaration*)); + for (int i = 0; i < mAnonymousTypeDecls.mSize; i++) + mTypeDeclaration->mAnonymousTypes.mVals[i] = mAnonymousTypeDecls[i]; + } + } + }; + public: BfAstAllocator* mAlloc; BfSystem* mSystem; @@ -137,11 +170,13 @@ public: BfResolvePassData* mResolvePassData; BfAstNode* mTypeMemberNodeStart; int mClassDepth; - int mMethodDepth; + int mMethodDepth; + int mLastErrorSrcEnd; BfTypeDeclaration* mCurTypeDecl; + CurTypeState* mCurTypeState; BfTypeDeclaration* mLastTypeDecl; BfMethodDeclaration* mCurMethodDecl; - BfAstNode* mLastBlockNode; + BfAstNode* mLastBlockNode; bool mStmtHasError; bool mPrevStmtHadError; bool mCompatMode; // Does C++ compatible parsing @@ -176,10 +211,14 @@ public: void AssertCurrentNode(BfAstNode* node); bool IsNodeRelevant(BfAstNode* astNode); + bool IsCursorInside(BfAstNode* astNode); bool IsNodeRelevant(BfAstNode* startNode, BfAstNode* endNode); void MoveNode(BfAstNode* srcNode, BfAstNode* newOwner); void ReplaceNode(BfAstNode* prevNode, BfAstNode* newNode); - + + void InitAnonymousType(BfTypeDeclaration* typeDecl); + bool CheckInlineTypeRefAttribute(BfAstNode* typeRef, BfAttributeDirective* attributes); + void CheckMultiuseAttributeTypeRef(BfAstNode* typeRef); bool SetProtection(BfAstNode* parentNode, BfAstNode*& protectionNodeRef, BfTokenNode* tokenNode); BfAstNode* CreateAllocNode(BfTokenNode* newNode); BfAstNode* ReplaceTokenStarter(BfAstNode* astNode, int idx = -1, bool allowIn = false); @@ -238,12 +277,15 @@ public: BfWhileStatement* CreateWhileStatement(BfAstNode* node); BfDoStatement* CreateDoStatement(BfAstNode* node); BfRepeatStatement* CreateRepeatStatement(BfAstNode* node); - BfAstNode* CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode = NULL); + BfAstNode* CreateTopLevelObject(BfTokenNode* tokenNode, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode = NULL, bool isAnonymous = false); BfAstNode* HandleTopLevel(BfBlock* node); BfInlineAsmStatement* CreateInlineAsmStatement(BfAstNode* asmNode); void HandleBlock(BfBlock* block, bool allowEndingExpression = false); - void HandleTypeDeclaration(BfTypeDeclaration* typeDecl, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode = NULL); + bool IsInitializerStatement(int checkIdx); + bool IsInitializerStatement(BfAstNode* node); + bool InitializerBlockHasInlineTypeDecl(BfBlock* block); + void HandleTypeDeclaration(BfTypeDeclaration* typeDecl, BfAttributeDirective* attributes, BfAstNode* deferredHeadNode = NULL, bool findInitializer = false); public: BfReducer(); diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 61b88b63..34bf9338 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -698,7 +698,8 @@ BfMethodInstance* BfMethodParam::GetDelegateParamInvoke() auto methodRefType = (BfMethodRefType*)mResolvedType; return methodRefType->mMethodRef; } - + else if (mResolvedType->IsTuple()) + return NULL; BF_ASSERT(mResolvedType->IsDelegate() || mResolvedType->IsFunction()); auto bfModule = BfModule::GetModuleFor(mResolvedType); BfMethodInstance* invokeMethodInstance = bfModule->GetRawMethodInstanceAtIdx(mResolvedType->ToTypeInstance(), 0, "Invoke"); @@ -841,6 +842,9 @@ BfMethodFlags BfMethodInstance::GetMethodFlags() else if (callingConvention == BfIRCallingConv_FastCall) methodFlags = (BfMethodFlags)(methodFlags | BfMethodFlags_FastCall); + methodFlags = (BfMethodFlags)(methodFlags | (mMethodDef->mAppendKind * BfMethodFlags_AppendBit0)); + methodFlags = (BfMethodFlags)(methodFlags | (mMethodDef->mCheckedKind * BfMethodFlags_CheckedBit0)); + return methodFlags; } @@ -896,6 +900,20 @@ BfModule * BfMethodInstance::GetModule() return mMethodInstanceGroup->mOwner->mModule; } +bool BfMethodInstance::ForcingThisPtr() +{ + if (mMethodDef->mHasExplicitThis) + { + auto thisType = mParams[0].mResolvedType; + if (thisType->IsCRepr()) + return true; + } + else if (mMethodInstanceGroup->mOwner->IsCRepr()) + return true; + + return (mCallingConvention == BfCallingConvention_Cdecl); +} + bool Beefy::BfMethodInstance::IsSpecializedGenericMethod() { return (mMethodInfoEx != NULL) && (mMethodInfoEx->mGenericParams.size() != 0) && (!mIsUnspecialized); @@ -1067,6 +1085,8 @@ bool BfMethodInstance::AllowsSplatting(int paramIdx) { if (mCallingConvention != BfCallingConvention_Unspecified) return false; + if (ForcingThisPtr()) + return false; if (mMethodDef->mIsNoSplat) return false; return !mMethodDef->HasNoThisSplat(); @@ -1090,8 +1110,8 @@ bool BfMethodInstance::HasThis() if (mMethodDef->mIsStatic) return false; if ((mMethodInfoEx != NULL) && (mMethodInfoEx->mClosureInstanceInfo != NULL) && (mMethodInfoEx->mClosureInstanceInfo->mThisOverride != NULL)) - return !mMethodInfoEx->mClosureInstanceInfo->mThisOverride->IsValuelessType(); - return (!mMethodInstanceGroup->mOwner->IsValuelessType()); + return !mMethodInfoEx->mClosureInstanceInfo->mThisOverride->IsValuelessNonOpaqueType(); + return (!mMethodInstanceGroup->mOwner->IsValuelessNonOpaqueType()); } bool BfMethodInstance::IsVirtual() @@ -1163,6 +1183,18 @@ void BfMethodInstance::GetParamName(int paramIdx, StringImpl& name, int& namePre BfParameterDef* paramDef = mMethodDef->mParams[methodParam->mParamDefIdx]; if (methodParam->mDelegateParamIdx != -1) { + if (methodParam->mResolvedType->IsTuple()) + { + auto tupleType = (BfTupleType*)methodParam->mResolvedType; + auto& fieldInstance = tupleType->mFieldInstances[methodParam->mDelegateParamIdx]; + if (methodParam->mDelegateParamNameCombine) + name = paramDef->mName + "__" + fieldInstance.GetFieldDef()->mName; + else + name = fieldInstance.GetFieldDef()->mName; + + return; + } + BfMethodInstance* invokeMethodInstance = methodParam->GetDelegateParamInvoke(); if (methodParam->mDelegateParamNameCombine) name = paramDef->mName + "__" + invokeMethodInstance->GetParamName(methodParam->mDelegateParamIdx); @@ -1210,6 +1242,12 @@ BfType* BfMethodInstance::GetParamType(int paramIdx, bool returnUnderlyingParams BfMethodParam* methodParam = &mParams[paramIdx]; if (methodParam->mDelegateParamIdx != -1) { + if (methodParam->mResolvedType->IsTuple()) + { + auto tupleType = (BfTupleType*)methodParam->mResolvedType; + return tupleType->mFieldInstances[methodParam->mDelegateParamIdx].mResolvedType; + } + BfMethodInstance* invokeMethodInstance = methodParam->GetDelegateParamInvoke(); return invokeMethodInstance->GetParamType(methodParam->mDelegateParamIdx, true); } @@ -1244,6 +1282,8 @@ bool BfMethodInstance::GetParamIsSplat(int paramIdx) if (methodParam->mDelegateParamIdx != -1) { BfMethodInstance* invokeMethodInstance = methodParam->GetDelegateParamInvoke(); + if (invokeMethodInstance == NULL) + return false; return invokeMethodInstance->GetParamIsSplat(methodParam->mDelegateParamIdx); } return methodParam->mIsSplat; @@ -1278,7 +1318,7 @@ bool BfMethodInstance::IsParamSkipped(int paramIdx) BfType* paramType = GetParamType(paramIdx); if ((paramType->CanBeValuelessType()) && (paramType->IsDataIncomplete())) resolveModule->PopulateType(paramType, BfPopulateType_Data); - if ((paramType->IsValuelessType()) && (!paramType->IsMethodRef())) + if ((paramType->IsValuelessNonOpaqueType()) && (!paramType->IsMethodRef())) return true; return false; } @@ -1450,7 +1490,11 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, bool doSplat = false; if (paramIdx == -1) { - if ((!mMethodDef->mIsMutating) && (checkType->IsTypedPrimitive())) + if (ForcingThisPtr()) + { + // Pass by pointer even for typed primitives + } + else if ((!mMethodDef->mIsMutating) && (checkType->IsTypedPrimitive())) { checkType = checkType->GetUnderlyingType(); } @@ -1498,7 +1542,7 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, if (checkType->CanBeValuelessType()) resolveModule->PopulateType(checkType, BfPopulateType_Data); - if ((checkType->IsValuelessType()) && (!checkType->IsMethodRef())) + if ((checkType->IsValuelessNonOpaqueType()) && (!checkType->IsMethodRef())) continue; if ((doSplat) && (!checkType->IsMethodRef())) @@ -1812,6 +1856,11 @@ int BfTypeInstance::GetSplatCount(bool force) return splatCount; } +bool BfTypeInstance::IsCRepr() +{ + return mIsCRepr; +} + bool BfTypeInstance::IsString() { return IsInstanceOf(mContext->mCompiler->mStringTypeDef); @@ -2450,6 +2499,46 @@ bool BfTypeInstance::GetResultInfo(BfType*& valueType, int& okTagId) return false; } +bool BfTypeInstance::IsAnonymous() +{ + return (mTypeDef->mTypeDeclaration != NULL) && (mTypeDef->mTypeDeclaration->IsAnonymous()); +} + +bool BfTypeInstance::IsAnonymousInitializerType() +{ + return (mTypeDef->mTypeDeclaration != NULL) && (mTypeDef->mTypeDeclaration->IsAnonymousInitializerType()); +} + +bool BfTypeInstance::HasAppendCtor() +{ + return mTypeDef->mHasAppendCtor; +} + +bool BfTypeInstance::BaseHasAppendCtor() +{ + if (mBaseType != NULL) + { + if (mBaseType->HasAppendCtor()) + return true; + return mBaseType->BaseHasAppendCtor(); + } + return false; +} + +bool BfTypeInstance::HasAppendedField(bool checkBase) +{ + for (auto& fieldInstance : mFieldInstances) + { + if (fieldInstance.IsAppendedObject()) + return true; + } + + if ((checkBase) && (mBaseType != NULL)) + return mBaseType->HasAppendedField(checkBase); + + return false; +} + void BfTypeInstance::ReportMemory(MemReporter* memReporter) { if (mGenericTypeInfo != NULL) @@ -2689,6 +2778,23 @@ bool BfTypeInstance::IsTypeMemberIncluded(BfTypeDef* typeDef, BfTypeDef* activeT return genericExEntry->mConstraintsPassed; } +bool BfTypeInstance::IsZeroGap() +{ + BF_ASSERT(mDefineState >= BfTypeDefineState_Defined); + + for (int fieldIdx = mFieldInstances.mSize - 1; fieldIdx >= 0; fieldIdx--) + { + auto fieldInstance = &mFieldInstances[fieldIdx]; + auto fieldDef = fieldInstance->GetFieldDef(); + if (fieldDef == NULL) + continue; + if ((!fieldDef->mIsStatic) && (fieldDef->mDeclaringType->IsExtension()) && (!fieldInstance->mResolvedType->IsValuelessType())) + return false; + } + + return true; +} + void BfGenericTypeInfo::ReportMemory(MemReporter* memReporter) { memReporter->Add(sizeof(BfGenericTypeInfo)); @@ -2741,7 +2847,9 @@ bool BfTypeInstance::IsValuelessType() return false; } if (mTypeDef->mIsOpaque) - return false; + { + return true; + } BF_ASSERT((mDefineState >= BfTypeDefineState_Defined) || (mTypeFailed)); BF_ASSERT(mInstSize >= 0); @@ -2753,6 +2861,39 @@ bool BfTypeInstance::IsValuelessType() return false; } +bool BfTypeInstance::IsValuelessCReprType() +{ + if (!mIsCRepr) + return false; + if (mInstSize > 1) + return false; + if ((mBaseType->mIsCRepr) && (!IsValuelessCReprType())) + return false; + + BF_ASSERT((mDefineState >= BfTypeDefineState_Defined) || (mTypeFailed)); + for (auto& fieldInst : mFieldInstances) + { + if (fieldInst.mDataIdx >= 0) + return false; + } + + return true; +} + +BfTypeInstance* BfTypeInstance::GetBaseType(bool remapValuelessCRepr) +{ + if (!remapValuelessCRepr) + return mBaseType; + auto checkType = mBaseType; + while (checkType != NULL) + { + if (!checkType->IsValuelessCReprType()) + break; + checkType = checkType->mBaseType; + } + return checkType; +} + bool BfTypeInstance::IsIRFuncUsed(BfIRFunction func) { for (auto& group : mMethodInstanceGroups) @@ -3522,9 +3663,20 @@ int BfResolvedTypeSet::DirectHash(BfTypeReference* typeRef, LookupContext* ctx, ctx->mFailed = true; return 0; } + if (((flags & BfHashFlag_DisallowPointer) != 0) && (resolvedType->IsPointer())) + { + ShowThisPointerWarning(ctx, typeRef); + resolvedType = resolvedType->GetUnderlyingType(); + } + return Hash(resolvedType, ctx, BfHashFlag_None, hashSeed); } +void BfResolvedTypeSet::ShowThisPointerWarning(LookupContext* ctx, BfTypeReference* typeRef) +{ + ctx->mModule->Warn(0, "Pointer types cannot be used as 'this'. If 'this' address is required, use 'mut' or [CRepr]", typeRef); +} + BfTypeDef* BfResolvedTypeSet::FindRootCommonOuterType(BfTypeDef* outerType, LookupContext* ctx, BfTypeInstance*& outOuterTypeInstance) { if (ctx->mModule->mCurTypeInstance == NULL) @@ -4055,12 +4207,22 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa // Parse attributes? BfTypeReference* fieldType = param->mTypeRef; + auto hashFlags = (BfHashFlags)(BfHashFlag_AllowRef); + if (isFirstParam) { if ((param->mNameNode != NULL) && (param->mNameNode->Equals("this"))) { + hashFlags = (BfHashFlags)(hashFlags | BfHashFlag_DisallowPointer); + if (auto refNode = BfNodeDynCast(fieldType)) fieldType = refNode->mElementType; + + if (auto pointerType = BfNodeDynCast(fieldType)) + { + ShowThisPointerWarning(ctx, pointerType); + fieldType = pointerType->mElementType; + } } } @@ -4074,10 +4236,10 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa continue; } } - } + } if (fieldType != NULL) - hashVal = HASH_MIX(hashVal, Hash(fieldType, ctx, (BfHashFlags)(BfHashFlag_AllowRef), hashSeed + 1)); + hashVal = HASH_MIX(hashVal, Hash(fieldType, ctx, hashFlags, hashSeed + 1)); hashVal = HASH_MIX(hashVal, HashNode(param->mNameNode)); isFirstParam = true; } @@ -4249,6 +4411,13 @@ int BfResolvedTypeSet::DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHa } return nameHash ^ HASH_TAG; } +// else if (auto inlineTypeRef = BfNodeDynCastExact(typeRef)) +// { +// String name; +// inlineTypeRef->mTypeDeclaration->GetAnonymousName(name); +// int nameHash = (int)Hash64(name.c_str(), (int)name.length()); +// return nameHash ^ HASH_TAG; +// } else { BF_FATAL("Not handled"); @@ -4334,6 +4503,12 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfType* rhs, LookupContext* ctx) return false; if (lhsDelegateInfo->mCallingConvention != rhsDelegateInfo->mCallingConvention) return false; + if (lhsDelegateInfo->mHasParams != rhsDelegateInfo->mHasParams) + return false; + if (lhsDelegateInfo->mHasVarArgs != rhsDelegateInfo->mHasVarArgs) + return false; + if (lhsDelegateInfo->mHasExplicitThis != rhsDelegateInfo->mHasExplicitThis) + return false; auto lhsMethodDef = lhsInst->mTypeDef->mMethods[0]; auto rhsMethodDef = rhsInst->mTypeDef->mMethods[0]; @@ -4341,7 +4516,7 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfType* rhs, LookupContext* ctx) if (lhsMethodDef->mCallingConvention != rhsMethodDef->mCallingConvention) return false; if (lhsMethodDef->mIsMutating != rhsMethodDef->mIsMutating) - return false; + return false; if (lhsDelegateInfo->mReturnType != rhsDelegateInfo->mReturnType) return false; if (lhsDelegateInfo->mParams.size() != rhsDelegateInfo->mParams.size()) @@ -4881,17 +5056,33 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* bool handled = false; auto lhsThisType = lhsDelegateInfo->mParams[0]; - auto rhsThisType = ctx->mModule->ResolveTypeRef(param0->mTypeRef, BfPopulateType_Identity, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoWarnOnMut | BfResolveTypeRefFlag_AllowRef)); bool wantsMutating = false; + if (auto refTypeRef = BfNodeDynCast(param0->mTypeRef)) + { + // This catches `ref Foo*` cases (which generate warnings) + if ((refTypeRef->mRefToken != NULL) && (refTypeRef->mRefToken->mToken == BfToken_Mut)) + wantsMutating = true; + } + auto rhsThisType = ctx->mModule->ResolveTypeRef(param0->mTypeRef, BfPopulateType_Identity, (BfResolveTypeRefFlags)(BfResolveTypeRefFlag_NoWarnOnMut | BfResolveTypeRefFlag_AllowRef)); + + if (rhsThisType == NULL) + return false; + if (rhsThisType->IsRef()) { - if (lhsThisType != rhsThisType->GetUnderlyingType()) + rhsThisType = rhsThisType->GetUnderlyingType(); + if (rhsThisType->IsPointer()) + rhsThisType = rhsThisType->GetUnderlyingType(); + + if (lhsThisType != rhsThisType) return false; wantsMutating = (lhsThisType->IsValueType()) || (lhsThisType->IsGenericParam()); } else { + if (rhsThisType->IsPointer()) + rhsThisType = rhsThisType->GetUnderlyingType(); if (lhsThisType != rhsThisType) return false; } @@ -4908,6 +5099,8 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* if (lhsParamsCount != (int)rhsDelegateType->mParams.size()) return false; + + bool rhsHadParams = false; for (int paramIdx = paramRefOfs; paramIdx < lhsDelegateInfo->mParams.size(); paramIdx++) { auto paramTypeRef = rhsDelegateType->mParams[paramIdx]->mTypeRef; @@ -4919,8 +5112,13 @@ bool BfResolvedTypeSet::Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* rhsParamName = rhsDelegateType->mParams[paramIdx]->mNameNode->ToStringView(); if (invokeMethodDef->mParams[paramIdx]->mName != rhsParamName) return false; + if ((rhsDelegateType->mParams[paramIdx]->mModToken != NULL) && (rhsDelegateType->mParams[paramIdx]->mModToken->mToken == BfToken_Params)) + rhsHadParams = true; } + if (rhsHadParams != lhsDelegateInfo->mHasParams) + return false; + if ((ctx->mModule->mCurTypeInstance == NULL) || (!ctx->mModule->mCurTypeInstance->IsGenericTypeInstance())) wantGeneric = false; @@ -5552,6 +5750,8 @@ String BfTypeUtils::TypeToString(BfAstNode* typeRefNode) } if (auto directStrTypeName = BfNodeDynCast(typeRef)) return directStrTypeName->mTypeName; + if (auto inlineTypeRef = BfNodeDynCast(typeRef)) + return inlineTypeRef->mTypeDeclaration->mAnonymousName; if (auto tupleTypeRef = BfNodeDynCast(typeRef)) { diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index ae9e8494..6d6e679a 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -127,6 +127,7 @@ public: DependencyFlag_VirtualCall = 0x2000000, DependencyFlag_WeakReference = 0x4000000, // Keeps alive but won't rebuild DependencyFlag_ValueTypeSizeDep = 0x8000000, // IE: int32[DepType.cVal] + DependencyFlag_TypeSignature = 0x10000000, DependencyFlag_DependentUsageMask = ~(DependencyFlag_UnspecializedType | DependencyFlag_MethodGenericArg | DependencyFlag_GenericArgRef) }; @@ -448,7 +449,8 @@ enum BfTypeRebuildFlags BfTypeRebuildFlag_RebuildQueued = 0x20000, BfTypeRebuildFlag_ConstEvalCancelled = 0x40000, BfTypeRebuildFlag_ChangedMidCompile = 0x80000, - BfTypeRebuildFlag_PendingGenericArgDep = 0x100000 + BfTypeRebuildFlag_PendingGenericArgDep = 0x100000, + BfTypeRebuildFlag_InRebuildType = 0x200000 }; class BfTypeDIReplaceCallback; @@ -459,6 +461,7 @@ enum BfTypeDefineState : uint8 BfTypeDefineState_Declaring, BfTypeDefineState_Declared, BfTypeDefineState_ResolvingBaseType, + BfTypeDefineState_HasCustomAttributes, BfTypeDefineState_HasInterfaces_Direct, BfTypeDefineState_CETypeInit, BfTypeDefineState_CEPostTypeInit, @@ -477,6 +480,7 @@ public: Array mParams; bool mHasExplicitThis; bool mHasVarArgs; + bool mHasParams; BfCallingConvention mCallingConvention; public: @@ -485,6 +489,7 @@ public: mReturnType = NULL; mHasExplicitThis = false; mHasVarArgs = false; + mHasParams = false; mCallingConvention = BfCallingConvention_Unspecified; } @@ -554,10 +559,12 @@ public: virtual bool IsUnspecializedTypeVariation() { return false; } virtual bool IsSplattable() { return false; } virtual int GetSplatCount(bool force = false) { return 1; } + virtual bool IsCRepr() { return false; } virtual bool IsVoid() { return false; } virtual bool IsVoidPtr() { return false; } virtual bool CanBeValuelessType() { return false; } virtual bool IsValuelessType() { BF_ASSERT(mSize != -1); BF_ASSERT(mDefineState >= BfTypeDefineState_Defined); return mSize == 0; } + virtual bool IsValuelessNonOpaqueType() { return IsValuelessType() && !IsOpaque(); } virtual bool IsSelf() { return false; } virtual bool IsDot() { return false; } virtual bool IsVar() { return false; } @@ -620,6 +627,7 @@ public: virtual bool IsOnDemand() { return false; } virtual bool IsTemporary() { return false; } virtual bool IsModifiedTypeType() { return false; } + virtual bool IsParamsType() { return false; } virtual bool IsConcreteInterfaceType() { return false; } virtual bool IsTypeAlias() { return false; } virtual bool HasPackingHoles() { return false; } @@ -629,6 +637,7 @@ public: virtual bool GetLoweredType(BfTypeUsage typeUsage, BfTypeCode* outTypeCode = NULL, BfTypeCode* outTypeCode2 = NULL) { return false; } virtual BfType* GetUnderlyingType() { return NULL; } virtual bool HasWrappedRepresentation() { return IsWrappableType(); } + virtual bool IsZeroGap() { return true; } virtual bool IsTypeMemberIncluded(BfTypeDef* declaringTypeDef, BfTypeDef* activeTypeDef = NULL, BfModule* module = NULL) { return true; } // May be 'false' only for generic extensions with constraints virtual bool IsTypeMemberAccessible(BfTypeDef* declaringTypeDef, BfTypeDef* activeTypeDef) { return true; } virtual bool IsTypeMemberAccessible(BfTypeDef* declaringTypeDef, BfProject* curProject) { return true; } @@ -903,6 +912,7 @@ public: bool mInCEMachine:1; bool mCeCancelled:1; bool mIsDisposed:1; + bool mHasAppendWantMark:1; BfMethodChainType mChainType; BfComptimeFlags mComptimeFlags; BfCallingConvention mCallingConvention; @@ -946,6 +956,7 @@ public: mInCEMachine = false; mCeCancelled = false; mIsDisposed = false; + mHasAppendWantMark = false; mChainType = BfMethodChainType_None; mComptimeFlags = BfComptimeFlag_None; mCallingConvention = BfCallingConvention_Unspecified; @@ -969,7 +980,7 @@ public: bool IsMixin() { return mMethodDef->mMethodType == BfMethodType_Mixin; - } + } BfImportKind GetImportKind(); BfMethodFlags GetMethodFlags(); @@ -977,6 +988,7 @@ public: void UndoDeclaration(bool keepIRFunction = false); BfTypeInstance* GetOwner(); BfModule* GetModule(); + bool ForcingThisPtr(); bool IsSpecializedGenericMethod(); bool IsSpecializedGenericMethodOrType(); bool IsSpecializedByAutoCompleteMethod(); @@ -1013,7 +1025,7 @@ public: BfExpression* GetParamInitializer(int paramIdx); BfTypeReference* GetParamTypeRef(int paramIdx); BfIdentifierNode* GetParamNameNode(int paramIdx); - int DbgGetVirtualMethodNum(); + int DbgGetVirtualMethodNum(); void GetIRFunctionInfo(BfModule* module, BfIRType& returnType, SizedArrayImpl& paramTypes, bool forceStatic = false); int GetIRFunctionParamCount(BfModule* module); @@ -1133,6 +1145,7 @@ public: BfType* mElementType; virtual bool IsModifiedTypeType() override { return true; } + virtual bool IsParamsType() override { return mModifiedKind == BfToken_Params; } virtual bool CanBeValuelessType() override { return true; } virtual bool IsValuelessType() override { return true; } @@ -2059,6 +2072,7 @@ public: bool mHasPackingHoles; bool mWantsGCMarking; bool mHasDeclError; + bool mHasAppendWantMark; public: BfTypeInstance() @@ -2111,6 +2125,7 @@ public: mWantsGCMarking = false; mHasParameterizedBase = false; mHasDeclError = false; + mHasAppendWantMark = false; mMergedFieldDataCount = 0; mConstHolder = NULL; } @@ -2135,6 +2150,7 @@ public: virtual bool IsIncomplete() override { return (mTypeIncomplete) || (mBaseTypeMayBeIncomplete); } virtual bool IsSplattable() override { BF_ASSERT((mInstSize >= 0) || (!IsComposite())); return mIsSplattable; } virtual int GetSplatCount(bool force = false) override; + virtual bool IsCRepr() override; virtual bool IsTypeInstance() override { return true; } virtual BfTypeCode GetTypeCode() override { return mTypeDef->mTypeCode; } virtual bool IsInterface() override { return mTypeDef->mTypeCode == BfTypeCode_Interface; } @@ -2159,6 +2175,8 @@ public: //virtual bool IsValuelessType() override { return (mIsTypedPrimitive) && (mInstSize == 0); } virtual bool CanBeValuelessType() override { return (mTypeDef->mTypeCode == BfTypeCode_Struct) || (mTypeDef->mTypeCode == BfTypeCode_Enum); } virtual bool IsValuelessType() override; + virtual bool IsValuelessCReprType(); + virtual BfTypeInstance* GetBaseType(bool remapValuelessCRepr = false); virtual bool HasPackingHoles() override { return mHasPackingHoles; } virtual bool IsTypeMemberAccessible(BfTypeDef* declaringTypeDef, BfTypeDef* activeTypeDef) override; virtual bool IsTypeMemberAccessible(BfTypeDef* declaringTypeDef, BfProject* curProject) override; @@ -2178,6 +2196,7 @@ public: virtual bool IsNullable() override; virtual bool HasVarConstraints(); virtual bool IsTypeMemberIncluded(BfTypeDef* declaringTypeDef, BfTypeDef* activeTypeDef = NULL, BfModule* module = NULL) override; + virtual bool IsZeroGap() override; virtual BfTypeInstance* GetImplBaseType() { return mBaseType; } @@ -2205,6 +2224,11 @@ public: bool HasBeenInstantiated() { return mHasBeenInstantiated || ((mAlwaysIncludeFlags & BfAlwaysIncludeFlag_AssumeInstantiated) != 0); } bool IncludeAllMethods() { return ((mAlwaysIncludeFlags & BfAlwaysIncludeFlag_IncludeAllMethods) != 0); } bool DefineStateAllowsStaticMethods() { return mDefineState >= BfTypeDefineState_HasInterfaces_Direct; } + bool IsAnonymous(); + bool IsAnonymousInitializerType(); + bool HasAppendCtor(); + bool BaseHasAppendCtor(); + bool HasAppendedField(bool checkBase); virtual void ReportMemory(MemReporter* memReporter) override; }; @@ -2533,7 +2557,7 @@ public: virtual bool IsUnspecializedType() override { return mElementType->IsUnspecializedType(); } virtual bool IsUnspecializedTypeVariation() override { return mElementType->IsUnspecializedTypeVariation(); } virtual bool CanBeValuelessType() override { return mElementType->CanBeValuelessType(); } - virtual bool IsValuelessType() override { return mElementType->IsValuelessType(); } + virtual bool IsValuelessType() override { return mElementType->IsValuelessNonOpaqueType(); } }; class BfArrayType : public BfTypeInstance @@ -2669,6 +2693,7 @@ public: Array mSetProperties; Array mSetField; bool mAwaitingValidation; + bool mIsMultiUse; BfAstNode* GetRefNode() { @@ -2690,11 +2715,7 @@ public: void ReportMemory(MemReporter* memReporter); }; -class BfResolvedTypeSetFuncs : public MultiHashSetFuncs -{ -}; - -class BfResolvedTypeSet : public MultiHashSet +class BfResolvedTypeSet : public MultiHashSet { public: enum BfHashFlags @@ -2703,6 +2724,7 @@ public: BfHashFlag_AllowRef = 1, BfHashFlag_AllowGenericParamConstValue = 2, BfHashFlag_AllowDotDotDot = 4, + BfHashFlag_DisallowPointer = 8 }; struct BfExprResult @@ -2749,7 +2771,7 @@ public: BfTypeDef* ResolveToTypeDef(BfTypeReference* typeReference, BfType** outType = NULL); }; - class Iterator : public MultiHashSet::Iterator + class Iterator : public MultiHashSet::Iterator { public: Iterator(MultiHashSet* set) : MultiHashSet::Iterator(set) @@ -2801,6 +2823,7 @@ public: static int DoHash(BfTypeReference* typeRef, LookupContext* ctx, BfHashFlags flags, int& hashSeed); static int Hash(BfTypeReference* typeRef, LookupContext* ctx, BfHashFlags flags = BfHashFlag_None, int hashSeed = 0); static int Hash(BfAstNode* typeRefNode, LookupContext* ctx, BfHashFlags flags = BfHashFlag_None, int hashSeed = 0); + static void ShowThisPointerWarning(LookupContext* ctx, BfTypeReference* typeRef); static bool Equals(BfType* lhs, BfType* rhs, LookupContext* ctx); static bool Equals(BfType* lhs, BfTypeReference* rhs, LookupContext* ctx); @@ -2835,28 +2858,51 @@ public: { return false; } - int bucket = (hashVal & 0x7FFFFFFF) % mHashSize; - auto checkEntryIdx = mHashHeads[bucket]; - while (checkEntryIdx != -1) + + while (true) { - auto checkEntry = &mEntries[checkEntryIdx]; + int startAllocSize = mAllocSize; + int bucket = (hashVal & 0x7FFFFFFF) % mHashSize; + auto startEntryIdx = mHashHeads[bucket]; + auto checkEntryIdx = startEntryIdx; + bool needsRerun = false; - // checkEntry->mType can be NULL if we're in the process of filling it in (and this Insert is from an element type) - // OR if the type resolution failed after node insertion - if ((checkEntry->mValue != NULL) && (hashVal == checkEntry->mHashCode) && (Equals(checkEntry->mValue, findType, ctx))) + while (checkEntryIdx != -1) { - *entryPtr = EntryRef(this, checkEntryIdx); - return false; - } - checkEntryIdx = checkEntry->mNext; + auto checkEntry = &mEntries[checkEntryIdx]; - tryCount++; - // If this fires off, this may indicate that our hashes are equivalent but Equals fails - if (tryCount >= 10) - { - NOP; + // checkEntry->mType can be NULL if we're in the process of filling it in (and this Insert is from an element type) + // OR if the type resolution failed after node insertion + if ((checkEntry->mValue != NULL) && (hashVal == checkEntry->mHashCode) && (Equals(checkEntry->mValue, findType, ctx))) + { + *entryPtr = EntryRef(this, checkEntryIdx); + return false; + } + + if ((mAllocSize != startAllocSize) || (startEntryIdx != mHashHeads[bucket])) + { + // It's possible for Equals to add types, buckets could be invalid or a new type could + // have been inserted at the start of our bucket + needsRerun = true; + break; + } + + checkEntryIdx = checkEntry->mNext; + + tryCount++; + // If this fires off, this may indicate that our hashes are equivalent but Equals fails + if (tryCount >= 10) + { + NOP; + } + BF_ASSERT(tryCount < 10); + } + + if (!needsRerun) + { + // Retry if we added entries + break; } - BF_ASSERT(tryCount < 10); } if ((ctx->mResolveFlags & BfResolveTypeRefFlag_NoCreate) != 0) diff --git a/IDEHelper/Compiler/BfSourceClassifier.cpp b/IDEHelper/Compiler/BfSourceClassifier.cpp index fda1207a..74b92df5 100644 --- a/IDEHelper/Compiler/BfSourceClassifier.cpp +++ b/IDEHelper/Compiler/BfSourceClassifier.cpp @@ -18,6 +18,7 @@ BfSourceClassifier::BfSourceClassifier(BfParser* bfParser, CharData* charData) mPrevNode = NULL; mCurMember = NULL; mCurLocalMethodDeclaration = NULL; + mSkipAnonymousTypes = false; } void BfSourceClassifier::ModifyFlags(BfAstNode* node, uint8 andFlags, uint8 orFlags) @@ -462,6 +463,12 @@ void BfSourceClassifier::Visit(BfTokenNode* tokenNode) SetElementType(tokenNode, BfSourceElementType_Normal); } +void BfSourceClassifier::Visit(BfCaseExpression* caseExpr) +{ + BfElementVisitor::Visit(caseExpr); + SetElementType(caseExpr->mNotToken, BfSourceElementType_Keyword); +} + void BfSourceClassifier::Visit(BfInvocationExpression* invocationExpr) { //BfElementVisitor::Visit(invocationExpr); @@ -657,12 +664,15 @@ void BfSourceClassifier::Visit(BfPropertyDeclaration* propertyDeclaration) void BfSourceClassifier::Visit(BfTypeDeclaration* typeDeclaration) { + if ((mSkipAnonymousTypes) && (typeDeclaration->IsAnonymous())) + return; + if (typeDeclaration->mIgnoreDeclaration) return; SetAndRestoreValue prevMember(mCurMember, typeDeclaration); - if (mSkipTypeDeclarations) + if ((mSkipTypeDeclarations) && (!typeDeclaration->IsAnonymous())) { if (auto defineBlock = BfNodeDynCast(typeDeclaration->mDefineNode)) { diff --git a/IDEHelper/Compiler/BfSourceClassifier.h b/IDEHelper/Compiler/BfSourceClassifier.h index ae0a9af9..cb87afd7 100644 --- a/IDEHelper/Compiler/BfSourceClassifier.h +++ b/IDEHelper/Compiler/BfSourceClassifier.h @@ -69,6 +69,7 @@ public: bool mSkipAttributes; bool mIsSideChannel; bool mPreserveFlags; + bool mSkipAnonymousTypes; uint8 mClassifierPassId; BfAstNode* mPrevNode; BfAstNode* mCurMember; @@ -124,6 +125,7 @@ public: virtual void Visit(BfLiteralExpression* literalExpr) override; virtual void Visit(BfStringInterpolationExpression* stringInterpolationExpression) override; virtual void Visit(BfTokenNode* tokenNode) override; + virtual void Visit(BfCaseExpression* caseExpr) override; virtual void Visit(BfInvocationExpression* invocationExpr) override; virtual void Visit(BfIndexerExpression* indexerExpr) override; virtual void Visit(BfConstructorDeclaration* ctorDeclaration) override; diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index f5c23244..4117d70f 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -543,11 +543,12 @@ BfDeferredCallEntry* BfModule::AddDeferredBlock(BfBlock* block, BfScopeData* sco return deferredCallEntry; } -BfDeferredCallEntry* BfModule::AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl& llvmArgs, BfScopeData* scopeData, BfAstNode* srcNode, bool bypassVirtual, bool doNullCheck) +BfDeferredCallEntry* BfModule::AddDeferredCall(const BfModuleMethodInstance& moduleMethodInstance, SizedArrayImpl& llvmArgs, BfScopeData* scopeData, BfAstNode* srcNode, bool bypassVirtual, bool doNullCheck, bool isAllocaFunc) { BfDeferredCallEntry* deferredCallEntry = new BfDeferredCallEntry(); BF_ASSERT(moduleMethodInstance); deferredCallEntry->mModuleMethodInstance = moduleMethodInstance; + deferredCallEntry->mIsAllocaFunc = isAllocaFunc; for (auto arg : llvmArgs) { @@ -783,7 +784,12 @@ void BfModule::EmitDeferredCall(BfModuleMethodInstance moduleMethodInstance, Siz } BfExprEvaluator expressionEvaluator(this); - expressionEvaluator.CreateCall(NULL, moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, ((flags & BfDeferredBlockFlag_BypassVirtual) != 0), llvmArgs); + + auto func = moduleMethodInstance.mFunc; + if ((flags & BfDeferredBlockFlag_IsAllocaFunc) != 0) + func = mBfIRBuilder->CreateLoad(func); + + expressionEvaluator.CreateCall(NULL, moduleMethodInstance.mMethodInstance, func, ((flags & BfDeferredBlockFlag_BypassVirtual) != 0), llvmArgs); if ((flags & BfDeferredBlockFlag_DoNullChecks) != 0) { @@ -914,6 +920,8 @@ void BfModule::EmitDeferredCall(BfScopeData* scopeData, BfDeferredCallEntry& def flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_DoNullChecks | BfDeferredBlockFlag_SkipObjectAccessCheck | BfDeferredBlockFlag_MoveNewBlocksToEnd); if (moveBlocks) flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_MoveNewBlocksToEnd); + if (deferredCallEntry.mIsAllocaFunc) + flags = (BfDeferredBlockFlags)(flags | BfDeferredBlockFlag_IsAllocaFunc); EmitDeferredCall(deferredCallEntry.mModuleMethodInstance, args, flags); } @@ -926,6 +934,7 @@ void BfModule::EmitDeferredCallProcessor(BfScopeData* scopeData, SLIList MapType; @@ -951,6 +960,7 @@ void BfModule::EmitDeferredCallProcessor(BfScopeData* scopeData, SLIListmModuleMethodInstance = moduleMethodInstance; callInfo->mBypassVirtual = deferredCallEntry->mBypassVirtual; + callInfo->mIsAllocaFunc = deferredCallEntry->mIsAllocaFunc; } else { @@ -1118,6 +1128,7 @@ void BfModule::EmitDeferredCallProcessor(BfScopeData* scopeData, SLIListmMethodDef; auto methodOwner = methodInstance->mMethodInstanceGroup->mOwner; @@ -1204,6 +1215,8 @@ void BfModule::EmitDeferredCallProcessor(BfScopeData* scopeData, SLIListCreateBr(condBB); @@ -1585,11 +1598,15 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD { if (initValue.mType->IsNullable()) { + auto nullableElementType = initValue.mType->GetUnderlyingType(); auto boolType = GetPrimitiveType(BfTypeCode_Boolean); initValue = LoadValue(initValue); - exprEvaluator->mResult = BfTypedValue(mBfIRBuilder->CreateExtractValue(initValue.mValue, 2), boolType); + exprEvaluator->mResult = BfTypedValue(mBfIRBuilder->CreateExtractValue(initValue.mValue, nullableElementType->IsValuelessType() ? 1 : 2), boolType); handledExprBoolResult = true; - initValue = BfTypedValue(mBfIRBuilder->CreateExtractValue(initValue.mValue, 1), initValue.mType->GetUnderlyingType()); + if (!nullableElementType->IsValuelessType()) + initValue = BfTypedValue(mBfIRBuilder->CreateExtractValue(initValue.mValue, 1), initValue.mType->GetUnderlyingType()); + else + initValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), nullableElementType); } else { @@ -1659,7 +1676,11 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD auto _CheckConst = [&] { - if (initValue.mValue.IsConst()) + if (initValue.IsAddr()) + { + isConst = false; + } + else if (initValue.mValue.IsConst()) { auto constant = mBfIRBuilder->GetConstant(initValue.mValue); @@ -1778,6 +1799,11 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD resolvedType = initValue.mType; unresolvedType = resolvedType; } + + if (auto autoComplete = mCompiler->GetAutoComplete()) + { + autoComplete->CheckResult(varDecl->mInitializer, initValue); + } } if ((!handledVarInit) && (!isConst)) { @@ -1878,7 +1904,8 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD if (!initValue) initValue = GetDefaultTypedValue(localDef->mResolvedType); - localDef->mAddr = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(localDef->mResolvedType), false, BfIRLinkageType_Internal, initValue.mValue, name);; + if (!localDef->mResolvedType->IsValuelessType()) + localDef->mAddr = mBfIRBuilder->CreateGlobalVariable(mBfIRBuilder->MapType(localDef->mResolvedType), false, BfIRLinkageType_Internal, initValue.mValue, name);; initHandled = true; } @@ -1900,7 +1927,7 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD localDef->mValue = initValue.mValue; if ((localDef->mAddr) && (!localDef->mResolvedType->IsValuelessType())) { - if (!initValue.mType->IsVar()) + if ((!initValue.mType->IsVar()) && (!initValue.mType->IsValuelessType())) wantsStore = true; } else @@ -2165,6 +2192,7 @@ void BfModule::HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl, Bf initTupleType = (BfTypeInstance*)initTupleValue.mType; CheckTupleVariableDeclaration(tupleExpr, initTupleValue.mType); + mBfIRBuilder->PopulateType(initTupleValue.mType); for (int varIdx = 0; varIdx < (int)tupleExpr->mValues.size(); varIdx++) { @@ -2183,7 +2211,7 @@ void BfModule::HandleTupleVariableDeclaration(BfVariableDeclaration* varDecl, Bf initValue = LoadValue(initValue); } else - initValue = BfTypedValue(mBfIRBuilder->CreateExtractValue(initTupleValue.mValue, fieldInstance->mDataIdx), resolvedType); + initValue = ExtractValue(initTupleValue, fieldInstance, fieldInstance->mDataIdx); } BfTupleNameNode* tupleNameNode = NULL; @@ -2375,6 +2403,7 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr BfTypedValue mArgValue; BfTypedValue mTupleElement; int mFieldIdx; + bool mIsOut; }; Array deferredAssigns; @@ -2426,9 +2455,9 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr if (prevHadFallthrough) Fail("Destructuring cannot be used when the previous case contains a fallthrough", expr); + + CreateOutVariable(expr, varDecl, NULL, tupleElement.mType, tupleElement); - auto localVar = HandleVariableDeclaration(varDecl, tupleElement, false, true); - localVar->mReadFromId = 0; // Don't give usage errors for binds continue; } @@ -2452,6 +2481,9 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr auto localVar = HandleVariableDeclaration(resolvedType, binOpExpr->mRight, tupleElement, false, true); localVar->mReadFromId = 0; // Don't give usage errors for binds + auto curScope = mCurMethodState->mCurScope; + if (curScope->mScopeKind == BfScopeKind_StatementTarget) + MoveLocalToParentScope(localVar); continue; } } @@ -2526,7 +2558,7 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr BfExprEvaluator exprEvaluator(this); exprEvaluator.mExpectingType = tupleFieldInstance->GetResolvedType(); - exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_AllowOutExpr; + exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(BfEvalExprFlags_AllowOutExpr | BfEvalExprFlags_AllowRefExpr); if (mCurMethodState->mDeferredLocalAssignData != NULL) { SetAndRestoreValue prevIsIfCondition(mCurMethodState->mDeferredLocalAssignData->mIsIfCondition, true); @@ -2548,25 +2580,30 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr if (argValue.mType->IsRef()) { auto refType = (BfRefType*)argValue.mType; - if (refType->mRefKind != BfRefType::RefKind_Out) - { - BfAstNode* refNode = expr; - if (auto unaryOperatorExpr = BfNodeDynCast(expr)) - refNode = unaryOperatorExpr->mOpToken; - Fail("Only 'out' refs can be used to assign to an existing value", refNode); - } - - DeferredAssign deferredAssign = { expr, argValue, tupleElement, tupleFieldIdx }; + bool isOut = refType->mRefKind == BfRefType::RefKind_Out; + if ((refType->mRefKind != BfRefType::RefKind_Out) && (refType->mRefKind != BfRefType::RefKind_Ref)) + { + BfAstNode* refNode = expr; + if (auto unaryOperatorExpr = BfNodeDynCast(expr)) + refNode = unaryOperatorExpr->mOpToken; + Fail("Invalid ref type", refNode); + } + + DeferredAssign deferredAssign = { expr, argValue, tupleElement, tupleFieldIdx, isOut }; deferredAssigns.push_back(deferredAssign); - if (mCurMethodState->mDeferredLocalAssignData != NULL) - { - SetAndRestoreValue prevIsIfCondition(mCurMethodState->mDeferredLocalAssignData->mIsIfCondition, true); - SetAndRestoreValue prevIfMayBeSkipped(mCurMethodState->mDeferredLocalAssignData->mIfMayBeSkipped, true); - exprEvaluator.MarkResultAssigned(); - } - else - { - exprEvaluator.MarkResultAssigned(); + + if (isOut) + { + if (mCurMethodState->mDeferredLocalAssignData != NULL) + { + SetAndRestoreValue prevIsIfCondition(mCurMethodState->mDeferredLocalAssignData->mIsIfCondition, true); + SetAndRestoreValue prevIfMayBeSkipped(mCurMethodState->mDeferredLocalAssignData->mIfMayBeSkipped, true); + exprEvaluator.MarkResultAssigned(); + } + else + { + exprEvaluator.MarkResultAssigned(); + } } continue; } @@ -2620,6 +2657,9 @@ void BfModule::HandleCaseEnumMatch_Tuple(BfTypedValue tupleVal, const BfSizedArr mBfIRBuilder->SetInsertPoint(falseBlockEnd); for (auto& deferredAssign : deferredAssigns) { + if (!deferredAssign.mIsOut) + continue; + auto tupleFieldInstance = &tupleType->mFieldInstances[deferredAssign.mFieldIdx]; // We have to re-process the expr because we haven't done it in this branch, and then clear the result out SetAndRestoreValue prevIgnoreErrors(mIgnoreErrors, mHadBuildError); // Don't fail twice @@ -2882,6 +2922,7 @@ BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVa tagVal = GetDefaultTypedValue(tagType); } + BfIRValue eqResult; for (int fieldIdx = 0; fieldIdx < (int)enumType->mFieldInstances.size(); fieldIdx++) { auto fieldInstance = &enumType->mFieldInstances[fieldIdx]; @@ -2913,6 +2954,13 @@ BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVa PopulateType(tupleType); mBfIRBuilder->PopulateType(tupleType); + if (tupleType->IsDeleting()) + { + mCompiler->RequestExtraCompile(); + InternalError("TryCaseEnumMatch using deleted type", expr); + return BfTypedValue(); + } + auto boolType = GetPrimitiveType(BfTypeCode_Boolean); tagId = -fieldInstance->mDataIdx - 1; @@ -2920,6 +2968,18 @@ BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVa auto dscrType = enumType->GetDiscriminatorType(); BfIRValue eqResult = mBfIRBuilder->CreateCmpEQ(tagVal.mValue, mBfIRBuilder->CreateConst(dscrType->mTypeDef->mTypeCode, tagId)); + bool isConstMatch = false; + bool isConstIgnore = false; + if (auto constant = mBfIRBuilder->GetConstant(eqResult)) + { + isConstMatch = constant->mBool; + isConstIgnore = !constant->mBool; + } + + SetAndRestoreValue prevIgnoreWrite(mBfIRBuilder->mIgnoreWrites); + if (isConstIgnore) + mBfIRBuilder->mIgnoreWrites = true; + BfIRBlock falseBlockStart; BfIRBlock falseBlockEnd; BfIRBlock doneBlockStart; @@ -2939,13 +2999,17 @@ BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVa BfIRBlock matchedBlockEnd = matchedBlockStart; if (matchBlock != NULL) *matchBlock = matchedBlockStart; - mBfIRBuilder->CreateCondBr(eqResult, matchedBlockStart, falseBlockStart ? falseBlockStart : doneBlockStart); + + if (isConstMatch) + mBfIRBuilder->CreateBr(matchedBlockStart); + else + mBfIRBuilder->CreateCondBr(eqResult, matchedBlockStart, falseBlockStart ? falseBlockStart : doneBlockStart); mBfIRBuilder->AddBlock(matchedBlockStart); mBfIRBuilder->SetInsertPoint(doneBlockEnd); BfIRValue phiVal; - if (eqBlock == NULL) + if ((eqBlock == NULL) && (!isConstIgnore) && (!isConstMatch)) phiVal = mBfIRBuilder->CreatePhi(mBfIRBuilder->MapType(boolType), 1 + (int)tupleType->mFieldInstances.size()); mBfIRBuilder->SetInsertPoint(matchedBlockEnd); @@ -3087,6 +3151,8 @@ BfTypedValue BfModule::TryCaseEnumMatch(BfTypedValue enumVal, BfTypedValue tagVa if (phiVal) return BfTypedValue(phiVal, boolType); + else if (eqResult) + return BfTypedValue(eqResult, boolType); else return GetDefaultTypedValue(boolType); } @@ -4162,47 +4228,73 @@ void BfModule::Visit(BfDeleteStatement* deleteStmt) auto val = CreateValueFromExpression(deleteStmt->mExpression); if (!val) return; - - if (val.mType->IsAllocType()) - val.mType = val.mType->GetUnderlyingType(); - - BfGenericParamType* genericType = NULL; - if (val.mType->IsGenericParam()) - genericType = (BfGenericParamType*)val.mType; - if ((val.mType->IsPointer()) && (val.mType->GetUnderlyingType()->IsGenericParam())) - genericType = (BfGenericParamType*)val.mType->GetUnderlyingType(); - + auto checkType = val.mType; - if (genericType != NULL) + for (int pass = 0; pass < 2; pass++) { - BfGenericParamFlags genericParamFlags = BfGenericParamFlag_None; - BfType* typeConstraint = NULL; - auto genericParam = GetMergedGenericParamData(genericType, genericParamFlags, typeConstraint); + BfGenericParamType* genericType = NULL; + if (checkType->IsGenericParam()) + genericType = (BfGenericParamType*)checkType; + if ((checkType->IsPointer()) && (checkType->GetUnderlyingType()->IsGenericParam())) + genericType = (BfGenericParamType*)checkType->GetUnderlyingType(); + if ((genericType != NULL) || (checkType->IsUnspecializedType())) + { + BfGenericParamFlags genericParamFlags = BfGenericParamFlag_None; + BfType* typeConstraint = NULL; + BfGenericParamInstance* genericParam = NULL; + if (genericType != NULL) + genericParam = GetMergedGenericParamData(genericType, genericParamFlags, typeConstraint); + if (genericParam == NULL) + GetMergedGenericParamData(checkType, genericParamFlags, typeConstraint); - if (typeConstraint != NULL) - checkType = typeConstraint; - bool canAlwaysDelete = checkType->IsDelegate() || checkType->IsFunction() || checkType->IsArray(); - if (auto checkTypeInst = checkType->ToTypeInstance()) - { - if ((checkTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || - (checkTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef))) - canAlwaysDelete = true; - } + if (typeConstraint != NULL) + checkType = typeConstraint; + bool canAlwaysDelete = checkType->IsDelegate() || checkType->IsFunction() || checkType->IsArray(); + if (auto checkTypeInst = checkType->ToTypeInstance()) + { + if ((checkTypeInst->IsInstanceOf(mCompiler->mDelegateTypeDef)) || + (checkTypeInst->IsInstanceOf(mCompiler->mFunctionTypeDef))) + canAlwaysDelete = true; + } - if (!canAlwaysDelete) - { - if (genericParamFlags & (BfGenericParamFlag_Delete | BfGenericParamFlag_Var)) - return; - if (genericParamFlags & BfGenericParamFlag_StructPtr) - return; - if ((genericParamFlags & BfGenericParamFlag_Struct) && (checkType->IsPointer())) - return; - auto genericParamInst = GetGenericParamInstance(genericType); - Fail(StrFormat("Must add 'where %s : delete' constraint to generic parameter to delete generic type '%s'", - genericParamInst->GetGenericParamDef()->mName.c_str(), TypeToString(val.mType).c_str()), deleteStmt->mExpression); - return; + if (!canAlwaysDelete) + { + bool success = false; + if (genericParamFlags & (BfGenericParamFlag_Delete | BfGenericParamFlag_Var)) + success = true; + else if (genericParamFlags & BfGenericParamFlag_StructPtr) + success = true; + else if ((genericParamFlags & BfGenericParamFlag_Struct) && (checkType->IsPointer())) + success = true; + + if (success) + { + if ((pass == 1) && (genericType != NULL)) + { + auto genericParamInst = GetGenericParamInstance(genericType); + Warn(0, StrFormat("Must add 'where alloctype(%s) : delete' constraint to generic parameter to delete generic type '%s'", + genericParamInst->GetGenericParamDef()->mName.c_str(), TypeToString(val.mType).c_str()), deleteStmt->mExpression); + } + return; + } + + if (genericType != NULL) + { + auto genericParamInst = GetGenericParamInstance(genericType); + Fail(StrFormat("Must add 'where %s : delete' constraint to generic parameter to delete generic type '%s'", + genericParamInst->GetGenericParamDef()->mName.c_str(), TypeToString(val.mType).c_str()), deleteStmt->mExpression); + return; + } + } } - } + if (pass == 0) + { + if (checkType->IsAllocType()) + checkType = checkType->GetUnderlyingType(); + else + break; + } + } if (checkType->IsVar()) { @@ -4392,12 +4484,7 @@ void BfModule::Visit(BfDeleteStatement* deleteStmt) } void BfModule::Visit(BfSwitchStatement* switchStmt) -{ - if (mModuleName == "BeefTest_TestProgram") - { - NOP; - } - +{ BfScopeData outerScope; outerScope.mInnerIsConditional = false; outerScope.mCloseNode = switchStmt; @@ -4591,7 +4678,7 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) BfPrimitiveType* intCoercibleType = GetIntCoercibleType(switchValue.mType); bool isConstSwitch = false; - if ((switchValue.mValue.IsConst()) || (switchValue.mType->IsValuelessType())) + if ((mBfIRBuilder->IsConstValue(switchValue.mValue)) || (switchValue.mType->IsValuelessType())) { isConstSwitch = true; } @@ -4753,6 +4840,11 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) else { eqTypedResult = TryCaseEnumMatch(switchValueAddr, enumTagVal, caseExpr, &caseBlock, ¬EqBB, &matchBlock, tagId, hadConditional, false, prevHadFallthrough); + if (auto constant = mBfIRBuilder->GetConstant(eqTypedResult.mValue)) + { + if (constant->mBool) + mayHaveMatch = true; + } if (hadConditional) hadCondCase = true; } @@ -4882,7 +4974,8 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) BfExprEvaluator exprEvaluator(this); BfAstNode* refNode = switchCase->mColonToken; - if ((caseValue.mType->IsPayloadEnum()) && (caseValue.mValue.IsConst()) && (switchValue.mType == caseValue.mType) && (isEnumDescValue)) + if ((caseValue.mType->IsPayloadEnum()) && (caseValue.mValue.IsConst()) && (switchValue.mType == caseValue.mType) && + ((isEnumDescValue) || (constantInt != NULL))) { if (!enumTagVal) { @@ -5091,6 +5184,7 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) for (auto& field : enumType->mFieldInstances) { auto fieldDef = field.GetFieldDef(); + if (fieldDef == NULL) continue; if (!fieldDef->IsEnumCaseEntry()) @@ -5119,23 +5213,117 @@ void BfModule::Visit(BfSwitchStatement* switchStmt) if (!isComprehensive) { - BfAstNode* refNode = switchStmt->mSwitchToken; Fail("Switch must be exhaustive, consider adding a default clause", switchStmt->mSwitchToken); - if ((switchStmt->mCloseBrace) && (mCompiler->IsAutocomplete()) && (mCompiler->mResolvePassData->mAutoComplete->CheckFixit((refNode)))) - { - BfParserData* parser = refNode->GetSourceData()->ToParserData(); - if (parser != NULL) - { - int fileLoc = switchStmt->mCloseBrace->GetSrcStart(); - mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("default:\tdefault:|%s|%d||default:", parser->mFileName.c_str(), fileLoc).c_str())); - } - } } } else isComprehensive = false; } + BfAstNode* refNode = switchStmt->mSwitchToken; + if ((switchStmt->mCloseBrace) && (mCompiler->IsAutocomplete()) && (mCompiler->mResolvePassData->mAutoComplete->CheckFixit((switchStmt->mSwitchToken)))) + { + BfParserData* parser = refNode->GetSourceData()->ToParserData(); + if (parser != NULL) + { + if (switchStmt->mDefaultCase == NULL) + { + int fileLoc = switchStmt->mCloseBrace->GetSrcStart(); + mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("Add default case\tdefault:|%s|%d||default:", parser->mFileName.c_str(), fileLoc).c_str())); + } + + if ((switchValue.mType->IsEnum()) && (!isComprehensive) || (switchStmt->mDefaultCase != NULL)) + { + int fileLoc; + + if ((handledCases.IsEmpty()) && (switchStmt->mOpenBrace != NULL)) + fileLoc = switchStmt->mOpenBrace->GetSrcEnd() + 1; + else if (switchStmt->mDefaultCase != NULL) + fileLoc = switchStmt->mDefaultCase->GetSrcStart(); + else + fileLoc = switchStmt->mCloseBrace->GetSrcStart(); + + String explicitInsertStr; + + auto enumType = switchValue.mType->ToTypeInstance(); + HashSet missingValues; + + if (enumType->IsPayloadEnum()) + { + for (auto& field : enumType->mFieldInstances) + { + auto fieldDef = field.GetFieldDef(); + if (fieldDef == NULL) + continue; + if (!fieldDef->IsEnumCaseEntry()) + continue; + if ((field.mDataIdx < 0)) + { + auto key = -field.mDataIdx - 1; + if ((!handledCases.ContainsKey(key)) && (missingValues.Add(key))) + { + auto paramDecl = fieldDef->mTypeRef; + + if ((field.mIsEnumPayloadCase) && (field.mResolvedType->IsTuple())) + { + auto payloadType = (BfTypeInstance*)field.mResolvedType; + if (!payloadType->mFieldInstances.empty()) + { + String fieldsStr; + int count = 0; + for (auto& payloadField : payloadType->mFieldInstances) + { + auto payloadFieldDef = payloadField.GetFieldDef(); + if (payloadFieldDef == NULL) + continue; + + String fieldName = payloadFieldDef->mName; + if ((fieldName.IsEmpty()) || (fieldName.length() > 0) && (isdigit(fieldName[0]))) + fieldName = StrFormat("p%d", count); + if (count > 0) + fieldsStr += ", "; + fieldsStr += StrFormat("let %s", fieldName.c_str()); + count++; + } + + if (!fieldsStr.IsEmpty()) + { + explicitInsertStr += StrFormat("|case .%s(%s):", fieldDef->mName.c_str(), fieldsStr.c_str()); + continue; + } + } + } + + explicitInsertStr += StrFormat("|case .%s:", fieldDef->mName.c_str()); + } + } + } + } + else + { + for (auto& field : enumType->mFieldInstances) + { + auto fieldDef = field.GetFieldDef(); + if ((fieldDef != NULL) && (fieldDef->IsEnumCaseEntry())) + { + if (field.mConstIdx != -1) + { + auto constant = enumType->mConstHolder->GetConstantById(field.mConstIdx); + if ((!handledCases.ContainsKey(constant->mInt64)) && (missingValues.Add(constant->mInt64))) + { + explicitInsertStr += StrFormat("|case .%s:", fieldDef->mName.c_str()); + } + } + } + } + } + + if (missingValues.GetCount() > 0) + mCompiler->mResolvePassData->mAutoComplete->AddEntry(AutoCompleteEntry("fixit", StrFormat("Add missing cases\tdefault:|%s|%d|%s", parser->mFileName.c_str(), fileLoc, explicitInsertStr.c_str()).c_str())); + } + } + } + if (!hadConstMatch) mBfIRBuilder->CreateBr(defaultBlock); @@ -5577,7 +5765,7 @@ void BfModule::Visit(BfContinueStatement* continueStmt) breakData = FindBreakData(continueStmt->mLabel); if ((breakData != NULL) && (!breakData->mIRContinueBlock)) { - Fail(StrFormat("'continue' not applicable in '%s", continueStmt->mLabel->ToString().c_str()), continueStmt); + Fail(StrFormat("'continue' not applicable in '%s'", continueStmt->mLabel->ToString().c_str()), continueStmt); return; } } @@ -7104,7 +7292,7 @@ void BfModule::Visit(BfForEachStatement* forEachStmt) arrayItem = LoadValue(arrayItem); arrayItem = Cast(forEachStmt->mCollectionExpression, arrayItem, varType, BfCastFlags_Explicit); arrayItem = LoadValue(arrayItem); - if (arrayItem) + if ((arrayItem) && (!arrayItem.mType->IsValuelessType())) mBfIRBuilder->CreateStore(arrayItem.mValue, varInst); } } @@ -7207,7 +7395,16 @@ void BfModule::Visit(BfDeferStatement* deferStmt) scope = &mCurMethodState->mHeadScope; } else if (deferStmt->mScopeName != NULL) + { scope = FindScope(deferStmt->mScopeName, true); + + if (scope == NULL) + { + AssertErrorState(); + // The scope doesn't exist, continue with the current scope so we still get an evaluation of the deferred code + scope = mCurMethodState->mCurScope; + } + } else scope = mCurMethodState->mCurScope; @@ -7247,9 +7444,12 @@ void BfModule::Visit(BfDeferStatement* deferStmt) } else if (auto exprStmt = BfNodeDynCast(deferStmt->mTargetNode)) { + BfDeferCallData deferCallData; + deferCallData.mRefNode = exprStmt->mExpression; + deferCallData.mScopeAlloc = scope; + BfExprEvaluator expressionEvaluator(this); - expressionEvaluator.mDeferCallRef = exprStmt->mExpression; - expressionEvaluator.mDeferScopeAlloc = scope; + expressionEvaluator.mDeferCallData = &deferCallData; expressionEvaluator.VisitChild(exprStmt->mExpression); if (mCurMethodState->mPendingNullConditional != NULL) FlushNullConditional(expressionEvaluator.mResult, true); diff --git a/IDEHelper/Compiler/BfSystem.cpp b/IDEHelper/Compiler/BfSystem.cpp index 1946d10f..2e7c6ec9 100644 --- a/IDEHelper/Compiler/BfSystem.cpp +++ b/IDEHelper/Compiler/BfSystem.cpp @@ -956,6 +956,8 @@ bool BfTypeDef::HasAutoProperty(BfPropertyDeclaration* propertyDeclaration) return false; if (propertyDeclaration->mExternSpecifier != NULL) return false; + if (propertyDeclaration->IsA()) + return false; for (auto methodDeclaration : propertyDeclaration->mMethods) { @@ -967,10 +969,8 @@ bool BfTypeDef::HasAutoProperty(BfPropertyDeclaration* propertyDeclaration) String BfTypeDef::GetAutoPropertyName(BfPropertyDeclaration* propertyDeclaration) { - String name = "prop__"; - if (propertyDeclaration->IsA()) - name += "indexer__"; - else if (propertyDeclaration->mNameNode != NULL) + String name = "prop__"; + if (propertyDeclaration->mNameNode != NULL) name += propertyDeclaration->mNameNode->ToString(); return name; } @@ -1091,6 +1091,12 @@ BfProject::~BfProject() BfLogSysM("Deleting project %p %s\n", this, mName.c_str()); } +void BfProject::ClearCache() +{ + mDependencySet.Clear(); + mDependencyKindDict.Clear(); +} + bool BfProject::ContainsReference(BfProject* refProject) { if (refProject->mDisabled) @@ -1113,6 +1119,59 @@ bool BfProject::IsTestProject() return mTargetType == BfTargetType_BeefTest; } +bool BfProject::HasDependency(BfProject* project) +{ + if (mDependencySet.IsEmpty()) + { + auto _AddProject = [&](BfProject* addProject) + { + if (mDependencySet.Add(addProject)) + { + for (auto dep : addProject->mDependencies) + mDependencySet.Add(dep); + } + }; + _AddProject(this); + + } + return mDependencySet.Contains(project); +} + +BfProject::DependencyKind BfProject::GetDependencyKind(BfProject* project) +{ + DependencyKind* depKindPtr = NULL; + if (mDependencyKindDict.TryAdd(project, NULL, &depKindPtr)) + { + *depKindPtr = DependencyKind_None; + if (project == this) + { + *depKindPtr = DependencyKind_Identity; + } + else if (HasDependency(project)) + { + *depKindPtr = DependencyKind_Dependency; + } + else if (project->HasDependency(this)) + { + *depKindPtr = DependencyKind_Dependent_Exclusive; + + for (auto checkProject : mSystem->mProjects) + { + if ((checkProject == this) || (checkProject == project) || (checkProject->mDisabled)) + continue; + if (checkProject->HasDependency(this)) + { + if (!checkProject->HasDependency(project)) + { + *depKindPtr = DependencyKind_Dependent_Shared; + } + } + } + } + } + return *depKindPtr; +} + ////////////////////////////////////////////////////////////////////////// BfErrorBase::~BfErrorBase() @@ -1164,6 +1223,37 @@ BfPassInstance::~BfPassInstance() delete bfError; } +BfPassInstance::StateInfo BfPassInstance::GetState() +{ + StateInfo stateInfo; + stateInfo.mOutStreamSize = (int)mOutStream.mSize; + stateInfo.mErrorsSize = mErrors.mSize; + stateInfo.mWarningCount = mWarningCount; + stateInfo.mDeferredErrorCount = mDeferredErrorCount; + stateInfo.mFailedIdx = mFailedIdx; + stateInfo.mWarnIdx = mWarnIdx; + return stateInfo; +} + +void BfPassInstance::RestoreState(StateInfo stateInfo) +{ + while (mOutStream.mSize > stateInfo.mOutStreamSize) + mOutStream.pop_back(); + + while (mErrors.mSize > stateInfo.mErrorsSize) + { + auto error = mErrors.back(); + mErrors.pop_back(); + mErrorSet.Remove(error); + delete error; + } + + mWarningCount = stateInfo.mWarningCount; + mDeferredErrorCount = stateInfo.mDeferredErrorCount; + mFailedIdx = stateInfo.mFailedIdx; + mWarnIdx = stateInfo.mWarnIdx; +} + void BfPassInstance::ClearErrors() { mFailedIdx = 0; @@ -1703,7 +1793,7 @@ BfError* BfPassInstance::WarnAt(int warningNumber, const StringImpl& warning, Bf return NULL; auto bfParser = bfSource->ToParserData(); - if ((bfParser != NULL) && (warningNumber > 0) && (!bfParser->IsWarningEnabledAtSrcIndex(warningNumber, srcIdx))) + if ((bfParser != NULL) && (!bfParser->IsWarningEnabledAtSrcIndex(warningNumber, srcIdx))) return NULL; if (!WantsRangeRecorded(bfParser, srcIdx, srcLen, true, isDeferred)) @@ -1984,6 +2074,8 @@ BfSystem::BfSystem() gPerfManager = new PerfManager(); //gPerfManager->StartRecording(); + mAnonymousAtomCount = 0; + mCurUniqueId = 0; mAtomUpdateIdx = 0; mAtomCreateIdx = 0; mTypeMapVersion = 1; @@ -2094,7 +2186,7 @@ BfSystem::~BfSystem() typeDef->mHash = typeCode + 1000; \ mSystemTypeDefs[name] = typeDef; -BfAtom* BfSystem::GetAtom(const StringImpl& string) +BfAtom* BfSystem::GetAtom(const StringImpl& string, BfAtom::Kind kind) { StringView* stringPtr = NULL; BfAtom* atom = NULL; @@ -2111,6 +2203,7 @@ BfAtom* BfSystem::GetAtom(const StringImpl& string) } #endif mAtomCreateIdx++; + atom->mKind = kind; atom->mIsSystemType = false; atom->mAtomUpdateIdx = ++mAtomUpdateIdx; atom->mString = *stringPtr; @@ -2121,6 +2214,9 @@ BfAtom* BfSystem::GetAtom(const StringImpl& string) for (char c : string) atom->mHash = ((atom->mHash ^ c) << 5) - atom->mHash; + if (kind == BfAtom::Kind_Anon) + mAnonymousAtomCount++; + BfLogSys(this, "Atom Allocated %p %s\n", atom, string.c_str()); return atom; @@ -2152,6 +2248,12 @@ void BfSystem::ReleaseAtom(BfAtom* atom) { if (--atom->mRefCount == 0) { + if (atom->mKind == BfAtom::Kind_Anon) + { + mAnonymousAtomCount--; + BF_ASSERT(mAnonymousAtomCount >= 0); + } + mAtomGraveyard.push_back(atom); return; } @@ -2450,6 +2552,21 @@ BfProject* BfSystem::GetProject(const StringImpl& projName) return NULL; } +uint64 BfSystem::GetTypeDeclListHash() +{ + HashContext hashCtx; + for (auto project : mProjects) + { + hashCtx.MixinStr(project->mName); + hashCtx.Mixin(project->mDisabled); + hashCtx.Mixin(project->mDependencies.mSize); + for (auto dep : project->mDependencies) + hashCtx.Mixin(dep->mIdx); + } + hashCtx.Mixin(mTypeDefs.mRevision); + return hashCtx.Finish128().mLow; +} + BfTypeReference* BfSystem::GetTypeRefElement(BfTypeReference* typeRef) { if (auto elementedType = BfNodeDynCast(typeRef)) @@ -3014,7 +3131,7 @@ void BfSystem::InjectNewRevision(BfTypeDef* typeDef) typeDef->mProtection = nextTypeDef->mProtection; - BF_ASSERT(typeDef->mTypeCode == nextTypeDef->mTypeCode); + BF_ASSERT((typeDef->mTypeCode == nextTypeDef->mTypeCode) || (nextTypeDef->mTypeCode == BfTypeCode_Inferred)); typeDef->mTypeCode = nextTypeDef->mTypeCode; typeDef->mShow = nextTypeDef->mShow; @@ -3121,6 +3238,7 @@ void BfSystem::AddToCompositePartial(BfPassInstance* passInstance, BfTypeDef* co typeDef->mSystem = partialTypeDef->mSystem; typeDef->mTypeCode = partialTypeDef->mTypeCode; typeDef->mShow = partialTypeDef->mShow; + typeDef->mIsOpaque = partialTypeDef->mIsOpaque; typeDef->mIsFunction = partialTypeDef->mIsFunction; typeDef->mIsDelegate = partialTypeDef->mIsDelegate; typeDef->mNestDepth = partialTypeDef->mNestDepth; diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index d08c1b81..9ad8755c 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -48,8 +48,16 @@ typedef HashSet BfProjectSet; class BfAtom { +public: + enum Kind + { + Kind_Normal, + Kind_Anon + }; + public: StringView mString; + Kind mKind; int mRefCount; int mPendingDerefCount; int mHash; @@ -231,6 +239,7 @@ enum BfTypeFlags BfTypeFlags_Static = 0x200000, BfTypeFlags_Abstract = 0x400000, + BfTypeFlags_HasAppendWantMark = 0x800000, }; enum BfMethodFlags @@ -245,7 +254,11 @@ enum BfMethodFlags BfMethodFlags_FastCall = 0x2000, BfMethodFlags_ThisCall = 0x3000, BfMethodFlags_Mutating = 0x4000, - BfMethodFlags_Constructor = 0x8000 + BfMethodFlags_Constructor = 0x8000, + BfMethodFlags_AppendBit0 = 0x10000, + BfMethodFlags_AppendBit1 = 0x20000, + BfMethodFlags_CheckedBit0 = 0x40000, + BfMethodFlags_CheckedBit1 = 0x80000, }; enum BfComptimeMethodFlags @@ -671,6 +684,8 @@ public: { if (fieldDeclaration->mNameNode != NULL) return fieldDeclaration->mNameNode; + if (fieldDeclaration->mTypeRef != NULL) + return fieldDeclaration->mTypeRef; } if (auto paramDeclaration = BfNodeDynCast(mFieldDeclaration)) @@ -836,6 +851,7 @@ enum BfCallingConvention : uint8 #define BF_METHODNAME_FIND_TLS_MEMBERS "GCFindTLSMembers" #define BF_METHODNAME_DYNAMICCAST "DynamicCastToTypeId" #define BF_METHODNAME_DYNAMICCAST_INTERFACE "DynamicCastToInterface" +#define BF_METHODNAME_DYNAMICCAST_SIGNATURE "DynamicCastToSignature" #define BF_METHODNAME_CALCAPPEND "this$calcAppend" #define BF_METHODNAME_ENUM_HASFLAG "HasFlag" #define BF_METHODNAME_ENUM_GETUNDERLYING "get__Underlying" @@ -878,6 +894,15 @@ enum BfComptimeFlags : int8 BfComptimeFlag_ConstEval = 4 }; +enum BfAllowAppendKind : int8 +{ + BfAllowAppendKind_No, + BfAllowAppendKind_Yes, + BfAllowAppendKind_ZeroGap, + + BfAllowAppendKind_Infer +}; + class BfMethodDef : public BfMemberDef { public: @@ -906,7 +931,7 @@ public: bool mCodeChanged; bool mWantsBody; bool mCLink; - bool mHasAppend; + BfAllowAppendKind mAppendKind; bool mAlwaysInline; bool mIsNoReturn; bool mIsMutating; @@ -960,7 +985,7 @@ public: mImportKind = BfImportKind_None; mMethodType = BfMethodType_Normal; mCallingConvention = BfCallingConvention_Unspecified; - mHasAppend = false; + mAppendKind = BfAllowAppendKind_No; mAlwaysInline = false; mParamNameMap = NULL; mNextWithSameName = NULL; @@ -970,6 +995,7 @@ public: static BfImportKind GetImportKindFromPath(const StringImpl& filePath); bool HasNoThisSplat() { return mIsMutating || mIsNoSplat; } + bool HasAppend() { return mAppendKind != BfAllowAppendKind_No; } void Reset(); void FreeMembers(); BfMethodDeclaration* GetMethodDeclaration(); @@ -1134,7 +1160,7 @@ public: BfShow mShow; bool mIsAlwaysInclude; bool mIsNoDiscard; - bool mIsPartial; + bool mIsPartial; bool mIsExplicitPartial; bool mPartialUsed; bool mIsCombinedPartial; @@ -1254,7 +1280,7 @@ public: bool HasCustomAttributes(); }; -struct BfTypeDefMapFuncs : public MultiHashSetFuncs +struct BfTypeDefMapFuncs : public AllocatorCLib { int GetHash(BfTypeDef* typeDef) { @@ -1394,6 +1420,15 @@ public: DeleteStage_AwaitingRefs, }; + enum DependencyKind + { + DependencyKind_None, + DependencyKind_Dependency, + DependencyKind_Identity, + DependencyKind_Dependent_Exclusive, + DependencyKind_Dependent_Shared + }; + public: BfSystem* mSystem; String mName; @@ -1414,6 +1449,8 @@ public: HashSet mUsedModules; HashSet mReferencedTypeData; + HashSet mDependencySet; + Dictionary mDependencyKindDict; Val128 mBuildConfigHash; Val128 mVDataConfigHash; @@ -1424,9 +1461,12 @@ public: BfProject(); ~BfProject(); + void ClearCache(); bool ContainsReference(BfProject* refProject); bool ReferencesOrReferencedBy(BfProject* refProject); bool IsTestProject(); + bool HasDependency(BfProject* project); + DependencyKind GetDependencyKind(BfProject* project); }; //CDH TODO move these out to separate header if list gets big/unwieldy @@ -1555,6 +1595,17 @@ public: class BfPassInstance { +public: + struct StateInfo + { + int mOutStreamSize; + int mErrorsSize; + int mWarningCount; + int mDeferredErrorCount; + int mFailedIdx; + int mWarnIdx; + }; + public: const int sMaxDisplayErrors = 100; const int sMaxErrors = 1000; @@ -1597,6 +1648,9 @@ public: ~BfPassInstance(); + StateInfo GetState(); + void RestoreState(StateInfo stateInfo); + void ClearErrors(); bool HasFailed(); bool HasMessages(); @@ -1783,6 +1837,8 @@ public: Array mAtomGraveyard; uint32 mAtomUpdateIdx; int32 mTypeMapVersion; // Increment when we add any new types or namespaces + int32 mAnonymousAtomCount; + int32 mCurUniqueId; OwnedVector mMethodGraveyard; OwnedVector mFieldGraveyard; @@ -1830,7 +1886,7 @@ public: BfSystem(); ~BfSystem(); - BfAtom* GetAtom(const StringImpl& string); + BfAtom* GetAtom(const StringImpl& string, BfAtom::Kind kind = BfAtom::Kind_Normal); BfAtom* FindAtom(const StringImpl& string); // Doesn't create a ref BfAtom* FindAtom(const StringView& string); // Doesn't create a ref void ReleaseAtom(BfAtom* atom); @@ -1850,6 +1906,7 @@ public: BfParser* CreateParser(BfProject* bfProject); BfCompiler* CreateCompiler(bool isResolveOnly); BfProject* GetProject(const StringImpl& projName); + uint64 GetTypeDeclListHash(); BfTypeReference* GetTypeRefElement(BfTypeReference* typeRef); BfTypeDef* FilterDeletedTypeDef(BfTypeDef* typeDef); diff --git a/IDEHelper/Compiler/CeDebugger.cpp b/IDEHelper/Compiler/CeDebugger.cpp index ee674066..696cea45 100644 --- a/IDEHelper/Compiler/CeDebugger.cpp +++ b/IDEHelper/Compiler/CeDebugger.cpp @@ -3559,7 +3559,7 @@ String CeDebugger::TypedValueToString(const BfTypedValue& origTypedValue, const return ""; String reflectedTypeName; - if (displayType->IsInstanceOf(mCompiler->mTypeTypeDef)) + if ((displayType->IsInstanceOf(mCompiler->mTypeTypeDef)) && (data != NULL)) { auto typeInst = displayType->ToTypeInstance(); auto typeIdField = typeInst->mTypeDef->GetFieldByName("mTypeId"); diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index e4115e93..40520c94 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -159,6 +159,7 @@ static CeOpInfo gOpInfo[] = {"CeOp_GetSP", CEOI_FrameRef}, {"CeOp_SetSP", CEOI_None, CEOI_FrameRef}, {"GetStaticField", CEOI_FrameRef, CEOI_IMM32}, + {"GetStaticField_Initializer", CEOI_FrameRef, CEOI_IMM32, CEOI_FrameRef}, {"GetMethod", CEOI_FrameRef, CEOI_IMM32}, {"GetMethod_Inner", CEOI_FrameRef, CEOI_IMM32}, {"GetMethod_Virt", CEOI_FrameRef, CEOI_FrameRef, CEOI_IMM32}, @@ -1565,48 +1566,73 @@ CeOperand CeBuilder::GetOperand(BeValue* value, bool allowAlloca, bool allowImme return result; } - BfFieldInstance** fieldInstancePtr = NULL; - if (mStaticFieldInstanceMap.TryGetValue(globalVar->mName, &fieldInstancePtr)) + CeOperand initializerValue; + + if (globalVar->mIsConstant) { + if (globalVar->mInitializer != NULL) + { + auto result = GetOperand(globalVar->mInitializer, false, true); + if (result.mKind == CeOperandKind_ConstStructTableIdx) + { + auto& constTableEntry = mCeFunction->mConstStructTable[result.mStructTableIdx]; + auto ptrType = mCeMachine->GetBeContext()->GetPointerTo(globalVar->mType); + auto dataResult = FrameAlloc(ptrType); + Emit(CeOp_ConstDataRef); + EmitFrameOffset(dataResult); + Emit((int32)result.mCallTableIdx); + return dataResult; + } + return result; + } + } + else + { + initializerValue = GetOperand(globalVar->mInitializer); + + BfFieldInstance** fieldInstancePtr = NULL; + mStaticFieldInstanceMap.TryGetValue(globalVar->mName, &fieldInstancePtr); + int* staticFieldTableIdxPtr = NULL; if (mStaticFieldMap.TryAdd(globalVar, NULL, &staticFieldTableIdxPtr)) { CeStaticFieldEntry staticFieldEntry; - staticFieldEntry.mTypeId = (*fieldInstancePtr)->mOwner->mTypeId; + if (fieldInstancePtr != NULL) + staticFieldEntry.mTypeId = (*fieldInstancePtr)->mOwner->mTypeId; + staticFieldEntry.mName = globalVar->mName; + if (globalVar->mLinkageType == Beefy::BfIRLinkageType_Internal) + { + staticFieldEntry.mName += "@"; + staticFieldEntry.mName += mCeFunction->mMethodInstance->GetOwner()->mModule->mModuleName; + } + staticFieldEntry.mSize = globalVar->mType->mSize; *staticFieldTableIdxPtr = (int)mCeFunction->mStaticFieldTable.size(); - mCeFunction->mStaticFieldTable.Add(staticFieldEntry); + mCeFunction->mStaticFieldTable.Add(staticFieldEntry); } auto result = FrameAlloc(mCeMachine->GetBeContext()->GetPointerTo(globalVar->mType)); - Emit(CeOp_GetStaticField); - EmitFrameOffset(result); - Emit((int32)*staticFieldTableIdxPtr); - - return result; - } - - if (globalVar->mInitializer != NULL) - { - auto result = GetOperand(globalVar->mInitializer, false, true); - if (result.mKind == CeOperandKind_ConstStructTableIdx) + if (initializerValue) { - auto& constTableEntry = mCeFunction->mConstStructTable[result.mStructTableIdx]; - auto ptrType = mCeMachine->GetBeContext()->GetPointerTo(globalVar->mType); - auto dataResult = FrameAlloc(ptrType); - Emit(CeOp_ConstDataRef); - EmitFrameOffset(dataResult); - Emit((int32)result.mCallTableIdx); - return dataResult; + Emit(CeOp_GetStaticField_Initializer); + EmitFrameOffset(result); + Emit((int32)*staticFieldTableIdxPtr); + EmitFrameOffset(initializerValue); + } + else + { + Emit(CeOp_GetStaticField); + EmitFrameOffset(result); + Emit((int32)*staticFieldTableIdxPtr); } return result; } - errorKind = CeErrorKind_GlobalVariable; - errorType = mCeMachine->GetBeContext()->GetPointerTo(globalVar->mType); + errorKind = CeErrorKind_GlobalVariable; + errorType = mCeMachine->GetBeContext()->GetPointerTo(globalVar->mType); } break; case BeCastConstant::TypeId: @@ -1736,7 +1762,7 @@ CeOperand CeBuilder::GetOperand(BeValue* value, bool allowAlloca, bool allowImme { CeConstStructData constStructData; constStructData.mQueueFixups = true; - errorKind = mCeMachine->WriteConstant(constStructData, structConstant, NULL); + errorKind = mCeMachine->WriteConstant(constStructData, structConstant, NULL, this); if (errorKind == CeErrorKind_None) { *constDataPtr = (int)mCeFunction->mConstStructTable.size(); @@ -1885,6 +1911,17 @@ CeOperand CeBuilder::GetOperand(BeValue* value, bool allowAlloca, bool allowImme // return GetOperand(callInst->mInlineResult); } break; + case BeTypeOfConstant::TypeId: + { + auto beTypeOf = (BeTypeOfConstant*)value; + auto ptrType = mCeMachine->GetBeContext()->GetVoidPtrType(); + CeOperand result = FrameAlloc(ptrType); + Emit(CeOp_GetReflectType); + EmitFrameOffset(result); + Emit((int32)beTypeOf->mBfTypeId); + return result; + } + break; } CeOperand* operandPtr = NULL; @@ -2086,7 +2123,12 @@ void CeBuilder::Build() mCeFunction->mFailed = true; return; } - mBeFunction = (BeFunction*)irCodeGen->GetBeValue(dupMethodInstance.mIRFunction.mId); + mBeFunction = (BeFunction*)irCodeGen->TryGetBeValue(dupMethodInstance.mIRFunction.mId); + if (mBeFunction == NULL) + { + mCeFunction->mFailed = true; + return; + } mIntPtrType = irCodeGen->mBeContext->GetPrimitiveType((mPtrSize == 4) ? BeTypeCode_Int32 : BeTypeCode_Int64); @@ -3587,6 +3629,7 @@ CeContext::CeContext() mReflectTypeIdOffset = -1; mExecuteId = -1; mStackSize = -1; + mRecursiveDepth = -1; mCurCallSource = NULL; mHeap = new ContiguousHeap(); @@ -3598,11 +3641,13 @@ CeContext::CeContext() mCallerActiveTypeDef = NULL; mCurExpectingType = NULL; mCurEmitContext = NULL; + mTypeDeclState = NULL; } CeContext::~CeContext() { delete mHeap; + delete mTypeDeclState; BF_ASSERT(mInternalDataMap.IsEmpty()); } @@ -3814,6 +3859,11 @@ void CeContext::AddFileRebuild(const StringImpl& path) } } +void CeContext::AddTypeSigRebuild(BfType* type) +{ + mCurModule->AddDependency(type, mCurModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_TypeSignature); +} + uint8* CeContext::CeMalloc(int size) { #ifdef CE_ENABLE_HEAP @@ -3828,6 +3878,13 @@ uint8* CeContext::CeMalloc(int size) #endif } +uint8* CeContext::CeMallocZero(int size) +{ + uint8* ptr = CeMalloc(size); + memset(ptr, 0, size); + return ptr; +} + bool CeContext::CeFree(addr_ce addr) { #ifdef CE_ENABLE_HEAP @@ -3870,14 +3927,79 @@ addr_ce CeContext::GetConstantData(BeConstant* constant) } CeConstStructData structData; - auto result = mCeMachine->WriteConstant(structData, writeConstant, this); + auto result = mCeMachine->WriteConstant(structData, writeConstant, this, NULL); BF_ASSERT(result == CeErrorKind_None); - uint8* ptr = CeMalloc(structData.mData.mSize); + uint8* ptr = CeMallocZero(structData.mData.mSize); memcpy(ptr, structData.mData.mVals, structData.mData.mSize); return (addr_ce)(ptr - mMemory.mVals); } +addr_ce CeContext::GetReflectTypeDecl(int typeId) +{ + if (mTypeDeclState == NULL) + mTypeDeclState = new CeTypeDeclState(); + if (mTypeDeclState->mReflectDeclMap.IsEmpty()) + { + CeRebuildKey rebuildKey; + rebuildKey.mKind = CeRebuildKey::Kind_TypeDeclListHash; + CeRebuildValue rebuildValue; + rebuildValue.mInt = mCeMachine->mCompiler->mSystem->GetTypeDeclListHash(); + AddRebuild(rebuildKey, rebuildValue); + } + + addr_ce* addrPtr = NULL; + if (!mTypeDeclState->mReflectDeclMap.TryAdd(typeId, NULL, &addrPtr)) + return *addrPtr; + + auto ceModule = mCeMachine->mCeModule; + SetAndRestoreValue ignoreWrites(ceModule->mBfIRBuilder->mIgnoreWrites, false); + + if (ceModule->mContext->mBfTypeType == NULL) + ceModule->mContext->ReflectInit(); + + if ((uintptr)typeId >= (uintptr)mCeMachine->mCeModule->mContext->mTypes.mSize) + return 0; + auto bfType = mCeMachine->mCeModule->mContext->mTypes[typeId]; + if (bfType == NULL) + return 0; + + if (bfType->mDefineState < BfTypeDefineState_HasCustomAttributes) + ceModule->PopulateType(bfType, BfPopulateType_CustomAttributes); + + BfProject* curProject = NULL; + auto activeTypeDef = mCurModule->GetActiveTypeDef(); + if (activeTypeDef != NULL) + curProject = activeTypeDef->mProject; + + if (curProject == NULL) + return 0; + + BfCreateTypeDataContext createTypeDataCtx; + auto irData = ceModule->CreateTypeDeclData(bfType, curProject); + + BeValue* beValue = NULL; + if (auto constant = mCeMachine->mCeModule->mBfIRBuilder->GetConstant(irData)) + { + if (constant->mConstType == BfConstType_BitCast) + { + auto bitcast = (BfConstantBitCast*)constant; + constant = mCeMachine->mCeModule->mBfIRBuilder->GetConstantById(bitcast->mTarget); + } + if (constant->mConstType == BfConstType_GlobalVar) + { + auto globalVar = (BfGlobalVar*)constant; + beValue = mCeMachine->mCeModule->mBfIRBuilder->mBeIRCodeGen->GetBeValue(globalVar->mStreamId); + } + } + + if (auto constant = BeValueDynCast(beValue)) + *addrPtr = GetConstantData(constant); + + // We need to 'get' again because we might have resized + return *addrPtr; +} + addr_ce CeContext::GetReflectType(int typeId) { addr_ce* addrPtr = NULL; @@ -3924,7 +4046,7 @@ addr_ce CeContext::GetReflectType(int typeId) return *addrPtr; } -addr_ce CeContext::GetReflectType(const String& typeName) +addr_ce CeContext::GetReflectType(const String& typeName, bool useDeclaration) { if (mCeMachine->mTempParser == NULL) { @@ -3964,7 +4086,7 @@ addr_ce CeContext::GetReflectType(const String& typeName) if (type == NULL) return 0; - return GetReflectType(type->mTypeId); + return useDeclaration ? GetReflectTypeDecl(type->mTypeId) : GetReflectType(type->mTypeId); } int CeContext::GetTypeIdFromType(addr_ce typeAddr) @@ -4039,10 +4161,8 @@ addr_ce CeContext::GetString(int stringId) int allocSize = stringTypeInst->mInstSize + (int)str.length() + 1; int charsOffset = stringTypeInst->mInstSize; - uint8* mem = CeMalloc(allocSize); - - memset(mem, 0, allocSize); - + uint8* mem = CeMallocZero(allocSize); + auto lenByteCount = stringTypeInst->mFieldInstances[0].mResolvedType->mSize; auto lenOffset = stringTypeInst->mFieldInstances[0].mDataOffset; auto allocSizeOffset = stringTypeInst->mFieldInstances[1].mDataOffset; @@ -4290,7 +4410,7 @@ bool CeContext::WriteConstant(BfModule* module, addr_ce addr, BfConstant* consta if (type->IsPointer()) { auto elementType = type->GetUnderlyingType(); - auto toPtr = CeMalloc(elementType->mSize); + auto toPtr = CeMallocZero(elementType->mSize); addr_ce toAddr = (addr_ce)(toPtr - mMemory.mVals); if (ptrSize == 4) CE_GETC(int32) = (int32)toAddr; @@ -4372,10 +4492,11 @@ bool CeContext::WriteConstant(BfModule* module, addr_ce addr, BfConstant* consta auto typeInst = type->ToTypeInstance(); int idx = 0; - if (typeInst->mBaseType != NULL) + auto baseType = typeInst->GetBaseType(true); + if (baseType != NULL) { auto baseConstant = module->mBfIRBuilder->GetConstant(aggConstant->mValues[0]); - if (!WriteConstant(module, addr, baseConstant, typeInst->mBaseType)) + if (!WriteConstant(module, addr, baseConstant, baseType)) return false; } @@ -4743,6 +4864,12 @@ BfIRValue CeContext::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType auto allocSizeOffset = stringTypeInst->mFieldInstances[1].mDataOffset; auto ptrOffset = stringTypeInst->mFieldInstances[2].mDataOffset; + if (addr + ptrOffset + 4 > memSize) + { + // Memory error + return irBuilder->CreateConstNull(irBuilder->MapType(typeInst)); + } + int32 lenVal = *(int32*)(instData + lenOffset); char* charPtr = NULL; @@ -4908,9 +5035,10 @@ BfIRValue CeContext::CreateConstant(BfModule* module, uint8* ptr, BfType* bfType return irBuilder->CreateConstNull(irBuilder->MapType(typeInst)); } - if (typeInst->mBaseType != NULL) + auto baseType = typeInst->GetBaseType(true); + if (baseType != NULL) { - auto result = CreateConstant(module, instData, typeInst->mBaseType); + auto result = CreateConstant(module, instData, baseType); if (!result) return BfIRValue(); fieldVals.Add(result); @@ -5003,8 +5131,8 @@ BfIRValue CeContext::CreateAttribute(BfAstNode* targetSrc, BfModule* module, BfI SetAndRestoreValue prevIgnoreWrites(module->mBfIRBuilder->mIgnoreWrites, true); module->mContext->mUnreifiedModule->PopulateType(customAttribute->mType); - if (ceAttrAddr == 0) - ceAttrAddr = CeMalloc(customAttribute->mType->mSize) - mMemory.mVals; + if (ceAttrAddr == 0) + ceAttrAddr = CeMallocZero(customAttribute->mType->mSize) - mMemory.mVals; BfIRValue ceAttrVal = module->mBfIRBuilder->CreateConstAggCE(module->mBfIRBuilder->MapType(customAttribute->mType, BfIRPopulateType_Identity), ceAttrAddr); BfTypedValue ceAttrTypedValue(ceAttrVal, customAttribute->mType); @@ -5122,7 +5250,7 @@ BfTypedValue CeContext::Call(CeCallSource callSource, BfModule* module, BfMethod SetAndRestoreValue moduleCurMethodInstance(module->mCurMethodInstance, methodInstance); SetAndRestoreValue moduleCurTypeInstance(module->mCurTypeInstance, methodInstance->GetOwner()); - SetAndRestoreValue prevCurExecuteId(mCurModule->mCompiler->mCurCEExecuteId, mCeMachine->mExecuteId); + SetAndRestoreValue prevCurExecuteId(mCurModule->mCompiler->mCurCEExecuteId, mCeMachine->mExecuteId); // Reentrancy may occur as methods need defining //SetAndRestoreValue prevMethodStateInConstEval(module->mCurMethodState, NULL); @@ -6036,6 +6164,164 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* OutputDebugStrF("Debug Val: %lld %llX\n", intVal, intVal); } } + else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectTypeDeclById) + { + int32 typeId = *(int32*)((uint8*)stackPtr + ceModule->mSystem->mPtrSize); + auto reflectType = GetReflectTypeDecl(typeId); + _FixVariables(); + CeSetAddrVal(stackPtr + 0, reflectType, ptrSize); + } + else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectTypeDeclByName) + { + addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + ptrSize); + String typeName; + if (!GetStringFromStringView(strViewPtr, typeName)) + { + _Fail("Invalid StringView"); + return false; + } + auto reflectType = GetReflectType(typeName, true); + _FixVariables(); + CeSetAddrVal(stackPtr + 0, reflectType, ptrSize); + } + else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectNextTypeDecl) + { + int32 typeId = *(int32*)((uint8*)stackPtr + ceModule->mSystem->mPtrSize); + + addr_ce reflectType = 0; + + auto context = mCeMachine->mCeModule->mContext; + if (mTypeDeclState == NULL) + mTypeDeclState = new CeTypeDeclState(); + + while (true) + { + typeId++; + if (typeId >= mCeMachine->mCeModule->mContext->mTypes.mSize) + { + int foundTypeCount = 0; + if (!mTypeDeclState->mCheckedAllTypeDefs) + { + mTypeDeclState->mCheckedAllTypeDefs = true; + for (auto typeDef : ceModule->mSystem->mTypeDefs) + { + if ((typeDef->mIsPartial) && (!typeDef->mIsCombinedPartial)) + continue; + if (typeDef->mTypeCode == BfTypeCode_TypeAlias) + continue; + if (typeDef->mTypeDeclaration == NULL) + continue; + if (mTypeDeclState->mIteratedTypeDefs.Contains(typeDef)) + continue; + + int lastTypeId = mCeMachine->mCompiler->mCurTypeId; + auto resolvedType = mCeMachine->mCeModule->ResolveTypeDef(typeDef, BfPopulateType_Identity); + if ((resolvedType != NULL) && (resolvedType->IsTypeInstance())) + { + if (resolvedType->mDefineState == BfTypeDefineState_Undefined) + foundTypeCount++; + } + } + } + + if (foundTypeCount > 0) + typeId = 0; + else + break; + } + + auto bfType = mCeMachine->mCeModule->mContext->mTypes[typeId]; + if (bfType != NULL) + { + if (bfType->IsOnDemand()) + continue; + if (bfType->IsBoxed()) + continue; + + auto bfTypeInst = bfType->ToTypeInstance(); + if (bfTypeInst == NULL) + continue; + + auto useTypeDef = bfTypeInst->mTypeDef; + useTypeDef = useTypeDef->GetLatest(); + if (!mTypeDeclState->mCheckedAllTypeDefs) + { + mTypeDeclState->mIteratedTypeDefs.Add(useTypeDef); + } + else + { + if (mTypeDeclState->mIteratedTypeDefs.Contains(useTypeDef)) + continue; + } + + if (bfTypeInst->IsGenericTypeInstance()) + { + if (!bfTypeInst->IsUnspecializedType()) + continue; + if (bfTypeInst->IsUnspecializedTypeVariation()) + continue; + } + + reflectType = GetReflectTypeDecl(typeId); + if (reflectType != 0) + break; + } + } + + _FixVariables(); + CeSetAddrVal(stackPtr + 0, reflectType, ptrSize); + } + else if (checkFunction->mFunctionKind == CeFunctionKind_GetBaseType) + { + int32 typeId = *(int32*)((uint8*)stackPtr + 4); + + int baseTypeId = 0; + BfType* type = GetBfType(typeId); + if (type != NULL) + { + AddTypeSigRebuild(type); + if (auto typeInst = type->ToTypeInstance()) + { + if (type->mDefineState < BfTypeDefineState_HasCustomAttributes) + ceModule->PopulateType(type, BfPopulateType_CustomAttributes); + if (typeInst->mBaseType != NULL) + baseTypeId = typeInst->mBaseType->mTypeId; + } + } + + *(addr_ce*)(stackPtr + 0) = baseTypeId; + } + else if (checkFunction->mFunctionKind == CeFunctionKind_HasDeclaredMember) + { + int32 typeId = *(int32*)((uint8*)stackPtr + 1); + int32 memberKind = *(int32*)((uint8*)stackPtr + 1 + 4); + addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + 1 + 4 + 4); + + bool hasMember; + + String typeName; + if (!GetStringFromStringView(strViewPtr, typeName)) + { + _Fail("Invalid StringView"); + return false; + } + + BfType* type = GetBfType(typeId); + if ((type != NULL) && (type->IsTypeInstance())) + { + AddTypeSigRebuild(type); + auto typeInst = type->ToTypeInstance(); + typeInst->mTypeDef->PopulateMemberSets(); + if (memberKind == 0) // Field + hasMember = typeInst->mTypeDef->mFieldSet.ContainsWith((StringImpl&)typeName); + else if (memberKind == 1) // Method + hasMember = typeInst->mTypeDef->mMethodSet.ContainsWith((StringImpl&)typeName); + else if (memberKind == 2) // Property + hasMember = typeInst->mTypeDef->mPropertySet.ContainsWith((StringImpl&)typeName); + } + + *(addr_ce*)(stackPtr + 0) = hasMember; + } else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectType) { addr_ce objAddr = *(addr_ce*)((uint8*)stackPtr + ceModule->mSystem->mPtrSize); @@ -6053,6 +6339,25 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* _FixVariables(); CeSetAddrVal(stackPtr + 0, reflectType, ptrSize); } + else if (checkFunction->mFunctionKind == CeFunctionKind_GetWrappedType) + { + int32 typeId = *(int32*)((uint8*)stackPtr + ceModule->mSystem->mPtrSize); + + BfType* type = GetBfType(typeId); + bool success = false; + if (type == NULL) + { + _Fail("Invalid type"); + return false; + } + + addr_ce reflectType = NULL; + type = ceModule->GetWrappedStructType(type); + if (type != NULL) + reflectType = GetReflectType(type->mTypeId); + _FixVariables(); + CeSetAddrVal(stackPtr + 0, reflectType, ptrSize); + } else if (checkFunction->mFunctionKind == CeFunctionKind_GetReflectTypeByName) { addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr + ptrSize); @@ -6062,7 +6367,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* _Fail("Invalid StringView"); return false; } - auto reflectType = GetReflectType(typeName); + auto reflectType = GetReflectType(typeName, false); _FixVariables(); CeSetAddrVal(stackPtr + 0, reflectType, ptrSize); } @@ -6089,9 +6394,61 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* SetAndRestoreValue prevMethodInstance(mCeMachine->mCeModule->mCurMethodInstance, mCallerMethodInstance); SetAndRestoreValue prevTypeInstance(mCeMachine->mCeModule->mCurTypeInstance, mCallerTypeInstance); - CeSetAddrVal(stackPtr + 0, GetString(mCeMachine->mCeModule->TypeToString(type)), ptrSize); + + bool simpleName = false; + if ((type->IsUnspecializedType()) && (!type->IsUnspecializedTypeVariation()) && (!type->IsGenericParam())) + simpleName = true; + String typeName; + if (simpleName) + mCeMachine->mCeModule->DoTypeToString(typeName, type, BfTypeNameFlags_None); + else + typeName = mCeMachine->mCeModule->TypeToString(type); + + CeSetAddrVal(stackPtr + 0, GetString(typeName), ptrSize); _FixVariables(); } + else if (checkFunction->mFunctionKind == CeFunctionKind_TypeName_ToString) + { + int32 typeId = *(int32*)((uint8*)stackPtr + ptrSize); + + BfType* type = GetBfType(typeId); + bool success = false; + if (type == NULL) + { + _Fail("Invalid type"); + return false; + } + + String str; + if (auto typeInst = type->ToTypeInstance()) + str = typeInst->mTypeDef->mName->ToString(); + + SetAndRestoreValue prevMethodInstance(mCeMachine->mCeModule->mCurMethodInstance, mCallerMethodInstance); + SetAndRestoreValue prevTypeInstance(mCeMachine->mCeModule->mCurTypeInstance, mCallerTypeInstance); + CeSetAddrVal(stackPtr + 0, GetString(str), ptrSize); + _FixVariables(); + } + else if (checkFunction->mFunctionKind == CeFunctionKind_Namespace_ToString) + { + int32 typeId = *(int32*)((uint8*)stackPtr + ptrSize); + + BfType* type = GetBfType(typeId); + bool success = false; + if (type == NULL) + { + _Fail("Invalid type"); + return false; + } + + String str; + if (auto typeInst = type->ToTypeInstance()) + typeInst->mTypeDef->mNamespace.ToString(str); + + SetAndRestoreValue prevMethodInstance(mCeMachine->mCeModule->mCurMethodInstance, mCallerMethodInstance); + SetAndRestoreValue prevTypeInstance(mCeMachine->mCeModule->mCurTypeInstance, mCallerTypeInstance); + CeSetAddrVal(stackPtr + 0, GetString(str), ptrSize); + _FixVariables(); + } else if (checkFunction->mFunctionKind == CeFunctionKind_Type_GetCustomAttribute) { int32 typeId = *(int32*)((uint8*)stackPtr + 1); @@ -6102,6 +6459,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* bool success = false; if (type != NULL) { + AddTypeSigRebuild(type); auto typeInst = type->ToTypeInstance(); if (typeInst != NULL) success = GetCustomAttribute(mCurModule, typeInst->mConstHolder, typeInst->mCustomAttributes, attributeIdx, resultPtr); @@ -6121,6 +6479,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* bool success = false; if (type != NULL) { + AddTypeSigRebuild(type); auto typeInst = type->ToTypeInstance(); if (typeInst != NULL) { @@ -6154,6 +6513,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* _Fail("Invalid method instance"); return false; } + AddTypeSigRebuild(methodInstance->GetOwner()); bool success = GetCustomAttribute(mCurModule, methodInstance->GetOwner()->mConstHolder, methodInstance->GetCustomAttributes(), attributeIdx, resultPtr); _FixVariables(); *(addr_ce*)(stackPtr + 0) = success; @@ -6167,6 +6527,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* addr_ce reflectType = 0; if (type != NULL) { + AddTypeSigRebuild(type); auto typeInst = type->ToTypeInstance(); if (typeInst != NULL) { @@ -6189,6 +6550,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* addr_ce reflectType = 0; if (type != NULL) { + AddTypeSigRebuild(type); auto typeInst = type->ToTypeInstance(); if (typeInst != NULL) { @@ -6223,6 +6585,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* _Fail("Invalid method instance"); return false; } + AddTypeSigRebuild(methodInstance->GetOwner()); auto attrType = GetCustomAttributeType(methodInstance->GetCustomAttributes(), attributeIdx); if (attrType != NULL) CeSetAddrVal(stackPtr + 0, GetReflectType(attrType->mTypeId), ptrSize); @@ -6294,10 +6657,11 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* // int32 mReturnType // int32 mParamCount // int32 mGenericArgCount - // int16 mFlags + // int32 mFlags + // int8 ComptimeMethodFlags // int32 mMethodIdx - int64 methodHandle = *(int64*)((uint8*)stackPtr + 4+4+4+2+1+4); + int64 methodHandle = *(int64*)((uint8*)stackPtr + 4+4+4+4+1+4); auto methodInstance = mCeMachine->GetMethodInstance(methodHandle); if (methodInstance == NULL) @@ -6306,6 +6670,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* return false; } + AddTypeSigRebuild(methodInstance->GetOwner()); int genericArgCount = 0; if (methodInstance->mMethodInfoEx != NULL) genericArgCount = methodInstance->mMethodInfoEx->mMethodGenericArguments.mSize; @@ -6313,9 +6678,9 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* *(int32*)(stackPtr + 0) = methodInstance->mReturnType->mTypeId; *(int32*)(stackPtr + 4) = methodInstance->GetParamCount(); *(int32*)(stackPtr + 4+4) = genericArgCount; - *(int16*)(stackPtr + 4+4+4) = methodInstance->GetMethodFlags(); - *(int32*)(stackPtr + 4+4+4+2) = methodInstance->GetComptimeMethodFlags(); - *(int32*)(stackPtr + 4+4+4+2+1) = methodInstance->mMethodDef->mIdx; + *(int32*)(stackPtr + 4+4+4) = methodInstance->GetMethodFlags(); + *(int32*)(stackPtr + 4+4+4+4) = methodInstance->GetComptimeMethodFlags(); + *(int32*)(stackPtr + 4+4+4+4+1) = methodInstance->mMethodDef->mIdx; } else if (checkFunction->mFunctionKind == CeFunctionKind_Method_GetParamInfo) { @@ -6566,14 +6931,7 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* { SetAndRestoreValue prevMethodInstance(mCurModule->mCurMethodInstance, mCallerMethodInstance); SetAndRestoreValue prevTypeInstance(mCurModule->mCurTypeInstance, mCallerTypeInstance); - -// int32 strInstAddr = *(int32*)((uint8*)stackPtr + 0); -// String emitStr; -// if (!GetStringFromAddr(strInstAddr, emitStr)) -// { -// _Fail("Invalid String"); -// return false; -// } + SetAndRestoreValue emitIgnoreWrites(ceModule->mBfIRBuilder->mIgnoreWrites, ignoreWrites.mPrevVal); addr_ce strViewPtr = *(addr_ce*)((uint8*)stackPtr); String emitStr; @@ -6585,6 +6943,19 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* mCurModule->CEMixin(mCurCallSource->mRefNode, emitStr); } + else if (checkFunction->mFunctionKind == CeFunctionKind_GetStringById) + { + int32 stringId = *(int32*)((uint8*)stackPtr + ptrSize); + + String string = ""; + BfStringPoolEntry* valuePtr = NULL; + mCurModule->mContext->mStringObjectIdMap.TryGetValue(stringId, &valuePtr); + if (valuePtr != NULL) + string = valuePtr->mString; + + CeSetAddrVal(stackPtr + 0, GetString(string), ptrSize); + _FixVariables(); + } else if (checkFunction->mFunctionKind == CeFunctionKind_Sleep) { int32 sleepMS = *(int32*)((uint8*)stackPtr); @@ -7516,10 +7887,15 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* SpecialCheck: if (*fastFinishPtr) { + BfTypeInstance* rebuildType = NULL; if ((mCurModule != NULL) && (mCurModule->mCurTypeInstance != NULL)) + rebuildType = mCurModule->mCurTypeInstance; + if ((mCurEmitContext != NULL) && (mCurEmitContext->mType != NULL)) + rebuildType = mCurEmitContext->mType->ToTypeInstance(); + if (rebuildType != NULL) { - mCurModule->mCurTypeInstance->mRebuildFlags = (BfTypeRebuildFlags)(mCurModule->mCurTypeInstance->mRebuildFlags | BfTypeRebuildFlag_ConstEvalCancelled); - mCurModule->DeferRebuildType(mCurModule->mCurTypeInstance); + rebuildType->mRebuildFlags = (BfTypeRebuildFlags)(rebuildType->mRebuildFlags | BfTypeRebuildFlag_ConstEvalCancelled); + mCurModule->DeferRebuildType(rebuildType); } if (*cancelingPtr) { @@ -7736,16 +8112,28 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* { CE_CHECKADDR(valueAddr, sizeof(int32)); - auto ifaceType = GetBfType(ifaceId); + auto wantType = GetBfType(ifaceId); int32 objTypeId = *(int32*)(memStart + valueAddr); auto valueType = GetBfType(objTypeId); - if ((ifaceType == NULL) || (valueType == NULL)) + if ((wantType == NULL) || (valueType == NULL)) { _Fail("Invalid type in CeOp_DynamicCastCheck"); return false; } - if (ceModule->TypeIsSubTypeOf(valueType->ToTypeInstance(), ifaceType->ToTypeInstance(), false)) + bool matches = false; + if (ceModule->TypeIsSubTypeOf(valueType->ToTypeInstance(), wantType->ToTypeInstance(), false)) + { + matches = true; + } + else if ((valueType->IsDelegate()) && (wantType->IsDelegate())) + { + int valueSignatureId = ceModule->GetDelegateSignatureId(valueType->ToTypeInstance()); + int checkSignatureId = ceModule->GetDelegateSignatureId(wantType->ToTypeInstance()); + matches = valueSignatureId == checkSignatureId; + } + + if (matches) CeSetAddrVal(&result, valueAddr, ptrSize); else CeSetAddrVal(&result, 0, ptrSize); @@ -8057,16 +8445,22 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* } break; case CeOp_GetStaticField: + case CeOp_GetStaticField_Initializer: { auto frameOfs = CE_GETINST(int32); int32 tableIdx = CE_GETINST(int32); + int32 initializerFrameOfs = 0; + if (op == CeOp_GetStaticField_Initializer) + { + initializerFrameOfs = CE_GETINST(int32);; + } CeFunction* ctorCallFunction = NULL; auto& ceStaticFieldEntry = ceFunction->mStaticFieldTable[tableIdx]; if (ceStaticFieldEntry.mBindExecuteId != mExecuteId) { - if ((mStaticCtorExecSet.TryAdd(ceStaticFieldEntry.mTypeId, NULL)) && (!ceStaticFieldEntry.mName.StartsWith("#"))) + if ((ceStaticFieldEntry.mTypeId > 0) && (mStaticCtorExecSet.TryAdd(ceStaticFieldEntry.mTypeId, NULL)) && (!ceStaticFieldEntry.mName.StartsWith("#"))) { auto bfType = GetBfType(ceStaticFieldEntry.mTypeId); BfTypeInstance* bfTypeInstance = NULL; @@ -8119,6 +8513,12 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* memset(ptr, 0, ceStaticFieldEntry.mSize); staticFieldInfo->mAddr = (addr_ce)(ptr - memStart); + if (op == CeOp_GetStaticField_Initializer) + { + void* initDataPtr = framePtr + initializerFrameOfs; + memcpy(ptr, initDataPtr, ceStaticFieldEntry.mSize); + } + if (ceStaticFieldEntry.mName.StartsWith("#")) { addr_ce resultAddr = 0; @@ -8226,6 +8626,18 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* if ((callEntry.mFunctionInfo->mCeFunction == NULL) && (!callEntry.mFunctionInfo->mMethodRef.IsNull())) { auto methodRef = callEntry.mFunctionInfo->mMethodRef; + if (methodRef.mMethodNum >= methodRef.mTypeInstance->mTypeDef->mMethods.mSize) + { + // Must be a comptime-generated method + ceModule->PopulateType(methodRef.mTypeInstance); + } + + if (methodRef.mMethodNum >= methodRef.mTypeInstance->mTypeDef->mMethods.mSize) + { + _Fail("OOB method reference"); + return false; + } + auto methodDef = methodRef.mTypeInstance->mTypeDef->mMethods[methodRef.mMethodNum]; auto moduleMethodInstance = ceModule->GetMethodInstance(methodRef.mTypeInstance, methodDef, methodRef.mMethodGenericArguments); @@ -8318,7 +8730,10 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* _Fail("Empty virtual table"); return false; } - auto methodInstance = (BfMethodInstance*)valueType->mVirtualMethodTable[virtualIdx].mImplementingMethod; + auto& methodRef = valueType->mVirtualMethodTable[virtualIdx].mImplementingMethod; + if (methodRef.mTypeInstance->mDefineState < BfTypeDefineState_DefinedAndMethodsSlotted) + ceModule->PopulateType(methodRef.mTypeInstance, BfPopulateType_DataAndMethods); + auto methodInstance = (BfMethodInstance*)methodRef; auto callFunction = mCeMachine->GetPreparedFunction(methodInstance); if (needsFunctionIds) @@ -9125,6 +9540,7 @@ CeMachine::CeMachine(BfCompiler* compiler) mCurContext = NULL; mCurCallSource = NULL; mExecuteId = -1; + mCurRecursiveDepth = 0; mCurFunctionId = 0; mRevisionExecuteTime = 0; @@ -9157,7 +9573,7 @@ CeMachine::~CeMachine() delete mTempParser; delete mTempReducer; delete mAppendAllocInfo; - delete mCeModule; + delete mCeModule; auto _RemoveFunctionInfo = [&](CeFunctionInfo* functionInfo) { @@ -9387,7 +9803,7 @@ void CeMachine::RemoveMethod(BfMethodInstance* methodInstance) methodInstance->mInCEMachine = false; } -CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constVal, CeContext* ceContext) +CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constVal, CeContext* ceContext, CeBuilder* ceBuilder) { auto ceModule = mCeModule; auto beType = constVal->GetType(); @@ -9456,7 +9872,7 @@ CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constV BF_ASSERT(!data.mQueueFixups); CeConstStructData gvData; - auto result = WriteConstant(gvData, globalVar->mInitializer, ceContext); + auto result = WriteConstant(gvData, globalVar->mInitializer, ceContext, NULL); if (result != CeErrorKind_None) return result; @@ -9468,6 +9884,19 @@ CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constV memcpy(ptr, &addr64, mCeModule->mSystem->mPtrSize); return CeErrorKind_None; } + else if (auto beTypeOfConst = BeValueDynCast(constVal)) + { + if (ceBuilder != NULL) + { + // Fake it to not break debugging + auto ptr = data.mData.GrowUninitialized(ceModule->mSystem->mPtrSize); + int64 addr64 = beTypeOfConst->mBfTypeId; + memcpy(ptr, &addr64, ceModule->mSystem->mPtrSize); + } + return CeErrorKind_None; + + return CeErrorKind_Error; + } else if (auto beFunc = BeValueDynCast(constVal)) { return CeErrorKind_FunctionPointer; @@ -9487,7 +9916,7 @@ CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constV if (wantZeroes > 0) data.mData.Insert(data.mData.size(), (uint8)0, wantZeroes); - auto result = WriteConstant(data, constStruct->mMemberValues[memberIdx], ceContext); + auto result = WriteConstant(data, constStruct->mMemberValues[memberIdx], ceContext, ceBuilder); if (result != CeErrorKind_None) return result; } @@ -9498,7 +9927,7 @@ CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constV { for (auto& memberVal : constStruct->mMemberValues) { - auto result = WriteConstant(data, memberVal, ceContext); + auto result = WriteConstant(data, memberVal, ceContext, ceBuilder); if (result != CeErrorKind_None) return result; } @@ -9512,7 +9941,7 @@ CeErrorKind CeMachine::WriteConstant(CeConstStructData& data, BeConstant* constV } else if (auto constCast = BeValueDynCast(constVal)) { - auto result = WriteConstant(data, constCast->mTarget, ceContext); + auto result = WriteConstant(data, constCast->mTarget, ceContext, ceBuilder); if (result != CeErrorKind_None) return result; } @@ -9650,10 +10079,34 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction) } else if (owner->IsInstanceOf(mCeModule->mCompiler->mTypeTypeDef)) { - if (methodDef->mName == "Comptime_GetTypeById") + if (methodDef->mName == "Comptime_GetTypeDeclarationById") + { + ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeDeclById; + } + if (methodDef->mName == "Comptime_GetTypeDeclarationByName") + { + ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeDeclByName; + } + else if (methodDef->mName == "Comptime_GetNextTypeDeclaration") + { + ceFunction->mFunctionKind = CeFunctionKind_GetReflectNextTypeDecl; + } + else if (methodDef->mName == "Comptime_Type_GetBaseType") + { + ceFunction->mFunctionKind = CeFunctionKind_GetBaseType; + } + else if (methodDef->mName == "Comptime_Type_HasDeclaredMember") + { + ceFunction->mFunctionKind = CeFunctionKind_HasDeclaredMember; + } + else if (methodDef->mName == "Comptime_GetTypeById") { ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeById; } + else if (methodDef->mName == "Comptime_GetWrappedType") + { + ceFunction->mFunctionKind = CeFunctionKind_GetWrappedType; + } else if (methodDef->mName == "Comptime_GetTypeByName") { ceFunction->mFunctionKind = CeFunctionKind_GetReflectTypeByName; @@ -9666,6 +10119,14 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction) { ceFunction->mFunctionKind = CeFunctionKind_Type_ToString; } + else if (methodDef->mName == "Comptime_TypeName_ToString") + { + ceFunction->mFunctionKind = CeFunctionKind_TypeName_ToString; + } + else if (methodDef->mName == "Comptime_Namespace_ToString") + { + ceFunction->mFunctionKind = CeFunctionKind_Namespace_ToString; + } else if (methodDef->mName == "Comptime_Type_GetCustomAttribute") { ceFunction->mFunctionKind = CeFunctionKind_Type_GetCustomAttribute; @@ -9753,6 +10214,10 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction) { ceFunction->mFunctionKind = CeFunctionKind_EmitMixin; } + else if (methodDef->mName == "Comptime_GetStringById") + { + ceFunction->mFunctionKind = CeFunctionKind_GetStringById; + } } else if (owner->IsInstanceOf(mCeModule->mCompiler->mDiagnosticsDebugTypeDef)) { @@ -9988,10 +10453,12 @@ void CeMachine::PrepareFunction(CeFunction* ceFunction, CeBuilder* parentBuilder CeBuilder ceBuilder; SetAndRestoreValue prevBuilder(mCurBuilder, &ceBuilder); - ceBuilder.mParentBuilder = parentBuilder; + ceBuilder.mParentBuilder = parentBuilder; ceBuilder.mPtrSize = mCeModule->mCompiler->mSystem->mPtrSize; ceBuilder.mCeMachine = this; ceBuilder.mCeFunction = ceFunction; + SetAndRestoreValue prevRecursiveDepth(mCurRecursiveDepth, mCurRecursiveDepth + 1); + ceBuilder.mRecursiveDepth = mCurRecursiveDepth; ceBuilder.Build(); ceFunction->mInitializeState = CeFunction::InitializeState_Initialized; @@ -10023,6 +10490,10 @@ CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance, BfIRValue f { if ((func.IsConst()) || (func.IsFake())) return NULL; + + auto funcVal = mCeModule->mBfIRBuilder->mBeIRCodeGen->TryGetBeValue(func.mId); + if (funcVal == NULL) + return NULL; } CeFunctionInfo** functionInfoPtr = NULL; @@ -10046,8 +10517,7 @@ CeFunction* CeMachine::GetFunction(BfMethodInstance* methodInstance, BfIRValue f } else { - auto funcVal = mCeModule->mBfIRBuilder->mBeIRCodeGen->GetBeValue(func.mId); - + auto funcVal = mCeModule->mBfIRBuilder->mBeIRCodeGen->GetBeValue(func.mId); if (auto function = BeValueDynCast(funcVal)) { String funcName = function->mName; @@ -10259,7 +10729,9 @@ CeContext* CeMachine::AllocContext() void CeMachine::ReleaseContext(CeContext* ceContext) { ceContext->mStringMap.Clear(); - ceContext->mReflectMap.Clear(); + delete ceContext->mTypeDeclState; + ceContext->mTypeDeclState = NULL; + ceContext->mReflectMap.Clear(); ceContext->mConstDataMap.Clear(); ceContext->mMemory.Clear(); if (ceContext->mMemory.mAllocSize > BF_CE_MAX_CARRYOVER_MEMORY) @@ -10280,8 +10752,44 @@ void CeMachine::ReleaseContext(CeContext* ceContext) BfTypedValue CeMachine::Call(CeCallSource callSource, BfModule* module, BfMethodInstance* methodInstance, const BfSizedArray& args, CeEvalFlags flags, BfType* expectingType) { - auto ceContext = AllocContext(); + auto ceContext = AllocContext(); + SetAndRestoreValue prevRecursiveDepth(mCurRecursiveDepth, mCurRecursiveDepth + 1); + ceContext->mRecursiveDepth = mCurRecursiveDepth; auto result = ceContext->Call(callSource, module, methodInstance, args, flags, expectingType); ReleaseContext(ceContext); return result; -} \ No newline at end of file +} + +BfError* CeMachine::FailCurrent(BfModule* srcModule, const StringImpl& error, BfAstNode* refNode) +{ + BfError* bfError = NULL; + + if ((mCurBuilder != NULL) && + ((mCurContext != NULL) || (mCurBuilder->mRecursiveDepth > mCurContext->mRecursiveDepth))) + { + String useError = error; + useError += StrFormat(" during const-eval generation of '%s'", srcModule->MethodToString(mCurBuilder->mCeFunction->mMethodInstance).c_str()); + bfError = srcModule->Fail(error, refNode); + + if (bfError != NULL) + { + auto filePos = mCurBuilder->mCeMachine->mCeModule->mCurFilePosition; + auto parser = filePos.mFileInstance->mParser; + if (parser != NULL) + { + srcModule->mCompiler->mPassInstance->MoreInfoAt( + StrFormat("See comptime method '%s' processing location", srcModule->MethodToString(mCurBuilder->mCeFunction->mMethodInstance).c_str()), + parser, filePos.mCurSrcPos, 1, BfFailFlag_None); + } + } + } + else + { + bfError = srcModule->Fail(error, refNode); + } + return bfError; +} + +void CeMachine::FailCurrentMoreInfo(const StringImpl& error, BfAstNode* refNode) +{ +} diff --git a/IDEHelper/Compiler/CeMachine.h b/IDEHelper/Compiler/CeMachine.h index 79bda5af..6b5d1fe2 100644 --- a/IDEHelper/Compiler/CeMachine.h +++ b/IDEHelper/Compiler/CeMachine.h @@ -111,6 +111,7 @@ enum CeOp : int16 CeOp_GetSP, CeOp_SetSP, CeOp_GetStaticField, + CeOp_GetStaticField_Initializer, CeOp_GetMethod, CeOp_GetMethod_Inner, CeOp_GetMethod_Virt, @@ -428,12 +429,20 @@ enum CeFunctionKind CeFunctionKind_DynCheckFailed, CeFunctionKind_FatalError, CeFunctionKind_DebugWrite, - CeFunctionKind_DebugWrite_Int, + CeFunctionKind_DebugWrite_Int, + CeFunctionKind_GetReflectTypeDeclById, + CeFunctionKind_GetReflectTypeDeclByName, + CeFunctionKind_GetReflectNextTypeDecl, + CeFunctionKind_GetBaseType, + CeFunctionKind_HasDeclaredMember, CeFunctionKind_GetReflectType, CeFunctionKind_GetReflectTypeById, + CeFunctionKind_GetWrappedType, CeFunctionKind_GetReflectTypeByName, CeFunctionKind_GetReflectSpecializedType, CeFunctionKind_Type_ToString, + CeFunctionKind_TypeName_ToString, + CeFunctionKind_Namespace_ToString, CeFunctionKind_Type_GetCustomAttribute, CeFunctionKind_Field_GetCustomAttribute, CeFunctionKind_Method_GetCustomAttribute, @@ -456,6 +465,7 @@ enum CeFunctionKind CeFunctionKind_EmitMethodEntry, CeFunctionKind_EmitMethodExit, CeFunctionKind_EmitMixin, + CeFunctionKind_GetStringById, CeFunctionKind_BfpDirectory_Create, CeFunctionKind_BfpDirectory_Rename, @@ -826,12 +836,13 @@ class CeBuilder { public: CeBuilder* mParentBuilder; - CeMachine* mCeMachine; + CeMachine* mCeMachine; CeFunction* mCeFunction; BeFunction* mBeFunction; CeOperand mReturnVal; BeType* mIntPtrType; int mPtrSize; + int mRecursiveDepth; String mError; BeDbgLoc* mCurDbgLoc; @@ -848,13 +859,14 @@ public: Dictionary mInnerFunctionMap; Dictionary mStaticFieldMap; Dictionary mStaticFieldInstanceMap; - Dictionary mDbgVariableMap; + Dictionary mDbgVariableMap; public: CeBuilder() { mParentBuilder = NULL; mPtrSize = 0; + mRecursiveDepth = -1; mCeFunction = NULL; mBeFunction = NULL; mCeMachine = NULL; @@ -951,16 +963,24 @@ public: { Kind_None, Kind_File, - Kind_Directory + Kind_Directory, + Kind_TypeDeclListHash, }; public: Kind mKind; String mString; + int mInt; + + CeRebuildKey() + { + mKind = Kind_None; + mInt = 0; + } bool operator==(const CeRebuildKey& other) const { - return (mKind == other.mKind) && (mString == other.mString); + return (mKind == other.mKind) && (mString == other.mString) && (mInt == other.mInt); } }; @@ -1089,6 +1109,20 @@ public: } }; +class CeTypeDeclState +{ +public: + Dictionary mReflectDeclMap; + HashSet mIteratedTypeDefs; + bool mCheckedAllTypeDefs; + +public: + CeTypeDeclState() + { + mCheckedAllTypeDefs = false; + } +}; + class CeContext { public: @@ -1097,6 +1131,7 @@ public: int mReflectTypeIdOffset; int mExecuteId; CeEvalFlags mCurEvalFlags; + int mRecursiveDepth; // These are only valid for the current execution ContiguousHeap* mHeap; @@ -1105,6 +1140,7 @@ public: int mStackSize; Dictionary mStringMap; Dictionary mReflectMap; + CeTypeDeclState* mTypeDeclState; Dictionary mConstDataMap; HashSet mStaticCtorExecSet; Dictionary mStaticFieldMap; @@ -1133,12 +1169,15 @@ public: void CalcWorkingDir(); void FixRelativePath(StringImpl& path); bool AddRebuild(const CeRebuildKey& key, const CeRebuildValue& value); + void AddTypeSigRebuild(BfType* type); void AddFileRebuild(const StringImpl& filePath); uint8* CeMalloc(int size); + uint8* CeMallocZero(int size); bool CeFree(addr_ce addr); addr_ce CeAllocArray(BfArrayType* arrayType, int count, addr_ce& elemsAddr); + addr_ce GetReflectTypeDecl(int typeId); addr_ce GetReflectType(int typeId); - addr_ce GetReflectType(const String& typeName); + addr_ce GetReflectType(const String& typeName, bool useDeclaration); int GetTypeIdFromType(addr_ce typeAddr); addr_ce GetReflectSpecializedType(addr_ce unspecializedType, addr_ce typeArgsSpanAddr); addr_ce GetString(int stringId); @@ -1216,6 +1255,7 @@ public: int mRevisionExecuteTime; int mCurFunctionId; int mExecuteId; + int mCurRecursiveDepth; CeAppendAllocInfo* mAppendAllocInfo; CeContext* mCurContext; @@ -1249,7 +1289,7 @@ public: void RemoveFunc(CeFunction* ceFunction); void RemoveMethod(BfMethodInstance* methodInstance); void CreateFunction(BfMethodInstance* methodInstance, CeFunction* ceFunction); - CeErrorKind WriteConstant(CeConstStructData& data, BeConstant* constVal, CeContext* ceContext); + CeErrorKind WriteConstant(CeConstStructData& data, BeConstant* constVal, CeContext* ceContext, CeBuilder* ceBuilder); void CheckFunctionKind(CeFunction* ceFunction); void PrepareFunction(CeFunction* methodInstance, CeBuilder* parentBuilder); @@ -1276,6 +1316,9 @@ public: CeContext* AllocContext(); void ReleaseContext(CeContext* context); BfTypedValue Call(CeCallSource callSource, BfModule* module, BfMethodInstance* methodInstance, const BfSizedArray& args, CeEvalFlags flags, BfType* expectingType); + + BfError* FailCurrent(BfModule* srcModule, const StringImpl& error, BfAstNode* refNode); + void FailCurrentMoreInfo(const StringImpl& error, BfAstNode* refNode); }; NS_BF_END diff --git a/IDEHelper/DbgExprEvaluator.cpp b/IDEHelper/DbgExprEvaluator.cpp index 786604fa..b018f47d 100644 --- a/IDEHelper/DbgExprEvaluator.cpp +++ b/IDEHelper/DbgExprEvaluator.cpp @@ -5934,8 +5934,13 @@ void DbgExprEvaluator::PerformBinaryOperation(ASTREF(BfExpression*)& leftExpress auto& displayStringList = debugVis->mStringViews; DwFormatInfo formatInfo; + formatInfo.mCallStackIdx = mCallStackIdx; formatInfo.mRawString = true; formatInfo.mLanguage = language; + formatInfo.mNamespaceSearch = mNamespaceSearchStr; + formatInfo.mExplicitThis = mExplicitThis; + if (mReferenceId != NULL) + formatInfo.mReferenceId = *mReferenceId; if (!displayEntry->mCondition.empty()) { if (!mDebugger->EvalCondition(debugVis, dbgCompileUnit, useTypedValue, formatInfo, displayEntry->mCondition, dbgVisWildcardCaptures, displayString)) @@ -5943,14 +5948,15 @@ void DbgExprEvaluator::PerformBinaryOperation(ASTREF(BfExpression*)& leftExpress } String displayStr = mDebugger->mDebugManager->mDebugVisualizers->DoStringReplace(displayEntry->mString, dbgVisWildcardCaptures); - mDebugger->ProcessEvalString(dbgCompileUnit, useTypedValue, displayStr, displayString, formatInfo, debugVis, false); + if (mDebugger->ProcessEvalString(dbgCompileUnit, useTypedValue, displayStr, displayString, formatInfo, debugVis, false)) + { + bool isEq = displayString == resultTypedValue->mCharPtr; - bool isEq = displayString == resultTypedValue->mCharPtr; - - auto boolType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); - mResult.mType = boolType; - mResult.mBool = isEq == ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality)); - return; + auto boolType = mDbgModule->GetPrimitiveType(DbgType_Bool, GetLanguage()); + mResult.mType = boolType; + mResult.mBool = isEq == ((binaryOp == BfBinaryOp_Equality) || (binaryOp == BfBinaryOp_StrictEquality)); + return; + } } } } diff --git a/IDEHelper/DbgModule.cpp b/IDEHelper/DbgModule.cpp index 90334231..e1628f0d 100644 --- a/IDEHelper/DbgModule.cpp +++ b/IDEHelper/DbgModule.cpp @@ -21,6 +21,7 @@ #include "Compiler/BfDemangler.h" #include "BeefySysLib/util/Hash.h" #include "BeefySysLib/util/BeefPerf.h" +#include "BeefySysLib/util/MultiDictionary.h" #include "DbgSymSrv.h" #include "MiniDumpDebugger.h" @@ -5173,8 +5174,7 @@ void DbgModule::ParseHotTargetSections(DataStream* stream, addr_target* resolved stream->Read(&coffReloc, sizeof(COFFRelocation)); PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18]; - //const char* symName = mSymbolData[coffReloc.mSymbolTableIndex]; - + bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC; if (symInfo->mNameOfs[0] != 0) @@ -5313,27 +5313,20 @@ void DbgModule::CommitHotTargetSections() } } -void DbgModule::HotReplaceType(DbgType* newType) +bool DbgModule::HasHotReplacedMethods(DbgType* type) +{ + for (auto newMethod : type->mMethodList) + { + if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize)) + return true; + } + return false; +} + +void DbgModule::HotReplaceMethods(DbgType* newType, DbgType* primaryType) { auto linkedModule = GetLinkedModule(); - newType->PopulateType(); - DbgType* primaryType = linkedModule->GetPrimaryType(newType); - if (primaryType == newType) - { - // There was no previous type - BF_ASSERT(primaryType->mHotNewType == NULL); - return; - } - - if (primaryType->mHotNewType != newType) - { - // We have already pulled in the new data from a previous new type - BF_ASSERT(primaryType->mHotNewType == NULL); - return; - } - primaryType->mHotNewType = NULL; - primaryType->PopulateType(); linkedModule->ParseGlobalsData(); linkedModule->ParseSymbolData(); @@ -5354,183 +5347,292 @@ void DbgModule::HotReplaceType(DbgType* newType) // Now actually remove the linedata from the defining module HashSet checkedFiles; - for (auto method : primaryType->mMethodList) - { - //method->mWasModuleHotReplaced = true; - method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned + //for (auto method : primaryType->mMethodList) - if (method->mLineInfo == NULL) - continue; + auto _RemoveLineInfo = [&](DbgSubprogram* method) + { + //method->mWasModuleHotReplaced = true; + method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned - //FIXME: Hot replacing lines - DbgSrcFile* lastSrcFile = NULL; - checkedFiles.Clear(); + if (method->mLineInfo == NULL) + return; - int prevCtx = -1; - auto inlineRoot = method->GetRootInlineParent(); - for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++) - { - auto& lineData = method->mLineInfo->mLines[lineIdx]; - if (lineData.mCtxIdx != prevCtx) + //FIXME: Hot replacing lines + DbgSrcFile* lastSrcFile = NULL; + checkedFiles.Clear(); + + int prevCtx = -1; + auto inlineRoot = method->GetRootInlineParent(); + for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++) { - auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx]; - auto srcFile = ctxInfo.mSrcFile; - prevCtx = lineData.mCtxIdx; - if (srcFile != lastSrcFile) + auto& lineData = method->mLineInfo->mLines[lineIdx]; + if (lineData.mCtxIdx != prevCtx) { - if (checkedFiles.Add(srcFile)) + auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx]; + auto srcFile = ctxInfo.mSrcFile; + prevCtx = lineData.mCtxIdx; + if (srcFile != lastSrcFile) { - // Remove linedata for old type - // These go into a hot-replaced list so we can still bind to them -- that is necessary because - // we may still have old versions of this method running (and may forever, if its in a loop on some thread) - // since we only patch entry points - //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true); + if (checkedFiles.Add(srcFile)) + { + // Remove linedata for old type + // These go into a hot-replaced list so we can still bind to them -- that is necessary because + // we may still have old versions of this method running (and may forever, if its in a loop on some thread) + // since we only patch entry points + //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true); - //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true); - srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true); + //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true); + srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true); + } + + lastSrcFile = srcFile; } - - lastSrcFile = srcFile; } } - } - } + }; //DbgType* primaryType = newType->GetPrimaryType(); - // We need to keep a persistent list of hot replaced methods so we can set hot jumps - // in old methods that may still be on the callstack. These entries get removed when - // we unload unused hot files in - while (!primaryType->mMethodList.IsEmpty()) + MultiDictionary oldProgramMap; + auto _AddToHotReplacedMethodList = [&](DbgSubprogram* oldMethod) + { + oldMethod->PopulateSubprogram(); + if (oldMethod->mBlock.IsEmpty()) + return; + auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC); + if (symInfo != NULL) + { + StringView key = StringView(symInfo->mName); + DbgSubprogram** oldestValuePtr = NULL; + int count = 0; + for (auto itr = oldProgramMap.TryGet(key); itr != oldProgramMap.end(); ++itr) + { + auto& checkProgram = itr.GetValue(); + if ((oldestValuePtr == NULL) || + (checkProgram->mCompileUnit->mDbgModule->mHotIdx < (*oldestValuePtr)->mCompileUnit->mDbgModule->mHotIdx)) + oldestValuePtr = &checkProgram; + count++; + } + + // If we hot jump from only the last version of the method then we potentially create a long "jump chain" + // from a call to the original method through every hot version to this current one. We also don't want to + // blindly update every old version because that will cause hot loading to become too slow over many iterations. + // We balance this by linking SOME old versions to the newest one, decreasing in frequency as we move through + // the older methods + if ((count == 0) || + (mDebugTarget->mHotChainBreakIdx++) % (1 << (count + 1)) == 0) + { + oldProgramMap.Add(StringView(symInfo->mName), oldMethod); + } + else if ((count > 1) && ((mDebugTarget->mHotChainBreakIdx++) % 2 == 0)) + { + // Prefer the older of the methods when we are not adding a new entry. This will also ensure that the + // original method gets updated very often + *oldestValuePtr = oldMethod; + } + } + }; + + if (newType != primaryType) { - auto method = primaryType->mMethodList.PopFront(); - - method->PopulateSubprogram(); - - primaryType->mHotReplacedMethodList.PushFront(method); - mHotPrimaryTypes.Add(primaryType); + // We need to keep a persistent list of hot replaced methods so we can set hot jumps + // in old methods that may still be on the callstack. These entries get removed when + // we unload unused hot files in + while (!primaryType->mMethodList.IsEmpty()) + { + auto method = primaryType->mMethodList.PopFront(); + method->PopulateSubprogram(); + primaryType->mHotReplacedMethodList.PushFront(method); + mHotPrimaryTypes.Add(primaryType); + } } - - Dictionary oldProgramMap; + for (auto oldMethod : primaryType->mHotReplacedMethodList) { - oldMethod->PopulateSubprogram(); - if (oldMethod->mBlock.IsEmpty()) - continue; - auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC); - if (symInfo != NULL) - { - oldProgramMap.TryAdd(symInfo->mName, oldMethod); - } + _AddToHotReplacedMethodList(oldMethod); } bool setHotJumpFailed = false; - while (!newType->mMethodList.IsEmpty()) - { - DbgSubprogram* newMethod = newType->mMethodList.PopFront(); - if (!newMethod->mBlock.IsEmpty()) + + auto _HotJump = [&](DbgSubprogram* oldMethod, DbgSubprogram* newMethod) { - BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC); + bool doHotJump = false; - newMethod->PopulateSubprogram(); - - auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC); - if (symInfo != NULL) + if (oldMethod->Equals(newMethod)) { - DbgSubprogram* oldMethod = NULL; - if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod)) + doHotJump = true; + } + else + { + // When mangles match but the actual signatures don't match, that can mean that the call signature was changed + // and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed. + // When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard + // fail if the old code gets called to avoid confusion of "why aren't my changes working?" + + // If we removed captures then we can still do the hot jump. Otherwise we have to fail... + doHotJump = false; + if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) && + (oldMethod->mHasThis) && (newMethod->mHasThis)) { - bool doHotJump = false; + auto oldParam = oldMethod->mParams.front(); + auto newParam = newMethod->mParams.front(); - if (oldMethod->Equals(newMethod)) + if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer())) { - doHotJump = true; - } - else - { - // When mangles match but the actual signatures don't match, that can mean that the call signature was changed - // and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed. - // When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard - // fail if the old code gets called to avoid confusion of "why aren't my changes working?" - - // If we removed captures then we can still do the hot jump. Otherwise we have to fail... - doHotJump = false; - if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) && - (oldMethod->mHasThis) && (newMethod->mHasThis)) + auto oldType = oldParam->mType->mTypeParam->GetPrimaryType(); + oldType->PopulateType(); + auto newType = newParam->mType->mTypeParam->GetPrimaryType(); + newType->PopulateType(); + if ((oldType->IsStruct()) && (newType->IsStruct())) { - auto oldParam = oldMethod->mParams.front(); - auto newParam = newMethod->mParams.front(); + bool wasMatch = true; - if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer())) + auto oldMember = oldType->mMemberList.front(); + auto newMember = newType->mMemberList.front(); + while (newMember != NULL) { - auto oldType = oldParam->mType->mTypeParam->GetPrimaryType(); - oldType->PopulateType(); - auto newType = newParam->mType->mTypeParam->GetPrimaryType(); - newType->PopulateType(); - if ((oldType->IsStruct()) && (newType->IsStruct())) + if (oldMember == NULL) { - bool wasMatch = true; - - auto oldMember = oldType->mMemberList.front(); - auto newMember = newType->mMemberList.front(); - while (newMember != NULL) - { - if (oldMember == NULL) - { - wasMatch = false; - break; - } - - if ((oldMember->mName == NULL) || (newMember->mName == NULL)) - { - wasMatch = false; - break; - } - - if (strcmp(oldMember->mName, newMember->mName) != 0) - { - wasMatch = false; - break; - } - - if (!oldMember->mType->Equals(newMember->mType)) - { - wasMatch = false; - break; - } - - oldMember = oldMember->mNext; - newMember = newMember->mNext; - } - - if (wasMatch) - doHotJump = true; + wasMatch = false; + break; } + + if ((oldMember->mName == NULL) || (newMember->mName == NULL)) + { + wasMatch = false; + break; + } + + if (strcmp(oldMember->mName, newMember->mName) != 0) + { + wasMatch = false; + break; + } + + if (!oldMember->mType->Equals(newMember->mType)) + { + wasMatch = false; + break; + } + + oldMember = oldMember->mNext; + newMember = newMember->mNext; } - if (!doHotJump) - { - mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC); - oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid; - } + if (wasMatch) + doHotJump = true; } } - if (doHotJump) + if (!doHotJump) { - if (!setHotJumpFailed) - { - if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC))) - setHotJumpFailed = true; - } - oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced; + mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC); + oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid; } } } - } - newMethod->mParentType = primaryType; - primaryType->mMethodList.PushBack(newMethod); + + if (doHotJump) + { + if (!setHotJumpFailed) + { + if (mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC))) + { + _RemoveLineInfo(oldMethod); + } + else + setHotJumpFailed = true; + } + oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced; + } + }; + + auto _ReplaceMethod = [&](DbgSubprogram* newMethod) + { + bool didReplace = false; + if (!newMethod->mBlock.IsEmpty()) + { + newMethod->PopulateSubprogram(); + + auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC); + BfLogDbg("Hot added new method %p %s Address:%p Sym:%s\n", + newMethod, newMethod->mName, newMethod->mBlock.mLowPC, + (symInfo != NULL) ? symInfo->mName : "???"); + if (symInfo != NULL) + { + DbgSubprogram* oldMethod = NULL; + for (auto itr = oldProgramMap.TryGet(StringView(symInfo->mName)); itr != oldProgramMap.end(); ++itr) + { + auto oldMethod = itr.GetValue(); + _HotJump(oldMethod, newMethod); + didReplace = true; + } + } + } + return didReplace; + }; + + if (newType == primaryType) + { + // In-place method swapping + + auto newMethod = newType->mMethodList.front(); + while (newMethod != NULL) + { + if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize)) + { + // Our object file contains this function + if (_ReplaceMethod(newMethod)) + { + auto nextMethod = newMethod->mNext; + newType->mMethodList.Remove(newMethod); + primaryType->mHotReplacedMethodList.PushFront(newMethod); + newMethod = nextMethod; + } + else + newMethod = newMethod->mNext; + } + else + { + _AddToHotReplacedMethodList(newMethod); + newMethod = newMethod->mNext; + } + } } + else + { + while (!newType->mMethodList.IsEmpty()) + { + DbgSubprogram* newMethod = newType->mMethodList.PopFront(); + _ReplaceMethod(newMethod); + newMethod->mParentType = primaryType; + primaryType->mMethodList.PushBack(newMethod); + } + } +} + +void DbgModule::HotReplaceType(DbgType* newType) +{ + auto linkedModule = GetLinkedModule(); + + newType->PopulateType(); + DbgType* primaryType = linkedModule->GetPrimaryType(newType); + if (primaryType == newType) + { + // There was no previous type + BF_ASSERT(primaryType->mHotNewType == NULL); + return; + } + + if (primaryType->mHotNewType != newType) + { + // We have already pulled in the new data from a previous new type + BF_ASSERT(primaryType->mHotNewType == NULL); + return; + } + primaryType->mHotNewType = NULL; + + HotReplaceMethods(newType, primaryType); //mDebugTarget->mSymbolMap.Get() @@ -6506,17 +6608,23 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind) { auto dbgType = *itr; auto primaryType = dbgType->GetPrimaryType(); + + if ((primaryType->mHotNewType == NULL) && (HasHotReplacedMethods(primaryType)) && (dbgType == primaryType)) + { + HotReplaceMethods(dbgType, primaryType); + } + if (primaryType != dbgType) { mHotPrimaryTypes.Remove(itr); mHotPrimaryTypes.Add(primaryType); didReplaceType = true; break; - } + } } if (!didReplaceType) break; - } + } BF_ASSERT(mTypes.size() == 0); for (int typeIdx = mStartTypeIdx; typeIdx < (int)linkedModule->mTypes.size(); typeIdx++) @@ -6525,7 +6633,7 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind) //if (!newType->mMethodList.IsEmpty()) if (!newType->mIsDeclaration) HotReplaceType(newType); - } + } } if (needHotTargetMemory != 0) diff --git a/IDEHelper/DbgModule.h b/IDEHelper/DbgModule.h index 813401d8..6087ac2b 100644 --- a/IDEHelper/DbgModule.h +++ b/IDEHelper/DbgModule.h @@ -665,8 +665,8 @@ public: class SubprogramRecord { public: - Array > mContexts; - Array > mLines; + Array mContexts; + Array mLines; int mCurContext; bool mHasInlinees; }; @@ -1273,6 +1273,8 @@ public: void DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolveSymbolAddr, PE_SymInfo* symInfo); void ParseHotTargetSections(DataStream* stream, addr_target* resovledSymbolAddrs); void CommitHotTargetSections(); + bool HasHotReplacedMethods(DbgType* type); + void HotReplaceMethods(DbgType* newType, DbgType* primaryType); void HotReplaceType(DbgType* newType); void ProcessHotSwapVariables(); virtual bool LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { return false; } diff --git a/IDEHelper/DebugTarget.cpp b/IDEHelper/DebugTarget.cpp index 17eb7102..ead54a61 100644 --- a/IDEHelper/DebugTarget.cpp +++ b/IDEHelper/DebugTarget.cpp @@ -33,6 +33,8 @@ DebugTarget::DebugTarget(WinDebugger* debugger) mHotHeapAddr = 0; mHotHeapReserveSize = 0; mLastHotHeapCleanIdx = 0; + mVDataHotIdx = -1; + mHotChainBreakIdx = 0; mIsEmpty = false; mWasLocallyBuilt = false; mCurModuleId = 0; @@ -2489,15 +2491,17 @@ DbgBreakKind DebugTarget::GetDbgBreakKind(addr_target address, CPURegisters* reg return DbgBreakKind_None; } +#define ADDR_ROUGH(address) ((address) & ~0x3FFF) + DbgModule* DebugTarget::FindDbgModuleForAddress(addr_target address) { - addr_target checkAddr = address & ~0xFFFF; + addr_target checkAddr = ADDR_ROUGH(address); FindDbgModuleCacheEntry* valuePtr = NULL; if (mFindDbgModuleCache.TryAdd(checkAddr, NULL, &valuePtr)) { for (auto dwarf : mDbgModules) { - if ((address >= dwarf->mImageBase) && (address < dwarf->mImageBase + dwarf->mImageSize)) + if ((checkAddr >= ADDR_ROUGH(dwarf->mImageBase)) && (checkAddr <= ADDR_ROUGH(dwarf->mImageBase + dwarf->mImageSize))) { if (valuePtr->mFirst == NULL) { diff --git a/IDEHelper/DebugTarget.h b/IDEHelper/DebugTarget.h index 1b5f060c..8aa7bd95 100644 --- a/IDEHelper/DebugTarget.h +++ b/IDEHelper/DebugTarget.h @@ -49,6 +49,8 @@ public: addr_target mHotHeapAddr; int64 mHotHeapReserveSize; int mLastHotHeapCleanIdx; + int mVDataHotIdx; + int mHotChainBreakIdx; String mTargetPath; DbgModule* mLaunchBinary; DbgModule* mTargetBinary; diff --git a/IDEHelper/IDEHelper.vcxproj b/IDEHelper/IDEHelper.vcxproj index 03458baa..f373f506 100644 --- a/IDEHelper/IDEHelper.vcxproj +++ b/IDEHelper/IDEHelper.vcxproj @@ -162,7 +162,7 @@ Level3 Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;IDEHELPER_EXPORTS;BFSYSLIB_DYNAMIC;%(PreprocessorDefinitions) - ../;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\AArch64;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include;..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\include + ../;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\AArch64;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include;..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\include false -D_SCL_SECURE_NO_WARNINGS %(AdditionalOptions) MultiThreadedDebug @@ -175,8 +175,8 @@ Windows DebugFull $(SolutionDir)\IDE\dist\$(TargetName).dll - rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMBitstreamReader.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMTransformUtils.lib;LLVMAggressiveInstCombine.lib;LLVMCFGuard.lib;LLVMTextAPI.lib;LLVMRemarks.lib;LLVMX86Info.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64Disassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMARMDesc.lib;LLVMARMCodeGen.lib;LLVMARMDisassembler.lib;LLVMWebAssemblyInfo.lib;LLVMWebAssemblyDesc.lib;LLVMWebAssemblyCodeGen.lib;LLVMWebAssemblyDisassembler.lib;LLVMWebAssemblyUtils.lib;LLVMTargetParser.lib;LLVMIRPrinter.lib;LLVMWebAssemblyAsmParser.lib;LLVMObjCARCOpts.lib;LLVMCodeGenTypes.lib;LLVMCoroutines.lib;LLVMFrontendOpenMP.lib;LLVMFrontendOffloading.lib;LLVMHipStdPar.lib;%(AdditionalDependencies) - ..\extern\llvm_win64_18_1_4\Debug\lib; ..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\lib;..\extern\curl\deps\lib;..\extern\jemalloc_win\x64\debug + ntdll.lib;rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMBitstreamReader.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMTransformUtils.lib;LLVMAggressiveInstCombine.lib;LLVMCFGuard.lib;LLVMTextAPI.lib;LLVMRemarks.lib;LLVMX86Info.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64Disassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMARMDesc.lib;LLVMARMCodeGen.lib;LLVMARMDisassembler.lib;LLVMWebAssemblyInfo.lib;LLVMWebAssemblyDesc.lib;LLVMWebAssemblyCodeGen.lib;LLVMWebAssemblyDisassembler.lib;LLVMWebAssemblyUtils.lib;LLVMTargetParser.lib;LLVMIRPrinter.lib;LLVMWebAssemblyAsmParser.lib;LLVMObjCARCOpts.lib;LLVMCodeGenTypes.lib;LLVMCoroutines.lib;LLVMFrontendOpenMP.lib;LLVMFrontendOffloading.lib;LLVMHipStdPar.lib;%(AdditionalDependencies) + ..\extern\llvm_win64_19_1_7\Debug\lib; ..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\lib;..\extern\curl\deps\lib;..\extern\jemalloc_win\x64\debug false $(SolutionDir)\IDE\dist\$(TargetName).lib MSVCRT @@ -225,7 +225,7 @@ true true zBP_DISABLED;WIN32;NDEBUG;_WINDOWS;_USRDLL;IDEHELPER_EXPORTS;BFSYSLIB_DYNAMIC;%(PreprocessorDefinitions) - ../;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_18_1_4\llvm\include;..\extern\llvm_win64_18_1_4\include;..\extern\llvm-project_18_1_4\llvm\lib\Target;..\extern\llvm_win64_18_1_4\lib\Target\X86;..\extern\llvm-project_18_1_4\llvm\tools\clang\include;..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\include + ../;../BeefySysLib/platform/win;../BeefySysLib/third_party;..\extern\llvm-project_19_1_7\llvm\include;..\extern\llvm_win64_19_1_7\include;..\extern\llvm-project_19_1_7\llvm\lib\Target;..\extern\llvm_win64_19_1_7\lib\Target\X86;..\extern\llvm-project_19_1_7\llvm\tools\clang\include;..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\include MultiThreaded false true @@ -237,8 +237,8 @@ true true $(SolutionDir)\IDE\dist\$(TargetName).dll - ..\extern\llvm_win64_18_1_4\Release\lib; ..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\lib;..\extern\curl\deps\lib;..\extern\jemalloc_win\x64\release - rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMBitstreamReader.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMTransformUtils.lib;LLVMAggressiveInstCombine.lib;LLVMCFGuard.lib;LLVMTextAPI.lib;LLVMRemarks.lib;LLVMX86Info.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64Disassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMARMDesc.lib;LLVMARMCodeGen.lib;LLVMARMDisassembler.lib;LLVMWebAssemblyInfo.lib;LLVMWebAssemblyDesc.lib;LLVMWebAssemblyCodeGen.lib;LLVMWebAssemblyDisassembler.lib;LLVMWebAssemblyUtils.lib;LLVMTargetParser.lib;LLVMIRPrinter.lib;LLVMObjCARCOpts.lib;LLVMCodeGenTypes.lib;LLVMCoroutines.lib;LLVMFrontendOpenMP.lib;LLVMFrontendOffloading.lib;LLVMHipStdPar.lib;%(AdditionalDependencies) + ..\extern\llvm_win64_19_1_7\Release\lib; ..\extern\curl\builds\libcurl-vc15-x64-release-static-zlib-static-ipv6-sspi-winssl\lib;..\extern\curl\deps\lib;..\extern\jemalloc_win\x64\release + ntdll.lib;rpcrt4.lib;cabinet.lib;winmm.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;libcurl_a.lib;wininet.lib;LLVMMCDisassembler.lib;LLVMSupport.lib;LLVMMC.lib;LLVMObject.lib;LLVMCore.lib;LLVMBitReader.lib;LLVMAsmParser.lib;LLVMMCParser.lib;LLVMCodeGen.lib;LLVMTarget.lib;LLVMScalarOpts.lib;LLVMInstCombine.lib;LLVMSelectionDAG.lib;LLVMProfileData.lib;LLVMAnalysis.lib;LLVMAsmPrinter.lib;LLVMBitWriter.lib;LLVMVectorize.lib;LLVMipo.lib;LLVMInstrumentation.lib;LLVMDebugInfoDWARF.lib;LLVMDebugInfoPDB.lib;LLVMDebugInfoCodeView.lib;LLVMGlobalISel.lib;LLVMBinaryFormat.lib;LLVMBitstreamReader.lib;LLVMLTO.lib;LLVMPasses.lib;LLVMLinker.lib;LLVMIRReader.lib;LLVMDemangle.lib;LLVMTransformUtils.lib;LLVMAggressiveInstCombine.lib;LLVMCFGuard.lib;LLVMTextAPI.lib;LLVMRemarks.lib;LLVMX86Info.lib;LLVMX86Desc.lib;LLVMX86CodeGen.lib;LLVMX86AsmParser.lib;LLVMX86Disassembler.lib;LLVMAArch64Info.lib;LLVMAArch64Utils.lib;LLVMAArch64Desc.lib;LLVMAArch64CodeGen.lib;LLVMAArch64Disassembler.lib;LLVMARMInfo.lib;LLVMARMUtils.lib;LLVMARMDesc.lib;LLVMARMCodeGen.lib;LLVMARMDisassembler.lib;LLVMWebAssemblyInfo.lib;LLVMWebAssemblyDesc.lib;LLVMWebAssemblyCodeGen.lib;LLVMWebAssemblyDisassembler.lib;LLVMWebAssemblyUtils.lib;LLVMTargetParser.lib;LLVMIRPrinter.lib;LLVMObjCARCOpts.lib;LLVMCodeGenTypes.lib;LLVMCoroutines.lib;LLVMFrontendOpenMP.lib;LLVMFrontendOffloading.lib;LLVMHipStdPar.lib;%(AdditionalDependencies) false true $(SolutionDir)\IDE\dist\$(TargetName).lib diff --git a/IDEHelper/IDEHelper.vcxproj.user b/IDEHelper/IDEHelper.vcxproj.user index 1ce90950..75967093 100644 --- a/IDEHelper/IDEHelper.vcxproj.user +++ b/IDEHelper/IDEHelper.vcxproj.user @@ -20,6 +20,6 @@ _NO_DEBUG_HEAP=1 - true + false \ No newline at end of file diff --git a/IDEHelper/Tests/LibA/src/LibA0.bf b/IDEHelper/Tests/LibA/src/LibA0.bf index 1be51f59..5e846182 100644 --- a/IDEHelper/Tests/LibA/src/LibA0.bf +++ b/IDEHelper/Tests/LibA/src/LibA0.bf @@ -42,7 +42,7 @@ namespace LibA val.Dispose(); } - public static void Alloc() where T : new, delete + public static void Alloc() where T : new where alloctype(T) : delete { let t = new T(); delete t; diff --git a/IDEHelper/Tests/src/Anonymous.bf b/IDEHelper/Tests/src/Anonymous.bf new file mode 100644 index 00000000..5461f7d1 --- /dev/null +++ b/IDEHelper/Tests/src/Anonymous.bf @@ -0,0 +1,109 @@ +using System; +using System.Collections; +namespace Tests; + +class Anonymous +{ + struct StructA + { + public [Union] struct { public int mX, mY; } mVals; + + [CRepr, SkipCall] struct { public int mA, mB; } GetVals() + { + decltype(GetVals()) retVals; + retVals.mA = 1; + retVals.mB = 2; + return retVals; + } + + public enum { Left, Right } GetDirection() => .Right; + + public enum : int { A, B, C } Val => .C; + } + + struct StructB + { + [Union] + public struct + { + public int mX; + public int mY; + }; + } + + struct StructC + { + public int mA; + + public this(int a) + { + mA = a; + } + + public Self Clone() => this; + } + + class ClassA + { + public int Val = 123; + } + + public static class : this(int x, int y) sValA = new .(3, 4) ~ delete _; + + [Test] + public static void TestBasics() + { + StructA sa = default; + sa.mVals.mX = 123; + Test.Assert(sa.mVals.mY == 123); + + var val = sa.[Friend]GetVals(); + Test.Assert(val.mA == 0); + Test.Assert(val.mB == 0); + + Test.Assert(sa.GetDirection() == .Right); + Test.Assert(sa.Val == .C); + + StructB sb = default; + sb.mX = 345; + Test.Assert(sb.mY == 345); + + var sc = StructC(456) + { + public int mB = 567; + + { + _.mB += 1000; + }, + mB += 10000 + }; + Test.Assert(sc.mA == 456); + Test.Assert(sc.mB == 11567); + + List scList = scope .() { sc }; + List scList2 = scope List() + { + (int, float) GetTuple() => default; + int this[float f] => 99; + + scList[0].Clone(), + (scList[0].Clone()), + (.)(scList[0].Clone()) + }; + Test.Assert(scList2.Count == 3); + + var ca = scope ClassA() + { + public override void ToString(String strBuffer) + { + strBuffer.Append("ClassA override"); + } + }; + + var str = ca.ToString(.. scope .()); + Test.Assert(str == "ClassA override"); + + Test.Assert(sValA.x == 3); + Test.Assert(sValA.y == 4); + } +} diff --git a/IDEHelper/Tests/src/Append.bf b/IDEHelper/Tests/src/Append.bf index 4af4c07e..0a044f7b 100644 --- a/IDEHelper/Tests/src/Append.bf +++ b/IDEHelper/Tests/src/Append.bf @@ -83,6 +83,22 @@ namespace Tests public int mC = 234; } + static class ClassG + { + public static append String sFirst; + public static append String sSecond; + public static append String sThird; + + static public StringView sValue => Self.sFirst; + + static public void Set(StringView value) + { + Self.sThird.Set(value); + Self.sSecond.Set(value); + Self.sFirst.Set(value); + } + } + static void CheckData(Object obj, int lastAllocSize, uint8[] data) { int objSize = typeof(Object).InstanceSize; @@ -129,6 +145,11 @@ namespace Tests Test.Assert(cf.mB.AllocSize == 1024); Test.Assert(cf.mC == 234); cf.mB.Append('!', 2048); + + ClassG.Set("1234567890"); + Test.Assert(ClassG.sFirst == "1234567890"); + Test.Assert(ClassG.sSecond == "1234567890"); + Test.Assert(ClassG.sThird == "1234567890"); } #endif } diff --git a/IDEHelper/Tests/src/Comptime.bf b/IDEHelper/Tests/src/Comptime.bf index da08ce75..892d4e84 100644 --- a/IDEHelper/Tests/src/Comptime.bf +++ b/IDEHelper/Tests/src/Comptime.bf @@ -551,6 +551,42 @@ namespace Tests } } + struct StructD + { + [Comptime] + public static Span GetTypes() + { + List list = scope .(); + list.Add(typeof(StructA)); + list.Add(typeof(EnumA)); + return list; + } + } + + struct StructE + { + [Comptime] + public static int GetSizes() + { + const let typeList = StructD.GetTypes(); + return typeList[0].InstanceSize + typeList[1].InstanceSize; + } + } + + public static int GetLocalVal1() + { + static int sVal = 100; + sVal++; + return sVal; + } + + [Comptime] + public static int GetLocalVal2() + { + GetLocalVal1(); + return GetLocalVal1(); + } + [Test] public static void TestBasics() { @@ -604,8 +640,6 @@ namespace Tests Test.Assert(typeof(decltype(f)) == typeof(float)); Test.Assert(ClassB.cTimesTen == 30); - - DictWrapper> dictWrap = scope .(); dictWrap.[Friend]mValue.Add(1, 2.3f); dictWrap.[Friend]mValue.Add(2, 3.4f); @@ -643,6 +677,12 @@ namespace Tests DefaultCtorTest dct = .(); Test.Assert(dct.mA == 123); + + const int typeSizes = StructE.GetSizes(); + Test.Assert(typeSizes == sizeof(StructA) + sizeof(EnumA)); + + const int cVal = GetLocalVal2(); + Test.Assert(cVal == 102); } } } diff --git a/IDEHelper/Tests/src/ConstExprs.bf b/IDEHelper/Tests/src/ConstExprs.bf index 0149d116..d474619e 100644 --- a/IDEHelper/Tests/src/ConstExprs.bf +++ b/IDEHelper/Tests/src/ConstExprs.bf @@ -85,6 +85,20 @@ namespace Tests } } + struct SpecialId : int + { + public const Self CONST = (.)(int)(void*)(char8*)"ABC"; + public static Self operator implicit(String str) => (.)(int)(void*)(char8*)str; + } + + public static void TestStr(SpecialId specialId) + { + char8* ptr = (.)(void*)(int)specialId; + + StringView sv = .(ptr); + Test.Assert(sv == "ABC"); + } + [Test] public static void TestBasics() { @@ -101,6 +115,9 @@ namespace Tests Test.Assert(TestRangedArray.cRangeEnd - TestRangedArray.cRangeStart == 7); Test.Assert(TestRangedArray.cError == "Invalid type: -3...^1"); + + TestStr(.CONST); + TestStr("ABC"); } } } diff --git a/IDEHelper/Tests/src/Delegates.bf b/IDEHelper/Tests/src/Delegates.bf index 260bec7c..49ef08ae 100644 --- a/IDEHelper/Tests/src/Delegates.bf +++ b/IDEHelper/Tests/src/Delegates.bf @@ -232,9 +232,16 @@ namespace Tests public static void TestCasting() { - delegate int(int, int) dlg0 = null; + delegate int(int, int) dlg0 = scope (a, b) => 1; delegate int(int a, int b) dlg1 = dlg0; delegate int(int a2, int b2) dlg2 = (.)dlg1; + delegate int(float a, float b) dlg3 = scope (a, b) => 2; + + Object obj = dlg0; + dlg1 = (.)obj; + dlg2 = (.)obj; + Test.Assert(obj is delegate int(int a, int b)); + Test.Assert(!(obj is delegate int(float a, float b))); function int(int, int) func0 = null; function int(int a, int b) func1 = func0; diff --git a/IDEHelper/Tests/src/Enums.bf b/IDEHelper/Tests/src/Enums.bf index 912c4638..8309ee11 100644 --- a/IDEHelper/Tests/src/Enums.bf +++ b/IDEHelper/Tests/src/Enums.bf @@ -88,6 +88,45 @@ namespace Tests // Started from 129 elements, no less } + [AllowDuplicates] + public enum EnumJ : uint32 + { + SDL_BUTTON_LEFT = 1, + SDL_BUTTON_LMASK = (1u << ((SDL_BUTTON_LEFT.Underlying) - 1)), + } + + [AllowDuplicates] + public enum EnumK + { + SDL_BUTTON_LEFT = 1, + SDL_BUTTON_LMASK = (1u << ((SDL_BUTTON_LEFT.Underlying) - 1)), + } + + public enum EnumL + { + case A; + + public static int operator implicit(Self self); + } + + public enum EnumM + { + public static implicit operator int(Self self); + + case A; + case B; + case C; + } + + [CRepr] + public enum EnumN + { + A, + B, + C, + D = 4 + } + [Test] static void TestBasic() { @@ -97,6 +136,13 @@ namespace Tests Test.Assert(sizeof(EnumB) == 2); Test.Assert(sizeof(EnumC) == 4); Test.Assert(sizeof(EnumD) == 8); + + EnumM em = ?; + int i = em; + uint u = (uint)em; + + i = 123; + EnumA e = (EnumA)i; } [Test] @@ -229,6 +275,45 @@ namespace Tests if (ei case .DY(var eh)) foundH = eh == .B; Test.Assert(foundH); + + Test.Assert((int)EnumJ.SDL_BUTTON_LMASK == 1); + Test.Assert(typeof(EnumJ).Size == 4); + + Test.Assert((int)EnumK.SDL_BUTTON_LMASK == 1); + Test.Assert(typeof(EnumK).Size == 8); + + EnumL el = .A; + Test.Assert(el == 0); + } + + [Test] + static void TestCrepr() + { + EnumN value = .B; + + Test.Assert(sizeof(EnumN) == sizeof(System.Interop.c_int)); + + Test.Assert(value.HasFlag(.A) == true); + Test.Assert(value.HasFlag(.B) == true); + Test.Assert(value.HasFlag(.B | .C) == false); + Test.Assert(value.HasFlag(.D) == false); + Test.Assert(value.Underlying == 1); + + value = .B | .C; + Test.Assert(value.HasFlag(.A) == true); + Test.Assert(value.HasFlag(.B) == true); + Test.Assert(value.HasFlag(.B | .C) == true); + Test.Assert(value.HasFlag(.D) == false); + Test.Assert(value.Underlying == 3); + + ref System.Interop.c_int valueRef = ref value.UnderlyingRef; + valueRef = 2; + Test.Assert(value == .C); + + String str = scope String(); + value.ToString(str); + + Test.Assert(str == "C"); } } } diff --git a/IDEHelper/Tests/src/ExtensionMethods.bf b/IDEHelper/Tests/src/ExtensionMethods.bf index 90238e28..0bf9591a 100644 --- a/IDEHelper/Tests/src/ExtensionMethods.bf +++ b/IDEHelper/Tests/src/ExtensionMethods.bf @@ -12,8 +12,21 @@ namespace Tests return 123; } + public static void AddTo(this ref StructA @this) + { + @this.mA += 1000; + } + + public static void AddTo2(this in StructA @this) + { +#unwarn + (&@this).mA += 1000; + } + class ClassA { + public int mA = 10000; + public static void Test() { Test.Assert("Abc".Remove(1.2f, 2.3f) == 123); @@ -29,6 +42,11 @@ namespace Tests } + struct StructA + { + public int mA; + } + [Test] public static void TestBasics() { @@ -46,6 +64,13 @@ namespace Tests float a = 1.2f; float b = 2.3f; Test.Assert(a.CompareIt(b) < 0); + + StructA sa = .() { mA = 123 }; + + sa.AddTo(); + Test.Assert(sa.mA == 1123); + sa.AddTo2(); + Test.Assert(sa.mA == 2123); } } diff --git a/IDEHelper/Tests/src/FuncRefs.bf b/IDEHelper/Tests/src/FuncRefs.bf index f1ecf2b5..3660da25 100644 --- a/IDEHelper/Tests/src/FuncRefs.bf +++ b/IDEHelper/Tests/src/FuncRefs.bf @@ -129,8 +129,8 @@ namespace Tests { public int Test; } - public static List mList = new .() ~ delete _; - private static Event mEvent; + public static List sList = new .() ~ delete _; + private static Event sEvent; [Test] public static void TestBasics() @@ -161,17 +161,17 @@ namespace Tests TestA ta = scope .(); ta.Vec = .(33, 44); - mList.Add(scope .()); - mEvent.Add(new (sender, e) => + sList.Add(scope .()); + sEvent.Add(new (sender, e) => { - mList.ForEach((b) => { b.Test = 1; }); + sList.ForEach((b) => { b.Test = 1; }); }); - mEvent(null, .Empty); - mEvent.Dispose(); - Test.Assert(mList.Back.Test == 1); + sEvent(null, .Empty); + sEvent.Dispose(); + Test.Assert(sList.Back.Test == 1); int i = 0; - mList.ForEach((l) => l.mList.ForEach((l) => + sList.ForEach((l) => sList.ForEach((l) => { i++; })); @@ -483,6 +483,21 @@ namespace Tests } } + public static void Test(F func) + where F: delegate void(delegate void(String, params Span)) + { + String str = scope .(); + + delegate void(String, params Span) setError = scope (errStr, args) => { str.AppendF(errStr, params args); }; + delegate void() call = scope () => func(setError); + call(); + } + + public static void Test() where T : var + { + Test((e) => e("hi!")); + } + [Test] public static void ClassTestA() { @@ -499,6 +514,8 @@ namespace Tests val.Test(); val = .(); val.TestDlg(); + + Test(); } [Test] diff --git a/IDEHelper/Tests/src/Functions.bf b/IDEHelper/Tests/src/Functions.bf index 7dcaf804..6bf073b9 100644 --- a/IDEHelper/Tests/src/Functions.bf +++ b/IDEHelper/Tests/src/Functions.bf @@ -182,6 +182,13 @@ namespace Tests { sVal = 123; } + + public static void TestDefer() + { + function void() func = => Func; + if (func != null) + defer:: func.Invoke(); + } } public static int UseFunc0(function int (T this, float f) func, T a, float b) @@ -254,6 +261,10 @@ namespace Tests ClassC.Test(); Test.Assert(Zoop.sVal == 123); + + Zoop.sVal = 0; + Zoop.TestDefer(); + Test.Assert(Zoop.sVal == 123); } } } diff --git a/IDEHelper/Tests/src/Generics.bf b/IDEHelper/Tests/src/Generics.bf index 7d0790ff..0f395b0c 100644 --- a/IDEHelper/Tests/src/Generics.bf +++ b/IDEHelper/Tests/src/Generics.bf @@ -168,7 +168,7 @@ namespace Tests } } - public static void Alloc0() where T : new, delete, IDisposable + public static void Alloc0() where T : new, IDisposable where alloctype(T) : delete { alloctype(T) val = new T(); val.Dispose(); @@ -199,6 +199,11 @@ namespace Tests delete val2; } + public static int IntPtrTest(T val) where T : int* + { + return *val; + } + public class ClassE { public static Self Instance = new ClassE() ~ delete _; @@ -513,6 +518,9 @@ namespace Tests var innerC = OuterA.InnerC.this(123); Test.Assert(innerC.mVal == 123); + + int iVal = 123; + Test.Assert(IntPtrTest(&iVal) == 123); } } diff --git a/IDEHelper/Tests/src/Generics2.bf b/IDEHelper/Tests/src/Generics2.bf index 93eb8ee9..ea68d7df 100644 --- a/IDEHelper/Tests/src/Generics2.bf +++ b/IDEHelper/Tests/src/Generics2.bf @@ -1,3 +1,5 @@ +#pragma warning disable 168 + using System; using System.Collections; @@ -21,6 +23,32 @@ namespace Tests } } + interface IZop + { + void Zop(); + } + + interface IZag + { + void Zag(); + } + + class ClassC where T : IDisposable + { + public void MethodA(T val) where T : IZop, delete, new + { + val.Zop(); + MethodB(val); + delete val; + } + + public void MethodB(T val) where T : IZop, delete, new + { + alloctype(T) z = new T(); + delete val; + } + } + struct TestFunc { private int mId; diff --git a/IDEHelper/Tests/src/Globals.bf b/IDEHelper/Tests/src/Globals.bf index b2eac727..69dbc8eb 100644 --- a/IDEHelper/Tests/src/Globals.bf +++ b/IDEHelper/Tests/src/Globals.bf @@ -1,9 +1,21 @@ using System; +namespace Other +{ + static + { + public const int sOther1 = 123; + } +} + +using Other; + namespace Tests { class Globals { + static uint8[sOther1] sArr; + public struct StructA : this(int64 a, int32 b) { } @@ -73,6 +85,8 @@ namespace Tests Test.Assert(LibSpace.MethodA() == 100); Test.Assert(LibSpace.MethodB() == 200); + + Test.Assert(sArr.Count == 123); } } } diff --git a/IDEHelper/Tests/src/Interfaces.bf b/IDEHelper/Tests/src/Interfaces.bf index 475f065d..71f25659 100644 --- a/IDEHelper/Tests/src/Interfaces.bf +++ b/IDEHelper/Tests/src/Interfaces.bf @@ -432,6 +432,29 @@ namespace Tests T sum = a + b; } + interface IIntVal + { + int IntVal { get; } + } + + struct StructC : IIntVal + { + int IIntVal.IntVal + { + get + { + return 123; + } + } + } + + static int GetIntVals(T val) where T : IIntVal + { + int a = (val).[Friend]IntVal; + int b = val.IntVal; + return a + b; + } + [Test] public static void TestDefaults() { @@ -473,6 +496,7 @@ namespace Tests Test.Assert(TestIFaceD2(cg) == 999); ClassH ch = scope .(); + Test.Assert(GetIntVals(StructC()) == 246); } } } diff --git a/IDEHelper/Tests/src/Loops.bf b/IDEHelper/Tests/src/Loops.bf index 3ed41260..d131a47a 100644 --- a/IDEHelper/Tests/src/Loops.bf +++ b/IDEHelper/Tests/src/Loops.bf @@ -189,6 +189,14 @@ namespace Tests for (var i in emptySpan.Reversed) Test.FatalError(); + + do + { + } + while (false) + { + + } } public static void TestEnumerator1(EnumeratorTest e) diff --git a/IDEHelper/Tests/src/Mixins.bf b/IDEHelper/Tests/src/Mixins.bf index 2f6ffda9..3f7290d4 100644 --- a/IDEHelper/Tests/src/Mixins.bf +++ b/IDEHelper/Tests/src/Mixins.bf @@ -97,6 +97,64 @@ namespace Tests } } + public static mixin Test(T a) where T : struct {} + public static mixin Test(Type value) {} + public static mixin Test(T a) where T : class, delete {} + + public class TestClass + { + public bool CallTest(TValue val) + where TValue : var + { + SelfOuter.Test!(val); + return true; + } + } + + public static mixin Test2(T a) where T : struct {} + public static mixin Test2(T a) where T : class, delete {} + + public class TestClass2 + { + public bool CallTest(TValue val) + where TValue : var + { + SelfOuter.Test2!(val); + return true; + } + } + + class Foo where T : class + { + static void Test () + { + Helper.Pop!(); + } + } + + extension Foo + { + + } + + class Helper + { + + } + + extension Helper + { + static public mixin Pop () + { + Pop2() + } + + static public T Pop2 () where TVal : var, struct, INumeric + { + return default; + } + } + [Test] public static void TestBasics() { @@ -130,6 +188,12 @@ namespace Tests DispClass dc = scope .(); DisposeIt!(dc); + + let test2 = scope TestClass(); + test2.CallTest(2); + + let test3 = scope TestClass2(); + test3.CallTest(2); } [Test] @@ -165,6 +229,8 @@ namespace Tests var sv2 = Unwrap!(svRes)..Trim(); Test.Assert(svRes.Value == "ab "); Test.Assert(sv2 == "ab"); + + Helper.Pop!(); } } diff --git a/IDEHelper/Tests/src/Nullable.bf b/IDEHelper/Tests/src/Nullable.bf index 237d0542..df2df0da 100644 --- a/IDEHelper/Tests/src/Nullable.bf +++ b/IDEHelper/Tests/src/Nullable.bf @@ -114,6 +114,24 @@ namespace Tests iNull ??= iNull2; Test.Assert(iNull == 123); + + Result ir = .Ok(234); + Result? irn = ir; + + if (irn case .Ok(let val)) + { + Test.Assert(val == 234); + } + else + { + Test.FatalError(); + } + + irn = null; + if (irn case .Ok(let val2)) + { + Test.FatalError(); + } } } } diff --git a/IDEHelper/Tests/src/Opaques.bf b/IDEHelper/Tests/src/Opaques.bf new file mode 100644 index 00000000..9bf64ad1 --- /dev/null +++ b/IDEHelper/Tests/src/Opaques.bf @@ -0,0 +1,36 @@ +using System; + +namespace Tests; + +class Opaques +{ + struct StructA + { + public int mA; + public int mB; + } + + struct StructB; + + public static void Modify(this ref StructB @this, int addA, int addB) + { + StructB* sbPtr = &@this; + StructA* saPtr = (.)(void*)sbPtr; + saPtr.mA += addA; + saPtr.mB += addB; + } + + static int Method1(StructB sb, void* ptr) => 0; + + [Test] + public static void TestBasics() + { + StructA sa = .() { mA = 123, mB = 234 }; + StructB* sb = (.)&sa; + sb.Modify(1000, 2000); + Test.Assert(sa.mA == 1123); + Test.Assert(sa.mB == 2234); + + Method1(default, default); + } +} \ No newline at end of file diff --git a/IDEHelper/Tests/src/Params.bf b/IDEHelper/Tests/src/Params.bf new file mode 100644 index 00000000..d0ba61c6 --- /dev/null +++ b/IDEHelper/Tests/src/Params.bf @@ -0,0 +1,356 @@ +#pragma warning disable 168 + +using System; +using System.Reflection; + +namespace Tests; + +class Params +{ + [AttributeUsage(.Method)] + struct StringFormatAttribute : Attribute, IOnMethodInit + { + [Comptime] + public void OnMethodInit(MethodInfo methodInfo, Self* prev) + { + String code = scope .(); + + String format = null; + + if (var constType = methodInfo.GetGenericArgType(0) as ConstExprType) + { + if (constType.ValueType == typeof(String)) + { + format = String.GetById((.)constType.ValueData); + } + } + + if (String.IsNullOrEmpty(format)) + return; + + int pos = 0; + int len = format.Length; + char8 ch = '\x00'; + + String s = null; + String fmt = ""; + int autoArgIdx = 0; + bool hasTempStr = false; + + void FormatError() + { + Runtime.FatalError("Format Error"); + } + + String bufOut = scope .(); + + void FlushOut() + { + if (bufOut.IsEmpty) + return; + code.Append("outStr.Append("); + bufOut.Quote(code); + code.Append(");\n"); + bufOut.Clear(); + } + + while (true) + { + int p = pos; + int i = pos; + while (pos < len) + { + ch = format[pos]; + + pos++; + if (ch == '}') + { + if (pos < len && format[pos] == '}') // Treat as escape character for }} + pos++; + else + FormatError(); + } + + if (ch == '{') + { + if (pos < len && format[pos] == '{') // Treat as escape character for {{ + pos++; + else + { + pos--; + break; + } + } + + bufOut.Append(ch); + } + + if (pos == len) break; + pos++; + int index = 0; + if (pos == len || (ch = format[pos]) < '0' || ch > '9') + { + if ((pos < len) && + ((ch == '}') || (ch == ':') || (ch == ','))) + index = autoArgIdx++; + else + FormatError(); + } + else + { + repeat + { + index = index * 10 + ch - '0'; + pos++; + if (pos == len) FormatError(); + ch = format[pos]; + } + while (ch >= '0' && ch <= '9' && index < 1000000); + } + + var paramType = methodInfo.GetParamType(index + 3); + var paramName = methodInfo.GetParamName(index + 3); + + while (pos < len && (ch = format[pos]) == ' ') pos++; + bool leftJustify = false; + int width = 0; + if (ch == ',') + { + pos++; + while (pos < len && format[pos] == ' ') pos++; + + if (pos == len) FormatError(); + ch = format[pos]; + if (ch == '-') + { + leftJustify = true; + pos++; + if (pos == len) FormatError(); + ch = format[pos]; + } + if (ch < '0' || ch > '9') FormatError(); + repeat + { + width = width * 10 + ch - '0'; + pos++; + if (pos == len) FormatError(); + ch = format[pos]; + } + while (ch >= '0' && ch <= '9' && width < 1000000); + } + + while (pos < len && (ch = format[pos]) == ' ') pos++; + + //Object arg = args[index]; + if (ch == ':') + { + if (fmt == "") + fmt = scope:: String(64); + else + fmt.Clear(); + + bool isFormatEx = false; + pos++; + p = pos; + i = pos; + while (true) + { + if (pos == len) FormatError(); + ch = format[pos]; + pos++; + if (ch == '{') + { + isFormatEx = true; + if (pos < len && format[pos] == '{') // Treat as escape character for {{ + pos++; + else + FormatError(); + } + else if (ch == '}') + { + // We only treat '}}' as an escape character if the format had an opening '{'. Otherwise we just close on the first '}' + if ((isFormatEx) && (pos < len && format[pos] == '}')) // Treat as escape character for }} + pos++; + else + { + pos--; + break; + } + } + + if (fmt == null) + { + fmt = scope:: String(0x100); + } + fmt.Append(ch); + } + } + if (ch != '}') Runtime.FatalError("Format error"); + pos++; + FlushOut(); + + bool checkNull = paramType.IsObject || paramType.IsInterface; + bool hasWidth = width != 0; + bool hasFmt = fmt != ""; + + if (checkNull) + { + code.AppendF($"if ({paramName} == null)\n"); + code.Append(" outStr.Append(\"null\");\nelse\n{\n"); + } + + if ((!leftJustify) && (width > 0)) + { + // We need a temporary string + if (!hasTempStr) + { + code.Insert(0, "var s = scope String(128);\n"); + hasTempStr = false; + } + else + { + code.Append("s.Clear();\n"); + } + + if (paramType.ImplementsInterface(typeof(IFormattable))) + { + if (!hasFmt) + { + code.AppendF("if (provider != null)\n "); + } + + code.AppendF($"((IFormattable){paramName}).ToString(s, "); + fmt.Quote(code); + code.AppendF($", provider);\n"); + + if (!hasFmt) + { + code.AppendF("else\n"); + code.AppendF($" {paramName}.ToString(s);\n"); + } + } + else + { + code.AppendF($"{paramName}.ToString(s);\n"); + } + + code.AppendF($"outStr.Append(' ', {width} - s.[Friend]mLength);\n"); + code.Append("outStr.Append(s);\n"); + } + else + { + if (hasWidth) + { + code.AppendF($"int start_{pos} = outStr.[Friend]mLength;\n"); + } + + if (paramType.ImplementsInterface(typeof(IFormattable))) + { + if (!hasFmt) + { + code.AppendF("if (provider != null)\n "); + } + + code.AppendF($"((IFormattable){paramName}).ToString(outStr, "); + fmt.Quote(code); + code.AppendF($", provider);\n"); + + if (!hasFmt) + { + code.AppendF("else\n"); + code.AppendF($" {paramName}.ToString(outStr);\n"); + } + } + else + { + code.AppendF($"{paramName}.ToString(outStr);\n"); + } + + if (hasWidth) + { + code.AppendF($"outStr.Append(' ', {width} - (outStr.[Friend]mLength - start_{pos}));\n"); + } + } + + if (checkNull) + { + code.Append("}\n"); + } + } + FlushOut(); + Compiler.EmitMethodEntry(methodInfo, code); + } + } + + [StringFormat] + static void StrFormat(String outStr, IFormatProvider provider, TStr formatStr, params TArgs args) where TStr : const String where TArgs : Tuple + { + + } + + class ClassA where T : Tuple + { + public static int Test(delegate int(char8 a, params T) dlg, params T par) + { + return dlg('A', params par); + } + } + + class ClassB : ClassA<(int a, float b)> + { + + } + + static void Test1(params T args) where T : Tuple + { + Test3(args); + } + + static void Test2(params T args) where T : Delegate + { + var vals = args; + Test3(args); + } + + static void Test3(T args) where T : Tuple + { + + } + + static void Test4(params (int, float) args) + { + var a = args; + int arg0 = a.0; + float arg1 = a.1; + } + + struct StructA + { + public int mA; + + public override void ToString(String strBuffer) + { + strBuffer.AppendF($"StructA({mA})"); + } + } + + [Test] + public static void TestBasics() + { + int val = ClassB.Test(scope (a, __a, b) => + { + return (.)a + (.)__a + (.)b; + }, 10, 2.3f); + Test.Assert(val == 65+10+2); + + Test1(1, 2.3f); + delegate void(int a, float b) dlg = default; + Test2(1, 2.3f); + + String tStr = null; + + StructA sa = .() { mA = 123 }; + String str = StrFormat(.. scope .(), null, $"This is a test string {123,-6} with some numbers in it {234,6} {0x1234:X} {sa} {tStr}."); + Test.Assert(str == "This is a test string 123 with some numbers in it 234 1234 StructA(123) null."); + } +} \ No newline at end of file diff --git a/IDEHelper/Tests/src/Switches.bf b/IDEHelper/Tests/src/Switches.bf index 22d101fc..e02ee4ae 100644 --- a/IDEHelper/Tests/src/Switches.bf +++ b/IDEHelper/Tests/src/Switches.bf @@ -34,6 +34,13 @@ namespace Tests } } + enum ETest + { + case A(int a); + case B(float f); + case C; + } + [Test] public static void TestBasics() { @@ -65,6 +72,47 @@ namespace Tests result = 4; } Test.Assert(result == 4); + + result = 0; + const int constVal = 123; + switch (constVal) + { + case 10: + result = 1; + case 123: + result = 2; + default: + result = 3; + } + Test.Assert(result == 2); + + result = 99; + const Result iResult = .Err; + bool eq = iResult case .Ok(ref result); + Test.Assert(result == 99); + + if (iResult not case .Ok(var result2)) + { + } + else + { + Test.FatalError(); + } + Test.Assert(result2 == 0); + + const ETest t = .B(234.5f); + switch (t) + { + case .A(let a): + result = 1; + case .B(let b): + result = (.)b; + case .C: + result = 3; + default: + result = 4; + } + Test.Assert(result == 234); } } } diff --git a/IDEHelper/Tests/src/UsingField.bf b/IDEHelper/Tests/src/UsingField.bf index 6fef581b..29ff70ec 100644 --- a/IDEHelper/Tests/src/UsingField.bf +++ b/IDEHelper/Tests/src/UsingField.bf @@ -48,14 +48,25 @@ namespace Tests [Union] struct Vector2 { - public struct Coords - { - public float mX; - public float mY; - } + public struct Coords : this(float mX, float mY); public float[2] mValues; - public using Coords mCoords; + using public Coords mCoords; + + public this(float x, float y) + { + mX = x; + mY = y; + } + } + + [Union] + struct Vector2b + { + public struct Coords : this(float mX, float mY); + + public float[2] mValues; + public using Coords; public this(float x, float y) { @@ -80,8 +91,16 @@ namespace Tests Test.Assert(sizeof(Vector2) == 8); Test.Assert(vec.mX == 1.2f); Test.Assert(vec.mY == 2.3f); + Test.Assert(vec.mCoords == .(1.2f, 2.3f)); Test.Assert(vec.mValues[0] == 1.2f); Test.Assert(vec.mValues[1] == 2.3f); + + Vector2b vecb = .(1.2f, 2.3f); + Test.Assert(sizeof(Vector2b) == 8); + Test.Assert(vecb.mX == 1.2f); + Test.Assert(vecb.mY == 2.3f); + Test.Assert(vecb.mValues[0] == 1.2f); + Test.Assert(vecb.mValues[1] == 2.3f); } } } \ No newline at end of file diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp index 8b3f8d73..e95f6a96 100644 --- a/IDEHelper/WinDebugger.cpp +++ b/IDEHelper/WinDebugger.cpp @@ -1151,9 +1151,14 @@ void WinDebugger::HotLoad(const Array& objectFiles, int hotIdx) int startingModuleIdx = (int)mDebugTarget->mDbgModules.size(); + bool hasHotVData = false; + bool failed = false; for (auto fileName : objectFiles) { + if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1)) + hasHotVData = true; + BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str()); DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx); if ((newBinary != NULL) && (newBinary->mFailed)) @@ -1186,6 +1191,9 @@ void WinDebugger::HotLoad(const Array& objectFiles, int hotIdx) mDebugTarget->RehupSrcFiles(); + if (hasHotVData) + mDebugTarget->mVDataHotIdx = hotIdx; + for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++) { auto breakpoint = mBreakpoints[breakIdx]; @@ -2912,7 +2920,8 @@ bool WinDebugger::DoUpdate() if (!handled) { - OutputMessage(StrFormat("Skipping first chance exception %08X at address %@ in thread %d\n", exceptionRecord->ExceptionCode, exceptionRecord->ExceptionAddress, threadInfo->mThreadId)); + if (mRunState != RunState_DebugEval) + OutputMessage(StrFormat("Skipping first chance exception %08X at address %@ in thread %d\n", exceptionRecord->ExceptionCode, exceptionRecord->ExceptionAddress, threadInfo->mThreadId)); ::ContinueDebugEvent(mDebuggerWaitingThread->mProcessId, mDebuggerWaitingThread->mThreadId, DBG_EXCEPTION_NOT_HANDLED); mIsDebuggerWaiting = false; } @@ -5982,6 +5991,8 @@ String WinDebugger::MaybeQuoteFormatInfoParam(const StringImpl& str) DbgTypedValue WinDebugger::EvaluateInContext(DbgCompileUnit* dbgCompileUnit, const DbgTypedValue& contextTypedValue, const StringImpl& subExpr, DwFormatInfo* formatInfo, String* outReferenceId, String* outErrors) { DbgEvaluationContext dbgEvaluationContext(this, dbgCompileUnit->mDbgModule, subExpr, formatInfo, contextTypedValue); + if (dbgEvaluationContext.mDbgExprEvaluator == NULL) + return DbgTypedValue(); dbgEvaluationContext.mDbgExprEvaluator->mDbgCompileUnit = dbgCompileUnit; if (formatInfo != NULL) { @@ -6743,8 +6754,10 @@ String WinDebugger::ReadString(DbgTypeCode charType, intptr addr, bool isLocalAd return retVal; } -void WinDebugger::ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValue useTypedValue, String& evalStr, String& displayString, DwFormatInfo& formatInfo, DebugVisualizerEntry* debugVis, bool limitLength) +bool WinDebugger::ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValue useTypedValue, String& evalStr, String& displayString, DwFormatInfo& formatInfo, DebugVisualizerEntry* debugVis, bool limitLength) { + bool success = true; + for (int i = 0; i < (int)evalStr.length(); i++) { char c = evalStr[i]; @@ -6786,22 +6799,31 @@ void WinDebugger::ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValu if ((formatInfo.mRawString) && (limitLength)) { displayString = result; - return; + return success; } - int crPos = result.IndexOf('\n'); - if (crPos != -1) - displayString += result.Substring(0, crPos); - else + if (displayStrFormatInfo.mRawString) + { displayString += result; + } + else + { + int crPos = result.IndexOf('\n'); + if (crPos != -1) + displayString += result.Substring(0, crPos); + else + displayString += result; + } } else if (debugVis != NULL) { + success = false; displayString += ""; DbgVisFailed(debugVis, evalString, errors); } else { + success = false; displayString += ""; } } @@ -6822,6 +6844,8 @@ void WinDebugger::ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValu displayString += c; } + + return success; } static bool IsNormalChar(uint32 c) @@ -8489,7 +8513,7 @@ String WinDebugger::DbgTypedValueToString(const DbgTypedValue& origTypedValue, c { addr_target objectSize = ReadMemory(ptrVal + sizeof(addr_target)); addr_target largeAllocInfo = ReadMemory(ptrVal + objectSize); - stackTraceLen = largeAllocInfo & 0xFFFF; + stackTraceLen = (largeAllocInfo >> 8) & 0xFFFF; stackTraceAddr = ptrVal + objectSize + sizeof(addr_target); } else if ((bfObjectFlags & BfObjectFlag_AllocInfo_Short) != 0) diff --git a/IDEHelper/WinDebugger.h b/IDEHelper/WinDebugger.h index 5aab5afc..60882d75 100644 --- a/IDEHelper/WinDebugger.h +++ b/IDEHelper/WinDebugger.h @@ -516,7 +516,7 @@ public: DbgTypedValue EvaluateInContext(DbgCompileUnit* dbgCompileUnit, const DbgTypedValue& contextTypedValue, const StringImpl& subExpr, DwFormatInfo* formatInfo = NULL, String* outReferenceId = NULL, String* outErrors = NULL); bool EvalCondition(DebugVisualizerEntry* debugVis, DbgCompileUnit* dbgCompileUnit, DbgTypedValue typedVal, DwFormatInfo& formatInfo, const StringImpl& condition, const Array& dbgVisWildcardCaptures, String& errorStr); DwDisplayInfo* GetDisplayInfo(const StringImpl& referenceId); - void ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValue useTypedValue, String& evalStr, String& displayString, DwFormatInfo& formatInfo, DebugVisualizerEntry* debugVis, bool limitLength); + bool ProcessEvalString(DbgCompileUnit* dbgCompileUnit, DbgTypedValue useTypedValue, String& evalStr, String& displayString, DwFormatInfo& formatInfo, DebugVisualizerEntry* debugVis, bool limitLength); String ReadString(DbgTypeCode charType, intptr addr, bool isLocalAddr, intptr maxLength, DwFormatInfo& formatInfo, bool wantStringView); String DbgTypedValueToString(const DbgTypedValue& typedValue, const StringImpl& expr, DwFormatInfo& formatFlags, DbgExprEvaluator* optEvaluator, bool fullPrecision = false); bool ShouldShowStaticMember(DbgType* dbgType, DbgVariable* member); diff --git a/bin/WasmLaunch.exe b/bin/WasmLaunch.exe index 8d2cc00b..13d5220a 100644 Binary files a/bin/WasmLaunch.exe and b/bin/WasmLaunch.exe differ diff --git a/bin/build.bat b/bin/build.bat index 6b19b894..2b1f6dde 100644 --- a/bin/build.bat +++ b/bin/build.bat @@ -14,14 +14,14 @@ PUSHD %~dp0..\ mkdir stats :STATS_HAS -@IF EXIST BeefDep0_Done.txt GOTO DEPS0_HAS +@IF EXIST BeefDep1_Done.txt GOTO DEPS0_HAS @ECHO Downloading dependencies (LLVM)... -bin\curl.exe -O https://www.beeflang.org/BeefDep0.zip +bin\curl.exe -O https://www.beeflang.org/BeefDep1.zip @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR @ECHO Extracting dependencies (takes a while)... -bin\tar.exe -xf BeefDep0.zip +bin\tar.exe -xf BeefDep1.zip @IF %ERRORLEVEL% NEQ 0 GOTO -del BeefDep0.zip +del BeefDep1.zip :DEPS0_HAS copy BeefLibs\SDL2\dist\SDL2.dll IDE\dist @@ -33,6 +33,9 @@ CALL bin/msbuild.bat BeefySysLib\BeefySysLib.vcxproj /p:Configuration=Debug /p:P CALL bin/msbuild.bat BeefySysLib\BeefySysLib.vcxproj /p:Configuration=Release /p:Platform=x64 /p:SolutionDir=%cd%\ /v:m %MSBUILD_FLAGS% @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR +CALL bin/msbuild.bat BeefySysLib\BeefySysLib.vcxproj /p:Configuration="Release Static" /p:Platform=x64 /p:SolutionDir=%cd%\ /v:m %MSBUILD_FLAGS% +@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR + CALL bin/msbuild.bat IDEHelper\IDEHelper.vcxproj /p:Configuration=Debug /p:Platform=x64 /p:SolutionDir=%cd%\ /v:m %MSBUILD_FLAGS% @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR diff --git a/bin/build.sh b/bin/build.sh index 18dc459d..0d1c26a5 100755 --- a/bin/build.sh +++ b/bin/build.sh @@ -43,15 +43,34 @@ else echo "Ninja isn't installed, consider installing it for faster build speeds." fi -LLVM_CONFIG=$(command -v llvm-config-18 2>/dev/null || command -v llvm-config 2>/dev/null) +LLVM_CONFIG=$(command -v llvm-config-19 2>/dev/null || + command -v /usr/lib/llvm18/bin/llvm-config 2>/dev/null || + command -v llvm-config 2>/dev/null) LLVM_FOUND=0 +LLVM_DIR="" if [ -n "$LLVM_CONFIG" ]; then LLVM_VERSION=$($LLVM_CONFIG --version) LLVM_MAJOR_VERSION=$(echo "$LLVM_VERSION" | cut -d. -f1) LLVM_MINOR_VERSION=$(echo "$LLVM_VERSION" | cut -d. -f2) - if [ "$LLVM_MAJOR_VERSION" = "18" ] && [ "$LLVM_MINOR_VERSION" = "1" ]; then + if [ "$LLVM_MAJOR_VERSION" = "19" ] && [ "$LLVM_MINOR_VERSION" = "1" ]; then LLVM_FOUND=1 + # Get the LLVM prefix directory and construct cmake path from it + LLVM_PREFIX=$($LLVM_CONFIG --prefix) + LLVM_DIR="$LLVM_PREFIX/lib/cmake/llvm" + else + # If first attempt didn't find 19.1, explicitly try the llvm18 path + LLVM_CONFIG="/usr/lib/llvm18/bin/llvm-config" + if [ -x "$LLVM_CONFIG" ]; then + LLVM_VERSION=$($LLVM_CONFIG --version) + LLVM_MAJOR_VERSION=$(echo "$LLVM_VERSION" | cut -d. -f1) + LLVM_MINOR_VERSION=$(echo "$LLVM_VERSION" | cut -d. -f2) + if [ "$LLVM_MAJOR_VERSION" = "19" ] && [ "$LLVM_MINOR_VERSION" = "1" ]; then + LLVM_FOUND=1 + LLVM_PREFIX=$($LLVM_CONFIG --prefix) + LLVM_DIR="$LLVM_PREFIX/lib/cmake/llvm" + fi + fi fi fi @@ -61,7 +80,7 @@ set -e ### Dependencies ### if [ $LLVM_FOUND == 0 ]; then - echo "ERROR: LLVM 18.1 was not detected on your system. Please install the package 'llvm-18-dev' and try again." >&2 + echo "ERROR: LLVM 19.1 was not detected on your system. Please install the package 'llvm-19-dev' and try again." >&2 exit fi @@ -83,12 +102,12 @@ fi cd jbuild_d -echo cmake $USE_NINJA $USE_SDL -DCMAKE_BUILD_TYPE=Debug ../ +echo cmake -DLLVM_DIR="$LLVM_DIR" $USE_NINJA $USE_SDL -DCMAKE_BUILD_TYPE=Debug ../ -cmake $USE_NINJA $USE_SDL $USE_FFI -DCMAKE_BUILD_TYPE=Debug ../ +cmake -DLLVM_DIR="$LLVM_DIR" $USE_NINJA $USE_SDL $USE_FFI -DCMAKE_BUILD_TYPE=Debug ../ cmake --build . cd ../jbuild -cmake $USE_NINJA $USE_SDL $USE_FFI -DCMAKE_BUILD_TYPE=RelWithDebInfo ../ +cmake -DLLVM_DIR="$LLVM_DIR" $USE_NINJA $USE_SDL $USE_FFI -DCMAKE_BUILD_TYPE=RelWithDebInfo ../ cmake --build . cd ../IDE/dist diff --git a/bin/miniserve.exe b/bin/miniserve.exe index 35b3fa4a..e26fc436 100644 Binary files a/bin/miniserve.exe and b/bin/miniserve.exe differ diff --git a/extern/llvm_build.bat b/extern/llvm_build.bat index cfdc8507..0adafed8 100644 --- a/extern/llvm_build.bat +++ b/extern/llvm_build.bat @@ -1,29 +1,37 @@ PUSHD %~dp0 -@IF EXIST llvm-project_18_1_4 GOTO LLVM_HAS -git clone --depth 1 --branch llvmorg-18.1.4 --config core.autocrlf=false https://github.com/llvm/llvm-project.git llvm-project_18_1_4 +@IF EXIST llvm-project_19_1_7 GOTO LLVM_HAS +git clone --depth 1 --branch llvmorg-19.1.7 --config core.autocrlf=false https://github.com/llvm/llvm-project.git llvm-project_19_1_7 @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR :LLVM_HAS -@IF EXIST llvm_win64_18_1_4 GOTO HAS_CONFIG -mkdir llvm_win64_18_1_4 -cd llvm_win64_18_1_4 -@REM cmake ../llvm-project_18_1_4/llvm -G"Visual Studio 17 2022" -Ax64 -Thost=x64 -DLLVM_ENABLE_PROJECTS=clang -D CMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86;WebAssembly" -cmake ../llvm-project_18_1_4/llvm -G"Visual Studio 17 2022" -Ax64 -Thost=x64 -D CMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86;WebAssembly" +@IF EXIST llvm_win64_19_1_7 GOTO HAS_CONFIG +mkdir llvm_win64_19_1_7 +cd llvm_win64_19_1_7 +@REM cmake ../llvm-project_19_1_7/llvm -G"Visual Studio 17 2022" -Ax64 -Thost=x64 -DLLVM_ENABLE_PROJECTS=clang -D CMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86;WebAssembly" +cmake ../llvm-project_19_1_7/llvm -G"Visual Studio 17 2022" -Ax64 -Thost=x64 -D CMAKE_MSVC_RUNTIME_LIBRARY="MultiThreaded$<$:Debug>" -DLLVM_TARGETS_TO_BUILD="AArch64;ARM;X86;WebAssembly" +@REM cmake ../llvm-project_19_1_7/llvm -G"Visual Studio 17 2022" @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR @GOTO DOBUILD :HAS_CONFIG -cd llvm_win64_18_1_4 +cd llvm_win64_19_1_7 @GOTO DOBUILD :DOBUILD -set /p LLVM_TARGETS=<../llvm_targets.txt -cmake --build . -t %LLVM_TARGETS% --config Debug + +@REM set /p LLVM_TARGETS=<../llvm_targets.txt +@REM cmake --build . -t %LLVM_TARGETS% --config Debug +@REM @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR +@REM cmake --build . -t %LLVM_TARGETS% --config Release +@REM @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR + +cmake --build . --config Debug @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR -cmake --build . -t %LLVM_TARGETS% --config Release +cmake --build . --config Release @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR + echo done > _Done.txt :SUCCESS diff --git a/wasm/build_wasm.bat b/wasm/build_wasm.bat index 6e677a2f..656da058 100644 --- a/wasm/build_wasm.bat +++ b/wasm/build_wasm.bat @@ -32,13 +32,13 @@ copy ..\BeefySysLib\third_party\putty\* src\BeefySysLib\third_party\putty IF "%1" EQU "setup" GOTO SUCCESS -call emcc src\rt\Chars.cpp src\rt\Math.cpp src\rt\Object.cpp src\rt\Thread.cpp src\rt\Internal.cpp src\BeefySysLib\platform\wasm\WasmCommon.cpp src\BeefySysLib\Common.cpp src\BeefySysLib\util\String.cpp src\BeefySysLib\util\UTF8.cpp src\BeefySysLib\third_party\utf8proc\utf8proc.c src\BeefySysLib\third_party\putty\wildcard.c -Isrc\ -Isrc\BeefySysLib -Isrc\BeefySysLib\platform\wasm -g -DBF_DISABLE_FFI -c +call emcc src\rt\Chars.cpp src\rt\Math.cpp src\rt\Object.cpp src\rt\Thread.cpp src\rt\Internal.cpp src\BeefySysLib\platform\wasm\WasmCommon.cpp src\BeefySysLib\Common.cpp src\BeefySysLib\util\String.cpp src\BeefySysLib\util\Hash.cpp src\BeefySysLib\util\UTF8.cpp src\BeefySysLib\third_party\utf8proc\utf8proc.c src\BeefySysLib\third_party\putty\wildcard.c -Isrc\ -Isrc\BeefySysLib -Isrc\BeefySysLib\platform\wasm -g -DBF_DISABLE_FFI -c @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR -call emar r %LIBPATH%\Beef042RT32_wasm.a Common.o Internal.o Chars.o Math.o Object.o String.o Thread.o UTF8.o utf8proc.o wildcard.o WasmCommon.o +call emar r %LIBPATH%\Beef042RT32_wasm.a Common.o Internal.o Chars.o Math.o Object.o String.o Thread.o Hash.o UTF8.o utf8proc.o wildcard.o WasmCommon.o @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR -call emcc src\rt\Chars.cpp src\rt\Math.cpp src\rt\Object.cpp src\rt\Thread.cpp src\rt\Internal.cpp src\BeefySysLib\platform\wasm\WasmCommon.cpp src\BeefySysLib\Common.cpp src\BeefySysLib\util\String.cpp src\BeefySysLib\util\UTF8.cpp src\BeefySysLib\third_party\utf8proc\utf8proc.c src\BeefySysLib\third_party\putty\wildcard.c -Isrc\ -Isrc\BeefySysLib -Isrc\BeefySysLib\platform\wasm -g -DBF_DISABLE_FFI -c -pthread +call emcc src\rt\Chars.cpp src\rt\Math.cpp src\rt\Object.cpp src\rt\Thread.cpp src\rt\Internal.cpp src\BeefySysLib\platform\wasm\WasmCommon.cpp src\BeefySysLib\Common.cpp src\BeefySysLib\util\String.cpp src\BeefySysLib\util\Hash.cpp src\BeefySysLib\util\UTF8.cpp src\BeefySysLib\third_party\utf8proc\utf8proc.c src\BeefySysLib\third_party\putty\wildcard.c -Isrc\ -Isrc\BeefySysLib -Isrc\BeefySysLib\platform\wasm -g -DBF_DISABLE_FFI -c -pthread @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR -call emar r %LIBPATH%\Beef042RT32_wasm_pthread.a Common.o Internal.o Chars.o Math.o Object.o String.o Thread.o UTF8.o utf8proc.o wildcard.o WasmCommon.o +call emar r %LIBPATH%\Beef042RT32_wasm_pthread.a Common.o Internal.o Chars.o Math.o Object.o String.o Thread.o Hash.o UTF8.o utf8proc.o wildcard.o WasmCommon.o @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR :SUCCESS diff --git a/wasm/fetch_wasm.bat b/wasm/fetch_wasm.bat index 0f92ce21..0ce8cf14 100644 --- a/wasm/fetch_wasm.bat +++ b/wasm/fetch_wasm.bat @@ -1,13 +1,13 @@ @ECHO OFF @ECHO Downloading Emscripten... -..\bin\curl.exe -O https://www.beeflang.org/EmsdkDep0.zip +..\bin\curl.exe -O https://www.beeflang.org/EmsdkDep1.zip @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR @ECHO Extracting Emscripten... cd .. -bin\tar.exe -xf wasm\EmsdkDep0.zip -@IF %ERRORLEVEL% NEQ 0 GOTO -del wasm\EmsdkDep0.zip +bin\tar.exe -xf wasm\EmsdkDep1.zip +@IF %ERRORLEVEL% NEQ 0 GOTO HADERROR +del wasm\EmsdkDep1.zip @IF %ERRORLEVEL% NEQ 0 GOTO HADERROR :SUCCESS