Przeglądaj źródła

Beefy::String changes, lambda hotswap fixes

Changed some string internals related to StringViewsma
Added an "incompatible capture" error for lambdas when the captures change
Brian Fiete 6 lat temu
rodzic
commit
2f01cc14dd

+ 62 - 6
BeefTools/BeefInstall/StubUI/src/Board.bf

@@ -4,6 +4,7 @@ using Beefy.gfx;
 using System.Diagnostics;
 using System;
 using System.IO;
+using System.Threading;
 
 namespace BIStubUI
 {
@@ -202,10 +203,13 @@ namespace BIStubUI
 		float mEatPct;
 
 		int mCloseTicks;
+		int mInstallTicks;
 		public bool mIsClosed;
+		bool mUninstallDone;
 
 		public bool mInstalling;
-		public float mInstallPct = 0.5f;
+		public bool mUninstalling;
+		public float mInstallPct;
 
 		public this()
 		{
@@ -256,7 +260,7 @@ namespace BIStubUI
 			mCancelButton.mImageHi = Images.sButtonHi;
 			mCancelButton.mOnMouseClick.Add(new (mouseArgs) =>
 				{
-					gApp.mCancelling = true;
+					//gApp.mCancelling = true;
 				});
 			mCancelButton.mMouseInsets = new Insets(4, 4, 4, 4);
 			AddWidget(mCancelButton);
@@ -289,6 +293,12 @@ namespace BIStubUI
 			mInstallPathBox.mInstallPath.Append(@"\BeefLang");
 		}
 
+		void Uninstall()
+		{
+			Thread.Sleep(10000);
+			mUninstallDone = true;
+		}
+
 		void StartInstall()
 		{
 			mInstalling = true;
@@ -320,13 +330,15 @@ namespace BIStubUI
 
 			if (mInstalling)
 			{
-				float installDiv = 1000.0f;
+				//float installDiv = 1000.0f;
 				//mInstallPct = (mUpdateCnt % installDiv) / installDiv;
 
 				//mInstallPct = 1.0f;
 
 				float totalWidth = 410;
 				float fillWidth = totalWidth * (mInstallPct*0.9f + 0.1f);
+				if (mUninstalling)
+					fillWidth = totalWidth * mInstallPct;
 
 				float barX = 200;
 				float barY = 280;
@@ -338,15 +350,19 @@ namespace BIStubUI
 
 					Color colorLeft = 0x800288E9;
 					Color colorRight = 0x80FFFFFF;
+					if (gApp.mClosing)
+					{
+						colorLeft = 0x80000000;
+						colorRight = 0x800288E9;
+					}
 					g.FillRectGradient(barX, barY, fillWidth, barHeight, colorLeft, colorRight, colorLeft, colorRight);
 
-					float pct = (mUpdateCnt % 60) / 60.0f;
-
+					float barPct = (mInstallTicks % 60) / 60.0f;
 					for (int i = 0; i < 16; i++)
 					{
 						Images.sPBBarHilite.mPixelSnapping = .Never;
 						using (g.PushColor(0x22FFFFFF))
-							g.Draw(Images.sPBBarHilite, barX - 16 - totalWidth + fillWidth + (i + pct) * 26, barY + 6);
+							g.Draw(Images.sPBBarHilite, barX - 16 - totalWidth + fillWidth + (i + barPct) * 26, barY + 6);
 					}
 
 					g.DrawButton(Images.sPBBarEmpty, barX + fillWidth - 30, barY + 5, totalWidth - fillWidth + 40);
@@ -395,6 +411,15 @@ namespace BIStubUI
 				base.DrawAll(g);
 		}
 
+		public bool IsDecompressing
+		{
+			get
+			{
+				//return gApp.
+				return false;
+			}
+		}
+
 		public override void Update()
 		{
 			base.Update();
@@ -403,6 +428,34 @@ namespace BIStubUI
 
 			if (gApp.mClosing)
 			{
+				mCancelButton.mDisabled = true;
+				mCancelButton.mMouseVisible = false;
+
+				if ((mInstalling) && (!mUninstallDone))
+				{
+					if ((!IsDecompressing) && (!mUninstalling))
+					{
+						mUninstalling = true;
+						ThreadPool.QueueUserWorkItem(new => Uninstall);
+					}
+					mInstallTicks--;
+					if (mInstallTicks < 0)
+						mInstallTicks = 0x3FFFFFFF;
+				}
+
+				if (mInstallPct > 0)
+				{
+					mInstallPct = (mInstallPct * 0.98f) - 0.003f;
+					if (!mUninstallDone)
+						mInstallPct = Math.Max(mInstallPct, 0.1f);
+					return;
+				}
+
+				if ((mInstalling) && (!mUninstallDone))
+				{
+					return;
+				}
+
 				if (mCloseTicks == 0)
 				{
 					gApp.mSoundManager.PlaySound(Sounds.sAbort);
@@ -427,6 +480,9 @@ namespace BIStubUI
 				return;
 			}
 
+			if (mInstalling)
+				mInstallTicks++;
+
 			if (mUpdateCnt == 1)
 				gApp.mSoundManager.PlaySound(Sounds.sBoing);
 

+ 9 - 0
BeefySysLib/util/Dictionary.h

@@ -483,6 +483,15 @@ public:
 		return true;
 	}
 
+// 	template <typename TAltKey>
+// 	bool TryAddWith(const TAltKey& key, TKey** keyPtr, TValue** valuePtr)
+// 	{
+// 		if (!Insert(key, false, keyPtr, valuePtr))
+// 			return false;
+// 		new (*valuePtr) TValue();
+// 		return true;
+// 	}
+
 	// Returns uninitialized valuePtr - must use placement new
 	bool TryAddRaw(const TKey& key, TKey** keyPtr, TValue** valuePtr)
 	{

+ 1 - 1
BeefySysLib/util/PerfTimer.h

@@ -157,7 +157,7 @@ public:
 	String mName;
 
 public:
-	DebugTimeGuard(int maxTicks, const StringImpl& name = "DebugTimeGuard")
+	DebugTimeGuard(int maxTicks, const StringImpl& name = StringImpl::MakeRef("DebugTimeGuard"))
 	{
 		mName = name;
 		mMaxTicks = maxTicks;

+ 45 - 10
BeefySysLib/util/String.cpp

@@ -129,6 +129,16 @@ String Beefy::operator+(const StringImpl& lhs, const StringView& rhs)
 	return str;
 }
 
+String Beefy::operator+(const StringImpl& lhs, const char* rhs)
+{
+	String str;
+	int rhsLen = (int)strlen(rhs);
+	str.Reserve(lhs.mLength + rhsLen + 1);
+	str.Append(lhs);
+	str.Append(rhs, rhsLen);
+	return str;
+}
+
 String Beefy::operator+(const StringImpl& lhs, char rhs)
 {
 	String str;
@@ -138,6 +148,26 @@ String Beefy::operator+(const StringImpl& lhs, char rhs)
 	return str;
 }
 
+String Beefy::operator+(const char* lhs, const StringImpl& rhs)
+{
+	String str;
+	int lhsLen = (int)strlen(lhs);
+	str.Reserve(rhs.mLength + lhsLen + 1);
+	str.Append(lhs, lhsLen);
+	str.Append(rhs);
+	return str;
+}
+
+String Beefy::operator+(const char* lhs, const StringView& rhs)
+{
+	String str;
+	int lhsLen = (int)strlen(lhs);
+	str.Reserve(rhs.mLength + lhsLen + 1);
+	str.Append(lhs, lhsLen);
+	str.Append(rhs);
+	return str;
+}
+
 bool Beefy::operator==(const char* lhs, const StringImpl& rhs)
 {
 	return rhs == lhs;
@@ -363,6 +393,11 @@ intptr StringImpl::CompareOrdinalHelper(const StringImpl & strA, intptr indexA,
 	return CompareOrdinalHelper(strA.GetPtr() + indexA, lengthA, strB.GetPtr() + indexB, lengthB);
 }
 
+void StringImpl::Append(const char* appendPtr)
+{
+	Append(appendPtr, (int)strlen(appendPtr));
+}
+
 void StringImpl::Append(const char* appendPtr, intptr length)
 {
 	intptr newCurrentIndex = mLength + length;
@@ -538,7 +573,7 @@ intptr StringImpl::Compare(const StringImpl & strA, intptr indexA, const StringI
 	return CompareOrdinalHelper(strA, indexA, lengthA, strB, indexB, lengthB);
 }
 
-void StringImpl::ReplaceLargerHelper(const StringImpl & find, const StringImpl & replace)
+void StringImpl::ReplaceLargerHelper(const StringView& find, const StringView& replace)
 {
 	Array<int> replaceEntries;
 
@@ -546,7 +581,7 @@ void StringImpl::ReplaceLargerHelper(const StringImpl & find, const StringImpl &
 
 	for (int startIdx = 0; startIdx < mLength - find.mLength; startIdx++)
 	{
-		if (EqualsHelper(GetPtr() + startIdx, find.GetPtr(), find.mLength))
+		if (EqualsHelper(GetPtr() + startIdx, find.mPtr, find.mLength))
 		{
 			replaceEntries.Add(startIdx);
 			startIdx += find.mLength - 1;
@@ -561,7 +596,7 @@ void StringImpl::ReplaceLargerHelper(const StringImpl & find, const StringImpl &
 	if (needSize > GetAllocSize())
 		Realloc((int_strsize)needSize);
 
-	auto replacePtr = replace.GetPtr();
+	auto replacePtr = replace.mPtr;
 	auto ptr = GetMutablePtr();
 
 	intptr lastDestStartIdx = destLength;
@@ -585,7 +620,7 @@ void StringImpl::ReplaceLargerHelper(const StringImpl & find, const StringImpl &
 	mLength = (int_strsize)destLength;
 }
 
-void StringImpl::Replace(const StringImpl & find, const StringImpl & replace)
+void StringImpl::Replace(const StringView& find, const StringView & replace)
 {
 	if (replace.mLength > find.mLength)
 	{
@@ -594,8 +629,8 @@ void StringImpl::Replace(const StringImpl & find, const StringImpl & replace)
 	}
 
 	auto ptr = GetMutablePtr();
-	auto findPtr = find.GetPtr();
-	auto replacePtr = replace.GetPtr();
+	auto findPtr = find.mPtr;
+	auto replacePtr = replace.mPtr;
 
 	int_strsize inIdx = 0;
 	int_strsize outIdx = 0;
@@ -693,7 +728,7 @@ bool StringImpl::HasMultibyteChars()
 	return false;
 }
 
-intptr StringImpl::IndexOf(const StringImpl& subStr, bool ignoreCase) const
+intptr StringImpl::IndexOf(const StringView& subStr, bool ignoreCase) const
 {
 	for (intptr ofs = 0; ofs <= mLength - subStr.mLength; ofs++)
 	{
@@ -704,15 +739,15 @@ intptr StringImpl::IndexOf(const StringImpl& subStr, bool ignoreCase) const
 	return -1;
 }
 
-intptr StringImpl::IndexOf(const StringImpl& subStr, int32 startIdx) const
+intptr StringImpl::IndexOf(const StringView& subStr, int32 startIdx) const
 {
 	return IndexOf(subStr, (int64)startIdx);
 }
 
-intptr StringImpl::IndexOf(const StringImpl& subStr, int64 startIdx) const
+intptr StringImpl::IndexOf(const StringView& subStr, int64 startIdx) const
 {
 	const char* ptr = GetPtr();
-	const char* subStrPtr = subStr.GetPtr();
+	const char* subStrPtr = subStr.mPtr;
 	for (intptr ofs = (intptr)startIdx; ofs <= mLength - subStr.mLength; ofs++)
 	{
 		if (strncmp(ptr + ofs, subStrPtr, subStr.mLength) == 0)

+ 52 - 14
BeefySysLib/util/String.h

@@ -33,6 +33,12 @@ public:
 	StringView(const StringImpl& str, int offset);
 	StringView(const StringImpl& str, int offset, int length);
 
+	StringView(const char* ptr)
+	{
+		this->mPtr = ptr;
+		this->mLength = (int)strlen(ptr);
+	}
+
 	StringView(const char* ptr, int length)
 	{
 		this->mPtr = ptr;
@@ -54,6 +60,13 @@ public:
 		return *this;
 	}
 
+	StringView& operator=(const char* str)
+	{
+		this->mPtr = str;
+		this->mLength = (int)strlen(str);
+		return *this;
+	}
+
 	bool operator==(const StringImpl& strB) const;
 
 	bool operator!=(const StringImpl& strB) const;
@@ -339,6 +352,26 @@ protected:
 
 
 public:	
+	static StringImpl MakeRef(const char* charPtr)
+	{
+		StringImpl str;
+		// This is just a ref - called when we pass a literal to a method (for example)
+		str.mPtr = (char*)charPtr;
+		str.mLength = (int_strsize)strlen(charPtr);
+		str.mAllocSizeAndFlags = str.mLength | StrPtrFlag;
+		return str;
+	}	
+
+	static StringImpl MakeRef(const StringView& strView)
+	{
+		StringImpl str;
+		// This is just a ref - called when we pass a literal to a method (for example)
+		str.mPtr = (char*)strView.mPtr;
+		str.mLength = (int_strsize)strView.mLength;
+		str.mAllocSizeAndFlags = str.mLength | StrPtrFlag;
+		return str;
+	}
+
 	StringImpl(const char* charPtr)
 	{
 		// This is just a ref - called when we pass a literal to a method (for example)
@@ -617,6 +650,7 @@ public:
 	static String CreateReference(const StringView& strView);
 	void Reserve(intptr newSize);
 
+	void Append(const char* appendPtr);
 	void Append(const char* appendPtr, intptr length);	
 	void Append(const StringView& str);	
 	void Append(const StringImpl& str);
@@ -672,22 +706,22 @@ public:
 		return EqualsHelper(a.GetPtr(), b.GetPtr(), a.mLength);
 	}
 
-	bool StartsWith(const StringImpl& b, CompareKind comparisonType = CompareKind_Ordinal) const
+	bool StartsWith(const StringView& b, CompareKind comparisonType = CompareKind_Ordinal) const
 	{
 		if (this->mLength < b.mLength)
 			return false;
 		if (comparisonType == CompareKind_OrdinalIgnoreCase)
-			return EqualsIgnoreCaseHelper(this->GetPtr(), b.GetPtr(), b.mLength);
-		return EqualsHelper(this->GetPtr(), b.GetPtr(), b.mLength);
+			return EqualsIgnoreCaseHelper(this->GetPtr(), b.mPtr, b.mLength);
+		return EqualsHelper(this->GetPtr(), b.mPtr, b.mLength);
 	}
 
-	bool EndsWith(const StringImpl& b, CompareKind comparisonType = CompareKind_Ordinal) const
+	bool EndsWith(const StringView& b, CompareKind comparisonType = CompareKind_Ordinal) const
 	{
 		if (this->mLength < b.mLength)
 			return false;
 		if (comparisonType == CompareKind_OrdinalIgnoreCase)
-			return EqualsIgnoreCaseHelper(this->GetPtr() + this->mLength - b.mLength, b.GetPtr(), b.mLength);
-		return EqualsHelper(this->GetPtr() + this->mLength - b.mLength, b.GetPtr(), b.mLength);
+			return EqualsIgnoreCaseHelper(this->GetPtr() + this->mLength - b.mLength, b.mPtr, b.mLength);
+		return EqualsHelper(this->GetPtr() + this->mLength - b.mLength, b.mPtr, b.mLength);
 	}
 
 	bool StartsWith(char c) const
@@ -704,8 +738,8 @@ public:
 		return GetPtr()[this->mLength - 1] == c;
 	}
 
-	void ReplaceLargerHelper(const StringImpl& find, const StringImpl& replace);	
-	void Replace(const StringImpl& find, const StringImpl& replace);	
+	void ReplaceLargerHelper(const StringView& find, const StringView& replace);
+	void Replace(const StringView& find, const StringView& replace);
 	void TrimEnd();	
 	void TrimStart();	
 	void Trim();	
@@ -721,9 +755,9 @@ public:
 	}
 
 	bool HasMultibyteChars();
-	intptr IndexOf(const StringImpl& subStr, bool ignoreCase = false) const;
-	intptr IndexOf(const StringImpl& subStr, int32 startIdx) const;
-	intptr IndexOf(const StringImpl& subStr, int64 startIdx) const;
+	intptr IndexOf(const StringView& subStr, bool ignoreCase = false) const;
+	intptr IndexOf(const StringView& subStr, int32 startIdx) const;
+	intptr IndexOf(const StringView& subStr, int64 startIdx) const;	
 	intptr IndexOf(char c, intptr startIdx = 0) const;	
 	intptr LastIndexOf(char c) const;	
 	intptr LastIndexOf(char c, intptr startCheck) const;
@@ -732,8 +766,8 @@ public:
 	{
 		return IndexOf(c) != -1;
 	}
-
-	bool Contains(const StringImpl& str) const
+	
+	bool Contains(const StringView& str) const
 	{
 		return IndexOf(str) != -1;
 	}
@@ -909,7 +943,7 @@ public: \
 	using StringImpl::StringImpl; \
 	using StringImpl::operator=; \
 	StringT() { mPtr = NULL; mLength = 0; mAllocSizeAndFlags = 0; } \
-	StringT(const char* str) { Init(str, (int_strsize)strlen(str)); } \
+	explicit StringT(const char* str) { Init(str, (int_strsize)strlen(str)); } \
 	StringT(const std::string& str) { Init(str.c_str(), (int_strsize)str.length()); } \
 	StringT(const StringImpl& str) : StringImpl(str) {} \
 	StringT(StringImpl&& str) : StringImpl(std::move(str)) {} \
@@ -927,7 +961,11 @@ BF_SPECIALIZE_STR(6)
 
 String operator+(const StringImpl& lhs, const StringImpl& rhs);
 String operator+(const StringImpl& lhs, const StringView& rhs);
+String operator+(const StringImpl& lhs, const char* rhs);
 String operator+(const StringImpl& lhs, char rhs);
+String operator+(const char* lhs, const StringImpl& rhs);
+String operator+(const char* lhs, const StringView& rhs);
+
 bool operator==(const char* lhs, const StringImpl& rhs);
 bool operator!=(const char* lhs, const StringImpl& rhs);
 // bool operator==(const StringView& lhs, const StringImpl& rhs);

+ 1 - 1
IDE/BeefProj.toml

@@ -27,7 +27,7 @@ TargetDirectory = "$(WorkspaceDir)/dist"
 TargetName = "BeefIDE_d"
 OtherLinkFlags = "$(LinkFlags) Comdlg32.lib kernel32.lib user32.lib advapi32.lib shell32.lib IDEHelper64_d.lib"
 CLibType = "Dynamic"
-DebugCommandArguments = "-proddir=C:\\Beef\\IDE\\Tests\\Test1 -platform=Win32 -test=scripts\\Break.txt -testNoExit"
+DebugCommandArguments = "-proddir=C:\\Beef\\IDE\\Tests\\Test1 -test=scripts\\HotSwap_Lambdas01.txt -testNoExit"
 DebugWorkingDirectory = "c:\\Beef\\IDE\\Tests\\EmptyTest"
 EnvironmentVars = ["_NO_DEBUG_HEAP=1"]
 

+ 28 - 0
IDE/Tests/Test1/scripts/HotSwap_Lambdas01.txt

@@ -0,0 +1,28 @@
+# This tests modifying anonymous lambdas, including changing captured data (both legally and illegally)
+
+ShowFile("src/HotSwap_Lambdas01.bf")
+GotoText("//Test_Start")
+ToggleBreakpoint()
+RunWithCompiling()
+
+StepOver()
+StepOver()
+StepOver()
+StepOver()
+AssertEvalEquals("val0", "200")
+AssertEvalEquals("val1", "423")
+AssertEvalEquals("val2", "757")
+
+ToggleCommentAt("Dlg0_0")
+ToggleCommentAt("Dlg1_0")
+ToggleCommentAt("Dlg2_0")
+Compile()
+SetExpectError("incompatible captures")
+StepOver()
+ExpectError()
+StepOut()
+StepOver()
+StepOver()
+AssertEvalEquals("val0", "200")
+AssertEvalEquals("val1", "300")
+AssertEvalEquals("val2", "523")

+ 62 - 0
IDE/Tests/Test1/src/HotSwap_Lambdas01.bf

@@ -0,0 +1,62 @@
+#pragma warning disable 168
+
+namespace IDETest
+{
+	class HotSwap_Lambdas01
+	{
+		class ClassA
+		{
+			public delegate int() mDlg0 ~ delete _;
+			public delegate int() mDlg1 ~ delete _;
+			public delegate int() mDlg2 ~ delete _;
+
+			int mA = 123;
+
+			public this()
+			{
+				int val = 234;
+
+				mDlg0 = new () =>
+				{
+					int ret = 200;
+					/*Dlg0_0
+					ret += mA;
+					*/
+					return ret;
+				};
+
+				mDlg1 = new () =>
+				{
+					int ret = 300;
+					//*Dlg1_0
+					ret += mA;
+					/*@*/
+					return ret;
+				};
+
+				mDlg2 = new () =>
+				{
+					int ret = 400;
+					//*Dlg2_0
+					ret += val;
+					/*@*/
+					ret += mA;
+					return ret;
+				};
+			}
+		}
+
+		public static void Test()
+		{
+			//Test_Start
+			ClassA ca = scope .();
+			int val0 = ca.mDlg0();
+			int val1 = ca.mDlg1();
+			int val2 = ca.mDlg2();
+
+			val0 = ca.mDlg0();
+			val1 = ca.mDlg1();
+			val2 = ca.mDlg2();
+		}
+	}
+}

+ 1 - 0
IDE/Tests/Test1/src/Program.bf

@@ -14,6 +14,7 @@ namespace IDETest
 			HotSwap_Data.Test();
 			HotSwap_GetUnusued.Test();
 			HotSwap_Interfaces2.Test();
+			HotSwap_Lambdas01.Test();
 			HotSwap_LocateSym01.Test();
 			HotSwap_Reflection.Test();
 			HotSwap_TLS.Test();

+ 0 - 1
IDE/mintest/BeefSpace.toml

@@ -10,7 +10,6 @@ IntermediateType = "ObjectAndIRCode"
 ConfigSelections = {mintest2 = {Enabled = false}}
 
 [Configs.Debug.Win64]
-BfOptimizationLevel = "O0"
 IntermediateType = "ObjectAndIRCode"
 COptimizationLevel = "Og"
 

+ 24 - 72
IDE/mintest/src/main3.bf

@@ -87,87 +87,39 @@ struct TestStruct
 	public int mB;
 }
 
-class Blurg
+class Bloozer
 {
-	[Export, CLink, StdCall]
-	public static void Poof()
-	{
-		PrintF("Poofs!\n");
-	}
-
-	[Export, CLink, StdCall]
-	public static void Poof2()
-	{
-		PrintF("Poofs2!\n");
-	}
+	int mA;
+}
 
-	static void Test0()
+class Blurg
+{
+	delegate void() mFuncA;
+	delegate void() mFuncB;
+	
+	public static void Hey()
 	{
-		Snorf sn = scope .();
-		int a = 124;
-	}
+		int a = 123;
+		Blurg blurg = scope .();
+		blurg.mFuncA = new () =>
+		{
+			PrintF("YoA!\n");
+			PrintF("A %d!\n", a);
+			PrintF("Blurg: %p\n", blurg);
+		};
 
+		blurg.mFuncB = new () =>
+		{
+			PrintF("YoB!\n");
+		};
 
-	public void DoRecurse(int depth, ref int val)
-	{
-		Thread.Sleep(1);
-		++val;
-		if (val < 5)
+		while (true)
 		{
-			DoRecurse(depth + 1, ref val);
+			blurg.mFuncA();
+			blurg.mFuncB();
 		}
 	}
 
-	public static void VoidCall()
-	{
-		
-	}
-
-	public static int GetInt()
-	{
-		float f = 10.0f;
-		//f = f % 3.0f;
-		
-		return 123;
-	}
-
-	public static void Hey()
-	{
-		float f = 1.2f;
-		//f = f % 2.3f;
-
-		TestStruct ts = .();
-		//int val = ts..mA;
-		ts.mA = 123;
-
-		GetInt();
-		GetInt();
-
-		VoidCall();
-		int val0 = GetInt();
-
-		Blurg bl = scope .();
-		int val = 0;
-		bl.DoRecurse(0, ref val);
-
-		Test0();
-		Test0();
-		Test0();
-		Test0();
-
-		Result<void*> voidPtrResult = default;
-
-		//void* val = voidPtrResult;
-
-
-		//Result<TestStruct> ts = .Ok(.());
-
-		//let val = ts.Get();
-		//Snorf.Bloog bl = .();
-
-		//Poof();
-	}
-
 }
 
 class CustomAlloc

+ 2 - 3
IDE/src/IDEApp.bf

@@ -3051,7 +3051,7 @@ namespace IDE
 
 			if (mRunningTestScript)
 			{
-				if ((mScriptManager.mExpectingError != null) && (text.Contains(mScriptManager.mExpectingError)))
+				if (mScriptManager.IsErrorExpected(text))
 				{
 					DeleteAndNullify!(mScriptManager.mExpectingError);
 					OutputLine("Received expected error: {0}", text);
@@ -10479,7 +10479,7 @@ namespace IDE
                     }
                     else if (cmd == "error")
                     {
-						if ((mRunningTestScript) && (!IsCrashDump))
+						if ((mRunningTestScript) && (!IsCrashDump) && (!mScriptManager.IsErrorExpected(param)))
 							mScriptManager.Fail(param);
 
 						bool isFirstMsg = true;
@@ -10553,7 +10553,6 @@ namespace IDE
 								if (isFirstMsg)
 								{
 									OutputLineSmart(scope String("ERROR: ", errorMsg));
-									Fail(errorMsg);
 									isFirstMsg = false;
 								}
 								else

+ 5 - 0
IDE/src/ScriptManager.bf

@@ -177,6 +177,11 @@ namespace IDE
 			//gApp.mRunningTestScript = false;
 		}
 
+		public bool IsErrorExpected(StringView err)
+		{
+			return (mExpectingError != null) && (err.Contains(mExpectingError));
+		}
+
 		public void Fail(StringView fmt, params Object[] args)
 		{
 			Fail(scope String()..AppendF(fmt, params args));

+ 1 - 1
IDEHelper/COFF.h

@@ -405,7 +405,7 @@ namespace std
 	{
 		size_t operator()(const NS_BF_DBG::CvModuleInfoNameEntry& val) const
 		{			
-			return NS_BF_DBG::CvModuleInfoNameEntry::GetHashCode(val.mModuleInfo->mModuleName);
+			return NS_BF_DBG::CvModuleInfoNameEntry::GetHashCode(Beefy::StringImpl::MakeRef(val.mModuleInfo->mModuleName));
 		}
 	};
 }

+ 9 - 7
IDEHelper/Compiler/BfExprEvaluator.cpp

@@ -9553,7 +9553,9 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
 	mModule->mBfIRBuilder->PopulateType(useTypeInstance);
 	mModule->PopulateType(useTypeInstance);
 
-	methodDef->mIsStatic = closureTypeInst == NULL;
+	// If we are allowing hot swapping, we need to always mangle the name to non-static because if we add a capture
+	//  later then we need to have the mangled names match
+	methodDef->mIsStatic = (closureTypeInst == NULL) && (!mModule->mCompiler->mOptions.mAllowHotSwapping);
 
 	SizedArray<BfIRType, 3> newTypes;
 	SizedArray<BfIRType, 8> origParamTypes;
@@ -9598,12 +9600,12 @@ BfLambdaInstance* BfExprEvaluator::GetLambdaInstance(BfLambdaBindExpression* lam
 		methodDef->mName.RemoveToEnd(prevSepPos);
 	}
 
-	if (closureTypeInst != NULL)
-	{
-		StringT<128> typeInstName;
-		BfMangler::Mangle(typeInstName,mModule->mCompiler->GetMangleKind(), closureTypeInst);
-		closureHashCtx.MixinStr(typeInstName);
-	}
+// 	if (closureTypeInst != NULL)
+// 	{
+// 		StringT<128> typeInstName;
+// 		BfMangler::Mangle(typeInstName,mModule->mCompiler->GetMangleKind(), closureTypeInst);
+// 		closureHashCtx.MixinStr(typeInstName);
+// 	}
 
 	auto checkMethodState = mModule->mCurMethodState;
 	while (checkMethodState != NULL)

+ 5 - 6
IDEHelper/Compiler/BfModule.cpp

@@ -778,11 +778,7 @@ BfModule* gLastCreatedModule = NULL;
 BfModule::BfModule(BfContext* context, const StringImpl& moduleName)	
 {
 	BfLogSys(context->mSystem, "BfModule::BFModule %p %s\n", this, moduleName.c_str());
-	if (moduleName.Contains("_Comparison_"))
-	{
-		NOP;
-	}
-
+	
 	gLastCreatedModule = this;
 
 	mContext = context;
@@ -14930,7 +14926,10 @@ void BfModule::ProcessMethod_ProcessDeferredLocals(int startIdx)
 			mCurMethodState->mClosureState = &closureState;
 			//closureState.mConstLocals = deferredLocalMethod->mConstLocals;
 			closureState.mReturnType = lambdaInstance->mMethodInstance->mReturnType;
-			closureState.mClosureType = lambdaInstance->mClosureTypeInstance;
+			if (lambdaInstance->mClosureTypeInstance != NULL)
+				closureState.mClosureType = lambdaInstance->mClosureTypeInstance;
+			else
+				closureState.mClosureType = lambdaInstance->mDelegateTypeInstance;
 			closureState.mClosureInstanceInfo = lambdaInstance->mMethodInstance->mMethodInfoEx->mClosureInstanceInfo;
 			mCurMethodState->mMixinState = lambdaInstance->mDeclMixinState;
 

+ 1 - 1
IDEHelper/Compiler/BfModule.h

@@ -1773,7 +1773,7 @@ public:
 	HashSet<int> mDefinedStrings;
 
 public:
-	BfVDataModule(BfContext* context) : BfModule(context, "vdata")
+	BfVDataModule(BfContext* context) : BfModule(context, StringImpl::MakeRef("vdata"))
 	{
 	}
 };

+ 3 - 3
IDEHelper/Compiler/MemReporter.cpp

@@ -59,10 +59,10 @@ void MemReporter::Report(int depth, Entry* entry)
 	}
 }
 
-void MemReporter::BeginSection(const StringImpl& name)
+void MemReporter::BeginSection(const StringView& name)
 {
 	Entry** entryPtr;
-	if (!mCurEntry->mChildren.TryAdd(name, NULL, &entryPtr))
+	if (!mCurEntry->mChildren.TryAdd(StringImpl::MakeRef(name), NULL, &entryPtr))
 	{		
 		mCurEntry = *entryPtr;
 		mCurEntry->mCount++;
@@ -82,7 +82,7 @@ void MemReporter::Add(int size)
 	mCurEntry->mSize += size;
 }
 
-void MemReporter::Add(const StringImpl& name, int size)
+void MemReporter::Add(const StringView& name, int size)
 {
 	BeginSection(name);
 	Add(size);

+ 8 - 8
IDEHelper/Compiler/MemReporter.h

@@ -44,9 +44,9 @@ public:
 	MemReporter();
 	~MemReporter();
 
-	void BeginSection(const StringImpl& name);
+	void BeginSection(const StringView& name);
 	void Add(int size);
-	void Add(const StringImpl& name, int size);
+	void Add(const StringView& name, int size);
 	
 	template <typename T>
 	void AddVec(const T& vec, bool addContainerSize = true)
@@ -55,7 +55,7 @@ public:
 	}
 	
 	template <typename T>
-	void AddVec(const StringImpl& name, const T& vec, bool addContainerSize = true)
+	void AddVec(const StringView& name, const T& vec, bool addContainerSize = true)
 	{
 		BeginSection(name);
 		Add((addContainerSize ? sizeof(T) : 0) + (int)vec.mAllocSize * sizeof(typename T::value_type));
@@ -79,7 +79,7 @@ public:
 	}
 
 	template <typename T>
-	void AddVecPtr(const StringImpl& name, const Array<T>& vec, bool addContainerSize = true)
+	void AddVecPtr(const StringView& name, const Array<T>& vec, bool addContainerSize = true)
 	{
 		Add(name, (addContainerSize ? sizeof(T) : 0) +
 			(int)vec.mAllocSize * sizeof(T) +
@@ -87,7 +87,7 @@ public:
 	}
 
 	template <typename T>
-	void AddMap(const StringImpl& name, const T& map, bool addContainerSize = true)
+	void AddMap(const StringView& name, const T& map, bool addContainerSize = true)
 	{		
 		Add(name, (addContainerSize ? sizeof(T) : 0) + map.mAllocSize * (sizeof(typename T::EntryPair) + sizeof(typename T::int_cosize)));
 	}
@@ -99,7 +99,7 @@ public:
 	}
 
 	template <typename T>
-	void AddHashSet(const StringImpl& name, const T& map, bool addContainerSize = true)
+	void AddHashSet(const StringView& name, const T& map, bool addContainerSize = true)
 	{
 		Add(name, (addContainerSize ? sizeof(T) : 0) + map.mAllocSize * (sizeof(typename T::Entry) + sizeof(typename T::int_cosize)));
 	}
@@ -115,7 +115,7 @@ public:
 		Add((addContainerSize ? sizeof(StringImpl) : 0) + (int)str.GetAllocSize());
 	}
 
-	void AddStr(const StringImpl& name, const StringImpl& str, bool addContainerSize = true)
+	void AddStr(const StringView& name, const StringImpl& str, bool addContainerSize = true)
 	{
 		Add(name, (addContainerSize ? sizeof(StringImpl) : 0) + (int)str.GetAllocSize());
 	}
@@ -125,7 +125,7 @@ public:
 
 
 	template <typename T>
-	void AddBumpAlloc(const StringImpl& name, const T& alloc)
+	void AddBumpAlloc(const StringView& name, const T& alloc)
 	{		
 		BeginSection(name);
 				

+ 177 - 16
IDEHelper/DbgModule.cpp

@@ -390,7 +390,7 @@ String DbgSubprogram::ToString()
 	{		
 		if (mLinkName[0] == '<')
 			return mLinkName;
-		str = BfDemangler::Demangle(mLinkName, language);
+		str = BfDemangler::Demangle(StringImpl::MakeRef(mLinkName), language);
 		// Strip off the params since we need to generate those ourselves		
 		int parenPos = (int)str.IndexOf('(');
 		if (parenPos != -1)
@@ -776,6 +776,13 @@ bool DbgSubprogram::ThisIsSplat()
 	return strncmp(mBlock.mVariables.mHead->mName, "$this$", 6) == 0;
 }
 
+bool DbgSubprogram::IsLambda()
+{
+	if (mName == NULL)
+		return false;
+	return StringView(mName).Contains('$');
+}
+
 //////////////////////////////////////////////////////////////////////////
 
 DbgSubprogram::~DbgSubprogram()
@@ -863,7 +870,7 @@ void DbgSrcFile::RehupLineData()
 	for (int idx = 0; idx < (int)mLineDataRefs.size(); idx++)
 	{
 		auto dbgSubprogram = mLineDataRefs[idx];
-		if (dbgSubprogram->mWasModuleHotReplaced)
+		if (dbgSubprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_None)
 		{
 			mLineDataRefs.RemoveAtFast(idx);
 			idx--;
@@ -1548,6 +1555,7 @@ String DbgType::ToString(DbgLanguage language, bool allowDirectBfObject)
 		else
 			return mParent->ToString(language) + "::" + nameP;
 	}
+	
 
 	switch (mTypeCode)
 	{
@@ -1999,6 +2007,39 @@ DbgModule::~DbgModule()
 		delete data;
 }
 
+DbgSubprogram* DbgModule::FindSubprogram(DbgType* dbgType, const char * methodName)
+{
+	dbgType = dbgType->GetPrimaryType();
+	dbgType->PopulateType();
+
+	if (dbgType->mNeedsGlobalsPopulated)					
+		PopulateTypeGlobals(dbgType);
+
+	for (auto methodNameEntry : dbgType->mMethodNameList)
+	{
+		if ((methodNameEntry->mCompileUnitId != -1) && (strcmp(methodNameEntry->mName, methodName) == 0))
+		{
+			// If we hot-replaced this type then we replaced and parsed all the methods too
+			if (!dbgType->mCompileUnit->mDbgModule->mIsHotObjectFile)
+				dbgType->mCompileUnit->mDbgModule->MapCompileUnitMethods(methodNameEntry->mCompileUnitId);
+			methodNameEntry->mCompileUnitId = -1;
+		}
+	}
+
+	DbgSubprogram* result = NULL;
+	for (auto method : dbgType->mMethodList)
+	{
+		if (strcmp(method->mName, methodName) == 0)
+		{
+			method->PopulateSubprogram();
+			if ((result == NULL) || (method->mBlock.mLowPC != 0))
+				result = method;
+		}
+	}
+
+	return result;
+}
+
 void DbgModule::Fail(const StringImpl& error)
 {
 	if (mFailMsgPtr != NULL)
@@ -4960,7 +5001,8 @@ void DbgModule::HotReplaceType(DbgType* newType)
 	HashSet<DbgSrcFile*> checkedFiles;
 	for (auto method : primaryType->mMethodList)
 	{
-		method->mWasModuleHotReplaced = true;
+		//method->mWasModuleHotReplaced = true;
+		method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
 
 		if (method->mLineInfo == NULL)
 			continue;
@@ -5014,35 +5056,154 @@ void DbgModule::HotReplaceType(DbgType* newType)
 		primaryType->mHotReplacedMethodList.PushFront(method);
 		mHotPrimaryTypes.Add(primaryType);
 	}
-				
+	
+	Dictionary<StringView, DbgSubprogram*> oldProgramMap;
+	for (auto oldMethod : primaryType->mHotReplacedMethodList)
+	{
+		oldMethod->PopulateSubprogram();
+		if (oldMethod->mBlock.IsEmpty())
+			continue;
+		auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
+		if (symInfo != NULL)
+		{
+			oldProgramMap.TryAdd(symInfo->mName, oldMethod);
+		}
+	}
+
 	bool setHotJumpFailed = false;
 	while (!newType->mMethodList.IsEmpty())
 	{
 		DbgSubprogram* newMethod = newType->mMethodList.PopFront();
 		if (!newMethod->mBlock.IsEmpty())
 		{
-			newMethod->PopulateSubprogram();
+			newMethod->PopulateSubprogram();			
 
-			bool found = false;
-			for (auto oldMethod : primaryType->mHotReplacedMethodList)
+			auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
+			if (symInfo != NULL)
 			{
-				if (oldMethod->mBlock.IsEmpty())
-					continue;
-				if (oldMethod->Equals(newMethod))
+				DbgSubprogram* oldMethod = NULL;
+				if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod))
 				{
-					if (!setHotJumpFailed)
+					bool doHotJump = false;
+
+					if (oldMethod->Equals(newMethod))
 					{
-						if (!mDebugger->SetHotJump(oldMethod, newMethod))
-							setHotJumpFailed = true;
-						oldMethod->mWasHotReplaced = true;
-					}					
+						doHotJump = true;
+					}
+					else
+					{
+						// When mangles match but the actual signatures don't match, that can mean that the call signature was changed 
+						// and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed.
+						// When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard
+						// fail if the old code gets called to avoid confusion of "why aren't my changes working?"
+
+						// If we removed captures then we can still do the hot jump. Otherwise we have to fail...
+						doHotJump = false;
+						if ((oldMethod->IsLambda()) && (oldMethod->GetParamCount() == 0) && (newMethod->GetParamCount() == 0) &&
+							(oldMethod->mHasThis) && (newMethod->mHasThis))
+						{
+							auto oldParam = oldMethod->mParams.front();
+							auto newParam = newMethod->mParams.front();
+
+							if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer()))
+							{
+								auto oldType = oldParam->mType->mTypeParam->GetPrimaryType();
+								auto newType = newParam->mType->mTypeParam->GetPrimaryType();
+								if ((oldType->IsStruct()) && (newType->IsStruct()))
+								{	
+									bool wasMatch = true;
+
+									auto oldMember = oldType->mMemberList.front();
+									auto newMember = newType->mMemberList.front();
+									while (newMember != NULL)
+									{
+										if (oldMember == NULL)
+										{
+											wasMatch = false;
+											break;
+										}
+
+										if ((oldMember->mName == NULL) || (newMember->mName == NULL))
+										{
+											wasMatch = false;
+											break;
+										}
+
+										if (strcmp(oldMember->mName, newMember->mName) != 0)
+										{
+											wasMatch = false;
+											break;
+										}
+
+										if (!oldMember->mType->Equals(newMember->mType))
+										{
+											wasMatch = false;
+											break;
+										}
+										
+										oldMember = oldMember->mNext;
+										newMember = newMember->mNext;
+									}
+
+									if (wasMatch)
+										doHotJump = true;
+								}
+							}
+
+							if (!doHotJump)
+							{
+								mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC);
+								oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid;
+							}							
+						}
+					}										
+
+					if (doHotJump)
+					{
+						if (!setHotJumpFailed)
+						{
+							if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
+								setHotJumpFailed = true;
+						}
+						oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
+					}
 				}
-			}
+			}			
 		}
 		newMethod->mParentType = primaryType;
 		primaryType->mMethodList.PushBack(newMethod);
 	}
 
+	//mDebugTarget->mSymbolMap.Get()
+
+// 	bool setHotJumpFailed = false;
+// 	while (!newType->mMethodList.IsEmpty())
+// 	{
+// 		DbgSubprogram* newMethod = newType->mMethodList.PopFront();
+// 		if (!newMethod->mBlock.IsEmpty())
+// 		{
+// 			newMethod->PopulateSubprogram();
+// 
+// 			bool found = false;
+// 			for (auto oldMethod : primaryType->mHotReplacedMethodList)
+// 			{
+// 				if (oldMethod->mBlock.IsEmpty())
+// 					continue;
+// 				if (oldMethod->Equals(newMethod))
+// 				{
+// 					if (!setHotJumpFailed)
+// 					{
+// 						if (!mDebugger->SetHotJump(oldMethod, newMethod))
+// 							setHotJumpFailed = true;
+// 						oldMethod->mWasHotReplaced = true;
+// 					}					
+// 				}
+// 			}
+// 		}
+// 		newMethod->mParentType = primaryType;
+// 		primaryType->mMethodList.PushBack(newMethod);
+// 	}
+
 	primaryType->mCompileUnit->mWasHotReplaced = true;	
 
 	primaryType->mNeedsGlobalsPopulated = newType->mNeedsGlobalsPopulated;

+ 12 - 4
IDEHelper/DbgModule.h

@@ -358,6 +358,14 @@ public:
 	};
 #endif
 
+	enum HotReplaceKind : uint8
+	{
+		HotReplaceKind_None = 0,
+		HotReplaceKind_Replaced = 1,
+		HotReplaceKind_Orphaned = 2, // Module was hot replaced but a new version of the subprogram wasn't found
+		HotReplaceKind_Invalid = 3 // Mangles matched but arguments were incompatible
+	};
+
 	const char* mName;	
 	const char* mLinkName;
 	int mTemplateNameIdx;	
@@ -376,8 +384,7 @@ public:
 	bool mVirtual;
 	bool mHasThis;
 	bool mNeedLineDataFixup;	
-	bool mWasHotReplaced;
-	bool mWasModuleHotReplaced; // Module was hot replaced but a new version of the subprogram wasn't found
+	HotReplaceKind mHotReplaceKind;	
 	bool mIsOptimized;
 	bool mHasLineAddrGaps; // There are gaps of addresses which are not covered by lineinfo			
 	DbgLineInfo* mLineInfo;
@@ -397,8 +404,7 @@ public:
 		mLinkName = NULL;				
 		mHasThis = false;
 		mNeedLineDataFixup = true;
-		mWasHotReplaced = false;
-		mWasModuleHotReplaced = false;
+		mHotReplaceKind = HotReplaceKind_None;		
 		mHasLineAddrGaps = false; 
 		mPrologueSize = -1;
 		mParentType = NULL;		
@@ -427,6 +433,7 @@ public:
 	String GetParamName(int paramIdx);
 	bool IsGenericMethod();
 	bool ThisIsSplat();	
+	bool IsLambda();
 
 	DbgSubprogram* GetRootInlineParent()
 	{		
@@ -1201,6 +1208,7 @@ public:
 	virtual String GetOldSourceCommand(const StringImpl& path) { return ""; }
 	virtual bool DbgIsStrMutable(const char* str) { return true; } // Always assume its a copy
 	virtual addr_target LocateSymbol(const StringImpl& name) { return 0; }
+	virtual DbgSubprogram* FindSubprogram(DbgType* dbgType, const char* methodName);
 
 	void Fail(const StringImpl& error);
 	void FindTemplateStr(const char*& name, int& templateNameIdx);

+ 1 - 1
IDEHelper/HotScanner.cpp

@@ -128,7 +128,7 @@ void DbgHotScanner::PopulateHotCallstacks()
 			{
 				auto subProgram = stackFrame->mSubProgram;
 
-				if (subProgram->mWasHotReplaced)
+				if (subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced)
 					subProgram = mDebugger->TryFollowHotJump(subProgram, stackFrame->mRegisters.GetPC());
 
 				AddSubProgram(subProgram, false, "");

+ 8 - 8
IDEHelper/Linker/BlCodeView.cpp

@@ -2052,14 +2052,14 @@ void BlCodeView::CreateLinkerSymStream()
 	
 	ENVBLOCKSYM envBlock = { 0 };
 	envBlock.rectyp = S_ENVBLOCK;	
-	str = "cwd"; str.Append(0);
-	str += cwd; str.Append(0);
-	str += "exe"; str.Append(0);
-	str += moduleFileName; str.Append(0);	
-	str += "pdb"; str.Append(0);
-	str += mMsf.mFileName; str.Append(0);
-	str += "cmd"; str.Append(0);
-	str += GetCommandLineA(); str.Append(0);
+	str = "cwd"; str.Append('\0');
+	str += cwd; str.Append('\0');
+	str += "exe"; str.Append('\0');
+	str += moduleFileName; str.Append('\0');
+	str += "pdb"; str.Append('\0');
+	str += mMsf.mFileName; str.Append('\0');
+	str += "cmd"; str.Append('\0');
+	str += GetCommandLineA(); str.Append('\0');
 	OUT_WITH_STR(ENVBLOCKSYM, envBlock, rgsz);
 
 	for (auto segment : mContext->mSegments)

+ 25 - 15
IDEHelper/WinDebugger.cpp

@@ -1863,10 +1863,20 @@ bool WinDebugger::DoUpdate()
 					bool isNonDebuggerBreak = false;
 
 					if (wasDebugBreakpoint)
-					{
+					{						
 						// Go ahead and set EIP back one instruction												
 						BF_CONTEXT_IP(lcContext)--;
 						BF_SetThreadContext(threadInfo->mHThread, &lcContext);
+
+						if ((dwSubprogram != NULL) && (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid) &&
+							(pcAddress == dwSubprogram->mBlock.mLowPC))
+						{
+							BfLogDbg("Hit HotReplaceKind_Invalid breakpoint\n");
+							mRunState = RunState_Paused;
+							mDebugManager->mOutMessages.push_back("error This lambda was replaced by a new version that has incompatible captures. A program restart is required.");
+							PhysRemoveBreakpoint(pcAddress);
+							break;
+						}
 					}					
 					else
 					{
@@ -2223,9 +2233,9 @@ bool WinDebugger::DoUpdate()
 							}
 						}
 						else
-						{							
+						{								
 							BfLogDbg("Ignoring break (old or ignored breakpoint)\n");
-							mRunState = RunState_Running;
+							mRunState = RunState_Running;							
 						}
 					}
 
@@ -2363,7 +2373,7 @@ bool WinDebugger::DoUpdate()
 						DbgSubprogram* dwSubprogram = NULL;
 						DbgLineData* dwLineData = FindLineDataAtAddress(pcAddress, &dwSubprogram, NULL, NULL, DbgOnDemandKind_LocalOnly);
 
-						if ((dwSubprogram != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC) && (dwSubprogram->mWasHotReplaced))
+						if ((dwSubprogram != NULL) && (pcAddress == dwSubprogram->mBlock.mLowPC) && (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced))
 						{
 							BfLogDbg("Stepping through hot thunk\n");
 							mRunState = RunState_Running;
@@ -2413,7 +2423,7 @@ bool WinDebugger::DoUpdate()
 						}
 						else if (dwSubprogram != NULL)
 						{	
-							if ((dwSubprogram->mWasHotReplaced) && ((mStepType == StepType_StepInto) || (mStepType == StepType_StepInto_Unfiltered)))
+							if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && ((mStepType == StepType_StepInto) || (mStepType == StepType_StepInto_Unfiltered)))
 							{
 								SingleStepX86();
 							}
@@ -2963,7 +2973,7 @@ void WinDebugger::CheckBreakpoint(WdBreakpoint* wdBreakpoint, DbgSrcFile* srcFil
 				if ((foundInSequence) && (subProgram != lastFoundSubprogram))
 					foundInSequence = false;
 
-				if ((subProgram->mWasHotReplaced) && (address < subProgram->mBlock.mLowPC + sizeof(HotJumpOp)))
+				if ((subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && (address < subProgram->mBlock.mLowPC + sizeof(HotJumpOp)))
 				{
 					// If this breakpoint ends up on the hot jmp instruction
 					continue;
@@ -2973,7 +2983,7 @@ void WinDebugger::CheckBreakpoint(WdBreakpoint* wdBreakpoint, DbgSrcFile* srcFil
 				{
 					lastFoundSubprogram = subProgram;
 
-					if ((subProgram != NULL) && (subProgram->mWasHotReplaced) && (address == subProgram->mBlock.mLowPC))
+					if ((subProgram != NULL) && (subProgram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) && (address == subProgram->mBlock.mLowPC))
 					{
 						// This instruction is actually the hot jump, we don't need a breakpoint here
 						foundInSequence = true;
@@ -4957,7 +4967,7 @@ bool WinDebugger::RollBackStackFrame(CPURegisters* registers, bool isStackStart)
 	return mDebugTarget->RollBackStackFrame(registers, NULL, isStackStart);	
 }
 
-bool WinDebugger::SetHotJump(DbgSubprogram* oldSubprogram, DbgSubprogram* newSubprogram)
+bool WinDebugger::SetHotJump(DbgSubprogram* oldSubprogram, addr_target newTarget, int newTargetSize)
 {
 	//AutoCrit autoCrit(mDebugManager->mCritSect);
 	BF_ASSERT(mDebugManager->mCritSect.mLockCount == 1);
@@ -4968,14 +4978,14 @@ bool WinDebugger::SetHotJump(DbgSubprogram* oldSubprogram, DbgSubprogram* newSub
 	if (jmpInstEnd > oldSubprogram->mBlock.mHighPC)
 	{
 		if ((oldSubprogram->mBlock.mHighPC - oldSubprogram->mBlock.mLowPC == 1) &&
-			(newSubprogram->mBlock.mHighPC - newSubprogram->mBlock.mLowPC == 1))
+			(newTargetSize == 1))
 			return true; // Special case for just stub 'ret' methods
-		String err = StrFormat("Failed to hot replace method, method '%s' too small to insert hot thunk", newSubprogram->ToString().c_str());
+		String err = StrFormat("Failed to hot replace method, method '%s' too small to insert hot thunk", oldSubprogram->ToString().c_str());
 		Fail(err);
 		return false;
 	}
 	
-	if (!oldSubprogram->mWasHotReplaced)
+	if (oldSubprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_Replaced)
 	{
 		for (int threadIdx = 0; threadIdx < (int)mThreadList.size(); threadIdx++)
 		{
@@ -5068,7 +5078,7 @@ bool WinDebugger::SetHotJump(DbgSubprogram* oldSubprogram, DbgSubprogram* newSub
 
 	HotJumpOp jumpOp;
 	jumpOp.mOpCode = 0xE9;
-	jumpOp.mRelTarget = newSubprogram->mBlock.mLowPC - oldSubprogram->mBlock.mLowPC - sizeof(HotJumpOp);
+	jumpOp.mRelTarget = newTarget - oldSubprogram->mBlock.mLowPC - sizeof(HotJumpOp);
 	WriteMemory(oldSubprogram->mBlock.mLowPC, jumpOp);
 	::FlushInstructionCache(mProcessInfo.hProcess, (void*)(intptr)oldSubprogram->mBlock.mLowPC, sizeof(HotJumpOp));
 	return true;
@@ -5076,7 +5086,7 @@ bool WinDebugger::SetHotJump(DbgSubprogram* oldSubprogram, DbgSubprogram* newSub
 
 DbgSubprogram* WinDebugger::TryFollowHotJump(DbgSubprogram* subprogram, addr_target addr)
 {
-	if (!subprogram->mWasHotReplaced)
+	if (subprogram->mHotReplaceKind != DbgSubprogram::HotReplaceKind_Replaced)
 		return subprogram;
 
 	if (addr != subprogram->mBlock.mLowPC)
@@ -10794,14 +10804,14 @@ String WinDebugger::GetStackFrameInfo(int stackFrameIdx, intptr* addr, String* o
 		if (!dbgModule->mDisplayName.empty())
 			demangledName = dbgModule->mDisplayName + "!" + demangledName;
 
-		if (dwSubprogram->mWasHotReplaced)
+		if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) || (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid))
 			demangledName = "#" + demangledName;
 
 		if (dbgModule->HasPendingDebugInfo())
 			*outFlags |= FrameFlags_HasPendingDebugInfo;
 		if (dbgModule->CanGetOldSource())
 			*outFlags |= FrameFlags_CanGetOldSource;
-		if (dwSubprogram->mWasHotReplaced)
+		if ((dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Replaced) || (dwSubprogram->mHotReplaceKind == DbgSubprogram::HotReplaceKind_Invalid))
 			*outFlags |= FrameFlags_WasHotReplaced;
 
 		if ((dwLineData != NULL) && (dwSrcFile != NULL))

+ 1 - 1
IDEHelper/WinDebugger.h

@@ -479,7 +479,7 @@ public:
 	bool PopulateRegisters(CPURegisters* registers, BF_CONTEXT& lcContext);
 	virtual bool PopulateRegisters(CPURegisters* registers);
 	bool RollBackStackFrame(CPURegisters* registers, bool isStackStart);
-	bool SetHotJump(DbgSubprogram* oldSubprogram, DbgSubprogram* newSubprogram);
+	bool SetHotJump(DbgSubprogram* oldSubprogram, addr_target newTarget, int newTargetSize);
 	DbgSubprogram* TryFollowHotJump(DbgSubprogram* subprogram, addr_target addr);	
 
 	bool ParseFormatInfo(DbgModule* dbgModule, const StringImpl& formatInfoStr, DwFormatInfo* formatInfo, BfPassInstance* bfPassInstance, int* assignExprOffset, String* assignExpr = NULL, String* errorString = NULL,  DbgTypedValue contextTypedValue = DbgTypedValue());