From b70745ef1e4c972538e51ff304dc645f5ee2d4dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20L=C3=BCbe=C3=9F?= Date: Fri, 17 Dec 2021 18:05:39 +0100 Subject: [PATCH] Merge branch 'master' of https://github.com/beefytech/Beef into FuzzyAutoComplete --- BeefLibs/Beefy2D/src/widgets/Dialog.bf | 3 + BeefLibs/corlib/src/Collections/Dictionary.bf | 43 +- BeefLibs/corlib/src/Compiler.bf | 190 +++- BeefLibs/corlib/src/Double.bf | 3 +- BeefLibs/corlib/src/GC.bf | 2 +- BeefLibs/corlib/src/IO/BufferedStream.bf | 21 +- BeefLibs/corlib/src/IO/FileDialog.bf | 580 ++++++++++++ BeefLibs/corlib/src/IO/FileStream.bf | 38 +- BeefLibs/corlib/src/IO/FolderBrowserDialog.bf | 149 +-- BeefLibs/corlib/src/IO/OpenFileDialog.bf | 521 ++--------- BeefLibs/corlib/src/IO/SaveFileDialog.bf | 42 + BeefLibs/corlib/src/Windows.bf | 209 +++++ BeefySysLib/util/Array.h | 4 +- IDE/Tests/CompileFail001/src/Generics.bf | 2 +- IDE/mintest/minlib/src/System/Compiler.bf | 5 + IDE/src/CommandQueueManager.bf | 16 +- IDE/src/Compiler/BfCompiler.bf | 38 +- IDE/src/Project.bf | 16 +- IDE/src/ui/ClassViewPanel.bf | 4 + IDE/src/ui/GenerateDialog.bf | 882 ++++++++++++++++++ IDE/src/ui/OutputPanel.bf | 2 +- IDE/src/ui/ProjectPanel.bf | 105 ++- IDE/src/ui/SourceViewPanel.bf | 5 + IDE/src/ui/WatchPanel.bf | 2 +- IDEHelper/Backend/BeCOFFObject.cpp | 18 +- IDEHelper/Compiler/BfAutoComplete.cpp | 2 +- IDEHelper/Compiler/BfCompiler.cpp | 275 +++++- IDEHelper/Compiler/BfCompiler.h | 11 +- IDEHelper/Compiler/BfContext.cpp | 26 +- IDEHelper/Compiler/BfExprEvaluator.cpp | 77 +- IDEHelper/Compiler/BfIRBuilder.cpp | 62 +- IDEHelper/Compiler/BfIRBuilder.h | 1 + IDEHelper/Compiler/BfModule.cpp | 95 +- IDEHelper/Compiler/BfModule.h | 1 + IDEHelper/Compiler/BfModuleTypeUtils.cpp | 126 ++- IDEHelper/Compiler/BfParser.cpp | 66 +- IDEHelper/Compiler/BfParser.h | 1 + IDEHelper/Compiler/BfPrinter.cpp | 54 +- IDEHelper/Compiler/BfPrinter.h | 1 + IDEHelper/Compiler/BfReducer.cpp | 7 +- IDEHelper/Compiler/BfResolvedTypeUtils.cpp | 16 +- IDEHelper/Compiler/BfResolvedTypeUtils.h | 5 +- IDEHelper/Compiler/BfSystem.cpp | 50 +- IDEHelper/Compiler/BfSystem.h | 1 + IDEHelper/Compiler/CeMachine.cpp | 39 +- IDEHelper/Compiler/CeMachine.h | 7 + IDEHelper/Tests/src/Comptime.bf | 60 ++ IDEHelper/WinDebugger.cpp | 10 +- 48 files changed, 2975 insertions(+), 918 deletions(-) create mode 100644 BeefLibs/corlib/src/IO/FileDialog.bf create mode 100644 IDE/src/ui/GenerateDialog.bf diff --git a/BeefLibs/Beefy2D/src/widgets/Dialog.bf b/BeefLibs/Beefy2D/src/widgets/Dialog.bf index b879a7e0..01723f6b 100644 --- a/BeefLibs/Beefy2D/src/widgets/Dialog.bf +++ b/BeefLibs/Beefy2D/src/widgets/Dialog.bf @@ -256,7 +256,10 @@ namespace Beefy.widgets public virtual void PopupWindow(WidgetWindow parentWindow, float offsetX = 0, float offsetY = 0) { if (mClosed) + { + BFApp.sApp.DeferDelete(this); return; + } mInPopupWindow = true; diff --git a/BeefLibs/corlib/src/Collections/Dictionary.bf b/BeefLibs/corlib/src/Collections/Dictionary.bf index 493a3309..1aaa7ccf 100644 --- a/BeefLibs/corlib/src/Collections/Dictionary.bf +++ b/BeefLibs/corlib/src/Collections/Dictionary.bf @@ -578,6 +578,43 @@ namespace System.Collections return oldPtr; } + private bool RemoveEntry(int32 hashCode, int_cosize index) + { + if (mBuckets != null) + { + int bucket = hashCode % (int_cosize)mAllocSize; + int lastIndex = -1; + + for (int_cosize i = mBuckets[bucket]; i >= 0; lastIndex = i, i = mEntries[i].mNext) + { + if (i == index) + { + if (lastIndex < 0) + { + mBuckets[bucket] = mEntries[index].mNext; + } + else + { + mEntries[lastIndex].mNext = mEntries[index].mNext; + } + mEntries[index].mHashCode = -1; + mEntries[index].mNext = mFreeList; +#if BF_ENABLE_REALTIME_LEAK_CHECK + mEntries[index].mKey = default; + mEntries[index].mValue = default; +#endif + mFreeList = index; + mFreeCount++; +#if VERSION_DICTIONARY + mVersion++; +#endif + return true; + } + } + } + return false; + } + public bool Remove(TKey key) { if (mBuckets != null) @@ -901,7 +938,7 @@ namespace System.Collections public void Remove() mut { int_cosize curIdx = mIndex - 1; - mDictionary.Remove(mDictionary.mEntries[curIdx].mKey); + mDictionary.RemoveEntry(mDictionary.mEntries[curIdx].mHashCode, curIdx); #if VERSION_DICTIONARY mVersion = mDictionary.mVersion; #endif @@ -1052,7 +1089,7 @@ namespace System.Collections public void Remove() mut { int_cosize curIdx = mIndex - 1; - mDictionary.Remove(mDictionary.mEntries[curIdx].mKey); + mDictionary.RemoveEntry(mDictionary.mEntries[curIdx].mHashCode, curIdx); #if VERSION_DICTIONARY mVersion = mDictionary.mVersion; #endif @@ -1158,7 +1195,7 @@ namespace System.Collections public void Remove() mut { int_cosize curIdx = mIndex - 1; - mDictionary.Remove(mDictionary.mEntries[curIdx].mKey); + mDictionary.RemoveEntry(mDictionary.mEntries[curIdx].mHashCode, curIdx); #if VERSION_DICTIONARY mVersion = mDictionary.mVersion; #endif diff --git a/BeefLibs/corlib/src/Compiler.bf b/BeefLibs/corlib/src/Compiler.bf index 45a8e3c3..3030c444 100644 --- a/BeefLibs/corlib/src/Compiler.bf +++ b/BeefLibs/corlib/src/Compiler.bf @@ -1,8 +1,189 @@ using System.Reflection; +using System.Diagnostics; +using System.Collections; +using System.Security.Cryptography; + namespace System { static class Compiler { + public abstract class Generator + { + public enum Flags + { + None = 0, + AllowRegenerate = 1 + } + + public String mCmdInfo = new String() ~ delete _; + public Dictionary mParams = new .() ~ delete _; + public abstract String Name { get; } + + public StringView ProjectName => mParams["ProjectName"]; + public StringView ProjectDir => mParams["ProjectDir"]; + public StringView FolderDir => mParams["FolderDir"]; + public StringView Namespace => mParams["Namespace"]; + public StringView DefaultNamespace => mParams["DefaultNamespace"]; + public StringView WorkspaceName => mParams["WorkspaceName"]; + public StringView WorkspaceDir => mParams["WorkspaceDir"]; + public StringView DateTime => mParams["DateTime"]; + public bool IsRegenerating => mParams.GetValueOrDefault("Regenerating") == "True"; + + public void Fail(StringView error) + { + mCmdInfo.AppendF("error\t"); + error.QuoteString(mCmdInfo); + mCmdInfo.Append("\n"); + } + + public void AddEdit(StringView dataName, StringView label, StringView defaultValue) + { + mCmdInfo.AppendF($"addEdit\t"); + dataName.QuoteString(mCmdInfo); + mCmdInfo.Append("\t"); + label.QuoteString(mCmdInfo); + mCmdInfo.Append("\t"); + defaultValue.QuoteString(mCmdInfo); + mCmdInfo.Append("\n"); + } + + public void AddCombo(StringView dataName, StringView label, StringView defaultValue, Span values) + { + mCmdInfo.AppendF($"addCombo\t"); + dataName.QuoteString(mCmdInfo); + mCmdInfo.Append("\t"); + label.QuoteString(mCmdInfo); + mCmdInfo.Append("\t"); + defaultValue.QuoteString(mCmdInfo); + for (var value in values) + { + mCmdInfo.Append("\t"); + value.QuoteString(mCmdInfo); + } + mCmdInfo.Append("\n"); + } + + public void AddCheckbox(StringView dataName, StringView label, bool defaultValue) + { + mCmdInfo.AppendF($"addCheckbox\t"); + dataName.QuoteString(mCmdInfo); + mCmdInfo.Append("\t"); + label.QuoteString(mCmdInfo); + mCmdInfo.AppendF($"\t{defaultValue}\n"); + } + + public bool GetString(StringView key, String outVal) + { + if (mParams.TryGetAlt(key, var matchKey, var value)) + { + outVal.Append(value); + return true; + } + return false; + } + + public virtual void InitUI() + { + } + + public virtual void Generate(String outFileName, String outText, ref Flags generateFlags) + { + } + + static String GetName() where T : Generator + { + T val = scope T(); + String str = val.Name; + return str; + } + + void HandleArgs(String args) + { + for (var line in args.Split('\n', .RemoveEmptyEntries)) + { + int tabPos = line.IndexOf('\t'); + var key = line.Substring(0, tabPos); + var value = line.Substring(tabPos + 1); + if (mParams.TryAdd(key, var keyPtr, var valuePtr)) + { + *keyPtr = key; + *valuePtr = value; + } + } + } + + static String InitUI(String args) where T : Generator + { + T val = scope T(); + val.HandleArgs(args); + val.InitUI(); + return val.mCmdInfo; + } + + static String Generate(String args) where T : Generator + { + T val = scope T(); + val.HandleArgs(args); + String fileName = scope .(); + String outText = scope .(); + Flags flags = .None; + val.Generate(fileName, outText, ref flags); + val.mCmdInfo.Append("fileName\t"); + fileName.QuoteString(val.mCmdInfo); + val.mCmdInfo.Append("\n"); + val.mCmdInfo.Append("data\n"); + + if (flags.HasFlag(.AllowRegenerate)) + { + bool writeArg = false; + for (var line in args.Split('\n', .RemoveEmptyEntries)) + { + int tabPos = line.IndexOf('\t'); + var key = line.Substring(0, tabPos); + var value = line.Substring(tabPos + 1); + + if (key == "Generator") + writeArg = true; + if (writeArg) + { + val.mCmdInfo.AppendF($"// {key}={value}\n"); + } + } + var hash = MD5.Hash(.((.)outText.Ptr, outText.Length)); + val.mCmdInfo.AppendF($"// GenHash={hash}\n\n"); + } + val.mCmdInfo.Append(outText); + return val.mCmdInfo; + } + } + + public class NewClassGenerator : Generator + { + public override String Name => "New Class"; + + public override void InitUI() + { + AddEdit("name", "Class Name", ""); + } + + public override void Generate(String outFileName, String outText, ref Flags generateFlags) + { + var name = mParams["name"]; + if (name.EndsWith(".bf", .OrdinalIgnoreCase)) + name.RemoveFromEnd(3); + outFileName.Append(name); + outText.AppendF( + $""" + namespace {Namespace} + {{ + class {name} + {{ + }} + }} + """); + } + } + public struct MethodBuilder { void* mNative; @@ -43,7 +224,7 @@ namespace System public static extern String CallerProject; [LinkName("#CallerExpression")] - public static extern String[0x0FFFFFFF] CallerExpression; + public static extern String[0x00FFFFFF] CallerExpression; [LinkName("#ProjectName")] public static extern String ProjectName; @@ -76,6 +257,7 @@ namespace System static extern void* Comptime_MethodBuilder_EmitStr(void* native, StringView str); static extern void* Comptime_CreateMethod(int32 typeId, StringView methodName, Type returnType, MethodFlags methodFlags); static extern void Comptime_EmitTypeBody(int32 typeId, StringView text); + static extern void Comptime_EmitAddInterface(int32 typeId, int32 ifaceTypeId); 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); @@ -94,6 +276,12 @@ namespace System Comptime_EmitTypeBody((.)owner.TypeId, text); } + [Comptime(OnlyFromComptime=true)] + public static void EmitAddInterface(Type owner, Type iface) + { + Comptime_EmitAddInterface((.)owner.TypeId, (.)iface.TypeId); + } + [Comptime(OnlyFromComptime=true)] public static void EmitMethodEntry(ComptimeMethodInfo methodHandle, StringView text) { diff --git a/BeefLibs/corlib/src/Double.bf b/BeefLibs/corlib/src/Double.bf index 63176ab3..5c75b453 100644 --- a/BeefLibs/corlib/src/Double.bf +++ b/BeefLibs/corlib/src/Double.bf @@ -21,8 +21,7 @@ namespace System public const double NegativeInfinity = (double)(- 1.0 / (double)(0.0)); public const double PositiveInfinity = (double)1.0 / (double)(0.0); public const double NaN = (double)0.0 / (double)0.0; - - static double NegativeZero = BitConverter.Convert(0x8000000000000000UL); + public const double NegativeZero = -0.0; public static int operator<=>(Double a, Double b) { diff --git a/BeefLibs/corlib/src/GC.bf b/BeefLibs/corlib/src/GC.bf index 231642a3..98055aa4 100644 --- a/BeefLibs/corlib/src/GC.bf +++ b/BeefLibs/corlib/src/GC.bf @@ -163,7 +163,7 @@ namespace System public static mixin Mark(T val) where T : struct { #if BF_ENABLE_REALTIME_LEAK_CHECK - val.[Friend]GCMarkMembers(); + val.[Friend, Unbound]GCMarkMembers(); #endif } diff --git a/BeefLibs/corlib/src/IO/BufferedStream.bf b/BeefLibs/corlib/src/IO/BufferedStream.bf index 08bde0ac..be432b08 100644 --- a/BeefLibs/corlib/src/IO/BufferedStream.bf +++ b/BeefLibs/corlib/src/IO/BufferedStream.bf @@ -19,7 +19,7 @@ namespace System.IO set { - mPos = Math.Min(value, Length); + mPos = value; } } @@ -43,20 +43,16 @@ namespace System.IO public override Result Seek(int64 pos, SeekKind seekKind = .Absolute) { - int64 length = Length; - - int64 newPos; switch (seekKind) { case .Absolute: - mPos = Math.Min(pos, length); - if (pos > length) - return .Err; + mPos = pos; case .FromEnd: - newPos = length - pos; + mPos = Length + pos; case .Relative: - mPos = Math.Min(mPos + pos, length); + mPos = mPos + pos; } + return .Ok; } @@ -176,7 +172,12 @@ namespace System.IO public override Result Close() { - return Flush(); + let ret = Flush(); + + mPos = 0; + mBufferPos = -Int32.MinValue; + mBufferEnd = -Int32.MinValue; + return ret; } } } diff --git a/BeefLibs/corlib/src/IO/FileDialog.bf b/BeefLibs/corlib/src/IO/FileDialog.bf new file mode 100644 index 00000000..1f81f2bd --- /dev/null +++ b/BeefLibs/corlib/src/IO/FileDialog.bf @@ -0,0 +1,580 @@ +// This file contains portions of code released by Microsoft under the MIT license as part +// of an open-sourcing initiative in 2014 of the C# core libraries. +// The original source was submitted to https://github.com/Microsoft/referencesource + +using System.Text; +using System.Collections; +using System.Threading; +using System.Diagnostics; + +#if BF_PLATFORM_WINDOWS +namespace System.IO +{ + enum DialogResult + { + None = 0, + OK = 1, + Cancel = 2 + } + + abstract class CommonDialog + { + public Windows.HWnd mHWnd; + public Windows.HWnd mDefaultControlHwnd; + public int mDefWndProc; + + private const int32 CDM_SETDEFAULTFOCUS = Windows.WM_USER + 0x51; + + public static Dictionary sHookMap = new Dictionary() ~ + { + Debug.Assert(sHookMap.Count == 0); + delete _; + }; + public static Monitor sMonitor = new Monitor() ~ delete _; + + public Result ShowDialog(INativeWindow owner = null) + { + Windows.HWnd hwndOwner = 0; + if (owner != null) + hwndOwner = (.)owner.Handle; + //Native.WndProc wndProc = scope => OwnerWndProc; + + //mDefWndProc = Native.SetWindowLong(mHWnd, Native.GWL_WNDPROC, (intptr)wndProc.GetFuncPtr().Value); + + var result = RunDialog(hwndOwner); + return result; + } + + public virtual int OwnerWndProc(Windows.HWnd hWnd, int32 msg, int wParam, int lParam) + { + return Windows.CallWindowProcW(mDefWndProc, hWnd, msg, wParam, lParam); + } + + protected virtual int HookProc(Windows.HWnd hWnd, int32 msg, int wParam, int lparam) + { + if (msg == Windows.WM_INITDIALOG) + { + //TODO: MoveToScreenCenter(hWnd); + // Under some circumstances, the dialog + // does not initially focus on any control. We fix that by explicitly + // setting focus ourselves. See ASURT 39435. + // + mDefaultControlHwnd = (Windows.HWnd)wParam; + if (mDefaultControlHwnd != 0) + Windows.SetFocus(mDefaultControlHwnd); + } + else if (msg == Windows.WM_SETFOCUS) + { + Windows.PostMessageW(hWnd, CDM_SETDEFAULTFOCUS, 0, 0); + } + else if (msg == CDM_SETDEFAULTFOCUS) + { + // If the dialog box gets focus, bounce it to the default control. + // so we post a message back to ourselves to wait for the focus change then push it to the default + // control. See ASURT 84016. + // + if (mDefaultControlHwnd != 0) + Windows.SetFocus(mDefaultControlHwnd); + } + return 0; + } + + protected abstract Result RunDialog(Windows.HWnd hWndOwner); + } + + abstract class FileDialog : CommonDialog + { + protected abstract Result RunFileDialog(ref Windows.OpenFileName ofn); + + protected override Result RunDialog(Windows.HWnd hWndOwner) + { + if (TryRunDialogVista(hWndOwner) case .Ok(let result)) + return .Ok(result); + + return RunDialogOld(hWndOwner); + } + + private const int32 FILEBUFSIZE = 8192; + protected const int32 OPTION_ADDEXTENSION = (int32)0x80000000; + + protected int32 mOptions; + private String mTitle ~ delete _; + private String mInitialDir ~ delete _; + private String mDefaultExt ~ delete _; + protected String[] mFileNames ~ DeleteContainerAndItems!(_); + private bool mSecurityCheckFileNames; + private String mFilter ~ delete _; + private String mFilterBuffer = new String() ~ delete _; + private int32 mFilterIndex; + private bool mSupportMultiDottedExtensions; + private bool mIgnoreSecondFileOkNotification; // Used for VS Whidbey 95342 + private int32 mOKNotificationCount; // Same + //private String char8Buffer = new String(FILEBUFSIZE) ~ delete _; + + public this() + { + Reset(); + } + + public virtual void Reset() + { + DeleteAndNullify!(mTitle); + DeleteAndNullify!(mInitialDir); + DeleteAndNullify!(mDefaultExt); + DeleteContainerAndItems!(mFileNames); + mFileNames = null; + DeleteAndNullify!(mFilter); + mFilterIndex = 1; + mSupportMultiDottedExtensions = false; + mOptions = Windows.OFN_HIDEREADONLY | Windows.OFN_PATHMUSTEXIST | + OPTION_ADDEXTENSION; + } + + + + protected int32 Options + { + get + { + return mOptions & ( + Windows.OFN_READONLY | + Windows.OFN_HIDEREADONLY | + Windows.OFN_NOCHANGEDIR | + Windows.OFN_SHOWHELP | + Windows.OFN_NOVALIDATE | + Windows.OFN_ALLOWMULTISELECT | + Windows.OFN_PATHMUSTEXIST | + Windows.OFN_FILEMUSTEXIST | + Windows.OFN_NODEREFERENCELINKS | + Windows.OFN_OVERWRITEPROMPT); + //return mOptions; + } + } + + public StringView Title + { + set + { + String.NewOrSet!(mTitle, value); + } + + get + { + return mTitle; + } + } + + public StringView InitialDirectory + { + set + { + String.NewOrSet!(mInitialDir, value); + } + + get + { + return mInitialDir; + } + } + + public String[] FileNames + { + get + { + return mFileNames; + } + } + + public StringView FileName + { + set + { + if (mFileNames == null) + { + mFileNames = new String[](new String(value)); + } + } + } + + public bool AddExtension + { + get + { + return GetOption(OPTION_ADDEXTENSION); + } + + set + { + SetOption(OPTION_ADDEXTENSION, value); + } + } + + public virtual bool CheckFileExists + { + get + { + return GetOption(Windows.OFN_FILEMUSTEXIST); + } + + set + { + SetOption(Windows.OFN_FILEMUSTEXIST, value); + } + } + + public bool DereferenceLinks + { + get + { + return !GetOption(Windows.OFN_NODEREFERENCELINKS); + } + set + { + SetOption(Windows.OFN_NODEREFERENCELINKS, !value); + } + } + + public bool CheckPathExists + { + get + { + return GetOption(Windows.OFN_PATHMUSTEXIST); + } + + set + { + SetOption(Windows.OFN_PATHMUSTEXIST, value); + } + } + + public bool Multiselect + { + get + { + return GetOption(Windows.OFN_ALLOWMULTISELECT); + } + + set + { + SetOption(Windows.OFN_ALLOWMULTISELECT, value); + } + } + + public bool ValidateNames + { + get + { + return !GetOption(Windows.OFN_NOVALIDATE); + } + + set + { + SetOption(Windows.OFN_NOVALIDATE, !value); + } + } + + public StringView DefaultExt + { + get + { + return mDefaultExt == null ? "" : mDefaultExt; + } + + set + { + delete mDefaultExt; + mDefaultExt = null; + + //if (!String.IsNullOrEmpty(value)) + if (value.Length > 0) + { + mDefaultExt = new String(value); + if (mDefaultExt.StartsWith(".")) + mDefaultExt.Remove(0, 1); + } + } + } + + public void GetFilter(String outFilter) + { + if (mFilter != null) + outFilter.Append(mFilter); + } + + public Result SetFilter(StringView value) + { + String useValue = scope String(value); + if (useValue != null && useValue.Length > 0) + { + var formats = String.StackSplit!(useValue, '|'); + if (formats == null || formats.Count % 2 != 0) + { + return .Err; + } + /// + /*String[] formats = value.Split('|'); + if (formats == null || formats.Length % 2 != 0) + { + throw new ArgumentException(SR.GetString(SR.FileDialogInvalidFilter)); + }*/ + String.NewOrSet!(mFilter, useValue); + } + else + { + useValue = null; + DeleteAndNullify!(mFilter); + } + + return .Ok; + } + + protected bool GetOption(int32 option) + { + return (mOptions & option) != 0; + } + + protected void SetOption(int32 option, bool value) + { + if (value) + { + mOptions |= option; + } + else + { + mOptions &= ~option; + } + } + + private static Result MakeFilterString(String s, bool dereferenceLinks, String filterBuffer) + { + String useStr = s; + if (useStr == null || useStr.Length == 0) + { + // Workaround for Whidbey bug #5165 + // Apply the workaround only when DereferenceLinks is true and OS is at least WinXP. + if (dereferenceLinks && System.Environment.OSVersion.Version.Major >= 5) + { + useStr = " |*.*"; + } + else if (useStr == null) + { + return .Err; + } + } + + filterBuffer.Set(s); + for (int32 i = 0; i < filterBuffer.Length; i++) + if (filterBuffer[i] == '|') + filterBuffer[i] = (char8)0; + filterBuffer.Append((char8)0); + return .Ok; + } + + private Result RunDialogOld(Windows.HWnd hWndOwner) + { + //RunDialogTest(hWndOwner); + + Windows.WndProc hookProcPtr = => StaticHookProc; + Windows.OpenFileName ofn = Windows.OpenFileName(); + + char16[FILEBUFSIZE] char16Buffer = .(0, ?); + + if (mFileNames != null && !mFileNames.IsEmpty) + { + //int len = UTF16.GetEncodedLen(fileNames[0]); + //char16Buffer = scope:: char16[len + 1]*; + UTF16.Encode(mFileNames[0], (char16*)&char16Buffer, FILEBUFSIZE); + } + // Degrade to the older style dialog if we're not on Win2K. + // We do this by setting the struct size to a different value + // + + if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || + Environment.OSVersion.Version.Major < 5) { + ofn.mStructSize = 0x4C; + } + ofn.mHwndOwner = hWndOwner; + ofn.mHInstance = (Windows.HInstance)Windows.GetModuleHandleW(null); + + if (mFilter != null) + { + Try!(MakeFilterString(mFilter, this.DereferenceLinks, mFilterBuffer)); + ofn.mFilter = mFilterBuffer.ToScopedNativeWChar!::(); + } + ofn.nFilterIndex = mFilterIndex; + ofn.mFile = (char16*)&char16Buffer; + ofn.nMaxFile = FILEBUFSIZE; + if (mInitialDir != null) + ofn.mInitialDir = mInitialDir.ToScopedNativeWChar!::(); + if (mTitle != null) + ofn.mTitle = mTitle.ToScopedNativeWChar!::(); + ofn.mFlags = Options | (Windows.OFN_EXPLORER | Windows.OFN_ENABLEHOOK | Windows.OFN_ENABLESIZING); + ofn.mHook = hookProcPtr; + ofn.mCustData = (int)Internal.UnsafeCastToPtr(this); + ofn.mFlagsEx = Windows.OFN_USESHELLITEM; + if (mDefaultExt != null && AddExtension) + ofn.mDefExt = mDefaultExt.ToScopedNativeWChar!::(); + + DeleteContainerAndItems!(mFileNames); + mFileNames = null; + //Security checks happen here + return RunFileDialog(ref ofn); + } + + static int StaticHookProc(Windows.HWnd hWnd, int32 msg, int wParam, int lparam) + { + if (msg == Windows.WM_INITDIALOG) + { + using (sMonitor.Enter()) + { + var ofn = (Windows.OpenFileName*)(void*)lparam; + sHookMap[(int)hWnd] = (CommonDialog)Internal.UnsafeCastToObject((void*)ofn.mCustData); + } + } + + CommonDialog dlg; + using (sMonitor.Enter()) + { + sHookMap.TryGetValue((int)hWnd, out dlg); + } + if (dlg == null) + return 0; + + dlg.[Friend]HookProc(hWnd, msg, wParam, lparam); + if (msg == Windows.WM_DESTROY) + { + using (sMonitor.Enter()) + { + sHookMap.Remove((int)hWnd); + } + } + return 0; + } + //TODO: Add ProcessFileNames for validation + + protected abstract Result CreateVistaDialog(); + + private Result TryRunDialogVista(Windows.HWnd hWndOwner) + { + Windows.COM_IFileDialog* dialog; + if (!(CreateVistaDialog() case .Ok(out dialog))) + return .Err; + + OnBeforeVistaDialog(dialog); + dialog.VT.Show(dialog, hWndOwner); + + List files = scope .(); + ProcessVistaFiles(dialog, files); + + DeleteContainerAndItems!(mFileNames); + mFileNames = new String[files.Count]; + files.CopyTo(mFileNames); + + dialog.VT.Release(dialog); + + return .Ok(files.IsEmpty ? .Cancel : .OK); + } + + private void OnBeforeVistaDialog(Windows.COM_IFileDialog* dialog) + { + dialog.VT.SetDefaultExtension(dialog, DefaultExt.ToScopedNativeWChar!()); + + if (mFileNames != null && !mFileNames.IsEmpty) + dialog.VT.SetFileName(dialog, mFileNames[0].ToScopedNativeWChar!()); + + if (!String.IsNullOrEmpty(mInitialDir)) + { + Windows.COM_IShellItem* folderShellItem = null; + Windows.SHCreateItemFromParsingName(mInitialDir.ToScopedNativeWChar!(), null, Windows.COM_IShellItem.sIID, (void**)&folderShellItem); + if (folderShellItem != null) + { + dialog.VT.SetDefaultFolder(dialog, folderShellItem); + dialog.VT.SetFolder(dialog, folderShellItem); + folderShellItem.VT.Release(folderShellItem); + } + } + + dialog.VT.SetTitle(dialog, mTitle.ToScopedNativeWChar!()); + dialog.VT.SetOptions(dialog, GetOptions()); + SetFileTypes(dialog); + } + + private Windows.COM_IFileDialog.FOS GetOptions() + { + const Windows.COM_IFileDialog.FOS BlittableOptions = + Windows.COM_IFileDialog.FOS.OVERWRITEPROMPT + | Windows.COM_IFileDialog.FOS.NOCHANGEDIR + | Windows.COM_IFileDialog.FOS.NOVALIDATE + | Windows.COM_IFileDialog.FOS.ALLOWMULTISELECT + | Windows.COM_IFileDialog.FOS.PATHMUSTEXIST + | Windows.COM_IFileDialog.FOS.FILEMUSTEXIST + | Windows.COM_IFileDialog.FOS.CREATEPROMPT + | Windows.COM_IFileDialog.FOS.NODEREFERENCELINKS; + + const int32 UnexpectedOptions = + (int32)(Windows.OFN_SHOWHELP // If ShowHelp is true, we don't use the Vista Dialog + | Windows.OFN_ENABLEHOOK // These shouldn't be set in options (only set in the flags for the legacy dialog) + | Windows.OFN_ENABLESIZING // These shouldn't be set in options (only set in the flags for the legacy dialog) + | Windows.OFN_EXPLORER); // These shouldn't be set in options (only set in the flags for the legacy dialog) + + Debug.Assert((UnexpectedOptions & mOptions) == 0, "Unexpected FileDialog options"); + + Windows.COM_IFileDialog.FOS ret = (Windows.COM_IFileDialog.FOS)mOptions & BlittableOptions; + + // Force no mini mode for the SaveFileDialog + ret |= Windows.COM_IFileDialog.FOS.DEFAULTNOMINIMODE; + + // Make sure that the Open dialog allows the user to specify + // non-file system locations. This flag will cause the dialog to copy the resource + // to a local cache (Temporary Internet Files), and return that path instead. This + // also affects the Save dialog by disallowing navigation to these areas. + // An example of a non-file system location is a URL (http://), or a file stored on + // a digital camera that is not mapped to a drive letter. + // This reproduces the behavior of the "classic" Open and Save dialogs. + ret |= Windows.COM_IFileDialog.FOS.FORCEFILESYSTEM; + + return ret; + } + + protected abstract void ProcessVistaFiles(Windows.COM_IFileDialog* dialog, List files); + + private Result SetFileTypes(Windows.COM_IFileDialog* dialog) + { + List filterItems = scope .(); + + // Expected input types + // "Text files (*.txt)|*.txt|All files (*.*)|*.*" + // "Image Files(*.BMP;*.JPG;*.GIF)|*.BMP;*.JPG;*.GIF|All files (*.*)|*.*" + if (!String.IsNullOrEmpty(mFilter)) + { + StringView[] tokens = mFilter.Split!('|'); + if (0 == tokens.Count % 2) + { + // All even numbered tokens should be labels + // Odd numbered tokens are the associated extensions + for (int i = 1; i < tokens.Count; i += 2) + { + Windows.COMDLG_FILTERSPEC ext; + ext.pszSpec = tokens[i].ToScopedNativeWChar!::(); // This may be a semicolon delimited list of extensions (that's ok) + ext.pszName = tokens[i - 1].ToScopedNativeWChar!::(); + filterItems.Add(ext); + } + } + } + + if (filterItems.IsEmpty) + return .Ok; + + Windows.COM_IUnknown.HResult hr = dialog.VT.SetFileTypes(dialog, (uint32)filterItems.Count, filterItems.Ptr); + if (hr.Failed) + return .Err; + + hr = dialog.VT.SetFileTypeIndex(dialog, (uint32)mFilterIndex); + if (hr.Failed) + return .Err; + + return .Ok; + } + } +} +#endif \ No newline at end of file diff --git a/BeefLibs/corlib/src/IO/FileStream.bf b/BeefLibs/corlib/src/IO/FileStream.bf index 0be6c773..de46955a 100644 --- a/BeefLibs/corlib/src/IO/FileStream.bf +++ b/BeefLibs/corlib/src/IO/FileStream.bf @@ -296,6 +296,15 @@ namespace System.IO } } + public override int64 Position + { + set + { + // Matches the behavior of Platform.BfpFile_Seek(mBfpFile, value, .Absolute); + mPos = Math.Max(value, 0); + } + } + public this() { @@ -416,16 +425,37 @@ namespace System.IO mFileAccess = access; } + public override Result Seek(int64 pos, SeekKind seekKind = .Absolute) + { + int64 newPos; + switch (seekKind) + { + case .Absolute: + newPos = pos; + case .FromEnd: + newPos = Length + pos; + case .Relative: + newPos = mPos + pos; + } + + // Matches the behaviour of Platform.BfpFile_Seek(mBfpFile, value, .Absolute); + mPos = Math.Max(newPos, 0); + if (seekKind == .Absolute && newPos < 0) + return .Err; + + return .Ok; + } + public override Result Close() { - var hadError = Flush() case .Err; + let ret = base.Close(); if (mBfpFile != null) Platform.BfpFile_Release(mBfpFile); + mBfpFile = null; mFileAccess = default; - if (hadError) - return .Err; - return .Ok; + mBfpFilePos = 0; + return ret; } protected override void UpdateLength() diff --git a/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf b/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf index 91844228..420757ac 100644 --- a/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf +++ b/BeefLibs/corlib/src/IO/FolderBrowserDialog.bf @@ -57,7 +57,7 @@ namespace System.IO ShowNewFolderButton = true; } - protected Result RunDialog_New(Windows.HWnd hWndOwner, FolderBrowserDialog.COM_IFileDialog* fileDialog) + protected Result RunDialog_New(Windows.HWnd hWndOwner, Windows.COM_IFileDialog* fileDialog) { //COM_IFileDialogEvents evts; /*COM_IFileDialogEvents.VTable funcs; @@ -106,8 +106,8 @@ namespace System.IO if (!mSelectedPath.IsEmpty) { - COM_IShellItem* folderShellItem = null; - Windows.SHCreateItemFromParsingName(mSelectedPath.ToScopedNativeWChar!(), null, COM_IShellItem.sIID, (void**)&folderShellItem); + Windows.COM_IShellItem* folderShellItem = null; + Windows.SHCreateItemFromParsingName(mSelectedPath.ToScopedNativeWChar!(), null, Windows.COM_IShellItem.sIID, (void**)&folderShellItem); if (folderShellItem != null) { fileDialog.VT.SetDefaultFolder(fileDialog, folderShellItem); @@ -121,7 +121,7 @@ namespace System.IO DialogResult result = .Cancel; mSelectedPath.Clear(); - COM_IShellItem* shellItem = null; + Windows.COM_IShellItem* shellItem = null; fileDialog.VT.GetResult(fileDialog, out shellItem); if (shellItem != null) { @@ -142,10 +142,10 @@ namespace System.IO protected override Result RunDialog(Windows.HWnd hWndOwner) { - FolderBrowserDialog.COM_IFileDialog* fileDialog = null; + Windows.COM_IFileDialog* fileDialog = null; Windows.COM_IUnknown.HResult hr; //if (mFolderKind == .Open) - hr = Windows.COM_IUnknown.CoCreateInstance(ref FolderBrowserDialog.COM_IFileDialog.sCLSID, null, .INPROC_SERVER, ref FolderBrowserDialog.COM_IFileDialog.sIID, (void**)&fileDialog); + hr = Windows.COM_IUnknown.CoCreateInstance(ref Windows.COM_IFileDialog.sCLSID, null, .INPROC_SERVER, ref Windows.COM_IFileDialog.sIID, (void**)&fileDialog); //else //hr = Windows.COM_IUnknown.CoCreateInstance(ref FolderBrowserDialog.COM_FileSaveDialog.sCLSID, null, .INPROC_SERVER, ref FolderBrowserDialog.COM_FileSaveDialog.sIID, (void**)&fileDialog); if (hr == 0) @@ -219,143 +219,6 @@ namespace System.IO } return 0; } - - struct FDE_SHAREVIOLATION_RESPONSE; - struct FDE_OVERWRITE_RESPONSE; - - struct COM_IFileDialogEvents : Windows.COM_IUnknown - { - public struct VTable : Windows.COM_IUnknown.VTable - { - public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnFileOk; - public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, COM_IShellItem* psiFolder) OnFolderChanging; - public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnFolderChange; - public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnSelectionChange; - public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, FDE_SHAREVIOLATION_RESPONSE* pResponse) OnShareViolation; - public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnTypeChange; - public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, COM_IShellItem* shellItem, FDE_OVERWRITE_RESPONSE* response) OnOverwrite; - - } - } - - struct COM_IShellItem : Windows.COM_IUnknown - { - public static Guid sIID = .(0x43826d1e, 0xe718, 0x42ee, 0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe); - - public enum SIGDN : uint32 - { - NORMALDISPLAY = 0x00000000, // SHGDN_NORMAL - PARENTRELATIVEPARSING = 0x80018001, // SHGDN_INFOLDER | SHGDN_FORPARSING - DESKTOPABSOLUTEPARSING = 0x80028000, // SHGDN_FORPARSING - PARENTRELATIVEEDITING = 0x80031001, // SHGDN_INFOLDER | SHGDN_FOREDITING - DESKTOPABSOLUTEEDITING = 0x8004c000, // SHGDN_FORPARSING | SHGDN_FORADDRESSBAR - FILESYSPATH = 0x80058000, // SHGDN_FORPARSING - URL = 0x80068000, // SHGDN_FORPARSING - PARENTRELATIVEFORADDRESSBAR = 0x8007c001, // SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR - PARENTRELATIVE = 0x80080001 // SHGDN_INFOLDER - } - - public struct VTable : Windows.COM_IUnknown.VTable - { - public function HResult(COM_IShellItem* self, void* pbc, ref Guid bhid, ref Guid riid, void** ppv) BindToHandler; - public function HResult(COM_IShellItem* self, out COM_IShellItem* ppsi) GetParent; - public function HResult(COM_IShellItem* self, SIGDN sigdnName, out char16* ppszName) GetDisplayName; - public function HResult(COM_IShellItem* self, uint sfgaoMask, out uint psfgaoAttribs) GetAttributes; - public function HResult(COM_IShellItem* self, COM_IShellItem* psi, uint32 hint, out int32 piOrder) Compare; - - } - public new VTable* VT - { - get - { - return (.)mVT; - } - } - } - - struct COMDLG_FILTERSPEC - { - public char16* pszName; - public char16* pszSpec; - } - - enum FDAP : uint32 - { - FDAP_BOTTOM = 0x00000000, - FDAP_TOP = 0x00000001, - } - - public struct COM_IFileDialog : Windows.COM_IUnknown - { - public static Guid sIID = .(0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, 0x5d, 0x13, 0x5f, 0xc8); - public static Guid sCLSID = .(0xdc1c5a9c, 0xe88a, 0x4dde, 0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7); - - ///s - public enum FOS : uint32 - { - OVERWRITEPROMPT = 0x00000002, - STRICTFILETYPES = 0x00000004, - NOCHANGEDIR = 0x00000008, - PICKFOLDERS = 0x00000020, - FORCEFILESYSTEM = 0x00000040, - ALLNONSTORAGEITEMS = 0x00000080, - NOVALIDATE = 0x00000100, - ALLOWMULTISELECT = 0x00000200, - PATHMUSTEXIST = 0x00000800, - FILEMUSTEXIST = 0x00001000, - CREATEPROMPT = 0x00002000, - SHAREAWARE = 0x00004000, - NOREADONLYRETURN = 0x00008000, - NOTESTFILECREATE = 0x00010000, - HIDEMRUPLACES = 0x00020000, - HIDEPINNEDPLACES = 0x00040000, - NODEREFERENCELINKS = 0x00100000, - DONTADDTORECENT = 0x02000000, - FORCESHOWHIDDEN = 0x10000000, - DEFAULTNOMINIMODE = 0x20000000 - } - - public struct VTable : Windows.COM_IUnknown.VTable - { - public function HResult(COM_IFileDialog* self, Windows.HWnd parent) Show; - public function HResult(COM_IFileDialog* self, uint cFileTypes, COMDLG_FILTERSPEC* rgFilterSpec) SetFileTypes; - public function HResult(COM_IFileDialog* self, uint iFileType) SetFileTypeIndex; - public function HResult(COM_IFileDialog* self, out uint piFileType) GetFileTypeIndex; - public function HResult(COM_IFileDialog* self, COM_IFileDialogEvents* pfde, out uint pdwCookie) Advise; - public function HResult(COM_IFileDialog* self, uint dwCookie) Unadvise; - public function HResult(COM_IFileDialog* self, FOS fos) SetOptions; - public function HResult(COM_IFileDialog* self, out FOS pfos) GetOptions; - public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetDefaultFolder; - public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetFolder; - public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetFolder; - public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetCurrentSelection; - public function HResult(COM_IFileDialog* self, char16* pszName) SetFileName; - public function HResult(COM_IFileDialog* self, out char16* pszName) GetFileName; - public function HResult(COM_IFileDialog* self, char16* pszTitle) SetTitle; - public function HResult(COM_IFileDialog* self, char16* pszText) SetOkButtonLabel; - public function HResult(COM_IFileDialog* self, char16* pszLabel) SetFileNameLabel; - public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetResult; - public function HResult(COM_IFileDialog* self, COM_IShellItem* psi, FDAP fdap) AddPlace; - public function HResult(COM_IFileDialog* self, char16* pszDefaultExtension) SetDefaultExtension; - public function HResult(COM_IFileDialog* self, int hr) Close; - public function HResult(COM_IFileDialog* self, ref Guid guid) SetClientGuid; - public function HResult(COM_IFileDialog* self) ClearClientData; - public function HResult(COM_IFileDialog* self, void* pFilter) SetFilter; - } - public new VTable* VT - { - get - { - return (.)mVT; - } - } - } - - public struct COM_FileSaveDialog : COM_IFileDialog - { - public static new Guid sIID = .(0x84bccd23, 0x5fde, 0x4cdb, 0xae, 0xa4, 0xaf, 0x64, 0xb8, 0x3d, 0x78, 0xab); - public static new Guid sCLSID = .(0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B); - } } } #endif \ No newline at end of file diff --git a/BeefLibs/corlib/src/IO/OpenFileDialog.bf b/BeefLibs/corlib/src/IO/OpenFileDialog.bf index 3697c1c5..c7c79d11 100644 --- a/BeefLibs/corlib/src/IO/OpenFileDialog.bf +++ b/BeefLibs/corlib/src/IO/OpenFileDialog.bf @@ -11,460 +11,6 @@ using System.Text; namespace System.IO { - enum DialogResult - { - None = 0, - OK = 1, - Cancel = 2 - } - - abstract class CommonDialog - { - public Windows.HWnd mHWnd; - public Windows.HWnd mDefaultControlHwnd; - public int mDefWndProc; - - private const int32 CDM_SETDEFAULTFOCUS = Windows.WM_USER + 0x51; - - public static Dictionary sHookMap = new Dictionary() ~ - { - Debug.Assert(sHookMap.Count == 0); - delete _; - }; - public static Monitor sMonitor = new Monitor() ~ delete _; - - public Result ShowDialog(INativeWindow owner = null) - { - Windows.HWnd hwndOwner = 0; - if (owner != null) - hwndOwner = (.)owner.Handle; - //Native.WndProc wndProc = scope => OwnerWndProc; - - //mDefWndProc = Native.SetWindowLong(mHWnd, Native.GWL_WNDPROC, (intptr)wndProc.GetFuncPtr().Value); - - var result = RunDialog(hwndOwner); - return result; - } - - public virtual int OwnerWndProc(Windows.HWnd hWnd, int32 msg, int wParam, int lParam) - { - return Windows.CallWindowProcW(mDefWndProc, hWnd, msg, wParam, lParam); - } - - protected virtual int HookProc(Windows.HWnd hWnd, int32 msg, int wParam, int lparam) - { - if (msg == Windows.WM_INITDIALOG) - { - //TODO: MoveToScreenCenter(hWnd); - // Under some circumstances, the dialog - // does not initially focus on any control. We fix that by explicitly - // setting focus ourselves. See ASURT 39435. - // - mDefaultControlHwnd = (Windows.HWnd)wParam; - if (mDefaultControlHwnd != 0) - Windows.SetFocus(mDefaultControlHwnd); - } - else if (msg == Windows.WM_SETFOCUS) - { - Windows.PostMessageW(hWnd, CDM_SETDEFAULTFOCUS, 0, 0); - } - else if (msg == CDM_SETDEFAULTFOCUS) - { - // If the dialog box gets focus, bounce it to the default control. - // so we post a message back to ourselves to wait for the focus change then push it to the default - // control. See ASURT 84016. - // - if (mDefaultControlHwnd != 0) - Windows.SetFocus(mDefaultControlHwnd); - } - return 0; - } - - protected abstract Result RunDialog(Windows.HWnd hWndOwner); - } - - abstract class FileDialog : CommonDialog - { - protected abstract Result RunFileDialog(ref Windows.OpenFileName ofn); - - protected override Result RunDialog(Windows.HWnd hWndOwner) - { - return RunDialogOld(hWndOwner); - } - - private const int32 FILEBUFSIZE = 8192; - protected const int32 OPTION_ADDEXTENSION = (int32)0x80000000; - - protected int32 mOptions; - private String mTitle ~ delete _; - private String mInitialDir ~ delete _; - private String mDefaultExt ~ delete _; - protected String[] mFileNames ~ DeleteContainerAndItems!(_); - private bool mSecurityCheckFileNames; - private String mFilter ~ delete _; - private String mFilterBuffer = new String() ~ delete _; - private int32 mFilterIndex; - private bool mSupportMultiDottedExtensions; - private bool mIgnoreSecondFileOkNotification; // Used for VS Whidbey 95342 - private int32 mOKNotificationCount; // Same - //private String char8Buffer = new String(FILEBUFSIZE) ~ delete _; - - public this() - { - Reset(); - } - - public virtual void Reset() - { - DeleteAndNullify!(mTitle); - DeleteAndNullify!(mInitialDir); - DeleteAndNullify!(mDefaultExt); - DeleteContainerAndItems!(mFileNames); - mFileNames = null; - DeleteAndNullify!(mFilter); - mFilterIndex = 1; - mSupportMultiDottedExtensions = false; - mOptions = Windows.OFN_HIDEREADONLY | Windows.OFN_PATHMUSTEXIST | - OPTION_ADDEXTENSION; - } - - protected int32 Options - { - get - { - return mOptions & ( - Windows.OFN_READONLY | - Windows.OFN_HIDEREADONLY | - Windows.OFN_NOCHANGEDIR | - Windows.OFN_SHOWHELP | - Windows.OFN_NOVALIDATE | - Windows.OFN_ALLOWMULTISELECT | - Windows.OFN_PATHMUSTEXIST | - Windows.OFN_FILEMUSTEXIST | - Windows.OFN_NODEREFERENCELINKS | - Windows.OFN_OVERWRITEPROMPT); - //return mOptions; - } - } - - public StringView Title - { - set - { - String.NewOrSet!(mTitle, value); - } - - get - { - return mTitle; - } - } - - public StringView InitialDirectory - { - set - { - String.NewOrSet!(mInitialDir, value); - } - - get - { - return mInitialDir; - } - } - - public String[] FileNames - { - get - { - return mFileNames; - } - } - - public StringView FileName - { - set - { - if (mFileNames == null) - { - mFileNames = new String[](new String(value)); - } - } - } - - public bool AddExtension - { - get - { - return GetOption(OPTION_ADDEXTENSION); - } - - set - { - SetOption(OPTION_ADDEXTENSION, value); - } - } - - public virtual bool CheckFileExists - { - get - { - return GetOption(Windows.OFN_FILEMUSTEXIST); - } - - set - { - SetOption(Windows.OFN_FILEMUSTEXIST, value); - } - } - - public bool DereferenceLinks - { - get - { - return !GetOption(Windows.OFN_NODEREFERENCELINKS); - } - set - { - SetOption(Windows.OFN_NODEREFERENCELINKS, !value); - } - } - - public bool CheckPathExists - { - get - { - return GetOption(Windows.OFN_PATHMUSTEXIST); - } - - set - { - SetOption(Windows.OFN_PATHMUSTEXIST, value); - } - } - - public bool Multiselect - { - get - { - return GetOption(Windows.OFN_ALLOWMULTISELECT); - } - - set - { - SetOption(Windows.OFN_ALLOWMULTISELECT, value); - } - } - - public bool ValidateNames - { - get - { - return !GetOption(Windows.OFN_NOVALIDATE); - } - - set - { - SetOption(Windows.OFN_NOVALIDATE, !value); - } - } - - public StringView DefaultExt - { - get - { - return mDefaultExt == null ? "" : mDefaultExt; - } - - set - { - delete mDefaultExt; - mDefaultExt = null; - - //if (!String.IsNullOrEmpty(value)) - if (value.Length > 0) - { - mDefaultExt = new String(value); - if (mDefaultExt.StartsWith(".")) - mDefaultExt.Remove(0, 1); - } - } - } - - public void GetFilter(String outFilter) - { - if (mFilter != null) - outFilter.Append(mFilter); - } - - public Result SetFilter(StringView value) - { - String useValue = scope String(value); - if (useValue != null && useValue.Length > 0) - { - var formats = String.StackSplit!(useValue, '|'); - if (formats == null || formats.Count % 2 != 0) - { - return .Err; - } - /// - /*String[] formats = value.Split('|'); - if (formats == null || formats.Length % 2 != 0) - { - throw new ArgumentException(SR.GetString(SR.FileDialogInvalidFilter)); - }*/ - String.NewOrSet!(mFilter, useValue); - } - else - { - useValue = null; - DeleteAndNullify!(mFilter); - } - - return .Ok; - } - - protected bool GetOption(int32 option) - { - return (mOptions & option) != 0; - } - - protected void SetOption(int32 option, bool value) - { - if (value) - { - mOptions |= option; - } - else - { - mOptions &= ~option; - } - } - - private static Result MakeFilterString(String s, bool dereferenceLinks, String filterBuffer) - { - String useStr = s; - if (useStr == null || useStr.Length == 0) - { - // Workaround for Whidbey bug #5165 - // Apply the workaround only when DereferenceLinks is true and OS is at least WinXP. - if (dereferenceLinks && System.Environment.OSVersion.Version.Major >= 5) - { - useStr = " |*.*"; - } - else if (useStr == null) - { - return .Err; - } - } - - filterBuffer.Set(s); - for (int32 i = 0; i < filterBuffer.Length; i++) - if (filterBuffer[i] == '|') - filterBuffer[i] = (char8)0; - filterBuffer.Append((char8)0); - return .Ok; - } - - public static mixin Testie() - { - int a = 123; - char16* buf; - if (a == 0) - { - buf = null; - } - else - { - buf = new char16[123]* ( ? ); - defer:mixin delete buf; - } - buf - } - - private Result RunDialogOld(Windows.HWnd hWndOwner) - { - //RunDialogTest(hWndOwner); - - Windows.WndProc hookProcPtr = => StaticHookProc; - Windows.OpenFileName ofn = Windows.OpenFileName(); - - char16[FILEBUFSIZE] char16Buffer = .(0, ?); - - if (mFileNames != null) - { - //int len = UTF16.GetEncodedLen(fileNames[0]); - //char16Buffer = scope:: char16[len + 1]*; - UTF16.Encode(mFileNames[0], (char16*)&char16Buffer, FILEBUFSIZE); - } - // Degrade to the older style dialog if we're not on Win2K. - // We do this by setting the struct size to a different value - // - - if (Environment.OSVersion.Platform != System.PlatformID.Win32NT || - Environment.OSVersion.Version.Major < 5) { - ofn.mStructSize = 0x4C; - } - ofn.mHwndOwner = hWndOwner; - ofn.mHInstance = (Windows.HInstance)Windows.GetModuleHandleW(null); - - if (mFilter != null) - { - Try!(MakeFilterString(mFilter, this.DereferenceLinks, mFilterBuffer)); - ofn.mFilter = mFilterBuffer.ToScopedNativeWChar!::(); - } - ofn.nFilterIndex = mFilterIndex; - ofn.mFile = (char16*)&char16Buffer; - ofn.nMaxFile = FILEBUFSIZE; - if (mInitialDir != null) - ofn.mInitialDir = mInitialDir.ToScopedNativeWChar!::(); - if (mTitle != null) - ofn.mTitle = mTitle.ToScopedNativeWChar!::(); - ofn.mFlags = Options | (Windows.OFN_EXPLORER | Windows.OFN_ENABLEHOOK | Windows.OFN_ENABLESIZING); - ofn.mHook = hookProcPtr; - ofn.mCustData = (int)Internal.UnsafeCastToPtr(this); - ofn.mFlagsEx = Windows.OFN_USESHELLITEM; - if (mDefaultExt != null && AddExtension) - ofn.mDefExt = mDefaultExt.ToScopedNativeWChar!::(); - - DeleteContainerAndItems!(mFileNames); - mFileNames = null; - //Security checks happen here - return RunFileDialog(ref ofn); - } - - static int StaticHookProc(Windows.HWnd hWnd, int32 msg, int wParam, int lparam) - { - if (msg == Windows.WM_INITDIALOG) - { - using (sMonitor.Enter()) - { - var ofn = (Windows.OpenFileName*)(void*)lparam; - sHookMap[(int)hWnd] = (CommonDialog)Internal.UnsafeCastToObject((void*)ofn.mCustData); - } - } - - CommonDialog dlg; - using (sMonitor.Enter()) - { - sHookMap.TryGetValue((int)hWnd, out dlg); - } - if (dlg == null) - return 0; - - dlg.[Friend]HookProc(hWnd, msg, wParam, lparam); - if (msg == Windows.WM_DESTROY) - { - using (sMonitor.Enter()) - { - sHookMap.Remove((int)hWnd); - } - } - return 0; - } - //TODO: Add ProcessFileNames for validation - } - class OpenFileDialog : FileDialog { public override void Reset() @@ -557,6 +103,73 @@ namespace System.IO return DialogResult.OK; } + + protected override void ProcessVistaFiles(Windows.COM_IFileDialog* dialog, List files) + { + mixin GetFilePathFromShellItem(Windows.COM_IShellItem* shellItem) + { + String str = null; + if (shellItem.VT.GetDisplayName(shellItem, .FILESYSPATH, let cStr) == .OK) + { + str = new String()..Append(cStr); + Windows.COM_IUnknown.CoTaskMemFree(cStr); + } + str + } + + Windows.COM_IFileOpenDialog* openDialog = (.)dialog; + if (Multiselect) + { + Windows.COM_IShellItemArray* results = null; + openDialog.VT.GetResults(openDialog, out results); + + if (results != null) + { + results.VT.GetCount(results, let count); + for (uint32 i < count) + { + Windows.COM_IShellItem* item = null; + results.VT.GetItemAt(results, i, out item); + if (item != null) + { + let filePath = GetFilePathFromShellItem!(item); + if (filePath != null) + files.Add(filePath); + } + } + results.VT.Release(results); + } + } + else + { + Windows.COM_IShellItem* shellItem = null; + openDialog.VT.GetResult(openDialog, out shellItem); + + if (shellItem != null) + { + let filePath = GetFilePathFromShellItem!(shellItem); + if (filePath != null) + files.Add(filePath); + shellItem.VT.Release(shellItem); + } + } + } + + protected override Result CreateVistaDialog() + { + Windows.COM_IFileDialog* fileDialog = null; + + Windows.COM_IUnknown.HResult hr = (Windows.COM_IUnknown.CoCreateInstance( + ref Windows.COM_IFileDialog.sCLSID, + null, + .INPROC_SERVER | .LOCAL_SERVER | .REMOTE_SERVER, + ref Windows.COM_IFileOpenDialog.sIID, + (void**)&fileDialog)); + if (hr.Failed) + return .Err; + + return fileDialog; + } } } diff --git a/BeefLibs/corlib/src/IO/SaveFileDialog.bf b/BeefLibs/corlib/src/IO/SaveFileDialog.bf index 89585e2d..58e1bca9 100644 --- a/BeefLibs/corlib/src/IO/SaveFileDialog.bf +++ b/BeefLibs/corlib/src/IO/SaveFileDialog.bf @@ -94,6 +94,48 @@ namespace System.IO return DialogResult.OK; } + + protected override void ProcessVistaFiles(Windows.COM_IFileDialog* dialog, System.Collections.List files) + { + mixin GetFilePathFromShellItem(Windows.COM_IShellItem* shellItem) + { + String str = null; + if (shellItem.VT.GetDisplayName(shellItem, .FILESYSPATH, let cStr) == .OK) + { + str = new String()..Append(cStr); + Windows.COM_IUnknown.CoTaskMemFree(cStr); + } + str + } + + Windows.COM_IFileSaveDialog* saveDialog = (.)dialog; + Windows.COM_IShellItem* shellItem = null; + saveDialog.VT.GetResult(saveDialog, out shellItem); + + if (shellItem != null) + { + let filePath = GetFilePathFromShellItem!(shellItem); + if (filePath != null) + files.Add(filePath); + shellItem.VT.Release(shellItem); + } + } + + protected override Result CreateVistaDialog() + { + Windows.COM_IFileDialog* fileDialog = null; + + Windows.COM_IUnknown.HResult hr = (Windows.COM_IUnknown.CoCreateInstance( + ref Windows.COM_IFileSaveDialog.sCLSID, + null, + .INPROC_SERVER | .LOCAL_SERVER | .REMOTE_SERVER, + ref Windows.COM_IFileSaveDialog.sIID, + (void**)&fileDialog)); + if (hr.Failed) + return .Err; + + return fileDialog; + } } } diff --git a/BeefLibs/corlib/src/Windows.bf b/BeefLibs/corlib/src/Windows.bf index 44f3ba04..23de97ad 100644 --- a/BeefLibs/corlib/src/Windows.bf +++ b/BeefLibs/corlib/src/Windows.bf @@ -1117,6 +1117,215 @@ namespace System TRUSTEE_W Trustee; } + struct COM_IFileDialogEvents : Windows.COM_IUnknown + { + struct FDE_SHAREVIOLATION_RESPONSE; + struct FDE_OVERWRITE_RESPONSE; + + public struct VTable : Windows.COM_IUnknown.VTable + { + public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnFileOk; + public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, COM_IShellItem* psiFolder) OnFolderChanging; + public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnFolderChange; + public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnSelectionChange; + public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, FDE_SHAREVIOLATION_RESPONSE* pResponse) OnShareViolation; + public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog) OnTypeChange; + public function HResult(COM_IFileDialogEvents* self, COM_IFileDialog* fileDialog, COM_IShellItem* shellItem, FDE_OVERWRITE_RESPONSE* response) OnOverwrite; + + } + } + + public struct COM_IShellItem : Windows.COM_IUnknown + { + public static Guid sIID = .(0x43826d1e, 0xe718, 0x42ee, 0xbc, 0x55, 0xa1, 0xe2, 0x61, 0xc3, 0x7b, 0xfe); + + public enum SIGDN : uint32 + { + NORMALDISPLAY = 0x00000000, // SHGDN_NORMAL + PARENTRELATIVEPARSING = 0x80018001, // SHGDN_INFOLDER | SHGDN_FORPARSING + DESKTOPABSOLUTEPARSING = 0x80028000, // SHGDN_FORPARSING + PARENTRELATIVEEDITING = 0x80031001, // SHGDN_INFOLDER | SHGDN_FOREDITING + DESKTOPABSOLUTEEDITING = 0x8004c000, // SHGDN_FORPARSING | SHGDN_FORADDRESSBAR + FILESYSPATH = 0x80058000, // SHGDN_FORPARSING + URL = 0x80068000, // SHGDN_FORPARSING + PARENTRELATIVEFORADDRESSBAR = 0x8007c001, // SHGDN_INFOLDER | SHGDN_FORPARSING | SHGDN_FORADDRESSBAR + PARENTRELATIVE = 0x80080001 // SHGDN_INFOLDER + } + + public struct VTable : Windows.COM_IUnknown.VTable + { + public function HResult(COM_IShellItem* self, void* pbc, ref Guid bhid, ref Guid riid, void** ppv) BindToHandler; + public function HResult(COM_IShellItem* self, out COM_IShellItem* ppsi) GetParent; + public function HResult(COM_IShellItem* self, SIGDN sigdnName, out char16* ppszName) GetDisplayName; + public function HResult(COM_IShellItem* self, uint sfgaoMask, out uint psfgaoAttribs) GetAttributes; + public function HResult(COM_IShellItem* self, COM_IShellItem* psi, uint32 hint, out int32 piOrder) Compare; + + } + public new VTable* VT + { + get + { + return (.)mVT; + } + } + } + + public struct COM_IShellItemArray : Windows.COM_IUnknown + { + public enum GETPROPERTYSTOREFLAGS : uint + { + DEFAULT = 0x00000000, + HANDLERPROPERTIESONLY = 0x00000001, + READWRITE = 0x00000002, + TEMPORARY = 0x00000004, + FASTPROPERTIESONLY = 0x00000008, + OPENSLOWITEM = 0x00000010, + DELAYCREATION = 0x00000020, + BESTEFFORT = 0x00000040, + NO_OPLOCK = 0x00000080, + PREFERQUERYPROPERTIES = 0x00000100, + EXTRINSICPROPERTIES = 0x00000200, + EXTRINSICPROPERTIESONLY = 0x00000400, + VOLATILEPROPERTIES = 0x00000800, + VOLATILEPROPERTIESONLY = 0x00001000, + MASK_VALID = 0x00001FFF + } + + [AllowDuplicates] + public enum SIATTRIBFLAGS : uint + { + AND = 0x1, + OR = 0x2, + APPCOMPAT = 0x3, + MASK = 0x3, + ALLITEMS = 0x4000 + } + + public struct PROPERTYKEY; + + public static Guid sIID = .(0xB63EA76D, 0x1F85, 0x456F, 0xA1, 0x9C, 0x48, 0x15, 0x9E, 0xFA, 0x85, 0x8B); + + public struct VTable : Windows.COM_IUnknown.VTable + { + public function HResult(COM_IShellItemArray* self, void* pbc, ref Guid rbhid, ref Guid riid, out void* ppvOut) BindToHandler; + public function HResult(COM_IShellItemArray* self, GETPROPERTYSTOREFLAGS flags, ref Guid riid, out void* ppv) GetPropertyStore; + public function HResult(COM_IShellItemArray* self, ref PROPERTYKEY keyType, ref Guid riid, out void* ppv) GetPropertyDescriptionList; + public function HResult(COM_IShellItemArray* self, SIATTRIBFLAGS dwAttribFlags, uint32 sfgaoMask, out uint32 psfgaoAttribs) GetAttributes; + public function HResult(COM_IShellItemArray* self, out uint32 pdwNumItems) GetCount; + public function HResult(COM_IShellItemArray* self, uint32 dwIndex, out COM_IShellItem* ppsi) GetItemAt; + public function HResult(COM_IShellItemArray* self, out void* ppenumShellItems) EnumItems; + } + public new VTable* VT + { + get + { + return (.)mVT; + } + } + } + + public struct COMDLG_FILTERSPEC + { + public char16* pszName; + public char16* pszSpec; + } + + enum FDAP : uint32 + { + FDAP_BOTTOM = 0x00000000, + FDAP_TOP = 0x00000001, + } + + public struct COM_IFileDialog : Windows.COM_IUnknown + { + public static Guid sIID = .(0x42f85136, 0xdb7e, 0x439c, 0x85, 0xf1, 0xe4, 0x07, 0x5d, 0x13, 0x5f, 0xc8); + public static Guid sCLSID = .(0xdc1c5a9c, 0xe88a, 0x4dde, 0xa5, 0xa1, 0x60, 0xf8, 0x2a, 0x20, 0xae, 0xf7); + + ///s + public enum FOS : uint32 + { + OVERWRITEPROMPT = 0x00000002, + STRICTFILETYPES = 0x00000004, + NOCHANGEDIR = 0x00000008, + PICKFOLDERS = 0x00000020, + FORCEFILESYSTEM = 0x00000040, + ALLNONSTORAGEITEMS = 0x00000080, + NOVALIDATE = 0x00000100, + ALLOWMULTISELECT = 0x00000200, + PATHMUSTEXIST = 0x00000800, + FILEMUSTEXIST = 0x00001000, + CREATEPROMPT = 0x00002000, + SHAREAWARE = 0x00004000, + NOREADONLYRETURN = 0x00008000, + NOTESTFILECREATE = 0x00010000, + HIDEMRUPLACES = 0x00020000, + HIDEPINNEDPLACES = 0x00040000, + NODEREFERENCELINKS = 0x00100000, + DONTADDTORECENT = 0x02000000, + FORCESHOWHIDDEN = 0x10000000, + DEFAULTNOMINIMODE = 0x20000000 + } + + public struct VTable : Windows.COM_IUnknown.VTable + { + public function HResult(COM_IFileDialog* self, Windows.HWnd parent) Show; + public function HResult(COM_IFileDialog* self, uint cFileTypes, COMDLG_FILTERSPEC* rgFilterSpec) SetFileTypes; + public function HResult(COM_IFileDialog* self, uint iFileType) SetFileTypeIndex; + public function HResult(COM_IFileDialog* self, out uint piFileType) GetFileTypeIndex; + public function HResult(COM_IFileDialog* self, COM_IFileDialogEvents* pfde, out uint pdwCookie) Advise; + public function HResult(COM_IFileDialog* self, uint dwCookie) Unadvise; + public function HResult(COM_IFileDialog* self, FOS fos) SetOptions; + public function HResult(COM_IFileDialog* self, out FOS pfos) GetOptions; + public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetDefaultFolder; + public function HResult(COM_IFileDialog* self, COM_IShellItem* psi) SetFolder; + public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetFolder; + public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetCurrentSelection; + public function HResult(COM_IFileDialog* self, char16* pszName) SetFileName; + public function HResult(COM_IFileDialog* self, out char16* pszName) GetFileName; + public function HResult(COM_IFileDialog* self, char16* pszTitle) SetTitle; + public function HResult(COM_IFileDialog* self, char16* pszText) SetOkButtonLabel; + public function HResult(COM_IFileDialog* self, char16* pszLabel) SetFileNameLabel; + public function HResult(COM_IFileDialog* self, out COM_IShellItem* ppsi) GetResult; + public function HResult(COM_IFileDialog* self, COM_IShellItem* psi, FDAP fdap) AddPlace; + public function HResult(COM_IFileDialog* self, char16* pszDefaultExtension) SetDefaultExtension; + public function HResult(COM_IFileDialog* self, int hr) Close; + public function HResult(COM_IFileDialog* self, ref Guid guid) SetClientGuid; + public function HResult(COM_IFileDialog* self) ClearClientData; + public function HResult(COM_IFileDialog* self, void* pFilter) SetFilter; + } + public new VTable* VT + { + get + { + return (.)mVT; + } + } + } + + public struct COM_IFileSaveDialog : COM_IFileDialog + { + public static new Guid sIID = .(0x84BCCD23, 0x5FDE, 0x4CDB, 0xAE, 0xA4, 0xAF, 0x64, 0xB8, 0x3D, 0x78, 0xAB); + public static new Guid sCLSID = .(0xC0B4E2F3, 0xBA21, 0x4773, 0x8D, 0xBA, 0x33, 0x5E, 0xC9, 0x46, 0xEB, 0x8B); + } + + public struct COM_IFileOpenDialog : COM_IFileDialog + { + public static new Guid sIID = .(0xD57C7288, 0xD4AD, 0x4768, 0xBE, 0x02, 0x9D, 0x96, 0x95, 0x32, 0xD9, 0x60); + + public struct VTable : COM_IFileDialog.VTable + { + public function HResult(COM_IFileOpenDialog* self, out COM_IShellItemArray* ppenum) GetResults; + public function HResult(COM_IFileOpenDialog* self, out COM_IShellItemArray* ppsai) GetSelectedItems; + } + public new VTable* VT + { + get + { + return (.)mVT; + } + } + } + [Import("version.lib"), CLink, CallingConvention(.Stdcall)] public static extern IntBool GetFileVersionInfoW(char16* lptstrFilename, uint32 dwHandle, uint32 dwLen, void* lpData); diff --git a/BeefySysLib/util/Array.h b/BeefySysLib/util/Array.h index e485023c..c5241db5 100644 --- a/BeefySysLib/util/Array.h +++ b/BeefySysLib/util/Array.h @@ -830,8 +830,8 @@ public: else if (size > this->mSize) { Reserve(size); - while (size > this->mSize) - this->mVals[this->mSize++] = T(); + memset(&this->mVals[this->mSize], 0, (size - this->mSize) * sizeof(T)); + this->mSize = (int_cosize)size; } } diff --git a/IDE/Tests/CompileFail001/src/Generics.bf b/IDE/Tests/CompileFail001/src/Generics.bf index 8a27fbf4..6f851c63 100644 --- a/IDE/Tests/CompileFail001/src/Generics.bf +++ b/IDE/Tests/CompileFail001/src/Generics.bf @@ -52,7 +52,7 @@ namespace IDETest void MethodB() { - function void() f = => MethodA; //FAIL Method 'IDETest.Generics.ClassA.MethodA(int a)' does not match function 'function void()' + function void() f = => MethodA; //FAIL Method 'void IDETest.Generics.ClassA.MethodA(int a)' does not match function 'function void()' } } diff --git a/IDE/mintest/minlib/src/System/Compiler.bf b/IDE/mintest/minlib/src/System/Compiler.bf index 2c559e2c..d6aefbda 100644 --- a/IDE/mintest/minlib/src/System/Compiler.bf +++ b/IDE/mintest/minlib/src/System/Compiler.bf @@ -2,6 +2,11 @@ namespace System { class Compiler { + public class Generator + { + + } + public static class Options { [LinkName("#AllocStackCount")] diff --git a/IDE/src/CommandQueueManager.bf b/IDE/src/CommandQueueManager.bf index 213fee60..4fa97410 100644 --- a/IDE/src/CommandQueueManager.bf +++ b/IDE/src/CommandQueueManager.bf @@ -15,9 +15,10 @@ namespace IDE public Action mOnThreadDone ~ delete _; [ThreadStatic] public static bool mBpSetThreadName; + public bool mAllowFastFinish; WaitEvent mWaitEvent = new WaitEvent() ~ delete _; - public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0) + public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0, bool allowFastFinish = true) { Debug.Assert(Thread.CurrentThread == IDEApp.sApp.mMainThread); @@ -26,6 +27,7 @@ namespace IDE BeefPerf.Event("DoBackground:starting", ""); + mAllowFastFinish = allowFastFinish; mOnThreadDone = onThreadDone; mThreadRunning = true; mWaitEvent.Reset(); @@ -47,6 +49,7 @@ namespace IDE delete threadStart; mThread = null; mThreadRunning = false; + mAllowFastFinish = true; BeefPerf.Event("DoBackground:threadEnd", ""); mWaitEvent.Set(); @@ -132,7 +135,7 @@ namespace IDE } - public virtual void RequestFastFinish() + public virtual void RequestFastFinish(bool force = false) { } @@ -190,15 +193,20 @@ namespace IDE return (mThreadWorker.mThreadRunning || mThreadWorkerHi.mThreadRunning); } + public bool IsPerformingBackgroundOperationHi() + { + return (mThreadWorkerHi.mThreadRunning); + } + public void DoBackground(ThreadStart threadStart, Action onThreadDone = null, int maxWait = 0) { CancelBackground(); mThreadWorker.DoBackground(threadStart, onThreadDone, maxWait); } - public void DoBackgroundHi(ThreadStart threadStart, Action onThreadDone = null) + public void DoBackgroundHi(ThreadStart threadStart, Action onThreadDone = null, bool allowFastFinish = true) { - mThreadWorkerHi.DoBackground(threadStart, onThreadDone); + mThreadWorkerHi.DoBackground(threadStart, onThreadDone, 0, allowFastFinish); } public void CheckThreadDone() diff --git a/IDE/src/Compiler/BfCompiler.bf b/IDE/src/Compiler/BfCompiler.bf index e00b3502..efc60b19 100644 --- a/IDE/src/Compiler/BfCompiler.bf +++ b/IDE/src/Compiler/BfCompiler.bf @@ -94,6 +94,15 @@ namespace IDE.Compiler [CallingConvention(.Stdcall), CLink] static extern char8* BfCompiler_GetUsedOutputFileNames(void* bfCompiler, void* bfProject, bool flushQueuedHotFiles, out bool hadOutputChanges); + [CallingConvention(.Stdcall), CLink] + static extern char8* BfCompiler_GetGeneratorTypeDefList(void* bfCompiler); + + [CallingConvention(.Stdcall), CLink] + static extern char8* BfCompiler_GetGeneratorInitData(void* bfCompiler, char8* typeDefName, char8* args); + + [CallingConvention(.Stdcall), CLink] + static extern char8* BfCompiler_GetGeneratorGenData(void* bfCompiler, char8* typeDefName, char8* args); + [CallingConvention(.Stdcall), CLink] static extern char8* BfCompiler_GetTypeDefList(void* bfCompiler); @@ -234,13 +243,13 @@ namespace IDE.Compiler public void GetAutocompleteInfo(String outAutocompleteInfo) { char8* result = BfCompiler_GetAutocompleteInfo(mNativeBfCompiler); - scope String(result).MoveTo(outAutocompleteInfo); + outAutocompleteInfo.Append(StringView(result)); } public void GetSymbolReferences(BfPassInstance passInstance, BfResolvePassData resolvePassData, String outSymbolReferences) { char8* result = BfCompiler_GetSymbolReferences(mNativeBfCompiler, passInstance.mNativeBfPassInstance, resolvePassData.mNativeResolvePassData); - scope String(result).MoveTo(outSymbolReferences); + outSymbolReferences.Append(StringView(result)); } /*public void UpdateRenameSymbols(BfPassInstance passInstance, BfResolvePassData resolvePassData) @@ -674,17 +683,21 @@ namespace IDE.Compiler public override void RequestCancelBackground() { - if ([Friend]mThreadWorker.mThreadRunning) + if (mThreadWorker.mThreadRunning) { if ((mNativeBfCompiler != null) && (!gApp.mDeterministic)) BfCompiler_Cancel(mNativeBfCompiler); } } - public override void RequestFastFinish() + public override void RequestFastFinish(bool force = false) { - if ([Friend]mThreadWorker.mThreadRunning || [Friend]mThreadWorkerHi.mThreadRunning) + if (mThreadWorker.mThreadRunning || mThreadWorkerHi.mThreadRunning) { + if ((!force) && + ((!mThreadWorker.mAllowFastFinish) || (!mThreadWorkerHi.mAllowFastFinish))) + return; + if ((mNativeBfCompiler != null) && (!gApp.mDeterministic)) BfCompiler_RequestFastFinish(mNativeBfCompiler); } @@ -710,6 +723,21 @@ namespace IDE.Compiler return BfCompiler_GetCurConstEvalExecuteId(mNativeBfCompiler); } + public void GetGeneratorTypeDefList(String outStr) + { + outStr.Append(BfCompiler_GetGeneratorTypeDefList(mNativeBfCompiler)); + } + + public void GetGeneratorInitData(String typeDefName, String args, String outStr) + { + outStr.Append(BfCompiler_GetGeneratorInitData(mNativeBfCompiler, typeDefName, args)); + } + + public void GetGeneratorGenData(String typeDefName, String args, String outStr) + { + outStr.Append(BfCompiler_GetGeneratorGenData(mNativeBfCompiler, typeDefName, args)); + } + public void GetTypeDefList(String outStr) { outStr.Append(BfCompiler_GetTypeDefList(mNativeBfCompiler)); diff --git a/IDE/src/Project.bf b/IDE/src/Project.bf index 7418ea2a..56922862 100644 --- a/IDE/src/Project.bf +++ b/IDE/src/Project.bf @@ -37,6 +37,7 @@ namespace IDE public ProjectFolder mParentFolder; public String mName = new String() ~ delete _; public String mComment = new String() ~ delete _; + public bool mDetached; public virtual bool IncludeInMap { @@ -105,6 +106,7 @@ namespace IDE public virtual void Detach() { + mDetached = true; ReleaseRef(); } } @@ -353,7 +355,7 @@ namespace IDE return .SimpleSource; } - public override void Dispose() + public void ClearEditData() { if (mEditData != null) { @@ -363,6 +365,11 @@ namespace IDE } } + public override void Dispose() + { + ClearEditData(); + } + public override void Detach() { Dispose(); @@ -843,6 +850,13 @@ namespace IDE childFileItem.OnRename(childFileItem.mPath, newChildPath); + String oldFullName = scope String(); + mProject.GetProjectFullPath(childFileItem.mPath, oldFullName); + String newFullName = scope String(); + mProject.GetProjectFullPath(newChildPath, newFullName); + + IDEApp.sApp.FileRenamed(childFileItem, oldFullName, newFullName); + childFileItem.mPath.Set(newChildPath); } } diff --git a/IDE/src/ui/ClassViewPanel.bf b/IDE/src/ui/ClassViewPanel.bf index 3088cccc..51fac019 100644 --- a/IDE/src/ui/ClassViewPanel.bf +++ b/IDE/src/ui/ClassViewPanel.bf @@ -598,10 +598,14 @@ namespace IDE.ui } } + var bfSystem = gApp.mBfResolveSystem; + String typeName = scope .(); GetName(item, typeName); String info = scope .(); + bfSystem.Lock(0); gApp.mBfResolveCompiler.GetTypeDefInfo(typeName, info); + bfSystem.Unlock(); for (let str in info.Split('\n')) { diff --git a/IDE/src/ui/GenerateDialog.bf b/IDE/src/ui/GenerateDialog.bf new file mode 100644 index 00000000..7bb15545 --- /dev/null +++ b/IDE/src/ui/GenerateDialog.bf @@ -0,0 +1,882 @@ +using Beefy.theme.dark; +using Beefy.widgets; +using System; +using System.Collections; +using Beefy.gfx; +using Beefy.events; +using IDE.Compiler; +using System.Threading; +using System.Security.Cryptography; +using Beefy.theme; + +namespace IDE.ui +{ + class GenerateListView : DarkListView + { + public GenerateDialog mNewClassDialog; + } + + class GenerateKindBar : DarkComboBox + { + public class Entry + { + public String mTypeName ~ delete _; + public String mName ~ delete _; + } + + public static Dictionary sMRU = new Dictionary() ~ delete _; + public static int32 sCurrentMRUIndex = 1; + + public GenerateDialog mNewClassDialog; + public List mEntries = new List() ~ DeleteContainerAndItems!(_); + public List mShownEntries = new List() ~ delete _; + public String mFilterString ~ delete _; + public String mCurLocation = new String() ~ delete _; + public bool mIgnoreChange = false; + + public this(GenerateDialog dialog) + { + mNewClassDialog = dialog; + mLabelAlign = FontAlign.Left; + Label = ""; + mLabelX = GS!(16); + mPopulateMenuAction.Add(new => PopulateNavigationBar); + MakeEditable(); + mEditWidget.mOnContentChanged.Add(new => NavigationBarChanged); + mEditWidget.mOnGotFocus.Add(new (widget) => mEditWidget.mEditWidgetContent.SelectAll()); + mEditWidget.mEditWidgetContent.mWantsUndo = false; + mEditWidget.mOnSubmit.Add(new => mNewClassDialog.[Friend]EditSubmitHandler); + mFocusDropdown = false; + } + + public ~this() + { + } + + static ~this() + { + for (var key in sMRU.Keys) + delete key; + } + + public void SetLocation(String location) + { + if (mCurMenuWidget == null) + { + mIgnoreChange = true; + mEditWidget.SetText(location); + mEditWidget.mEditWidgetContent.SelectAll(); + // SetText can attempt to scroll to the right to make the cursor position visible. Just scroll back to the start. + mEditWidget.HorzScrollTo(0); + //mNewClassDialog.SelectKind(); + mIgnoreChange = false; + } + } + private void PopulateNavigationBar(Menu menu) + { + List findStrs = null; + if (mFilterString != null) + findStrs = scope:: List(mFilterString.Split(' ')); + + EntryLoop: for (int32 entryIdx = 0; entryIdx < mEntries.Count; entryIdx++) + { + var entry = mEntries[entryIdx]; + if (findStrs != null) + { + for (let findStr in findStrs) + { + if (entry.mName.IndexOf(findStr, true) == -1) + continue EntryLoop; + } + } + + mShownEntries.Add(entry); + var menuItem = menu.AddItem(entry.mName); + menuItem.mOnMenuItemSelected.Add(new (evt) => + { + mNewClassDialog.mPendingUIFocus = true; + ShowEntry(entryIdx, entry); + }); + } + } + + void ShowEntry(int32 entryIdx, Entry entry) + { + mEditWidget.SetText(entry.mName); + mEditWidget.mEditWidgetContent.SelectAll(); + mCurMenuWidget?.Close(); + } + + private void NavigationBarChanged(EditEvent theEvent) + { + if (mIgnoreChange) + return; + + var editWidget = (EditWidget)theEvent.mSender; + var searchText = scope String(); + editWidget.GetText(searchText); + searchText.Trim(); + mFilterString = searchText; + ShowDropdown(); + mFilterString = null; + } + + bool mIgnoreShowDropdown; + + public override MenuWidget ShowDropdown() + { + if (mIgnoreShowDropdown) + return null; + mIgnoreShowDropdown = true; + defer { mIgnoreShowDropdown = false; } + + if (!mEditWidget.mHasFocus) + SetFocus(); + + if (mFilterString == null) + mEditWidget.Content.SelectAll(); + + mShownEntries.Clear(); + base.ShowDropdown(); + + int32 bestItem = -1; + int32 bestPri = -1; + var menuWidget = (DarkMenuWidget)mCurMenuWidget; + + for (int32 itemIdx = 0; itemIdx < menuWidget.mItemWidgets.Count; itemIdx++) + { + var menuItemWidget = (DarkMenuItem)menuWidget.mItemWidgets[itemIdx]; + + int32 pri; + sMRU.TryGetValue(menuItemWidget.mMenuItem.mLabel, out pri); + if (pri > bestPri) + { + bestItem = itemIdx; + bestPri = pri; + } + } + + if (bestItem != -1) + { + mCurMenuWidget.mOnSelectionChanged.Add(new => SelectionChanged); + mCurMenuWidget.SetSelection(bestItem); + } + + return menuWidget; + } + + void SelectionChanged(int selIdx) + { + if (mEditWidget.mEditWidgetContent.HasSelection()) + { + bool prevIgnoreShowDropdown = mIgnoreShowDropdown; + mIgnoreShowDropdown = true; + mEditWidget.SetText(""); + mIgnoreShowDropdown = prevIgnoreShowDropdown; + } + } + + public override void MenuClosed() + { + } + } + + class GenerateDialog : IDEDialog + { + public class UIEntry + { + public String mName ~ delete _; + public String mData ~ delete _; + public String mLabel ~ delete _; + public Widget mWidget; + } + + public enum ThreadState + { + None, + Executing, + Done + } + + public bool mPendingGenList; + public GenerateKindBar mKindBar; + public ThreadState mThreadState; + public int mThreadWaitCount; + public String mNamespace ~ delete _; + public String mProjectName ~ delete _; + public ProjectItem mProjectItem ~ _.ReleaseRef(); + public String mFolderPath ~ delete _; + public List mUIEntries = new .() ~ DeleteContainerAndItems!(_); + public GenerateKindBar.Entry mSelectedEntry; + public GenerateKindBar.Entry mPendingSelectedEntry; + public String mUIData ~ delete _; + public float mUIHeight = 0; + public OutputPanel mOutputPanel; + public bool mPendingUIFocus; + public bool mSubmitting; + public bool mSubmitQueued; + public bool mRegenerating; + + public this(ProjectItem projectItem, bool allowHashMismatch = false) + { + var project = projectItem.mProject; + mProjectItem = projectItem; + mProjectItem.AddRef(); + + mNamespace = new .(); + + var projectFolder = projectItem as ProjectFolder; + var projectSource = projectItem as ProjectSource; + if (projectSource != null) + { + projectFolder = projectSource.mParentFolder; + mRegenerating = true; + } + + projectFolder.GetRelDir(mNamespace); mNamespace.Replace('/', '.'); mNamespace.Replace('\\', '.'); mNamespace.Replace(" ", ""); + if (mNamespace.StartsWith("src.")) + { + mNamespace.Remove(0, 4); + if (!project.mBeefGlobalOptions.mDefaultNamespace.IsWhiteSpace) + { + mNamespace.Insert(0, "."); + mNamespace.Insert(0, project.mBeefGlobalOptions.mDefaultNamespace); + } + } + else if (projectItem.mParentFolder == null) + { + mNamespace.Clear(); + mNamespace.Append(project.mBeefGlobalOptions.mDefaultNamespace); + } + else + mNamespace.Clear(); + + mFolderPath = projectFolder.GetFullImportPath(.. new .()); + mProjectName = new String(projectItem.mProject.mProjectName); + + mWindowFlags = .ClientSized | .TopMost | .Caption | .Border | .SysMenu | .Resizable | .PopupPosition; + + if (!mRegenerating) + { + AddOkCancelButtons(new (evt) => + { + Submit(); + evt.mCloseDialog = false; + }, null, 0, 1); + } + + Title = "Generate File"; + + mKindBar = new GenerateKindBar(this); + AddWidget(mKindBar); + mKindBar.mEditWidget.mOnContentChanged.Add(new (theEvent) => { SelectKind(); }); + + if (mRegenerating) + { + mSubmitQueued = true; + mKindBar.SetVisible(false); + + SourceViewPanel sourceViewPanel = gApp.ShowProjectItem(projectSource, false); + + String filePath = projectSource.GetFullImportPath(.. scope .()); + + String text = scope .(); + sourceViewPanel.mEditWidget.GetText(text); + + StringView generatorName = default; + StringView hash = default; + + int dataIdx = -1; + for (var line in text.Split('\n')) + { + if (!line.StartsWith("// ")) + { + dataIdx = @line.MatchPos + 1; + break; + } + int eqPos = line.IndexOf('='); + if (eqPos == -1) + break; + StringView key = line.Substring(3, eqPos - 3); + StringView value = line.Substring(eqPos + 1); + if (key == "Generator") + generatorName = value; + else if (key == "GenHash") + hash = value; + else + { + UIEntry uiEntry = new .(); + uiEntry.mName = new .(key); + uiEntry.mData = new .(value); + mUIEntries.Add(uiEntry); + } + } + + if ((generatorName == default) || (hash == default)) + { + Close(); + gApp.Fail(scope $"File '{filePath}' was not generated by a generator that includes regeneration information"); + return; + } + + if ((dataIdx != -1) && (!allowHashMismatch)) + { + var origHash = MD5Hash.Parse(hash).GetValueOrDefault(); + + StringView dataStr = text.Substring(dataIdx); + var checkHash = MD5.Hash(.((.)dataStr.Ptr, dataStr.Length)); + + if (origHash != checkHash) + { + Close(); + Dialog dialog = ThemeFactory.mDefault.CreateDialog("Regenerate?", "This file has been modified since it was generated. Are you sure you want to regenerate?", DarkTheme.sDarkTheme.mIconWarning); + dialog.AddButton("Yes", new (evt) => + { + gApp.mProjectPanel.Regenerate(true); + //dialog.Close(); + }); + dialog.AddButton("No", new (evt) => + { + //dialog.Close(); + }); + dialog.PopupWindow(gApp.GetActiveWindow()); + return; + } + } + + GenerateKindBar.Entry entry = new .(); + entry.mName = new .(generatorName); + entry.mTypeName = new .(generatorName); + mKindBar.mEntries.Add(entry); + mPendingSelectedEntry = entry; + } + else + mPendingGenList = true; + + mKindBar.mMouseVisible = false; + mTabWidgets.Add(mKindBar.mEditWidget); + } + + public ~this() + { + var bfCompiler = gApp.mBfResolveCompiler; + if (mThreadState == .Executing) + { + bfCompiler.WaitForBackground(); + } + } + + public void SelectKind() + { + GenerateKindBar.Entry foundEntry = null; + + String text = mKindBar.mEditWidget.GetText(.. scope .()); + for (var entry in mKindBar.mEntries) + if (entry.mName == text) + foundEntry = entry; + + if (foundEntry == null) + return; + + if (mSelectedEntry == foundEntry) + return; + + mPendingSelectedEntry = foundEntry; + } + + public void ThreadProc() + { + var bfSystem = gApp.mBfResolveSystem; + var bfCompiler = gApp.mBfResolveCompiler; + + String outStr = scope String(); + + bfSystem.Lock(0); + defer bfSystem.Unlock(); + + if (mSelectedEntry != null) + { + String args = scope .(); + var project = gApp.mWorkspace.FindProject(mProjectName); + if (project == null) + return; + using (gApp.mMonitor.Enter()) + { + if (mRegenerating) + args.Append("Regenerating\tTrue\n"); + + args.AppendF( + $""" + ProjectName\t{mProjectName} + ProjectDir\t{project.mProjectPath} + FolderDir\t{mFolderPath} + Namespace\t{mNamespace} + DefaultNamespace\t{project.mBeefGlobalOptions.mDefaultNamespace} + WorkspaceName\t{gApp.mWorkspace.mName} + WorkspaceDir\t{gApp.mWorkspace.mDir} + DateTime\t{DateTime.Now} + + """); + + if (mSubmitting) + { + args.AppendF($"Generator\t{mSelectedEntry.mTypeName}\n"); + for (var uiEntry in mUIEntries) + { + String data = scope .(); + if (uiEntry.mData != null) + { + data.Append(uiEntry.mData); + } + else if (var editWidget = uiEntry.mWidget as EditWidget) + { + editWidget.GetText(data); + } + else if (var comboBox = uiEntry.mWidget as DarkComboBox) + { + comboBox.GetLabel(data); + } + else if (var checkBox = uiEntry.mWidget as CheckBox) + { + checkBox.Checked.ToString(data); + } + data.Replace('\n', '\r'); + args.AppendF($"{uiEntry.mName}\t{data}\n"); + } + } + } + + mUIData = new String(); + if (mSubmitting) + bfCompiler.GetGeneratorGenData(mSelectedEntry.mTypeName, args, mUIData); + else + bfCompiler.GetGeneratorInitData(mSelectedEntry.mTypeName, args, mUIData); + } + else + { + bfCompiler.GetGeneratorTypeDefList(outStr); + + for (var line in outStr.Split('\n', .RemoveEmptyEntries)) + { + if (line.StartsWith("!error")) + { + ShowError(line.Substring(7)); + + RehupMinSize(); + continue; + } + + var entry = new GenerateKindBar.Entry(); + var partItr = line.Split('\t'); + entry.mTypeName = new String(partItr.GetNext().Value); + if (partItr.GetNext() case .Ok(let val)) + entry.mName = new String(val); + else + { + entry.mName = new String(entry.mTypeName); + int termPos = entry.mName.LastIndexOf('.'); + if (termPos != -1) + entry.mName.Remove(0, termPos + 1); + termPos = entry.mName.LastIndexOf('+'); + if (termPos != -1) + entry.mName.Remove(0, termPos + 1); + } + mKindBar.mEntries.Add(entry); + } + } + } + + public override void CalcSize() + { + mWidth = GS!(320); + mHeight = GS!(96); + mMinWidth = mWidth; + } + + protected override void RehupMinSize() + { + mWidgetWindow.SetMinimumSize(GS!(240), (.)mUIHeight + GS!(24), true); + } + + void ShowError(StringView error) + { + if (mOutputPanel == null) + { + IDEApp.Beep(.Error); + mOutputPanel = new OutputPanel(); + AddWidget(mOutputPanel); + ResizeComponents(); + } + String str = scope .(); + str.Append(error); + str.Replace('\r', '\n'); + str.Append("\n"); + mOutputPanel.WriteSmart(str); + } + + public override void Update() + { + base.Update(); + + if ((!mKindBar.mEditWidget.mHasFocus) && (mWidgetWindow.mHasFocus)) + { + var sel = mPendingSelectedEntry ?? mSelectedEntry; + String editText = mKindBar.mEditWidget.GetText(.. scope .()); + if ((sel != null) && (editText != sel.mName)) + { + mKindBar.mIgnoreChange = true; + mKindBar.mEditWidget.SetText(sel.mName); + mKindBar.mIgnoreChange = false; + } + } + + if (mThreadState == .Done) + { + if (mSelectedEntry != null) + { + List oldEntries = scope .(); + Dictionary entryMap = scope .(); + if (!mSubmitting) + { + for (var uiEntry in mUIEntries) + { + if (!entryMap.TryAdd(uiEntry.mName, uiEntry)) + oldEntries.Add(uiEntry); + } + mUIEntries.Clear(); + } + + if (mUIData != null) + { + if (mOutputPanel != null) + { + mOutputPanel.RemoveSelf(); + DeleteAndNullify!(mOutputPanel); + } + + String fileName = default; + StringView genText = default; + bool hadError = false; + + if (mUIData.IsEmpty) + { + gApp.Fail("Generator failed to return results"); + } + + LinesLoop: for (var line in mUIData.Split('\n', .RemoveEmptyEntries)) + { + var partItr = line.Split('\t'); + var kind = partItr.GetNext().Value; + + switch (kind) + { + case "!error": + ShowError(line.Substring(7)); + case "addEdit": + if (mSubmitting) + break; + UIEntry uiEntry = new UIEntry(); + uiEntry.mName = partItr.GetNext().Value.UnQuoteString(.. new .()); + uiEntry.mLabel = partItr.GetNext().Value.UnQuoteString(.. new .()); + var defaultValue = partItr.GetNext().Value.UnQuoteString(.. scope .()); + DarkEditWidget editWidget = new DarkEditWidget(); + uiEntry.mWidget = editWidget; + editWidget.SetText(defaultValue); + editWidget.mEditWidgetContent.SelectAll(); + editWidget.mOnSubmit.Add(new => EditSubmitHandler); + AddWidget(editWidget); + mUIEntries.Add(uiEntry); + mTabWidgets.Add(editWidget); + case "addCombo": + if (mSubmitting) + break; + UIEntry uiEntry = new UIEntry(); + uiEntry.mName = partItr.GetNext().Value.UnQuoteString(.. new .()); + uiEntry.mLabel = partItr.GetNext().Value.UnQuoteString(.. new .()); + var defaultValue = partItr.GetNext().Value.UnQuoteString(.. scope .()); + List choices = new List(); + DarkComboBox comboBox = new DarkComboBox(); + while (partItr.GetNext() case .Ok(let val)) + { + choices.Add(val.UnQuoteString(.. new .())); + } + comboBox.mOnDeleted.Add(new (widget) => { DeleteContainerAndItems!(choices); }); + comboBox.mPopulateMenuAction.Add(new (menu) => + { + for (var choice in choices) + { + var item = menu.AddItem(choice); + item.mOnMenuItemSelected.Add(new (menu) => + { + comboBox.Label = menu.mLabel; + }); + } + }); + uiEntry.mWidget = comboBox; + comboBox.Label = defaultValue; + AddWidget(comboBox); + mUIEntries.Add(uiEntry); + mTabWidgets.Add(comboBox); + case "addCheckbox": + if (mSubmitting) + break; + UIEntry uiEntry = new UIEntry(); + uiEntry.mName = partItr.GetNext().Value.UnQuoteString(.. new .()); + uiEntry.mLabel = partItr.GetNext().Value.UnQuoteString(.. new .()); + var defaultValue = partItr.GetNext().Value; + DarkCheckBox checkbox = new DarkCheckBox(); + uiEntry.mWidget = checkbox; + checkbox.Label = uiEntry.mLabel; + checkbox.Checked = defaultValue == "True"; + AddWidget(checkbox); + mUIEntries.Add(uiEntry); + mTabWidgets.Add(checkbox); + case "error": + hadError = true; + gApp.Fail(line.Substring(6).UnQuoteString(.. scope .())); + case "fileName": + fileName = line.Substring(9).UnQuoteString(.. scope:: .()); + case "options": + case "data": + genText = .(mUIData, @line.MatchPos + 1); + break LinesLoop; + } + } + + ResizeComponents(); + RehupMinSize(); + + if (fileName?.EndsWith(".bf", .OrdinalIgnoreCase) == true) + fileName.RemoveFromEnd(3); + + if ((fileName != null) && (!mRegenerating)) + { + for (char8 c in fileName.RawChars) + { + if (!c.IsLetterOrDigit && c != '_') + { + gApp.Fail(scope $"Invalid generated file name: {fileName}"); + hadError = true; + break; + } + } + + if (fileName.IsEmpty) + { + gApp.Fail("Geneator failed to specify file name"); + hadError = true; + } + } + + if ((!hadError) && (genText != default) && (fileName != null)) + { + if (!mProjectItem.mDetached) + { + if (mRegenerating) + { + gApp.mProjectPanel.Regenerate(mProjectItem as ProjectSource, genText); + } + else + { + gApp.mProjectPanel.Generate(mProjectItem as ProjectFolder, fileName, genText); + } + } + Close(); + } + + if ((hadError) && (mRegenerating) && (mOutputPanel == null)) + { + Close(); + } + + if (mPendingUIFocus) + { + mPendingUIFocus = false; + if (!mUIEntries.IsEmpty) + mUIEntries[0].mWidget.SetFocus(); + } + + DeleteAndNullify!(mUIData); + } + + // + + if (mSubmitting) + { + if (!mClosed) + { + mSubmitting = false; + mSubmitQueued = false; + mDefaultButton?.mDisabled = false; + mEscButton?.mDisabled = false; + } + } + else + { + for (var uiEntry in entryMap.Values) + oldEntries.Add(uiEntry); + + for (var uiEntry in oldEntries) + { + mTabWidgets.Remove(uiEntry.mWidget); + uiEntry.mWidget.RemoveSelf(); + DeleteAndNullify!(uiEntry.mWidget); + } + + ClearAndDeleteItems(oldEntries); + } + } + else + { + mKindBar.mMouseVisible = true; + mKindBar.SetFocus(); + mKindBar.SetLocation("New Class"); + } + mThreadState = .None; + MarkDirty(); + } + + bool isWorking = false; + if (mThreadState == .None) + { + if ((mPendingGenList) || (mPendingSelectedEntry != null)) + { + isWorking = true; + var bfCompiler = gApp.mBfResolveCompiler; + if (!bfCompiler.IsPerformingBackgroundOperation()) + { + bfCompiler.CheckThreadDone(); + + mPendingGenList = false; + if (mPendingSelectedEntry != null) + { + if (mSubmitQueued) + mSubmitting = true; + mSelectedEntry = mPendingSelectedEntry; + mPendingSelectedEntry = null; + } + mThreadState = .Executing; + bfCompiler.DoBackgroundHi(new => ThreadProc, new () => + { + mThreadState = .Done; + }, false); + } + } + } + + gApp.mBfResolveCompiler.CheckThreadDone(); + + if ((mThreadState == .Executing) || (isWorking)) + { + mThreadWaitCount++; + if (mUpdateCnt % 8 == 0) + MarkDirty(); + } + else + mThreadWaitCount = 0; + } + + public override void Resize(float x, float y, float width, float height) + { + base.Resize(x, y, width, height); + ResizeComponents(); + //mClassViewPanel.Resize(0, 0, width, height - GS!(34)); + } + + public override void PopupWindow(WidgetWindow parentWindow, float offsetX = 0, float offsetY = 0) + { + base.PopupWindow(parentWindow, offsetX, offsetY); + //mKindBar.SetFocus(); + } + + public override void Draw(Graphics g) + { + base.Draw(g); + + void DrawLabel(Widget widget, StringView label) + { + if (widget == null) + return; + if (widget is CheckBox) + return; + 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); + } + + public override void DrawAll(Graphics g) + { + base.DrawAll(g); + + if (mThreadWaitCount > 10) + { + using (g.PushColor(0x60505050)) + g.FillRect(0, 0, mWidth, mHeight - ((mDefaultButton != null) ? GS!(40) : GS!(0))); + IDEUtils.DrawWait(g, mWidth/2, mHeight/2, mUpdateCnt); + } + } + + public override void ResizeComponents() + { + base.ResizeComponents(); + + mUIHeight = GS!(32); + + float insetSize = GS!(12); + + mKindBar.Resize(insetSize, mUIHeight, mWidth - insetSize - insetSize, GS!(22)); + mUIHeight += GS!(52); + + for (var uiEntry in mUIEntries) + { + if (uiEntry.mWidget == null) + continue; + + float height = GS!(22); + if (uiEntry.mWidget is ComboBox) + height = GS!(26); + + if (uiEntry.mWidget is CheckBox) + { + mUIHeight -= GS!(20); + height = GS!(20); + } + + uiEntry.mWidget.Resize(insetSize, mUIHeight, mWidth - insetSize - insetSize, height); + mUIHeight += height + GS!(28); + } + + if (mOutputPanel != null) + { + float startY = mKindBar.mVisible ? GS!(60) : GS!(36); + mOutputPanel.Resize(insetSize, startY, mWidth - insetSize - insetSize, Math.Max(mHeight - startY - ((mDefaultButton != null) ? GS!(44) : GS!(12)), GS!(32))); + mUIHeight = Math.Max(mUIHeight, GS!(160)); + } + } + + public override void Close() + { + if (mThreadState == .Executing) + { + var bfCompiler = gApp.mBfResolveCompiler; + bfCompiler.RequestFastFinish(true); + bfCompiler.WaitForBackground(); + } + base.Close(); + } + + public override void Submit() + { + mDefaultButton.mDisabled = true; + mEscButton.mDisabled = true; + + if (mSubmitQueued) + return; + mSubmitQueued = true; + mPendingSelectedEntry = mPendingSelectedEntry ?? mSelectedEntry; + } + } +} diff --git a/IDE/src/ui/OutputPanel.bf b/IDE/src/ui/OutputPanel.bf index 8d002fa1..e99e10a6 100644 --- a/IDE/src/ui/OutputPanel.bf +++ b/IDE/src/ui/OutputPanel.bf @@ -327,7 +327,7 @@ namespace IDE.ui int lineIdx = (curLine + lineOfs) % lineCount; if (content.GotoRefrenceAtLine(lineIdx)) - break; + break; } } diff --git a/IDE/src/ui/ProjectPanel.bf b/IDE/src/ui/ProjectPanel.bf index 0019a29e..2e05a409 100644 --- a/IDE/src/ui/ProjectPanel.bf +++ b/IDE/src/ui/ProjectPanel.bf @@ -792,16 +792,96 @@ namespace IDE.ui } } - public void NewClass(ProjectFolder folder) + public void GenerateCode(ProjectFolder folder) { - DarkDialog dialog = (DarkDialog)ThemeFactory.mDefault.CreateDialog("New Class", "Class Name"); + /*DarkDialog dialog = (DarkDialog)ThemeFactory.mDefault.CreateDialog("New Class", "Class Name"); dialog.mMinWidth = GS!(300); dialog.mDefaultButton = dialog.AddButton("OK", new (evt) => DoNewClass(folder, evt)); dialog.mEscButton = dialog.AddButton("Cancel"); dialog.AddEdit("Unnamed"); - dialog.PopupWindow(gApp.GetActiveWindow()); + dialog.PopupWindow(gApp.GetActiveWindow());*/ + + var dialog = new GenerateDialog(folder); + dialog.PopupWindow(gApp.GetActiveWindow()); } + public void Regenerate(bool allowHashMismatch) + { + mListView.GetRoot().WithSelectedItems(scope (selectedItem) => + { + if (mListViewToProjectMap.GetValue(selectedItem) case .Ok(var sourceProjectItem)) + { + var dialog = new GenerateDialog(sourceProjectItem, allowHashMismatch); + dialog.PopupWindow(gApp.GetActiveWindow()); + } + }); + } + + public void Regenerate(ProjectSource projectSource, StringView fileText) + { + var sourceViewPanel = gApp.ShowProjectItem(projectSource, false); + sourceViewPanel.mEditWidget.SetText(scope .(fileText)); + } + + public void Generate(ProjectFolder folder, StringView fileName, StringView fileText) + { + let project = folder.mProject; + if (project.mNeedsCreate) + project.FinishCreate(); + String relFileName = scope .(fileName); + if (!relFileName.Contains('.')) + relFileName.Append(".bf"); + + String fullFilePath = scope String(); + String relPath = scope String(); + folder.GetRelDir(relPath); + if (relPath.Length > 0) + relPath.Append("/"); + relPath.Append(relFileName); + folder.mProject.GetProjectFullPath(relPath, fullFilePath); + String dirName = scope String(); + Path.GetDirectoryPath(fullFilePath, dirName); + Directory.CreateDirectory(dirName).IgnoreError(); + + if (File.Exists(fullFilePath)) + { + var error = scope String(); + error.AppendF("File '{0}' already exists", fullFilePath); + IDEApp.sApp.Fail(error); + return; + } + + if (File.WriteAllText(fullFilePath, fileText) case .Err) + { + var error = scope String(); + error.AppendF("Failed to create file '{0}'", fullFilePath); + gApp.Fail(error); + return; + } + + ProjectSource projectSource = new ProjectSource(); + projectSource.mIncludeKind = (folder.mIncludeKind == .Auto) ? .Auto : .Manual; + projectSource.mName.Set(relFileName); + projectSource.mPath = new String(); + folder.mProject.GetProjectRelPath(fullFilePath, projectSource.mPath); + projectSource.mProject = folder.mProject; + projectSource.mParentFolder = folder; + folder.AddChild(projectSource); + let projectItem = AddProjectItem(projectSource); + if (projectItem != null) + { + mListView.GetRoot().SelectItemExclusively(projectItem); + mListView.EnsureItemVisible(projectItem, false); + } + Sort(); + if (folder.mIncludeKind != .Auto) + folder.mProject.SetChanged(); + + gApp.RecordHistoryLocation(true); + gApp.ShowProjectItem(projectSource); + gApp.RecordHistoryLocation(true); + } + void DoNewClass(ProjectFolder folder, DialogEvent evt) { Dialog dlg = (Dialog)evt.mSender; @@ -1470,7 +1550,10 @@ namespace IDE.ui } if (doReleaseRef) + { + projectItem.mDetached = true; projectItem.ReleaseRef(); + } //TODO: Defer this, projectItem is needed for a backgrounded QueueProjectSourceRemoved //delete projectItem; } @@ -2373,7 +2456,15 @@ namespace IDE.ui }); } - if (let projectFolder = projectItem as ProjectFolder) + if (projectItem is ProjectSource) + { + item = menu.AddItem("Regenerate"); + item.mOnMenuItemSelected.Add(new (item) => + { + Regenerate(false); + }); + } + else if (let projectFolder = projectItem as ProjectFolder) { //if (projectFolder.mIncludeKind == .Manual) { @@ -2471,17 +2562,17 @@ namespace IDE.ui } }); - item = menu.AddItem("New Class..."); + item = menu.AddItem("Generate File..."); item.mOnMenuItemSelected.Add(new (item) => { var projectFolder = GetSelectedProjectFolder(); if (projectFolder != null) { if (CheckProjectModify(projectFolder.mProject)) - NewClass(projectFolder); + GenerateCode(projectFolder); } }); - + item = menu.AddItem("Import File..."); item.mOnMenuItemSelected.Add(new (item) => { mImportFileDeferred = true; /* ImportFile();*/ }); diff --git a/IDE/src/ui/SourceViewPanel.bf b/IDE/src/ui/SourceViewPanel.bf index e94ca7d2..6e814b31 100644 --- a/IDE/src/ui/SourceViewPanel.bf +++ b/IDE/src/ui/SourceViewPanel.bf @@ -546,6 +546,11 @@ namespace IDE.ui public ~this() { + if (mProjectSource?.mEditData?.HasTextChanged() == true) + { + mProjectSource.ClearEditData(); + } + if (mInPostRemoveUpdatePanels) { //Debug.WriteLine("Removing sourceViewPanel from mPostRemoveUpdatePanel {0} in ~this ", this); diff --git a/IDE/src/ui/WatchPanel.bf b/IDE/src/ui/WatchPanel.bf index 497beab4..06da55be 100644 --- a/IDE/src/ui/WatchPanel.bf +++ b/IDE/src/ui/WatchPanel.bf @@ -1295,7 +1295,7 @@ namespace IDE.ui addrsCount = mWatchSeriesInfo.mAddrs.Length / entryAddrSize; int totalCount = mWatchSeriesInfo.mCount; - if (totalCount == -1) + if ((totalCount == -1) && (mWatchSeriesInfo.mContinuationData != null)) { //int wantNewCount = Math.Min(idx + 32, mWatchSeriesInfo.mCount) - addrsCount; bool continuationDone = false; diff --git a/IDEHelper/Backend/BeCOFFObject.cpp b/IDEHelper/Backend/BeCOFFObject.cpp index 3ef70a7a..7dffb212 100644 --- a/IDEHelper/Backend/BeCOFFObject.cpp +++ b/IDEHelper/Backend/BeCOFFObject.cpp @@ -555,6 +555,7 @@ void BeCOFFObject::DbgTEndTag() BF_ASSERT(mTTagStartPos != -1); DbgTAlign(); int tagSize = mDebugTSect.mData.GetPos() - mTTagStartPos; + BF_ASSERT(tagSize <= 0xFFFF); *((int16*)&mDebugTSect.mData.mData[mTTagStartPos]) = (int16)(tagSize - 2); mTTagStartPos = -1; } @@ -597,11 +598,7 @@ int BeCOFFObject::DbgGetTypeId(BeDbgType* dbgType, bool doDefine) DbgGetTypeId(structType->mDerivedFrom); for (auto member : structType->mMembers) { - auto type = member->mType; - //TODO: - //if (member->mName == "VersionName") - //continue; - + auto type = member->mType; DbgGetTypeId(type); } for (auto func : structType->mMethods) @@ -627,7 +624,7 @@ int BeCOFFObject::DbgGetTypeId(BeDbgType* dbgType, bool doDefine) auto _CheckFieldOverflow = [&]() { int tagSize = mDebugTSect.mData.GetPos() - mTTagStartPos; - if (tagSize >= 2000) + if (tagSize >= 0xE000) { int extFieldListTag = mCurTagId++; @@ -1013,8 +1010,7 @@ void BeCOFFObject::DbgGenerateTypeInfo() void BeCOFFObject::DbgStartSection(int sectionNum) { auto& outS = mDebugSSect.mData; - BF_ASSERT(mSectionStartPos == -1); - + BF_ASSERT(mSectionStartPos == -1); outS.Write((int32)sectionNum); outS.Write(0); // Temporary - size mSectionStartPos = outS.GetPos(); @@ -1023,7 +1019,7 @@ void BeCOFFObject::DbgStartSection(int sectionNum) void BeCOFFObject::DbgEndSection() { auto& outS = mDebugSSect.mData; - int totalLen = outS.GetPos() - mSectionStartPos; + int totalLen = outS.GetPos() - mSectionStartPos; *((int32*)&outS.mData[mSectionStartPos - 4]) = totalLen; mSectionStartPos = -1; while ((outS.GetPos() & 3) != 0) @@ -1132,7 +1128,7 @@ void BeCOFFObject::DbgSEndTag() { BF_ASSERT(mSTagStartPos != -1); int tagSize = mDebugSSect.mData.GetPos() - mSTagStartPos; - + BF_ASSERT(tagSize <= 0xFFFF); *((uint16*)&mDebugSSect.mData.mData[mSTagStartPos]) = (uint16)(tagSize - 2); mSTagStartPos = -1; } @@ -2146,7 +2142,7 @@ bool BeCOFFObject::Generate(BeModule* module, const StringImpl& fileName) if (mWriteToLib) { DynMemStream memStream; - + Generate(module); mStream = &memStream; diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index b83b2d46..f15efa6e 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -1647,7 +1647,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress { "abstract", "base", "class", "const", "delegate", "extern", "enum", "explicit", "extension", "function", - "interface", "in", "internal", "mixin", "namespace", "new", + "interface", "in", "implicit", "internal", "mixin", "namespace", "new", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "rettype", "return", "scope", "sealed", "static", "struct", "this", "typealias", "using", "virtual", "volatile", "T", "where" diff --git a/IDEHelper/Compiler/BfCompiler.cpp b/IDEHelper/Compiler/BfCompiler.cpp index f45e4b49..8ce3868c 100644 --- a/IDEHelper/Compiler/BfCompiler.cpp +++ b/IDEHelper/Compiler/BfCompiler.cpp @@ -420,6 +420,7 @@ BfCompiler::BfCompiler(BfSystem* bfSystem, bool isResolveOnly) mInternalTypeDef = NULL; mPlatformTypeDef = NULL; mCompilerTypeDef = NULL; + mCompilerGeneratorTypeDef = NULL; mDiagnosticsDebugTypeDef = NULL; mIDisposableTypeDef = NULL; mIIntegerTypeDef = NULL; @@ -6765,6 +6766,7 @@ bool BfCompiler::DoCompile(const StringImpl& outputDirectory) mInternalTypeDef = _GetRequiredType("System.Internal"); mPlatformTypeDef = _GetRequiredType("System.Platform"); mCompilerTypeDef = _GetRequiredType("System.Compiler"); + mCompilerGeneratorTypeDef = _GetRequiredType("System.Compiler.Generator"); mDiagnosticsDebugTypeDef = _GetRequiredType("System.Diagnostics.Debug"); mIDisposableTypeDef = _GetRequiredType("System.IDisposable"); mIIntegerTypeDef = _GetRequiredType("System.IInteger"); @@ -8107,6 +8109,149 @@ String BfCompiler::GetTypeDefList() return result; } +String BfCompiler::GetGeneratorString(BfTypeDef* typeDef, BfTypeInstance* typeInst, const StringImpl& generatorMethodName, const StringImpl* args) +{ + if (typeInst == NULL) + { + auto type = mContext->mUnreifiedModule->ResolveTypeDef(typeDef, BfPopulateType_BaseType); + if (type != NULL) + typeInst = type->ToTypeInstance(); + if (typeInst == NULL) + return ""; + } + + BfTypeVector typeVector; + typeVector.Add(typeInst); + + auto generatorTypeInst = mContext->mUnreifiedModule->ResolveTypeDef(mCompilerGeneratorTypeDef)->ToTypeInstance(); + auto methodDef = generatorTypeInst->mTypeDef->GetMethodByName(generatorMethodName); + auto moduleMethodInstance = mContext->mUnreifiedModule->GetMethodInstance(generatorTypeInst, methodDef, typeVector); + + SetAndRestoreValue prevMethodInstance(mContext->mUnreifiedModule->mCurMethodInstance, moduleMethodInstance.mMethodInstance); + SetAndRestoreValue prevTypeInstance(mContext->mUnreifiedModule->mCurTypeInstance, typeInst); + + BfExprEvaluator exprEvaluator(mContext->mUnreifiedModule); + exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_Comptime; + + SizedArray irArgs; + if (args != NULL) + irArgs.Add(mContext->mUnreifiedModule->GetStringObjectValue(*args)); + auto callResult = exprEvaluator.CreateCall(NULL, moduleMethodInstance.mMethodInstance, moduleMethodInstance.mFunc, false, irArgs, NULL, BfCreateCallFlags_None); + + if (callResult.mValue.IsConst()) + { + auto stringPtr = mContext->mUnreifiedModule->GetStringPoolString(callResult.mValue, mContext->mUnreifiedModule->mBfIRBuilder); + if (stringPtr != NULL) + return *stringPtr; + } + return ""; +} + +void BfCompiler::HandleGeneratorErrors(StringImpl& result) +{ + if ((mPassInstance->mErrors.IsEmpty()) && (mPassInstance->mOutStream.IsEmpty())) + return; + + result.Clear(); + + for (auto& msg : mPassInstance->mOutStream) + { + String error = msg; + error.Replace('\n', '\r'); + result += "!error\t"; + result += error; + result += "\n"; + } +} + +String BfCompiler::GetGeneratorTypeDefList() +{ + String result; + + BfProject* curProject = NULL; + Dictionary projectIds; + + BfResolvePassData resolvePassData; + SetAndRestoreValue prevResolvePassData(mResolvePassData, &resolvePassData); + BfPassInstance passInstance(mSystem); + SetAndRestoreValue prevPassInstance(mPassInstance, &passInstance); + + for (auto typeDef : mSystem->mTypeDefs) + { + if (typeDef->mProject->mDisabled) + continue; + + if (typeDef->mIsPartial) + continue; + + auto type = mContext->mUnreifiedModule->ResolveTypeDef(typeDef, BfPopulateType_BaseType); + if ((type != NULL) && (type->IsTypeInstance())) + { + auto typeInst = type->ToTypeInstance(); + if ((typeInst->mBaseType != NULL) && (typeInst->mBaseType->IsInstanceOf(mCompilerGeneratorTypeDef))) + { + result += typeDef->mProject->mName; + result += ":"; + result += BfTypeUtils::TypeToString(typeDef, BfTypeNameFlag_InternalName); + String nameString = GetGeneratorString(typeDef, typeInst, "GetName", NULL); + if (!nameString.IsEmpty()) + result += "\t" + nameString; + result += "\n"; + } + } + } + + HandleGeneratorErrors(result); + + return result; +} + +String BfCompiler::GetGeneratorInitData(const StringImpl& typeName, const StringImpl& args) +{ + BfResolvePassData resolvePassData; + SetAndRestoreValue prevResolvePassData(mResolvePassData, &resolvePassData); + BfPassInstance passInstance(mSystem); + SetAndRestoreValue prevPassInstance(mPassInstance, &passInstance); + + Array typeDefs; + GetTypeDefs(typeName, typeDefs); + + String result; + for (auto typeDef : typeDefs) + { + result += GetGeneratorString(typeDef, NULL, "InitUI", &args); + if (!result.IsEmpty()) + break; + } + + HandleGeneratorErrors(result); + + return result; +} + +String BfCompiler::GetGeneratorGenData(const StringImpl& typeName, const StringImpl& args) +{ + BfResolvePassData resolvePassData; + SetAndRestoreValue prevResolvePassData(mResolvePassData, &resolvePassData); + BfPassInstance passInstance(mSystem); + SetAndRestoreValue prevPassInstance(mPassInstance, &passInstance); + + Array typeDefs; + GetTypeDefs(typeName, typeDefs); + + String result; + for (auto typeDef : typeDefs) + { + result += GetGeneratorString(typeDef, NULL, "Generate", &args); + if (!result.IsEmpty()) + break; + } + + HandleGeneratorErrors(result); + + return result; +} + struct TypeDefMatchHelper { public: @@ -8600,9 +8745,9 @@ String BfCompiler::GetTypeDefMatches(const StringImpl& searchStr) return result; } -String BfCompiler::GetTypeDefInfo(const StringImpl& inTypeName) +void BfCompiler::GetTypeDefs(const StringImpl& inTypeName, Array& typeDefs) { - BfProject* project = NULL; + BfProject* project = NULL; int idx = 0; int sep = (int)inTypeName.IndexOf(':'); @@ -8615,7 +8760,7 @@ String BfCompiler::GetTypeDefInfo(const StringImpl& inTypeName) String typeName; int genericCount = 0; int pendingGenericCount = 0; - for ( ; idx < (int)inTypeName.length(); idx++) + for (; idx < (int)inTypeName.length(); idx++) { char c = inTypeName[idx]; if (c == '<') @@ -8626,7 +8771,7 @@ String BfCompiler::GetTypeDefInfo(const StringImpl& inTypeName) genericCount++; else if (c == '>') { - pendingGenericCount = genericCount; + pendingGenericCount = genericCount; genericCount = 0; } } @@ -8640,10 +8785,10 @@ String BfCompiler::GetTypeDefInfo(const StringImpl& inTypeName) typeName += c; } } - + bool isGlobals = false; if (typeName == ":static") - { + { typeName.clear(); isGlobals = true; } @@ -8657,63 +8802,73 @@ String BfCompiler::GetTypeDefInfo(const StringImpl& inTypeName) if (typeName[i] == '+') typeName[i] = '.'; - String result; - TypeDefMatchHelper matchHelper(result); - - BfAtomComposite nameComposite; + BfAtomComposite nameComposite; if ((typeName.IsEmpty()) || (mSystem->ParseAtomComposite(typeName, nameComposite))) - { + { auto itr = mSystem->mTypeDefs.TryGet(nameComposite); while (itr) - { + { auto typeDef = *itr; if ((!typeDef->mIsPartial) && (typeDef->mProject == project) && (typeDef->mFullName == nameComposite) && (typeDef->IsGlobalsContainer() == isGlobals) && (typeDef->GetSelfGenericParamCount() == pendingGenericCount)) - { - auto refNode = typeDef->GetRefNode(); - result += "S"; - matchHelper.AddLocation(refNode); - result += "\n"; - - for (auto fieldDef : typeDef->mFields) - { - result += "F"; - result += fieldDef->mName; - matchHelper.AddFieldDef(fieldDef); - } - - for (auto propDef : typeDef->mProperties) - { - if (propDef->GetRefNode() == NULL) - continue; - - result += "P"; - matchHelper.AddPropertyDef(typeDef, propDef); - } - - for (auto methodDef : typeDef->mMethods) - { - if ((methodDef->mMethodType != BfMethodType_Normal) && - (methodDef->mMethodType != BfMethodType_Mixin) && - (methodDef->mMethodType != BfMethodType_Ctor) && - (methodDef->mMethodType != BfMethodType_Dtor)) - continue; - - if (methodDef->mMethodDeclaration == NULL) - continue; - - result += "M"; - matchHelper.AddMethodDef(methodDef); - } + { + typeDefs.Add(typeDef); } itr.MoveToNextHashMatch(); } } +} +String BfCompiler::GetTypeDefInfo(const StringImpl& inTypeName) +{ + Array typeDefs; + GetTypeDefs(inTypeName, typeDefs); + + String result; + TypeDefMatchHelper matchHelper(result); + + for (auto typeDef : typeDefs) + { + auto refNode = typeDef->GetRefNode(); + result += "S"; + matchHelper.AddLocation(refNode); + result += "\n"; + + for (auto fieldDef : typeDef->mFields) + { + result += "F"; + result += fieldDef->mName; + matchHelper.AddFieldDef(fieldDef); + } + + for (auto propDef : typeDef->mProperties) + { + if (propDef->GetRefNode() == NULL) + continue; + + result += "P"; + matchHelper.AddPropertyDef(typeDef, propDef); + } + + for (auto methodDef : typeDef->mMethods) + { + if ((methodDef->mMethodType != BfMethodType_Normal) && + (methodDef->mMethodType != BfMethodType_Mixin) && + (methodDef->mMethodType != BfMethodType_Ctor) && + (methodDef->mMethodType != BfMethodType_Dtor)) + continue; + + if (methodDef->mMethodDeclaration == NULL) + continue; + + result += "M"; + matchHelper.AddMethodDef(methodDef); + } + } return result; } @@ -8990,6 +9145,30 @@ BF_EXPORT void BF_CALLTYPE BfCompiler_ProgramDone() #endif } +BF_EXPORT const char* BF_CALLTYPE BfCompiler_GetGeneratorTypeDefList(BfCompiler* bfCompiler) +{ + String& outString = *gTLStrReturn.Get(); + outString.clear(); + outString = bfCompiler->GetGeneratorTypeDefList(); + return outString.c_str(); +} + +BF_EXPORT const char* BF_CALLTYPE BfCompiler_GetGeneratorInitData(BfCompiler* bfCompiler, char* typeDefName, char* args) +{ + String& outString = *gTLStrReturn.Get(); + outString.clear(); + outString = bfCompiler->GetGeneratorInitData(typeDefName, args); + return outString.c_str(); +} + +BF_EXPORT const char* BF_CALLTYPE BfCompiler_GetGeneratorGenData(BfCompiler* bfCompiler, char* typeDefName, char* args) +{ + String& outString = *gTLStrReturn.Get(); + outString.clear(); + outString = bfCompiler->GetGeneratorGenData(typeDefName, args); + return outString.c_str(); +} + BF_EXPORT const char* BF_CALLTYPE BfCompiler_GetTypeDefList(BfCompiler* bfCompiler) { String& outString = *gTLStrReturn.Get(); diff --git a/IDEHelper/Compiler/BfCompiler.h b/IDEHelper/Compiler/BfCompiler.h index d859da0f..9c2dbef6 100644 --- a/IDEHelper/Compiler/BfCompiler.h +++ b/IDEHelper/Compiler/BfCompiler.h @@ -374,6 +374,7 @@ public: BfTypeDef* mInternalTypeDef; BfTypeDef* mPlatformTypeDef; BfTypeDef* mCompilerTypeDef; + BfTypeDef* mCompilerGeneratorTypeDef; BfTypeDef* mDiagnosticsDebugTypeDef; BfTypeDef* mIDisposableTypeDef; BfTypeDef* mIIntegerTypeDef; @@ -511,9 +512,15 @@ public: void ProcessAutocompleteTempType(); void GetSymbolReferences(); void Cancel(); - void RequestFastFinish(); + void RequestFastFinish(); String GetTypeDefList(); - String GetTypeDefMatches(const StringImpl& searchSrc); + String GetGeneratorString(BfTypeDef* typeDef, BfTypeInstance* typeInst, const StringImpl& generatorMethodName, const StringImpl* args); + void HandleGeneratorErrors(StringImpl& result); + String GetGeneratorTypeDefList(); + String GetGeneratorInitData(const StringImpl& typeName, const StringImpl& args); + String GetGeneratorGenData(const StringImpl& typeName, const StringImpl& args); + String GetTypeDefMatches(const StringImpl& searchSrc); + void GetTypeDefs(const StringImpl& typeName, Array& typeDefs); String GetTypeDefInfo(const StringImpl& typeName); int GetEmitSource(const StringImpl& fileName, StringImpl* outBuffer); diff --git a/IDEHelper/Compiler/BfContext.cpp b/IDEHelper/Compiler/BfContext.cpp index 2de7a71a..f338c2f1 100644 --- a/IDEHelper/Compiler/BfContext.cpp +++ b/IDEHelper/Compiler/BfContext.cpp @@ -892,6 +892,9 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild return; } + if ((typeInst->IsBoxed()) && (typeInst->mTypeDef->mEmitParent != NULL)) + typeInst->mTypeDef = typeInst->mTypeDef->mEmitParent; + if (mSystem->mWorkspaceConfigChanged) { typeInst->mTypeOptionsIdx = -2; @@ -1060,8 +1063,12 @@ void BfContext::RebuildType(BfType* type, bool deleteOnDemandTypes, bool rebuild if (typeInst->mTypeDef->mEmitParent != NULL) { auto emitTypeDef = typeInst->mTypeDef; - typeInst->mTypeDef = emitTypeDef->mEmitParent; - delete emitTypeDef; + typeInst->mTypeDef = emitTypeDef->mEmitParent; + BfLogSysM("Type %p queueing delete of typeDef %p, resetting typeDef to %p\n", typeInst, emitTypeDef, typeInst->mTypeDef); + emitTypeDef->mDefState = BfTypeDef::DefState_Deleted; + AutoCrit autoCrit(mSystem->mDataLock); + BF_ASSERT(!mSystem->mTypeDefDeleteQueue.Contains(emitTypeDef)); + mSystem->mTypeDefDeleteQueue.push_back(emitTypeDef); } //typeInst->mTypeDef->ClearEmitted(); @@ -1912,10 +1919,17 @@ void BfContext::UpdateRevisedTypes() if (typeDef->mEmitParent != NULL) { - auto emitTypeDef = typeDef; - typeDef = typeDef->mEmitParent; - if (typeDef->mNextRevision != NULL) - emitTypeDef->mDefState = BfTypeDef::DefState_EmittedDirty; + if (typeDef->mDefState == BfTypeDef::DefState_Deleted) + { + typeInst->mTypeDef = typeDef->mEmitParent; + } + else + { + auto emitTypeDef = typeDef; + typeDef = typeDef->mEmitParent; + if (typeDef->mNextRevision != NULL) + emitTypeDef->mDefState = BfTypeDef::DefState_EmittedDirty; + } } if (typeDef->mProject->mDisabled) diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index e6d3ba2b..a7750009 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -3578,7 +3578,7 @@ void BfExprEvaluator::GetLiteral(BfAstNode* refNode, const BfVariant& variant) if ((mExpectingType != NULL) && (mExpectingType->IsIntegral()) && (mExpectingType->IsChar() == IsCharType(variant.mTypeCode))) { auto primType = (BfPrimitiveType*)mExpectingType; - if (mModule->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, variant.mInt64)) + if (mModule->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, variant.mUInt64)) { mResult = BfTypedValue(mModule->mBfIRBuilder->CreateConst(primType->mTypeDef->mTypeCode, variant.mUInt64), mExpectingType); break; @@ -5090,7 +5090,9 @@ void BfExprEvaluator::ResolveArgValues(BfResolvedArgs& resolvedArgs, BfResolveAr BfExprEvaluator exprEvaluator(mModule); exprEvaluator.mResolveGenericParam = (flags & BfResolveArgsFlag_AllowUnresolvedTypes) == 0; - exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_AllowRefExpr | BfEvalExprFlags_AllowOutExpr); + exprEvaluator.mBfEvalExprFlags = (BfEvalExprFlags)(exprEvaluator.mBfEvalExprFlags | BfEvalExprFlags_AllowRefExpr | BfEvalExprFlags_AllowOutExpr | + (mBfEvalExprFlags & (BfEvalExprFlags_Comptime))); + bool handled = false; bool evaluated = false; @@ -6373,16 +6375,23 @@ void BfExprEvaluator::FinishDeferredEvals(BfResolvedArgs& argValues) auto variableDeclaration = BfNodeDynCast((*argValues.mArguments)[argIdx]); if ((variableDeclaration != NULL) && (variableDeclaration->mNameNode != NULL)) { - BfLocalVariable* localVar = new BfLocalVariable(); - localVar->mName = variableDeclaration->mNameNode->ToString(); - localVar->mResolvedType = mModule->GetPrimitiveType(BfTypeCode_Var); - localVar->mAddr = mModule->mBfIRBuilder->GetFakeVal(); - localVar->mReadFromId = 0; - localVar->mWrittenToId = 0; - localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional; - mModule->CheckVariableDef(localVar); - localVar->Init(); - mModule->AddLocalVariableDef(localVar, true); + if (mModule->mCurMethodState == NULL) + { + mModule->Fail("Illegal local variable", variableDeclaration); + } + else + { + BfLocalVariable* localVar = new BfLocalVariable(); + localVar->mName = variableDeclaration->mNameNode->ToString(); + localVar->mResolvedType = mModule->GetPrimitiveType(BfTypeCode_Var); + localVar->mAddr = mModule->mBfIRBuilder->GetFakeVal(); + localVar->mReadFromId = 0; + localVar->mWrittenToId = 0; + localVar->mAssignedKind = BfLocalVarAssignKind_Unconditional; + mModule->CheckVariableDef(localVar); + localVar->Init(); + mModule->AddLocalVariableDef(localVar, true); + } } } } @@ -10139,6 +10148,12 @@ void BfExprEvaluator::Visit(BfInitializerExpression* initExpr) for (auto elementExpr : initExpr->mValues) { + if ((mBfEvalExprFlags & BfEvalExprFlags_Comptime) != 0) + { + mModule->Fail("Comptime cannot evaluate initializer expressions", elementExpr); + break; + } + bool wasValidInitKind = false; if (auto assignExpr = BfNodeDynCast(elementExpr)) @@ -11133,8 +11148,7 @@ void BfExprEvaluator::Visit(BfCastExpression* castExpr) bool BfExprEvaluator::IsExactMethodMatch(BfMethodInstance* methodA, BfMethodInstance* methodB, bool ignoreImplicitParams) { if (methodA->mReturnType != methodB->mReturnType) - return false; - + return false; int implicitParamCountA = methodA->GetImplicitParamCount(); if (methodA->HasExplicitThis()) implicitParamCountA++; @@ -11696,7 +11710,11 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) } else { - if (!IsExactMethodMatch(methodInstance, bindMethodInstance, true)) + bool isExactMethodMatch = IsExactMethodMatch(methodInstance, bindMethodInstance, true); + if ((mExpectingType != NULL) && (mExpectingType->IsFunction()) && (methodInstance->mMethodDef->mIsMutating != bindMethodInstance->mMethodDef->mIsMutating)) + isExactMethodMatch = false; + + if (!isExactMethodMatch) { if (bindResult.mCheckedMultipleMethods) { @@ -11704,8 +11722,8 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) mModule->TypeToString(delegateTypeInstance).c_str()), delegateBindExpr->mTarget); } else - { - mModule->Fail(StrFormat("Method '%s' does not match %s '%s'", mModule->MethodToString(bindMethodInstance).c_str(), bindTypeName, + { + mModule->Fail(StrFormat("Method '%s' does not match %s '%s'", mModule->MethodToString(bindMethodInstance, (BfMethodNameFlags)(BfMethodNameFlag_ResolveGenericParamNames | BfMethodNameFlag_IncludeReturnType | BfMethodNameFlag_IncludeMut)).c_str(), bindTypeName, mModule->TypeToString(delegateTypeInstance).c_str()), delegateBindExpr->mTarget); } mResult = BfTypedValue(); @@ -11725,7 +11743,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) } bool hasIncompatibleCallingConventions = !mModule->mSystem->IsCompatibleCallingConvention(methodInstance->mCallingConvention, bindMethodInstance->mCallingConvention); - + auto _GetInvokeMethodName = [&]() { String methodName = "Invoke$"; @@ -11789,7 +11807,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) if (mExpectingType->IsFunction()) { BfIRValue result; - if ((hasIncompatibleCallingConventions) && (mModule->HasCompiledOutput())) + if ((hasIncompatibleCallingConventions) && (mModule->HasExecutedOutput())) { // { @@ -11799,11 +11817,14 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) if (result) { - String methodName = _GetInvokeMethodName(); + String methodName = _GetInvokeMethodName(); SizedArray irParamTypes; BfIRType irReturnType; - bindMethodInstance->GetIRFunctionInfo(mModule, irReturnType, irParamTypes); + methodInstance->GetIRFunctionInfo(mModule, irReturnType, irParamTypes); + + int thisFuncParamIdx = methodInstance->GetThisIdx(); + int thisBindParamIdx = methodInstance->GetThisIdx(); auto prevActiveFunction = mModule->mBfIRBuilder->GetActiveFunction(); auto prevInsertBlock = mModule->mBfIRBuilder->GetInsertBlock(); @@ -11949,7 +11970,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) // Do we need a special delegate type for this? if (((captureThisByValue) || (needsSplat) || (implicitParamCount > 0) /*|| (hasIncompatibleCallingConventions)*/) && - (mModule->HasCompiledOutput())) + (mModule->HasExecutedOutput())) { hasCaptures = true; auto curProject = mModule->mCurTypeInstance->mTypeDef->mProject; @@ -12030,7 +12051,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) // Do we need specialized calling code for this? BfIRValue funcValue; if (((needsSplat) || (implicitParamCount > 0) || (hasIncompatibleCallingConventions)) && - (mModule->HasCompiledOutput())) + (mModule->HasExecutedOutput())) { int fieldIdx = 0; for (int implicitParamIdx = bindMethodInstance->HasThis() ? -1 : 0; implicitParamIdx < implicitParamCount; implicitParamIdx++) @@ -12208,7 +12229,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) // >> delegate.mTarget = bindResult.mTarget BfIRValue valPtr; - if (mModule->HasCompiledOutput()) + if (mModule->HasExecutedOutput()) { if ((implicitParamCount > 0) || (needsSplat)) // Point back to self, it contains capture data valPtr = mModule->mBfIRBuilder->CreateBitCast(mResult.mValue, mModule->mBfIRBuilder->GetPrimitiveType(BfTypeCode_NullPtr)); @@ -12226,7 +12247,7 @@ void BfExprEvaluator::Visit(BfDelegateBindExpression* delegateBindExpr) if (!funcValue) { - if ((mModule->HasCompiledOutput()) && (!mModule->mBfIRBuilder->mIgnoreWrites)) + if ((mModule->HasExecutedOutput()) && (!mModule->mBfIRBuilder->mIgnoreWrites)) mModule->AssertErrorState(); return; } @@ -13188,7 +13209,7 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam mModule->mIncompleteMethodCount++; SetAndRestoreValue prevClosureState(mModule->mCurMethodState->mClosureState, &closureState); - if (mModule->HasCompiledOutput()) + if (mModule->HasExecutedOutput()) mModule->SetupIRMethod(methodInstance, methodInstance->mIRFunction, methodInstance->mAlwaysInline); // This keeps us from giving errors twice. ProcessMethod can give errors when we capture by value but needed to @@ -14415,7 +14436,7 @@ void BfExprEvaluator::CreateObject(BfObjectCreateExpression* objCreateExpr, BfAs { if (!bindResult.mFunc) { - BF_ASSERT((!mModule->HasCompiledOutput()) || (mModule->mBfIRBuilder->mIgnoreWrites)); + BF_ASSERT((!mModule->HasExecutedOutput()) || (mModule->mBfIRBuilder->mIgnoreWrites)); appendSizeValue = mModule->GetConstValue(0); } else @@ -19717,7 +19738,7 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) } } } - else if (((mModule->HasCompiledOutput()) || (mModule->mIsComptimeModule)) && + else if (((mModule->HasExecutedOutput()) || (mModule->mIsComptimeModule)) && (wantsChecks)) { if (checkedKind == BfCheckedKind_NotSet) diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index cc43725c..95307c23 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -857,6 +857,27 @@ BfIRValue BfIRConstHolder::CreateConstArrayZero(int count) return irValue; } +BfIRValue BfIRConstHolder::CreateConstBitCast(BfIRValue val, BfIRType type) +{ + auto constVal = GetConstant(val); + + auto bitCast = mTempAlloc.Alloc(); + if ((constVal == NULL) || (constVal->IsNull())) + bitCast->mConstType = BfConstType_BitCastNull; + else + bitCast->mConstType = BfConstType_BitCast; + BF_ASSERT(val.mId != -1); + bitCast->mTarget = val.mId; + bitCast->mToType = type; + + BfIRValue castedVal(BfIRValueFlags_Const, mTempAlloc.GetChunkedId(bitCast)); +#ifdef CHECK_CONSTHOLDER + castedVal.mHolder = this; +#endif + BF_ASSERT((void*)GetConstant(castedVal) == (void*)bitCast); + return castedVal; +} + BfIRValue BfIRConstHolder::CreateTypeOf(BfType* type) { BfTypeOf_Const* typeOf = mTempAlloc.Alloc(); @@ -2970,10 +2991,10 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type) diFieldTypes.push_back(memberType); } - bool isPayloadEnum = (typeInstance->IsEnum()) && (!typeInstance->IsTypedPrimitive()); - for (auto& fieldInstanceRef : typeInstance->mFieldInstances) + bool isPayloadEnum = (typeInstance->IsEnum()) && (!typeInstance->IsTypedPrimitive()); + for (int fieldIdx = 0; fieldIdx < typeInstance->mFieldInstances.mSize; fieldIdx++) { - auto fieldInstance = &fieldInstanceRef; + auto fieldInstance = &typeInstance->mFieldInstances[fieldIdx]; if (!fieldInstance->mFieldIncluded) continue; auto fieldDef = fieldInstance->GetFieldDef(); @@ -3091,18 +3112,15 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type) { staticValue = ConstToMemory(staticValue); wasMadeAddr = true; - } - else if (resolvedFieldType->IsPointer()) + } + else if (constant->mTypeCode == BfTypeCode_StringId) { int stringId = constant->mInt32; const StringImpl& str = mModule->mContext->mStringObjectIdMap[stringId].mString; - staticValue = mModule->GetStringCharPtr(str); - } - else if (constant->mTypeCode == BfTypeCode_StringId) - { - int stringId = constant->mInt32; - const StringImpl& str = mModule->mContext->mStringObjectIdMap[stringId].mString; - staticValue = mModule->GetStringObjectValue(str); + if (resolvedFieldType->IsPointer()) + staticValue = mModule->GetStringCharPtr(str); + else + staticValue = mModule->GetStringObjectValue(str); } else { @@ -4417,25 +4435,7 @@ BfIRValue BfIRBuilder::CreateNot(BfIRValue val) BfIRValue BfIRBuilder::CreateBitCast(BfIRValue val, BfIRType type) { if (val.IsConst()) - { - auto constVal = GetConstant(val); - - auto bitCast = mTempAlloc.Alloc(); - if (constVal->IsNull()) - bitCast->mConstType = BfConstType_BitCastNull; - else - bitCast->mConstType = BfConstType_BitCast; - bitCast->mTarget = val.mId; - bitCast->mToType = type; - - BfIRValue castedVal(BfIRValueFlags_Const, mTempAlloc.GetChunkedId(bitCast)); -#ifdef CHECK_CONSTHOLDER - castedVal.mHolder = this; -#endif - BF_ASSERT((void*)GetConstant(castedVal) == (void*)bitCast); - return castedVal; - } - + return CreateConstBitCast(val, type); auto retVal = WriteCmd(BfIRCmd_BitCast, val, type); NEW_CMD_INSERTED_IRVALUE; return retVal; diff --git a/IDEHelper/Compiler/BfIRBuilder.h b/IDEHelper/Compiler/BfIRBuilder.h index 3d045deb..64c0f55d 100644 --- a/IDEHelper/Compiler/BfIRBuilder.h +++ b/IDEHelper/Compiler/BfIRBuilder.h @@ -934,6 +934,7 @@ public: BfIRValue CreateConstAggCE(BfIRType type, addr_ce ptr); BfIRValue CreateConstArrayZero(BfIRType type, int count); BfIRValue CreateConstArrayZero(int count); + BfIRValue CreateConstBitCast(BfIRValue val, BfIRType type); BfIRValue CreateTypeOf(BfType* type); BfIRValue CreateTypeOf(BfType* type, BfIRValue typeData); BfIRValue GetUndefConstValue(BfIRType type); diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index f9074614..760f6c68 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -7889,7 +7889,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS { if (BfIRConstHolder::IsInt(primType->mTypeDef->mTypeCode)) { - if (!mCompiler->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, constExprValueType->mValue.mInt64)) + if (!mCompiler->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, constExprValueType->mValue.mUInt64)) { if ((!ignoreErrors) && (PreFail())) *errorOut = Fail(StrFormat("Const generic argument '%s', declared with const '%lld', does not fit into const constraint '%s' for '%s'", genericParamInst->GetName().c_str(), @@ -9617,6 +9617,11 @@ bool BfModule::HasCompiledOutput() return (!mSystem->mIsResolveOnly) && (mGeneratesCode) && (!mIsComptimeModule); } +bool BfModule::HasExecutedOutput() +{ + return ((!mSystem->mIsResolveOnly) && (mGeneratesCode)) || (mIsComptimeModule); +} + // We will skip the object access check for any occurrences of this value void BfModule::SkipObjectAccessCheck(BfTypedValue typedVal) { @@ -10519,7 +10524,7 @@ bool BfModule::HasMixin(BfTypeInstance* typeInstance, const StringImpl& methodNa } StringT<128> BfModule::MethodToString(BfMethodInstance* methodInst, BfMethodNameFlags methodNameFlags, BfTypeVector* typeGenericArgs, BfTypeVector* methodGenericArgs) -{ +{ auto methodDef = methodInst->mMethodDef; bool allowResolveGenericParamNames = ((methodNameFlags & BfMethodNameFlag_ResolveGenericParamNames) != 0); @@ -10764,8 +10769,16 @@ StringT<128> BfModule::MethodToString(BfMethodInstance* methodInst, BfMethodName if (accessorString.length() != 0) { - methodName += " " + accessorString; + methodName += " "; + methodName += accessorString; } + + if ((methodNameFlags & BfMethodNameFlag_IncludeMut) != 0) + { + if ((methodDef->mIsMutating) && (methodInst->GetOwner()->IsValueType())) + methodName += " mut"; + } + return methodName; } @@ -10860,8 +10873,17 @@ void BfModule::CurrentAddToConstHolder(BfIRValue& irVal) auto origConst = irVal; if ((constant->mConstType == BfConstType_BitCast) || (constant->mConstType == BfConstType_BitCastNull)) { - auto bitcast = (BfConstantBitCast*)constant; - constant = mBfIRBuilder->GetConstantById(bitcast->mTarget); + auto bitcast = (BfConstantBitCast*)constant; + BfIRValue newVal; + if (bitcast->mTarget) + { + newVal = BfIRValue(BfIRValueFlags_Const, bitcast->mTarget); + CurrentAddToConstHolder(newVal); + } + else + newVal = mCurTypeInstance->GetOrCreateConstHolder()->CreateConstNull(); + irVal = mCurTypeInstance->GetOrCreateConstHolder()->CreateConstBitCast(newVal, bitcast->mToType); + return; } irVal = mCurTypeInstance->CreateConst(constant, mBfIRBuilder); @@ -10986,7 +11008,7 @@ BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* con wantType = mContext->mTypes[constant->mIRType.mId]; if (wantType == NULL) - return constHolder->CreateConstNull(); + return mBfIRBuilder->CreateConstNull(); return GetDefaultValue(wantType); } @@ -11034,8 +11056,21 @@ BfIRValue BfModule::ConstantToCurrent(BfConstant* constant, BfIRConstHolder* con return mBfIRBuilder->CreateIntToPtr(ConstantToCurrent(fromTarget, constHolder, NULL), toIRType); } + if ((constant->mConstType == BfConstType_BitCast) || (constant->mConstType == BfConstType_BitCastNull)) + { + auto bitcast = (BfConstantBitCast*)constant; + auto fromTarget = constHolder->GetConstantById(bitcast->mTarget); + BfIRType toIRType = bitcast->mToType; + if (toIRType.mKind == BfIRTypeData::TypeKind_TypeId) + { + auto toType = mContext->mTypes[toIRType.mId]; + toIRType = mBfIRBuilder->MapType(toType); + } + return mBfIRBuilder->CreateBitCast(ConstantToCurrent(fromTarget, constHolder, NULL), toIRType); + } + if (constant->mConstType == BfConstType_Agg) - { + { auto constArray = (BfConstantAgg*)constant; if ((wantType == NULL) && (constArray->mType.mKind == BfIRTypeData::TypeKind_TypeId)) @@ -14456,7 +14491,7 @@ BfLocalVariable* BfModule::AddLocalVariableDef(BfLocalVariable* localVarDef, boo if ((localVarDef->mNameNode != NULL) && (mCurMethodInstance != NULL)) { bool isClosureProcessing = (mCurMethodState->mClosureState != NULL) && (!mCurMethodState->mClosureState->mCapturing); - if ((!isClosureProcessing) && (mCompiler->mResolvePassData != NULL) && (localVarDef->mNameNode != NULL) && (!mIsComptimeModule)) + if ((!isClosureProcessing) && (mCompiler->mResolvePassData != NULL) && (localVarDef->mNameNode != NULL) && (rootMethodState->mMethodInstance != NULL) && (!mIsComptimeModule)) mCompiler->mResolvePassData->HandleLocalReference(localVarDef->mNameNode, rootMethodState->mMethodInstance->GetOwner()->mTypeDef, rootMethodState->mMethodInstance->mMethodDef, localVarDef->mLocalVarId); } @@ -15273,14 +15308,18 @@ void BfModule::AssertErrorState() { if (mCurTypeInstance->mTypeFailed) return; - if (mCurTypeInstance->mTypeDef->mSource->mParsingFailed) + if ((mCurTypeInstance->mTypeDef->GetDefinition()->mSource != NULL) && (mCurTypeInstance->mTypeDef->GetDefinition()->mSource->mParsingFailed)) return; } if (mCurMethodInstance != NULL) { - if ((mCurMethodInstance->mMethodDef->mDeclaringType != NULL) && (mCurMethodInstance->mMethodDef->mDeclaringType->mSource->mParsingFailed)) + if ((mCurMethodInstance->mMethodDef->mDeclaringType != NULL) && + (mCurMethodInstance->mMethodDef->mDeclaringType->mSource != NULL) && + (mCurMethodInstance->mMethodDef->mDeclaringType->mSource->mParsingFailed)) return; - if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL) && (mCurMethodState->mMixinState->mMixinMethodInstance->mMethodDef->mDeclaringType->mSource->mParsingFailed)) + if ((mCurMethodState != NULL) && (mCurMethodState->mMixinState != NULL) && + (mCurMethodState->mMixinState->mMixinMethodInstance->mMethodDef->mDeclaringType->mSource != NULL) && + (mCurMethodState->mMixinState->mMixinMethodInstance->mMethodDef->mDeclaringType->mSource->mParsingFailed)) return; } @@ -16203,6 +16242,8 @@ void BfModule::EmitDtorBody() BfIRValue BfModule::CreateDllImportGlobalVar(BfMethodInstance* methodInstance, bool define) { + BF_ASSERT(methodInstance->mIsReified); + auto typeInstance = methodInstance->GetOwner(); bool foundDllImportAttr = false; @@ -16467,7 +16508,7 @@ void BfModule::SetupIRMethod(BfMethodInstance* methodInstance, BfIRFunction func auto elementType = refType->mElementType; PopulateType(elementType, BfPopulateType_Data); addDeref = elementType->mSize; - if ((addDeref <= 0) && (!elementType->IsValuelessType())) + if ((addDeref <= 0) && (!elementType->IsValuelessType()) && (!elementType->IsOpaque())) AssertErrorState(); } if ((resolvedTypeRef->IsComposite()) && (!resolvedTypeRef->IsTypedPrimitive())) @@ -17041,7 +17082,7 @@ void BfModule::EmitCtorBody(bool& skipBody) break; } - if ((HasCompiledOutput()) && (matchedMethod != NULL)) + if ((HasExecutedOutput()) && (matchedMethod != NULL)) { SizedArray args; auto ctorBodyMethodInstance = GetMethodInstance(mCurTypeInstance->mBaseType, matchedMethod, BfTypeVector()); @@ -18486,7 +18527,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) return; } - if (HasCompiledOutput()) + if (HasExecutedOutput()) { BF_ASSERT(mIsModuleMutable); } @@ -19713,7 +19754,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) skipBody = true; skipEndChecks = true; - if ((HasCompiledOutput()) || (mIsComptimeModule)) + if (HasExecutedOutput()) { // Clear out DebugLoc - to mark the ".addr" code as part of prologue mBfIRBuilder->ClearDebugLocation(); @@ -19743,12 +19784,8 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) mBfIRBuilder->CreateRetVoid(); } else - { - BF_ASSERT(!innerMethodInstance.mMethodInstance->mMethodDef->mDeclaringType->IsEmitted()); + { auto innerMethodDef = innerMethodInstance.mMethodInstance->mMethodDef; - if (innerType->mTypeDef->IsEmitted()) - innerMethodDef = innerType->mTypeDef->mEmitParent->mMethods[innerMethodDef->mIdx]; - BF_ASSERT(innerMethodDef == methodDef); SizedArray innerParams; @@ -19977,7 +20014,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) else if ((mCurTypeInstance->IsEnum()) && (!mCurTypeInstance->IsBoxed()) && (methodDef->mName == BF_METHODNAME_TO_STRING)) { auto enumType = ResolveTypeDef(mCompiler->mEnumTypeDef); - if ((HasCompiledOutput()) || (mIsComptimeModule)) + if (HasExecutedOutput()) { EmitEnumToStringBody(); } @@ -19990,7 +20027,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) else if ((mCurTypeInstance->IsTuple()) && (!mCurTypeInstance->IsBoxed()) && (methodDef->mName == BF_METHODNAME_TO_STRING)) { auto enumType = ResolveTypeDef(mCompiler->mEnumTypeDef); - if ((HasCompiledOutput()) || (mIsComptimeModule)) + if (HasExecutedOutput()) { EmitTupleToStringBody(); } @@ -20032,7 +20069,7 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup) { mBfIRBuilder->CreateRetVoid(); } - else if ((HasCompiledOutput()) || (mIsComptimeModule)) + else if (HasExecutedOutput()) { String autoPropName = typeDef->GetAutoPropertyName(propertyDeclaration); BfFieldInstance* fieldInstance = GetFieldByName(mCurTypeInstance, autoPropName); @@ -22782,18 +22819,6 @@ void BfModule::DoMethodDeclaration(BfMethodDeclaration* methodDeclaration, bool //BF_ASSERT(mCompiler->IsAutocomplete()); BfLogSysM("DoMethodDeclaration isTemporaryFunc bailout\n"); return; // Bail out early for autocomplete pass - } - - if ((methodInstance->GetImportCallKind() != BfImportCallKind_None) && (!mBfIRBuilder->mIgnoreWrites) && (!methodInstance->mIRFunction)) - { - BfLogSysM("DllImportGlobalVar DoMethodDeclaration processing %p\n", methodInstance); - // If this is in an extension then we did create the global variable already in the original obj - bool doDefine = mExtensionCount == 0; - BfIRValue dllImportGlobalVar = CreateDllImportGlobalVar(methodInstance, doDefine); - func = mBfIRBuilder->GetFakeVal(); - methodInstance->mIRFunction = func; - BF_ASSERT(dllImportGlobalVar); - mFuncReferences[mCurMethodInstance] = dllImportGlobalVar; } //TODO: We used to have this (this != mContext->mExternalFuncModule) check, but it caused us to keep around diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 30a11776..efa5ae8f 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1633,6 +1633,7 @@ public: bool IsTargetingBeefBackend(); bool WantsLifetimes(); bool HasCompiledOutput(); + bool HasExecutedOutput(); void SkipObjectAccessCheck(BfTypedValue typedVal); void EmitObjectAccessCheck(BfTypedValue typedVal); void EmitEnsureInstructionAt(); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 81db527c..70fa7133 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -2076,6 +2076,9 @@ void BfModule::FinishCEParseContext(BfAstNode* refNode, BfTypeInstance* typeInst void BfModule::UpdateCEEmit(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance, const StringImpl& ctxString, BfAstNode* refNode) { + for (int ifaceTypeId : ceEmitContext->mInterfaces) + typeInstance->mCeTypeInfo->mPendingInterfaces.Add(ifaceTypeId); + if (ceEmitContext->mEmitData.IsEmpty()) return; @@ -2198,7 +2201,7 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* } } } - else if (!ceEmitContext->mEmitData.IsEmpty()) + else if (ceEmitContext->HasEmissions()) { if (typeInstance->mCeTypeInfo == NULL) typeInstance->mCeTypeInfo = new BfCeTypeInfo(); @@ -2210,7 +2213,7 @@ void BfModule::HandleCEAttributes(CeEmitContext* ceEmitContext, BfTypeInstance* typeInstance->mCeTypeInfo->mNext->mTypeIFaceMap[typeId] = entry; } - if (!ceEmitContext->mEmitData.IsEmpty()) + if (ceEmitContext->HasEmissions()) { String ctxStr = "comptime ApplyToType of "; ctxStr += TypeToString(attrType); @@ -2747,7 +2750,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy resolvedTypeRef->mSize = typeInstance->mAlign = mSystem->mPtrSize; } - BF_ASSERT((typeInstance->mMethodInstanceGroups.size() == 0) || (typeInstance->mMethodInstanceGroups.size() == typeDef->mMethods.size()) || (typeInstance->mCeTypeInfo != NULL)); + BF_ASSERT((typeInstance->mMethodInstanceGroups.size() == 0) || (typeInstance->mMethodInstanceGroups.size() == typeDef->mMethods.size()) || (typeInstance->mCeTypeInfo != NULL) || (typeInstance->IsBoxed())); typeInstance->mMethodInstanceGroups.Resize(typeDef->mMethods.size()); for (int i = 0; i < (int)typeInstance->mMethodInstanceGroups.size(); i++) { @@ -2927,6 +2930,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy { bool hadType = false; + BfAstNode* deferredErrorNode = NULL; + char* deferredError = NULL; + for (auto baseTypeRef : typeDef->mBaseTypes) { SetAndRestoreValue prevTypeRef(mContext->mCurTypeState->mCurBaseTypeRef, baseTypeRef); @@ -2946,12 +2952,14 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } else { - Fail("Underlying enum type already specified", baseTypeRef); + deferredError = "Underlying enum type already specified"; + deferredErrorNode = baseTypeRef; } } else { - Fail("Invalid underlying enum type", baseTypeRef); + deferredError = "Invalid underlying enum type"; + deferredErrorNode = baseTypeRef; } } else @@ -2961,6 +2969,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } + if (deferredError != NULL) + Fail(deferredError, deferredErrorNode, true); + if (underlyingType == NULL) { underlyingType = GetPrimitiveType(BfTypeCode_Int64); @@ -3305,6 +3316,27 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy wantPopulateInterfaces = true; } + if ((typeInstance->mCeTypeInfo != NULL) && (!typeInstance->mCeTypeInfo->mPendingInterfaces.IsEmpty())) + { + for (auto ifaceTypeId : typeInstance->mCeTypeInfo->mPendingInterfaces) + { + auto ifaceType = mContext->mTypes[ifaceTypeId]; + if ((ifaceType == NULL) || (!ifaceType->IsInterface())) + continue; + auto ifaceInst = ifaceType->ToTypeInstance(); + + if (ifaceSet.Add(ifaceInst)) + { + // Not base type + BfInterfaceDecl ifaceDecl; + ifaceDecl.mIFaceTypeInst = ifaceInst; + ifaceDecl.mTypeRef = NULL; + ifaceDecl.mDeclaringType = typeDef->GetDefinition(); + interfaces.Add(ifaceDecl); + } + } + } + if (_CheckTypeDone()) return; @@ -3694,6 +3726,25 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy if (innerType->IsIncomplete()) PopulateType(innerType, BfPopulateType_Data); + auto innerTypeInst = innerType->ToTypeInstance(); + if (innerTypeInst != NULL) + { + if (typeInstance->mTypeDef != innerTypeInst->mTypeDef) + { + // Rebuild with proper typedef (generally from inner type comptime emission) + typeInstance->mTypeDef = innerTypeInst->mTypeDef; + DoPopulateType(resolvedTypeRef, populateType); + return; + } + + while (typeInstance->mInterfaces.mSize < innerTypeInst->mInterfaces.mSize) + { + auto ifaceEntry = innerTypeInst->mInterfaces[typeInstance->mInterfaces.mSize]; + typeInstance->mInterfaces.Add(ifaceEntry); + AddDependency(ifaceEntry.mInterfaceType, typeInstance, BfDependencyMap::DependencyFlag_ImplementsInterface); + } + } + auto baseType = typeInstance->mBaseType; dataPos = baseType->mInstSize; int alignSize = BF_MAX(innerType->mAlign, baseType->mInstAlign); @@ -3997,9 +4048,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } if ((typeInstance->mDefineState < BfTypeDefineState_CEPostTypeInit) && (tryCE)) - { + { BF_ASSERT(!typeInstance->mTypeDef->IsEmitted()); + if (typeInstance->mCeTypeInfo != NULL) + typeInstance->mCeTypeInfo->mPendingInterfaces.Clear(); + typeInstance->mDefineState = BfTypeDefineState_CETypeInit; bool hadNewMembers = false; DoCEEmit(typeInstance, hadNewMembers); @@ -4054,6 +4108,9 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy } } + if ((typeInstance->mCeTypeInfo != NULL) && (!typeInstance->mCeTypeInfo->mPendingInterfaces.IsEmpty())) + hadNewMembers = true; + if ((typeInstance->mTypeDef->IsEmitted()) && (typeInstance->mCeTypeInfo == NULL)) { BF_ASSERT(mCompiler->mCanceling); @@ -5658,16 +5715,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) } else { - auto matchedMethodDef = matchedMethod->mMethodDef; - if (matchedMethodDef->mDeclaringType->IsEmitted()) - { - Fail("Boxed interface binding error to emitted method", mCurTypeInstance->mTypeDef->GetRefNode()); - continue; - } - - if (underlyingTypeInstance->mTypeDef->IsEmitted()) - matchedMethodDef = underlyingTypeInstance->mTypeDef->mEmitParent->mMethods[matchedMethodDef->mIdx]; - + auto matchedMethodDef = matchedMethod->mMethodDef; if (!matchedMethod->mIsForeignMethodDef) { BfMethodInstanceGroup* boxedMethodInstanceGroup = &typeInstance->mMethodInstanceGroups[matchedMethod->mMethodDef->mIdx]; @@ -5676,7 +5724,7 @@ void BfModule::DoTypeInstanceMethodProcessing(BfTypeInstance* typeInstance) boxedMethodInstanceGroup->mOnDemandKind = BfMethodOnDemandKind_Decl_AwaitingDecl; VerifyOnDemandMethods(); } - } + } auto methodFlags = matchedMethod->mIsForeignMethodDef ? BfGetMethodInstanceFlag_ForeignMethodDef : BfGetMethodInstanceFlag_None; methodFlags = (BfGetMethodInstanceFlags)(methodFlags | BfGetMethodInstanceFlag_MethodInstanceOnly); @@ -11505,29 +11553,37 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp { SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); auto constraintTypeInst = genericParamInst->mTypeConstraint->ToTypeInstance(); - if ((constraintTypeInst != NULL) && (constraintTypeInst->IsInstanceOf(mCompiler->mEnumTypeDef)) && (explicitCast)) + + if ((constraintTypeInst != NULL) && (constraintTypeInst->IsDelegateOrFunction())) { - // Enum->int - if ((explicitCast) && (toType->IsInteger())) - return typedVal.mValue; + // Could be a methodref - can't cast to anything else } - - BfTypedValue fromTypedValue; - if (typedVal.mKind == BfTypedValueKind_GenericConstValue) - fromTypedValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint, false, BfDefaultValueKind_Undef); else - fromTypedValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), genericParamInst->mTypeConstraint, genericParamInst->mTypeConstraint->IsValueType()); - - auto result = CastToValue(srcNode, fromTypedValue, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail)); - if (result) { - if ((genericParamInst->mTypeConstraint->IsDelegate()) && (toType->IsDelegate())) + if ((constraintTypeInst != NULL) && (constraintTypeInst->IsInstanceOf(mCompiler->mEnumTypeDef)) && (explicitCast)) { - // Don't allow cast when we are constrained by a delegate type, because BfMethodRefs can match and we require an actual alloc - Fail(StrFormat("Unable to cast '%s' to '%s' because delegate constraints allow valueless direct method references", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode); - return BfIRValue(); + // Enum->int + if ((explicitCast) && (toType->IsInteger())) + return typedVal.mValue; + } + + BfTypedValue fromTypedValue; + if (typedVal.mKind == BfTypedValueKind_GenericConstValue) + fromTypedValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint, false, BfDefaultValueKind_Undef); + else + fromTypedValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), genericParamInst->mTypeConstraint, genericParamInst->mTypeConstraint->IsValueType()); + + auto result = CastToValue(srcNode, fromTypedValue, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail)); + if (result) + { + if ((genericParamInst->mTypeConstraint->IsDelegate()) && (toType->IsDelegate())) + { + // Don't allow cast when we are constrained by a delegate type, because BfMethodRefs can match and we require an actual alloc + Fail(StrFormat("Unable to cast '%s' to '%s' because delegate constraints allow valueless direct method references", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode); + return BfIRValue(); + } + return result; } - return result; } } @@ -11755,7 +11811,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp if (allowCast) { - if (ignoreWrites) + if ((ignoreWrites) && (!typedVal.mValue.IsConst())) return mBfIRBuilder->GetFakeVal(); return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType)); } diff --git a/IDEHelper/Compiler/BfParser.cpp b/IDEHelper/Compiler/BfParser.cpp index dce0e396..e96ba38f 100644 --- a/IDEHelper/Compiler/BfParser.cpp +++ b/IDEHelper/Compiler/BfParser.cpp @@ -2447,10 +2447,9 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mTokenEnd = mSrcIdx; return; } - - bool wasNeg = false; + bool hadOverflow = false; - int64 val = 0; + uint64 val = 0; int numberBase = 10; int expVal = 0; int expSign = 0; @@ -2460,8 +2459,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) int hexDigits = 0; if (c == '-') { - wasNeg = true; //TODO: This never actually gets set any more (eaten as BfToken_Minus above). Move checks that use this to later in pipeline, then remove this - c = mSrc[mSrcIdx++]; + BF_FATAL("Parsing error"); } val = c - '0'; @@ -2641,7 +2639,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) // This is actually a integer followed by an Int32 call (like 123.ToString) mSrcIdx -= 2; mTokenEnd = mSrcIdx; - mLiteral.mInt64 = val; + mLiteral.mUInt64 = val; mLiteral.mTypeCode = BfTypeCode_IntUnknown; mSyntaxToken = BfSyntaxToken_Literal; return; @@ -2668,27 +2666,24 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) if (endNumber) { mTokenEnd = mSrcIdx - 1; - mSrcIdx--; - if (wasNeg) - val = -val; + mSrcIdx--; if ((numberBase == 0x10) && ((hexDigits >= 16) || ((hadSeps) && (hexDigits > 8)) || ((hadLeadingHexSep) && (hexDigits == 8)))) { if (hexDigits > 16) mPassInstance->FailAt("Too many hex digits for int64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); - mLiteral.mInt64 = val; - if ((val < 0) && (!wasNeg)) + mLiteral.mUInt64 = val; + if (val >= 0x8000000000000000) mLiteral.mTypeCode = BfTypeCode_UInt64; else mLiteral.mTypeCode = BfTypeCode_Int64; } else { - mLiteral.mInt64 = val; + mLiteral.mUInt64 = val; mLiteral.mTypeCode = BfTypeCode_IntUnknown; - if ((numberBase == 0x10) && (hexDigits == 7)) mLiteral.mWarnType = BfWarning_BF4201_Only7Hex; if ((numberBase == 0x10) && (hexDigits == 9)) @@ -2699,7 +2694,12 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mPassInstance->FailAt("Value doesn't fit into int64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); mLiteral.mTypeCode = BfTypeCode_Int64; } - else if ((val < -0x80000000LL) || (val > 0xFFFFFFFFLL)) + //else if ((val < -0x80000000LL) || (val > 0xFFFFFFFFLL)) + else if (val >= 0x8000000000000000) + { + mLiteral.mTypeCode = BfTypeCode_UInt64; + } + else if (val > 0xFFFFFFFFLL) { mLiteral.mTypeCode = BfTypeCode_Int64; } @@ -2709,7 +2709,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) return; } - int64 prevVal = val; + uint64 prevVal = val; if ((c >= '0') && (c <= '9') && (c < '0' + numberBase)) { if (numberBase == 0x10) @@ -2731,9 +2731,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) } else if ((c == 'u') || (c == 'U')) - { - if (wasNeg) - val = -val; + { if ((mSrc[mSrcIdx] == 'l') || (mSrc[mSrcIdx] == 'L')) { if (mSrc[mSrcIdx] == 'l') @@ -2744,7 +2742,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mLiteral.mUInt64 = (uint64)val; if (hexDigits > 16) mPassInstance->FailAt("Too many hex digits for int64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); - else if ((hadOverflow) || (wasNeg)) + else if (hadOverflow) mPassInstance->FailAt("Value doesn't fit into uint64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); mSyntaxToken = BfSyntaxToken_Literal; return; @@ -2752,7 +2750,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mTokenEnd = mSrcIdx; mLiteral.mTypeCode = BfTypeCode_UIntPtr; mLiteral.mUInt32 = (uint32)val; - if ((hadOverflow) || (wasNeg) || ((uint64)val != (uint64)mLiteral.mUInt32)) + if ((hadOverflow) || ((uint64)val != (uint64)mLiteral.mUInt32)) mPassInstance->FailAt("Value doesn't fit into uint32", mSourceData, mTokenStart, mSrcIdx - mTokenStart); mSyntaxToken = BfSyntaxToken_Literal; return; @@ -2760,9 +2758,7 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) else if ((c == 'l') || (c == 'L')) { if (c == 'l') - TokenFail("Uppercase 'L' required for int64"); - if (wasNeg) - val = -val; + TokenFail("Uppercase 'L' required for int64"); if ((mSrc[mSrcIdx] == 'u') || (mSrc[mSrcIdx] == 'U')) { mSrcIdx++; @@ -2771,25 +2767,24 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) mLiteral.mUInt64 = (uint64)val; if (hexDigits > 16) mPassInstance->FailAt("Too many hex digits for int64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); - else if ((hadOverflow) || (wasNeg)) + else if (hadOverflow) mPassInstance->FailAt("Value doesn't fit into uint64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); mSyntaxToken = BfSyntaxToken_Literal; return; - } + } mTokenEnd = mSrcIdx; mLiteral.mTypeCode = BfTypeCode_Int64; mLiteral.mInt64 = (int64)val; - - bool signMatched = true; - if (val != 0) - signMatched = (val < 0) == wasNeg; - + if (val == 0x8000000000000000) + mLiteral.mTypeCode = BfTypeCode_UInt64; + else if (val >= 0x8000000000000000) + hadOverflow = true; if (numberBase == 0x10) { if (hexDigits > 16) mPassInstance->FailAt("Too many hex digits for int64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); } - else if ((hadOverflow) || (!signMatched)) + else if (hadOverflow) mPassInstance->FailAt("Value doesn't fit into int64", mSourceData, mTokenStart, mSrcIdx - mTokenStart); mSyntaxToken = BfSyntaxToken_Literal; return; @@ -2813,17 +2808,14 @@ void BfParser::NextToken(int endIdx, bool outerIsInterpolate) else { mTokenEnd = mSrcIdx - 1; - mSrcIdx--; - if (wasNeg) - val = -val; - mLiteral.mInt64 = val; + mSrcIdx--; + mLiteral.mUInt64 = val; mLiteral.mTypeCode = BfTypeCode_IntUnknown; mSyntaxToken = BfSyntaxToken_Literal; TokenFail("Unexpected character while parsing number", 0); return; } - - //if ((val < 0) && (val != -0x8000000000000000)) + if ((uint64)prevVal > (uint64)val) hadOverflow = true; } diff --git a/IDEHelper/Compiler/BfParser.h b/IDEHelper/Compiler/BfParser.h index cc9a27ed..3c95ed8d 100644 --- a/IDEHelper/Compiler/BfParser.h +++ b/IDEHelper/Compiler/BfParser.h @@ -226,6 +226,7 @@ public: void SetSource(const char* data, int length); void MoveSource(const char* data, int length); // Takes ownership of data ptr void RefSource(const char* data, int length); + void MakeNegative(uint64& val, bool& hadOverflow); void NextToken(int endIdx = -1, bool outerIsInterpolate = false); BfAstNode* CreateNode(); diff --git a/IDEHelper/Compiler/BfPrinter.cpp b/IDEHelper/Compiler/BfPrinter.cpp index ccc2d82e..f30db3a9 100644 --- a/IDEHelper/Compiler/BfPrinter.cpp +++ b/IDEHelper/Compiler/BfPrinter.cpp @@ -300,6 +300,11 @@ int BfPrinter::CalcOrigLineSpacing(BfAstNode* bfAstNode, int* lineStartIdx) void BfPrinter::WriteIgnoredNode(BfAstNode* node) { + if ((!mOutString.IsEmpty()) && (!isspace((uint8)mOutString[mOutString.mLength - 1]))) + { + Write(" "); + } + bool wasExpectingNewLine = mExpectingNewLine; mTriviaIdx = std::max(mTriviaIdx, node->GetTriviaStart()); @@ -1759,9 +1764,14 @@ void BfPrinter::Visit(BfLambdaBindExpression* lambdaBindExpr) else { ExpectSpace(); - VisitChild(lambdaBindExpr->mBody); + VisitChild(lambdaBindExpr->mBody); } - } + } + VisitChild(lambdaBindExpr->mDtor); + mNextStateModify.mExpectingSpace = false; + mVirtualNewLineIdx = mNextStateModify.mWantNewLineIdx; + mCurIndentLevel = mNextStateModify.mWantVirtualIndent; + mVirtualIndentLevel = mNextStateModify.mWantVirtualIndent; } void BfPrinter::Visit(BfObjectCreateExpression* newExpr) @@ -2643,7 +2653,7 @@ void BfPrinter::Visit(BfIndexerDeclaration* indexerDeclaration) } void BfPrinter::Visit(BfFieldDeclaration* fieldDeclaration) -{ +{ bool isEnumDecl = false; if (auto enumEntry = BfNodeDynCast(fieldDeclaration)) @@ -2703,18 +2713,10 @@ void BfPrinter::Visit(BfFieldDeclaration* fieldDeclaration) QueueVisitChild(fieldDeclaration->mInitializer); } - auto fieldDtor = fieldDeclaration->mFieldDtor; - while (fieldDtor != NULL) - { - ExpectSpace(); - QueueVisitChild(fieldDtor->mTildeToken); - ExpectSpace(); - QueueVisitChild(fieldDtor->mBody); - fieldDtor = fieldDtor->mNextFieldDtor; - } - mNextStateModify.mExpectingSpace = false; FlushVisitChild(); + VisitChild(fieldDeclaration->mFieldDtor); + mNextStateModify.mExpectingSpace = false; } void BfPrinter::Visit(BfEnumCaseDeclaration* enumCaseDeclaration) @@ -2763,6 +2765,32 @@ void BfPrinter::Visit(BfTypeAliasDeclaration* typeDeclaration) VisitChild(typeDeclaration->mEndSemicolon); } +void BfPrinter::Visit(BfFieldDtorDeclaration* fieldDtorDeclaration) +{ + ExpectSpace(); + if (fieldDtorDeclaration->mBody != NULL) + { + if (fieldDtorDeclaration->mBody->IsA()) + { + ExpectNewLine(); + ExpectIndent(); + VisitChild(fieldDtorDeclaration->mTildeToken); + VisitChild(fieldDtorDeclaration->mBody); + ExpectUnindent(); + } + else + { + VisitChild(fieldDtorDeclaration->mTildeToken); + ExpectSpace(); + VisitChild(fieldDtorDeclaration->mBody); + } + } + else + VisitChild(fieldDtorDeclaration->mTildeToken); + + VisitChild(fieldDtorDeclaration->mNextFieldDtor); +} + void BfPrinter::Visit(BfTypeDeclaration* typeDeclaration) { SetAndRestoreValue prevTypeDecl(mCurTypeDecl, typeDeclaration); diff --git a/IDEHelper/Compiler/BfPrinter.h b/IDEHelper/Compiler/BfPrinter.h index 815222c5..cf4dc2b6 100644 --- a/IDEHelper/Compiler/BfPrinter.h +++ b/IDEHelper/Compiler/BfPrinter.h @@ -220,6 +220,7 @@ public: virtual void Visit(BfFieldDeclaration* fieldDeclaration) override; virtual void Visit(BfEnumCaseDeclaration* enumCaseDeclaration) override; virtual void Visit(BfTypeAliasDeclaration* typeDeclaration) override; + virtual void Visit(BfFieldDtorDeclaration* fieldDtorDeclaration) override; virtual void Visit(BfTypeDeclaration* typeDeclaration) override; virtual void Visit(BfUsingDirective* usingDirective) override; virtual void Visit(BfUsingModDirective* usingDirective) override; diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index efc75e6d..baa65d07 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -5912,12 +5912,9 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i ReplaceNode(tokenNode, operatorDecl); operatorDecl->mOperatorToken = tokenNode; - auto nextIdentifier = ExpectIdentifierAfter(operatorDecl, "type"); - if (nextIdentifier == NULL) - return operatorDecl; - mVisitorPos.mReadPos--; // Backtrack, that's part of our type - auto typeRef = CreateTypeRefAfter(operatorDecl); + if (typeRef == NULL) + return operatorDecl; MEMBER_SET_CHECKED(operatorDecl, mReturnType, typeRef); operatorDecl->mIsConvOperator = true; diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index 7b85a000..26ba94bc 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -1298,15 +1298,6 @@ void BfMethodInstance::GetIRFunctionInfo(BfModule* module, BfIRType& returnType, } } -// if ((paramIdx == 0) && (GetParamName(0) == "this") && (checkType->IsPointer())) -// { -// // We don't actually pass a this pointer for mut methods in valueless structs -// auto underlyingType = checkType->GetUnderlyingType(); -// module->PopulateType(underlyingType, BfPopulateType_Data); -// if (underlyingType->IsValuelessType()) -// continue; -// } - if (checkType->CanBeValuelessType()) module->PopulateType(checkType, BfPopulateType_Data); if ((checkType->IsValuelessType()) && (!checkType->IsMethodRef())) @@ -1563,6 +1554,7 @@ BfTypeInstance::~BfTypeInstance() if ((mTypeDef != NULL) && (mTypeDef->mEmitParent != NULL)) { mMethodInstanceGroups.Clear(); + BfLogSys(mModule->mSystem, "Type %p dtor deleting typeDef %p\n", this, mTypeDef); delete mTypeDef; } } @@ -2636,6 +2628,12 @@ void BfTupleType::Finish() ////////////////////////////////////////////////////////////////////////// +BfBoxedType::~BfBoxedType() +{ + if ((mTypeDef != NULL) && (mTypeDef->mEmitParent != NULL)) + mTypeDef = NULL; +} + BfType* BfBoxedType::GetModifiedElementType() { if ((mBoxedFlags & BoxedFlags_StructPtr) != 0) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 0f7a6fa9..9b9a1321 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -61,7 +61,8 @@ enum BfMethodNameFlags : uint8 BfMethodNameFlag_ResolveGenericParamNames = 1, BfMethodNameFlag_OmitTypeName = 2, BfMethodNameFlag_IncludeReturnType = 4, - BfMethodNameFlag_OmitParams = 8 + BfMethodNameFlag_OmitParams = 8, + BfMethodNameFlag_IncludeMut = 0x10 }; enum BfGetMethodInstanceFlags : uint16 @@ -1827,6 +1828,7 @@ class BfCeTypeInfo public: Dictionary mOnCompileMap; Dictionary mTypeIFaceMap; + Array mPendingInterfaces; Val128 mHash; bool mFailed; BfCeTypeInfo* mNext; @@ -2109,6 +2111,7 @@ public: mBoxedBaseType = NULL; mBoxedFlags = BoxedFlags_None; } + ~BfBoxedType(); virtual bool IsBoxed() override { return true; } diff --git a/IDEHelper/Compiler/BfSystem.cpp b/IDEHelper/Compiler/BfSystem.cpp index 4ff7e3d1..de407542 100644 --- a/IDEHelper/Compiler/BfSystem.cpp +++ b/IDEHelper/Compiler/BfSystem.cpp @@ -793,11 +793,6 @@ BfTypeDef::~BfTypeDef() { BfLogSysM("BfTypeDef::~BfTypeDef %p\n", this); - if ((mHash == -1330357811) && (IsEmitted())) - { - NOP; - } - delete mNextRevision; FreeMembers(); @@ -2282,6 +2277,41 @@ bool BfSystem::DoesLiteralFit(BfTypeCode typeCode, int64 value) return false; } +bool BfSystem::DoesLiteralFit(BfTypeCode typeCode, uint64 value) +{ + if (typeCode == BfTypeCode_IntPtr) + typeCode = (mPtrSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64; + if (typeCode == BfTypeCode_UIntPtr) + typeCode = (mPtrSize == 4) ? BfTypeCode_UInt32 : BfTypeCode_UInt64; + + if (value >= 0x8000000000000000) + return typeCode == BfTypeCode_UInt64; + + switch (typeCode) + { + case BfTypeCode_Int8: + return (value < 0x80); + case BfTypeCode_Int16: + return (value < 0x8000); + case BfTypeCode_Int32: + return (value < 0x80000000LL); + case BfTypeCode_Int64: + return true; + + case BfTypeCode_UInt8: + return (value < 0x100); + case BfTypeCode_UInt16: + return (value < 0x10000); + case BfTypeCode_UInt32: + return (value < 0x100000000LL); + case BfTypeCode_UInt64: + return true; + default: break; + } + + return false; +} + BfParser* BfSystem::CreateParser(BfProject* bfProject) { AutoCrit crit(mDataLock); @@ -3693,10 +3723,14 @@ void BfSystem::RemoveOldData() { AutoCrit autoCrit(mDataLock); - for (auto typeDef : mTypeDefDeleteQueue) + for (int i = 0; i < (int)mTypeDefDeleteQueue.size(); i++) + { + auto typeDef = mTypeDefDeleteQueue[i]; + mTypeDefDeleteQueue[i] = NULL; + BfLogSys(this, "RemoveOldData deleting from mTypeDefDeleteQueue %p\n", typeDef); delete typeDef; + } mTypeDefDeleteQueue.Clear(); - if (!mProjectDeleteQueue.IsEmpty()) { @@ -3729,6 +3763,7 @@ void BfSystem::RemoveOldData() void BfSystem::VerifyTypeDef(BfTypeDef* typeDef) { +#if defined _DEBUG && false auto _FindTypeDef = [&](BfTypeReference* typeRef) { if (auto directStrTypeRef = BfNodeDynCast(typeRef)) @@ -3762,6 +3797,7 @@ void BfSystem::VerifyTypeDef(BfTypeDef* typeDef) _FindTypeDef(paramDef->mTypeRef); } } +#endif } BfTypeOptions* BfSystem::GetTypeOptions(int optionsIdx) diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index 42a35340..1a4fbd93 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -1610,6 +1610,7 @@ public: void CreateBasicTypes(); bool DoesLiteralFit(BfTypeCode typeCode, int64 value); + bool DoesLiteralFit(BfTypeCode typeCode, uint64 value); BfParser* CreateParser(BfProject* bfProject); BfCompiler* CreateCompiler(bool isResolveOnly); BfProject* GetProject(const StringImpl& projName); diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index 0b9384c3..b8054687 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -1288,7 +1288,7 @@ void CeBuilder::Build() auto methodInstance = mCeFunction->mMethodInstance; if (methodInstance != NULL) - { + { BfMethodInstance dupMethodInstance; dupMethodInstance.CopyFrom(methodInstance); auto methodDef = methodInstance->mMethodDef; @@ -1638,10 +1638,10 @@ void CeBuilder::Build() EmitBinaryOp(CeOp_Shl_I8, CeOp_InvalidOp, ceLHS, ceRHS, result); break; case BeBinaryOpKind_RightShift: - EmitBinaryOp(CeOp_Shr_I8, CeOp_InvalidOp, ceLHS, ceRHS, result); + EmitBinaryOp(CeOp_Shr_U8, CeOp_InvalidOp, ceLHS, ceRHS, result); break; case BeBinaryOpKind_ARightShift: - EmitBinaryOp(CeOp_Shr_U8, CeOp_InvalidOp, ceLHS, ceRHS, result); + EmitBinaryOp(CeOp_Shr_I8, CeOp_InvalidOp, ceLHS, ceRHS, result); break; default: Fail("Invalid binary op"); @@ -2476,7 +2476,18 @@ void CeBuilder::Build() EmitFrameOffset(ceSize); } break; + case BfIRIntrinsic_MemSet: + { + CeOperand ceDestPtr = GetOperand(castedInst->mArgs[0].mValue); + CeOperand ceValue = GetOperand(castedInst->mArgs[1].mValue); + CeOperand ceSize = GetOperand(castedInst->mArgs[2].mValue); + Emit(CeOp_MemSet); + EmitFrameOffset(ceDestPtr); + EmitFrameOffset(ceValue); + EmitFrameOffset(ceSize); + } + break; case BfIRIntrinsic_AtomicFence: // Nothing to do @@ -4729,6 +4740,17 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* return false; } } + else if (checkFunction->mFunctionKind == CeFunctionKind_EmitAddInterface) + { + int32 typeId = *(int32*)((uint8*)stackPtr); + int32 ifaceTypeId = *(int32*)((uint8*)stackPtr + sizeof(int32)); + if ((mCurEmitContext == NULL) || (mCurEmitContext->mType->mTypeId != typeId)) + { + _Fail("Code cannot be emitted for this type in this context"); + return false; + } + mCurEmitContext->mInterfaces.Add(ifaceTypeId); + } else if (checkFunction->mFunctionKind == CeFunctionKind_EmitMethodEntry) { int64 methodHandle = *(int64*)((uint8*)stackPtr); @@ -4972,7 +4994,12 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* if (*fastFinishPtr) { if (*cancelingPtr) - _Fail("Comptime evaluation canceled"); + { + if ((mCurModule != NULL) && (mCurModule->mCurTypeInstance != NULL)) + mCurModule->DeferRebuildType(mCurModule->mCurTypeInstance); + else + _Fail("Comptime evaluation canceled"); + } return false; } @@ -6817,6 +6844,10 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction) { ceFunction->mFunctionKind = CeFunctionKind_EmitTypeBody; } + if (methodDef->mName == "Comptime_EmitAddInterface") + { + ceFunction->mFunctionKind = CeFunctionKind_EmitAddInterface; + } else if (methodDef->mName == "Comptime_EmitMethodEntry") { ceFunction->mFunctionKind = CeFunctionKind_EmitMethodEntry; diff --git a/IDEHelper/Compiler/CeMachine.h b/IDEHelper/Compiler/CeMachine.h index 071b1e4a..ca36f4b0 100644 --- a/IDEHelper/Compiler/CeMachine.h +++ b/IDEHelper/Compiler/CeMachine.h @@ -289,6 +289,7 @@ enum CeFunctionKind CeFunctionKind_Method_GetParamInfo, CeFunctionKind_EmitTypeBody, + CeFunctionKind_EmitAddInterface, CeFunctionKind_EmitMethodEntry, CeFunctionKind_EmitMethodExit, CeFunctionKind_EmitMixin, @@ -687,6 +688,7 @@ class CeEmitContext public: BfType* mType; BfMethodInstance* mMethodInstance; + Array mInterfaces; String mEmitData; String mExitEmitData; bool mFailed; @@ -697,6 +699,11 @@ public: mMethodInstance = NULL; mFailed = false; } + + bool HasEmissions() + { + return !mEmitData.IsEmpty() || !mInterfaces.IsEmpty(); + } }; class CeContext diff --git a/IDEHelper/Tests/src/Comptime.bf b/IDEHelper/Tests/src/Comptime.bf index 385b6fa7..453e39c7 100644 --- a/IDEHelper/Tests/src/Comptime.bf +++ b/IDEHelper/Tests/src/Comptime.bf @@ -174,7 +174,61 @@ namespace Tests public float mY; public float mZ; } + + class SerializationContext + { + public String mStr = new String() ~ delete _; + public void Serialize(String name, T val) where T : struct + { + mStr.AppendF($"{name} {val}\n"); + } + } + interface ISerializable + { + void Serialize(SerializationContext ctx); + } + + [AttributeUsage(.Enum | .Struct | .Class, .NotInherited | .ReflectAttribute | .DisallowAllowMultiple)] + struct SerializableAttribute : Attribute, IComptimeTypeApply + { + [Comptime] + public void ApplyToType(Type type) + { + const String SERIALIZE_NAME = "void ISerializable.Serialize(SerializationContext ctx)\n"; + + String serializeBuffer = new .(); + + Compiler.Assert(!type.IsUnion); + + for (let field in type.GetFields()) + { + if (!field.IsInstanceField || field.DeclaringType != type) + continue; + + serializeBuffer.AppendF($"\n\tctx.Serialize(\"{field.Name}\", {field.Name});"); + } + + Compiler.EmitTypeBody(type, scope $"{SERIALIZE_NAME}{{{serializeBuffer}\n}}\n"); + Compiler.EmitAddInterface(type, typeof(ISerializable)); + } + } + + [Serializable] + struct Foo : this(float x, float y) + { + } + + public class ComponentHandler + where T : struct + { + uint8* data; + protected override void GCMarkMembers() + { + T* ptr = (T*)data; + GC.Mark!((*ptr)); + } + } [Test] public static void TestBasics() { @@ -209,6 +263,12 @@ namespace Tests 4 mY 8 mZ """); + + Foo bar = .(10, 2); + ISerializable iSer = bar; + SerializationContext serCtx = scope .(); + iSer.Serialize(serCtx); + Test.Assert(serCtx.mStr == "x 10\ny 2\n"); } } } diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp index c2d98fa6..247f57e9 100644 --- a/IDEHelper/WinDebugger.cpp +++ b/IDEHelper/WinDebugger.cpp @@ -8415,7 +8415,7 @@ void WinDebugger::HandleCustomExpandedItems(String& retVal, DbgCompileUnit* dbgC evalStr += ", refid=\"" + referenceId + ".[]\""; if (isReadOnly) evalStr += ", ne"; - retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)sizeValue.GetInt64() / dimSize1, 50000) + + retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64() / dimSize1, 50000) + "\t[{0}]\t" + evalStr; } else if (lowerDimSizes.size() == 2) @@ -8431,7 +8431,7 @@ void WinDebugger::HandleCustomExpandedItems(String& retVal, DbgCompileUnit* dbgC evalStr += ", refid=\"" + referenceId + ".[]\""; if (isReadOnly) evalStr += ", ne"; - retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)sizeValue.GetInt64() / dimSize1 / dimSize2, 50000) + + retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64() / dimSize1 / dimSize2, 50000) + "\t[{0}]\t" + evalStr; } } @@ -8449,7 +8449,7 @@ void WinDebugger::HandleCustomExpandedItems(String& retVal, DbgCompileUnit* dbgC evalStr += ", refid=\"" + referenceId + ".[]\""; if (isReadOnly) evalStr += ", ne"; - retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)sizeValue.GetInt64() / dimSize1 / dimSize2 / dimSize3, 50000) + + retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64() / dimSize1 / dimSize2 / dimSize3, 50000) + "\t[{0}]\t" + evalStr; } } @@ -8459,7 +8459,7 @@ void WinDebugger::HandleCustomExpandedItems(String& retVal, DbgCompileUnit* dbgC evalStr += ", refid=\"" + referenceId + ".[]\""; if (isReadOnly) evalStr += ", ne"; - retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)sizeValue.GetInt64(), 50000) + + retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64(), 50000) + "\t[{0}]\t" + evalStr; } } @@ -8476,7 +8476,7 @@ void WinDebugger::HandleCustomExpandedItems(String& retVal, DbgCompileUnit* dbgC evalStr += ", refid=\"" + referenceId + ".[]\""; if (isReadOnly) evalStr += ", ne"; - retVal += "\n:repeat" + StrFormat("\t%d\t%d\t%d", 0, (int)sizeValue.GetInt64(), 50000) + + retVal += "\n:repeat" + StrFormat("\t%d\t%lld\t%d", 0, sizeValue.GetInt64(), 50000) + "\t[{0}]\t" + evalStr; } }