Explorar o código

Improved hotswapping with extension modules

Brian Fiete hai 6 meses
pai
achega
fd4fd43ce3

+ 1 - 0
BeefySysLib/BeefySysLib.vcxproj

@@ -2184,6 +2184,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"</Co
     <ClInclude Include="util\MappedFile.h" />
     <ClInclude Include="util\MathUtils.h" />
     <ClInclude Include="util\Matrix4.h" />
+    <ClInclude Include="util\MultiDictionary.h" />
     <ClInclude Include="util\MultiHashSet.h" />
     <ClInclude Include="util\PerfTimer.h" />
     <ClInclude Include="util\Point.h" />

+ 3 - 0
BeefySysLib/BeefySysLib.vcxproj.filters

@@ -1132,6 +1132,9 @@
     <ClInclude Include="third_party\putty\wildcard.h">
       <Filter>src\third_party\putty</Filter>
     </ClInclude>
+    <ClInclude Include="util\MultiDictionary.h">
+      <Filter>src\util</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="third_party\libffi\i686-pc-cygwin\src\x86\win32.asm">

+ 17 - 12
BeefySysLib/util/Array.h

@@ -4,16 +4,16 @@
 
 NS_BF_BEGIN;
 
-template <typename T>
 class AllocatorCLib
 {
 public:
+	template <typename T>
 	T* allocate(intptr count)
 	{
 		return (T*)malloc(sizeof(T) * count);
 	}
 
-	void deallocate(T* ptr)
+	void deallocate(void* ptr)
 	{
 		free(ptr);
 	}
@@ -27,9 +27,14 @@ public:
 	{
 		free(ptr);
 	}
+
+	bool deallocateAll()
+	{
+		return false;
+	}
 };
 
-template <typename T, typename TAlloc = AllocatorCLib<T> >
+template <typename T, typename TAlloc = AllocatorCLib >
 class ArrayBase : public TAlloc
 {
 public:
@@ -445,7 +450,7 @@ protected:
 
 	void SetBufferSize(intptr newSize)
 	{
-		T* newVals = TAlloc::allocate(newSize);
+		T* newVals = TAlloc::allocate<T>(newSize);
 		if (this->mVals != NULL)
 		{
 			if (this->mSize > 0)
@@ -625,7 +630,7 @@ public:
 		{
 			intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
 
-			T* newVals = TAlloc::allocate(newSize);
+			T* newVals = TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -653,7 +658,7 @@ public:
 		{
 			intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
 
-			T* newVals = TAlloc::allocate(newSize);
+			T* newVals = TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -682,7 +687,7 @@ public:
 		{
 			intptr newSize = BF_MAX(this->mSize + count, this->mAllocSize + this->mAllocSize / 2 + 1);
 
-			T* newVals = TAlloc::allocate(newSize);
+			T* newVals = TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -764,7 +769,7 @@ public:
 protected:
 	void SetBufferSize(intptr newSize)
 	{
-		T* newVals = TAlloc::allocate(newSize);
+		T* newVals = TAlloc::allocate<T>(newSize);
 		if (this->mVals != NULL)
 		{
 			if (this->mSize > 0)
@@ -928,7 +933,7 @@ public:
 		{
 			intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
 
-			T* newVals = TAlloc::allocate(newSize);
+			T* newVals = TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -956,7 +961,7 @@ public:
 		{
 			intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
 
-			T* newVals = TAlloc::allocate(newSize);
+			T* newVals = TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -984,7 +989,7 @@ public:
 		{
 			intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
 
-			T* newVals = TAlloc::allocate(newSize);
+			T* newVals = TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -1102,7 +1107,7 @@ public:
 	}
 };
 
-template <typename T, typename TAlloc = AllocatorCLib<T> >
+template <typename T, typename TAlloc = AllocatorCLib >
 class Array : public ArrayImpl<T, TAlloc, std::is_pod<T>::value>
 {
 public:

+ 6 - 22
BeefySysLib/util/BumpAllocator.h

@@ -255,23 +255,24 @@ class BumpAllocator : public BumpAllocatorT<0x2000>
 
 };
 
-template <typename T, int ALLOC_SIZE = 0x2000>
-class AllocatorBump
+template <int ALLOC_SIZE = 0x2000>
+class AllocatorBumpT
 {
 public:
 	BumpAllocatorT<ALLOC_SIZE>* mAlloc;
 
-	AllocatorBump()
+	AllocatorBumpT()
 	{
 		mAlloc = NULL;
 	}
 
+	template <typename T>
 	T* allocate(intptr count)
 	{
 		return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T));
 	}
 
-	void deallocate(T* ptr)
+	void deallocate(void* ptr)
 	{		
 	}
 
@@ -286,26 +287,9 @@ public:
 	}
 };
 
-template <int ALLOC_SIZE = 0x2000>
-class RawAllocatorBump
+class AllocatorBump : public AllocatorBumpT<0x2000>
 {
-public:
-	BumpAllocatorT<ALLOC_SIZE>* mAlloc;
-
-	RawAllocatorBump()
-	{
-		mAlloc = NULL;
-	}
-		
-	void* rawAllocate(intptr size)
-	{
-		return mAlloc->AllocBytes(size, 16);
-	}
 
-	void rawDeallocate(void* ptr)
-	{
-
-	}
 };
 
 NS_BF_END

+ 4 - 4
BeefySysLib/util/Deque.h

@@ -8,7 +8,7 @@ NS_BF_BEGIN;
 #define DEQUE_IDX(i) (this->mVals[((i) + this->mOffset) % this->mAllocSize])
 #define DEQUE_IDX_ON(arr, i) ((arr).mVals[((i) + (arr).mOffset) % (arr).mAllocSize])
 
-template <typename T, typename TAlloc = AllocatorCLib<T> >
+template <typename T, typename TAlloc = AllocatorCLib >
 class DequeBase : public TAlloc
 {
 public:
@@ -370,7 +370,7 @@ protected:
 	
  	void Grow(intptr newSize)
  	{
- 		T* newVals = TAlloc::allocate(newSize);
+ 		T* newVals = TAlloc::allocate<T>(newSize);
  		if (this->mVals != NULL)
  		{
 			if (this->mSize > 0)
@@ -707,7 +707,7 @@ class DequeImpl<T, TAlloc, true> : public DequeBase<T, TAlloc>
 protected:
 	void Grow(intptr newSize)
 	{
-		T* newVals = TAlloc::allocate(newSize);
+		T* newVals = TAlloc::allocate<T>(newSize);
 		if (this->mVals != NULL)
 		{
 			if (this->mSize > 0)
@@ -1043,7 +1043,7 @@ public:
 	}
 };
 
-template <typename T, typename TAlloc = AllocatorCLib<T> >
+template <typename T, typename TAlloc = AllocatorCLib >
 class Deque : public DequeImpl<T, TAlloc, std::is_pod<T>::value>
 {
 public:

+ 1 - 1
BeefySysLib/util/Dictionary.h

@@ -9,7 +9,7 @@ NS_BF_BEGIN;
 
 #ifdef NEW_DICTIONAY
 
-template <typename TKey, typename TValue, typename TAlloc = AllocatorCLib<TKey> >
+template <typename TKey, typename TValue, typename TAlloc = AllocatorCLib >
 class Dictionary : public TAlloc
 {
 public:

+ 1 - 1
BeefySysLib/util/HashSet.h

@@ -5,7 +5,7 @@
 
 NS_BF_BEGIN;
 
-template <typename TKey, typename TAlloc = AllocatorCLib<TKey> >
+template <typename TKey, typename TAlloc = AllocatorCLib >
 class HashSet : public TAlloc
 {
 public:

+ 523 - 0
BeefySysLib/util/MultiDictionary.h

@@ -0,0 +1,523 @@
+#pragma once
+
+#include "../Common.h"
+#include "Array.h"
+
+NS_BF_BEGIN;
+
+struct MultiDictionaryFuncs : AllocatorCLib
+{
+	template <typename T>
+	size_t GetHash(const T& value)
+	{
+		return BeefHash<T>()(value);
+	}
+
+	template <typename T>
+	bool Matches(const T& lhs, const T& rhs)
+	{
+		return lhs == rhs;
+	}
+};
+
+template <typename TKey, typename TValue, typename TFuncs = MultiDictionaryFuncs>
+class MultiDictionary : public TFuncs
+{
+public:
+	struct Entry
+	{
+		TKey mKey;
+		TValue mValue;
+		int mNext;
+		int mHashCode;
+	};
+
+	struct EntryRef
+	{
+	public:
+		MultiDictionary* mSet;
+		int mIndex;
+
+	public:
+		EntryRef()
+		{
+			mSet = NULL;
+			mIndex = -1;
+		}
+
+		EntryRef(MultiDictionary* set, int index)
+		{
+			mSet = set;
+			mIndex = index;
+		}
+
+		Entry* operator*()
+		{
+			return &this->mSet->mEntries[this->mIndex];
+		}
+
+		Entry* operator->()
+		{
+			return &this->mSet->mEntries[this->mIndex];
+		}
+
+		operator bool() const
+		{
+			return this->mIndex != -1;
+		}
+	};
+
+	struct Iterator
+	{
+	public:
+		MultiDictionary* mSet;
+		int mCurEntry;
+		int mCurBucket;
+
+	public:
+		Iterator(MultiDictionary* set)
+		{
+			this->mSet = set;
+			this->mCurBucket = 0;
+			this->mCurEntry = -1;
+		}
+
+		Iterator& operator++()
+		{
+			if (this->mCurEntry != -1)
+			{
+				this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext;
+				if (this->mCurEntry != -1)
+					return *this;
+				this->mCurBucket++;
+			}
+
+			if (mSet->mHashHeads == NULL)
+			{
+				this->mCurBucket = this->mSet->mHashSize;
+				return *this; // At end
+			}
+
+			while (this->mCurBucket < mSet->mHashSize)
+			{
+				this->mCurEntry = this->mSet->mHashHeads[mCurBucket];
+				if (this->mCurEntry != -1)
+					return *this;
+				this->mCurBucket++;
+			}
+
+			return *this; // At end
+		}
+
+		TKey GetKey()
+		{
+			return this->mSet->mEntries[this->mCurEntry].mKey;
+		}
+
+		TValue GetValue()
+		{
+			return this->mSet->mEntries[this->mCurEntry].mValue;
+		}
+		
+		bool operator!=(const Iterator& itr) const
+		{
+			return ((itr.mCurEntry != this->mCurEntry) || (itr.mCurBucket != this->mCurBucket));
+		}
+
+		operator bool() const
+		{
+			return this->mCurEntry != -1;
+		}
+
+		void MoveToNextHashMatch()
+		{
+			int wantHash = this->mSet->mEntries[this->mCurEntry].mHashCode;
+			do
+			{
+				this->mCurEntry = this->mSet->mEntries[this->mCurEntry].mNext;
+			} while ((this->mCurEntry != -1) && (this->mSet->mEntries[this->mCurEntry].mHashCode != wantHash));
+		}
+	};
+
+protected:
+
+	int GetPrimeish(int min)
+	{
+		// This is a minimal effort to help address-aligned dataa
+		return (min | 1);
+	}
+
+	int ExpandSize(int oldSize)
+	{
+		int newSize = 2 * oldSize;
+
+		// Allow the hashtables to grow to maximum possible size (~2G elements) before encoutering capacity overflow.
+		// Note that this check works even when mAllocSize overflowed thanks to the (uint) cast
+		/*if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize)
+		{
+		Contract.Assert( MaxPrimeArrayLength == GetPrime(MaxPrimeArrayLength), "Invalid MaxPrimeArrayLength");
+		return MaxPrimeArrayLength;
+		}*/
+
+		return GetPrimeish(newSize);
+	}
+
+	void ResizeEntries()
+	{
+		ResizeEntries(ExpandSize(mCount));
+	}
+
+	void ResizeEntries(int newSize)
+	{
+		BF_ASSERT(newSize >= mAllocSize);
+		Entry* newEntries = TFuncs::allocate<Entry>(newSize);
+
+		for (int i = 0; i < mCount; i++)
+		{
+			auto& newEntry = newEntries[i];
+			auto& oldEntry = mEntries[i];
+			newEntry.mHashCode = oldEntry.mHashCode;
+			newEntry.mNext = oldEntry.mNext;
+			new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
+			new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
+		}
+		for (int i = mCount; i < newSize; i++)
+		{
+			newEntries[i].mHashCode = -1;
+		}
+
+		TFuncs::deallocate(mEntries);
+
+		mEntries = newEntries;
+		mAllocSize = (int)newSize;
+	}
+
+	void FreeIdx(int entryIdx)
+	{
+		this->mEntries[entryIdx].mNext = this->mFreeList;
+		this->mFreeList = entryIdx;
+		this->mFreeCount++;
+	}
+
+	int AllocEntry()
+	{
+		int index;
+		if (this->mFreeCount > 0)
+		{
+			index = this->mFreeList;
+			this->mFreeList = this->mEntries[index].mNext;
+			this->mFreeCount--;
+		}
+		else
+		{
+			if (this->mCount == this->mAllocSize)
+				ResizeEntries();
+			index = mCount;
+			this->mCount++;
+		}
+		return index;
+	}
+
+public:
+	int* mHashHeads;
+	int mAllocSize;
+	Entry* mEntries;
+	int mFreeList;
+	int mFreeCount;
+
+	static const int cDefaultHashSize = 17;
+	int mHashSize;
+	int mCount;
+
+	MultiDictionary()
+	{
+		this->mHashHeads = NULL;
+		this->mHashSize = cDefaultHashSize;
+		this->mEntries = NULL;
+		this->mAllocSize = 0;
+		this->mCount = 0;
+		this->mFreeList = -1;
+		this->mFreeCount = 0;
+	}
+
+	~MultiDictionary()
+	{
+		this->Clear();
+	}
+
+	void EnsureFreeCount(int wantFreeCount)
+	{
+		int freeCount = mFreeCount + (mAllocSize - mCount);
+		if (freeCount >= wantFreeCount)
+			return;
+		ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount));
+	}
+
+	int GetCount() const
+	{
+		return mCount - mFreeCount;
+	}
+
+	int size() const
+	{
+		return mCount - mFreeCount;
+	}
+
+	EntryRef AddRaw(int hash)
+	{
+		if (this->mHashHeads == NULL)
+		{
+			this->mHashHeads = TFuncs::allocate<int>(mHashSize);
+			memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
+		}
+
+		int index = AllocEntry();
+		int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+		int headEntry = this->mHashHeads[hashIdx];
+
+		Entry* newEntry = &mEntries[index];
+		newEntry->mValue = T();
+		newEntry->mNext = headEntry;
+		newEntry->mHashCode = hash;
+
+		mHashHeads[hashIdx] = index;
+
+		return EntryRef(this, index);
+	}
+
+	void Add(TKey key, TValue value)
+	{
+		if (this->mHashHeads == NULL)
+		{
+			this->mHashHeads = TFuncs::allocate<int>(mHashSize);
+			memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
+		}
+
+		int index = AllocEntry();
+		int hash = TFuncs::GetHash(key);
+		int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+		int headEntry = this->mHashHeads[hashIdx];
+
+		Entry* newEntry = &mEntries[index];
+		newEntry->mKey = key;
+		newEntry->mValue = value;
+		newEntry->mNext = headEntry;
+		newEntry->mHashCode = hash;
+
+		mHashHeads[hashIdx] = index;
+	}
+
+	void AddAfter(TKey key, TValue value, Entry* afterEntry)
+	{
+		int hash = TFuncs::GetHash(key);
+		int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+		BF_ASSERT(hash == afterEntry->mHashCode);
+
+		int index = AllocEntry();
+		Entry* newEntry = &mEntries[index];
+		newEntry->mKey = key;
+		newEntry->mValue = value;
+		newEntry->mNext = afterEntry->mNext;
+		newEntry->mHashCode = hash;
+
+		afterEntry->mNext = index;
+	}
+
+	void Rehash(int newHashSize)
+	{
+		auto newHashHeads = TFuncs::allocate<int>(newHashSize);
+		memset(newHashHeads, -1, sizeof(int) * newHashSize);
+
+		if (mHashHeads != NULL)
+		{
+			SizedArray<int, 1024> entryList;
+			for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
+			{
+				int checkEntryIdx = mHashHeads[hashIdx];
+				if (checkEntryIdx != -1)
+				{
+					// We want to keep elements with equal hashes in their insert order so we need to 
+					// iterate through the linked list in reverse
+					entryList.Clear();
+
+					while (checkEntryIdx != -1)
+					{
+						entryList.Add(checkEntryIdx);
+						checkEntryIdx = mEntries[checkEntryIdx].mNext;
+					}
+
+					for (int i = (int)entryList.mSize - 1; i >= 0; i--)
+					{
+						int checkEntryIdx = entryList[i];
+						auto checkEntry = &mEntries[checkEntryIdx];
+						int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize;
+						checkEntry->mNext = newHashHeads[newHashIdx];
+						newHashHeads[newHashIdx] = checkEntryIdx;
+					}
+				}
+			}
+
+			TFuncs::deallocate(mHashHeads);
+		}
+		mHashHeads = newHashHeads;
+		mHashSize = newHashSize;
+	}
+
+	void CheckRehash()
+	{
+		// Make the lookup load reasonable
+		if (mHashSize < mCount)
+		{
+			this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1);
+		}
+	}
+
+	template <typename TKey>
+	bool TryGet(const TKey& key, TKey* outKey, TValue* outValue)
+	{
+		if (mHashHeads == NULL)
+			return false;
+
+		this->CheckRehash();
+
+		int hash = TFuncs::GetHash(key);
+		int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+		int checkEntryIdx = this->mHashHeads[hashIdx];
+		while (checkEntryIdx != -1)
+		{
+			Entry* checkEntry = &mEntries[checkEntryIdx];
+			if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
+			{
+				if (outKey != NULL)
+					*outKey = checkEntry->mKey;
+				if (outValue != NULL)
+					*outValue = checkEntry->mValue;
+				return true;
+			}
+			checkEntryIdx = checkEntry->mNext;
+		}
+		return false;
+	}
+
+	template <typename TKey>
+	Iterator TryGet(const TKey& key)
+	{
+		if (mHashHeads == NULL)
+			return end();
+
+		this->CheckRehash();
+
+		int hash = TFuncs::GetHash(key);
+		int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+		int checkEntryIdx = this->mHashHeads[hashIdx];
+		while (checkEntryIdx != -1)
+		{
+			auto checkEntry = &this->mEntries[checkEntryIdx];
+			if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
+			{
+				Iterator itr(this);
+				itr.mCurEntry = checkEntryIdx;
+				itr.mCurBucket = hashIdx;
+				return itr;
+			}
+			checkEntryIdx = checkEntry->mNext;
+		}
+
+		return end();
+	}
+
+	template <typename TKey>
+	bool Remove(const TKey& key)
+	{
+		if (mHashHeads == NULL)
+			return false;
+
+		this->CheckRehash();
+
+		int hash = TFuncs::GetHash(key);
+		int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+
+		int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
+		int checkEntryIdx = *srcCheckEntryPtr;
+		while (checkEntryIdx != -1)
+		{
+			auto checkEntry = &mEntries[checkEntryIdx];
+			if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
+			{
+				*srcCheckEntryPtr = checkEntry->mNext;
+				FreeIdx(checkEntryIdx);
+				return true;
+			}
+			srcCheckEntryPtr = &checkEntry->mNext;
+			checkEntryIdx = checkEntry->mNext;
+		}
+		return false;
+	}
+
+	Iterator Erase(const Iterator& itr)
+	{
+		Iterator next = itr;
+		++next;
+
+		bool found = false;
+
+		auto entryIdx = itr.mCurEntry;
+		auto entry = &mEntries[entryIdx];
+		int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize;
+
+		int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
+		int checkEntryIdx = *srcCheckEntryPtr;
+		while (checkEntryIdx != -1)
+		{
+			auto checkEntry = &mEntries[checkEntryIdx];
+			if (checkEntryIdx == itr.mCurEntry)
+			{
+				*srcCheckEntryPtr = checkEntry->mNext;
+				found = true;
+			}
+			srcCheckEntryPtr = &checkEntry->mNext;
+			checkEntryIdx = checkEntry->mNext;
+		}
+
+		BF_ASSERT(found);
+		FreeIdx(entryIdx);
+
+		return next;
+	}
+
+	void Clear()
+	{
+		if (!TFuncs::deallocateAll())
+		{
+			auto itr = begin();
+			auto endItr = end();
+			while (itr != endItr)
+			{
+				auto entry = itr.mCurEntry;
+				++itr;
+				FreeIdx(entry);
+			}
+			TFuncs::deallocate(this->mHashHeads);
+			TFuncs::deallocate(this->mEntries);
+		}
+
+		this->mHashSize = cDefaultHashSize;
+		this->mHashHeads = NULL;
+		this->mEntries = NULL;
+		this->mCount = 0;
+	}
+
+	Iterator begin()
+	{
+		return ++Iterator(this);
+	}
+
+	Iterator end()
+	{
+		Iterator itr(this);
+		itr.mCurBucket = this->mHashSize;
+		return itr;
+	}
+};
+
+NS_BF_END;

+ 10 - 10
BeefySysLib/util/MultiHashSet.h

@@ -30,7 +30,7 @@ struct MultiHashSetFuncs
 	}
 };
 
-template <typename T, typename TFuncs = AllocatorCLib<T> >
+template <typename T, typename TFuncs = AllocatorCLib >
 class MultiHashSet : public TFuncs
 {
 public:
@@ -180,7 +180,7 @@ protected:
 	void ResizeEntries(int newSize)
 	{
 		BF_ASSERT(newSize >= mAllocSize);
-		Entry* newEntries = (Entry*)TFuncs::Allocate(sizeof(Entry) * newSize, alignof(Entry));
+		Entry* newEntries = TFuncs::allocate<Entry>(newSize);
 		
 		for (int i = 0; i < mCount; i++)
 		{
@@ -195,7 +195,7 @@ protected:
 			newEntries[i].mHashCode = -1;
 		}
 		
-		TFuncs::Deallocate(mEntries);
+		TFuncs::deallocate(mEntries);
 		
 		mEntries = newEntries;
 		mAllocSize = (int)newSize;
@@ -276,7 +276,7 @@ public:
 	{
 		if (this->mHashHeads == NULL)
 		{
-			this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int));
+			this->mHashHeads = TFuncs::allocate<int>(mHashSize);
 			memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
 		}
 
@@ -298,7 +298,7 @@ public:
 	{
 		if (this->mHashHeads == NULL)
 		{			
-			this->mHashHeads = (int*)TFuncs::Allocate(sizeof(int) * mHashSize, alignof(int));
+			this->mHashHeads = TFuncs::allocate<int>(mHashSize);
 			memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
 		}
 
@@ -332,7 +332,7 @@ public:
 
 	void Rehash(int newHashSize)
 	{
-		auto newHashHeads = (int*)TFuncs::Allocate(sizeof(int) * newHashSize, alignof(int));
+		auto newHashHeads = TFuncs::allocate<int>(newHashSize);
 		memset(newHashHeads, -1, sizeof(int) * newHashSize);
 		
 		if (mHashHeads != NULL)
@@ -364,7 +364,7 @@ public:
 				}
 			}
 
-			TFuncs::Deallocate(mHashHeads);
+			TFuncs::deallocate(mHashHeads);
 		}
 		mHashHeads = newHashHeads;
 		mHashSize = newHashSize;
@@ -492,7 +492,7 @@ public:
 	
 	void Clear()
 	{
-		if (!TFuncs::DeallocateAll())
+		if (!TFuncs::deallocateAll())
 		{	
 			auto itr = begin();
 			auto endItr = end();
@@ -502,8 +502,8 @@ public:
 				++itr;
 				FreeIdx(entry);
 			}			
-			TFuncs::Deallocate(this->mHashHeads);
-			TFuncs::Deallocate(this->mEntries);
+			TFuncs::deallocate(this->mHashHeads);
+			TFuncs::deallocate(this->mEntries);
 		}
 		
 		this->mHashSize = cDefaultHashSize;

+ 9 - 9
BeefySysLib/util/SizedArray.h

@@ -6,7 +6,7 @@
 
 NS_BF_BEGIN;
 
-template <typename T, typename TAlloc = AllocatorCLib<T> >
+template <typename T, typename TAlloc = AllocatorCLib >
 class SizedArrayBase : protected TAlloc
 {
 public:
@@ -378,7 +378,7 @@ protected:
 
 	void Grow(intptr newSize)
 	{
-		T* newVals = TAlloc::allocate(newSize);
+		T* newVals = TAlloc::allocate<T>(newSize);
 		if (this->mVals != NULL)
 		{
 			if (this->mSize > 0)
@@ -533,7 +533,7 @@ public:
 		{
 			intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
 
-			T* newVals =  TAlloc::allocate(newSize);
+			T* newVals =  TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -562,7 +562,7 @@ public:
 		{
 			intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
 
-			T* newVals =  TAlloc::allocate(newSize);
+			T* newVals =  TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -638,7 +638,7 @@ public:
 protected:
 	void Grow(intptr newSize)
 	{
-		T* newVals =  TAlloc::allocate(newSize);
+		T* newVals =  TAlloc::allocate<T>(newSize);
 		if (this->mVals != NULL)
 		{
 			if (this->mSize > 0)
@@ -761,7 +761,7 @@ public:
 		{
 			intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
 
-			T* newVals =  TAlloc::allocate(newSize);
+			T* newVals =  TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -790,7 +790,7 @@ public:
 		{
 			intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
 
-			T* newVals =  TAlloc::allocate(newSize);
+			T* newVals =  TAlloc::allocate<T>(newSize);
 			if (this->mVals != NULL)
 			{
 				if (idx > 0) // Copy left of idx
@@ -854,14 +854,14 @@ public:
 	}
 };
 
-template <typename T, typename TAlloc = AllocatorCLib<T> >
+template <typename T, typename TAlloc = AllocatorCLib >
 class SizedArrayImpl : public SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value>
 {
 public:
 	typedef SizedArrayBaseT<T, TAlloc, std::is_pod<T>::value> _Base;
 };
 
-template <typename T, int TInternalSize, typename TAlloc = AllocatorCLib<T> >
+template <typename T, int TInternalSize, typename TAlloc = AllocatorCLib >
 class SizedArray : public SizedArrayImpl<T, TAlloc>
 {
 public:

+ 2 - 2
IDEHelper/Compiler/BfModule.h

@@ -1063,8 +1063,8 @@ public:
 	BfIRBlock mIRHeadBlock;
 	BfIRBlock mIRInitBlock;
 	BfIRBlock mIREntryBlock;
-	Array<BfLocalVariable*, AllocatorBump<BfLocalVariable*> > mLocals;
-	HashSet<BfLocalVarEntry, AllocatorBump<BfLocalVariable*> > mLocalVarSet;
+	Array<BfLocalVariable*, AllocatorBump> mLocals;
+	HashSet<BfLocalVarEntry, AllocatorBump> mLocalVarSet;
 	Array<BfLocalMethod*> mLocalMethods;
 	Dictionary<String, BfLocalMethod*> mLocalMethodMap;
 	Dictionary<String, BfLocalMethod*> mLocalMethodCache; // So any lambda 'capturing' and 'processing' stages use the same local method

+ 2 - 6
IDEHelper/Compiler/BfResolvedTypeUtils.h

@@ -2690,11 +2690,7 @@ public:
 	void ReportMemory(MemReporter* memReporter);
 };
 
-class BfResolvedTypeSetFuncs : public MultiHashSetFuncs
-{
-};
-
-class BfResolvedTypeSet : public MultiHashSet<BfType*, BfResolvedTypeSetFuncs>
+class BfResolvedTypeSet : public MultiHashSet<BfType*, AllocatorCLib>
 {
 public:
 	enum BfHashFlags
@@ -2749,7 +2745,7 @@ public:
 		BfTypeDef* ResolveToTypeDef(BfTypeReference* typeReference, BfType** outType = NULL);
 	};
 
-	class Iterator : public MultiHashSet<BfType*, BfResolvedTypeSetFuncs>::Iterator
+	class Iterator : public MultiHashSet<BfType*, AllocatorCLib>::Iterator
 	{
 	public:
 		Iterator(MultiHashSet* set) : MultiHashSet::Iterator(set)

+ 1 - 1
IDEHelper/Compiler/BfSystem.h

@@ -1254,7 +1254,7 @@ public:
 	bool HasCustomAttributes();
 };
 
-struct BfTypeDefMapFuncs : public MultiHashSetFuncs
+struct BfTypeDefMapFuncs : public AllocatorCLib
 {
 	int GetHash(BfTypeDef* typeDef)
 	{

+ 234 - 156
IDEHelper/DbgModule.cpp

@@ -21,6 +21,7 @@
 #include "Compiler/BfDemangler.h"
 #include "BeefySysLib/util/Hash.h"
 #include "BeefySysLib/util/BeefPerf.h"
+#include "BeefySysLib/util/MultiDictionary.h"
 #include "DbgSymSrv.h"
 #include "MiniDumpDebugger.h"
 
@@ -5173,8 +5174,7 @@ void DbgModule::ParseHotTargetSections(DataStream* stream, addr_target* resolved
 				stream->Read(&coffReloc, sizeof(COFFRelocation));
 
 				PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18];
-				//const char* symName = mSymbolData[coffReloc.mSymbolTableIndex];
-
+				
 				bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC;
 
 				if (symInfo->mNameOfs[0] != 0)
@@ -5313,26 +5313,19 @@ void DbgModule::CommitHotTargetSections()
 	}
 }
 
-void DbgModule::HotReplaceType(DbgType* newType)
-{
-	auto linkedModule = GetLinkedModule();
-
-	newType->PopulateType();
-	DbgType* primaryType = linkedModule->GetPrimaryType(newType);
-	if (primaryType == newType)
+bool DbgModule::HasHotReplacedMethods(DbgType* type)
+{	
+	for (auto newMethod : type->mMethodList)
 	{
-		// There was no previous type
-		BF_ASSERT(primaryType->mHotNewType == NULL);
-		return;
+		if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
+			return true;
 	}
+	return false;
+}
 
-	if (primaryType->mHotNewType != newType)
-	{
-		// We have already pulled in the new data from a previous new type
-		BF_ASSERT(primaryType->mHotNewType == NULL);
-		return;
-	}
-	primaryType->mHotNewType = NULL;
+void DbgModule::HotReplaceMethods(DbgType* newType, DbgType* primaryType)
+{
+	auto linkedModule = GetLinkedModule();
 
 	primaryType->PopulateType();
 	linkedModule->ParseGlobalsData();
@@ -5354,184 +5347,265 @@ void DbgModule::HotReplaceType(DbgType* newType)
 
 	// Now actually remove the linedata from the defining module
 	HashSet<DbgSrcFile*> checkedFiles;
-	for (auto method : primaryType->mMethodList)
-	{
-		//method->mWasModuleHotReplaced = true;
-		method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
+	//for (auto method : primaryType->mMethodList)
 
-		if (method->mLineInfo == NULL)
-			continue;
+	auto _RemoveLineInfo = [&](DbgSubprogram* method)
+		{
+			//method->mWasModuleHotReplaced = true;
+			method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
 
-		//FIXME: Hot replacing lines
-		DbgSrcFile* lastSrcFile = NULL;
-		checkedFiles.Clear();
-
-		int prevCtx = -1;
-		auto inlineRoot = method->GetRootInlineParent();
- 		for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++)
- 		{
- 			auto& lineData = method->mLineInfo->mLines[lineIdx];
-			if (lineData.mCtxIdx != prevCtx)
-			{
-				auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx];
-				auto srcFile = ctxInfo.mSrcFile;
-				prevCtx = lineData.mCtxIdx;
-				if (srcFile != lastSrcFile)
+			if (method->mLineInfo == NULL)
+				return;
+
+			//FIXME: Hot replacing lines
+			DbgSrcFile* lastSrcFile = NULL;
+			checkedFiles.Clear();
+
+			int prevCtx = -1;
+			auto inlineRoot = method->GetRootInlineParent();
+			for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++)
+			{
+				auto& lineData = method->mLineInfo->mLines[lineIdx];
+				if (lineData.mCtxIdx != prevCtx)
 				{
-					if (checkedFiles.Add(srcFile))
+					auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx];
+					auto srcFile = ctxInfo.mSrcFile;
+					prevCtx = lineData.mCtxIdx;
+					if (srcFile != lastSrcFile)
 					{
-						// Remove linedata for old type
-						//  These go into a hot-replaced list so we can still bind to them -- that is necessary because
-						//   we may still have old versions of this method running (and may forever, if its in a loop on some thread)
-						//   since we only patch entry points
-						//srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true);
-
-						//srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true);
-						srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true);
-					}
+						if (checkedFiles.Add(srcFile))
+						{
+							// Remove linedata for old type
+							//  These go into a hot-replaced list so we can still bind to them -- that is necessary because
+							//   we may still have old versions of this method running (and may forever, if its in a loop on some thread)
+							//   since we only patch entry points
+							//srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true);
+
+							//srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true);
+							srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true);
+						}
 
-					lastSrcFile = srcFile;
+						lastSrcFile = srcFile;
+					}
 				}
 			}
- 		}
-	}
+		};
 
 	//DbgType* primaryType = newType->GetPrimaryType();
 
-	// We need to keep a persistent list of hot replaced methods so we can set hot jumps
-	//  in old methods that may still be on the callstack.  These entries get removed when
-	//  we unload unused hot files in
-	while (!primaryType->mMethodList.IsEmpty())
-	{
-		auto method = primaryType->mMethodList.PopFront();
-
-		method->PopulateSubprogram();
+	MultiDictionary<StringView, DbgSubprogram*> oldProgramMap;
+	auto _AddToHotReplacedMethodList = [&](DbgSubprogram* oldMethod)
+		{	
+			int hotIdx = oldMethod->mCompileUnit->mDbgModule->mHotIdx;
+			if ((hotIdx > 0) && (hotIdx < mDebugTarget->mVDataHotIdx))
+			{
+				// Too old
+				return;
+			}			
 
-		primaryType->mHotReplacedMethodList.PushFront(method);
-		mHotPrimaryTypes.Add(primaryType);
-	}
+			oldMethod->PopulateSubprogram();
+			if (oldMethod->mBlock.IsEmpty())
+				return;			
+			auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
+			if (symInfo != NULL)
+			{				
+				oldProgramMap.Add(StringView(symInfo->mName), oldMethod);				
+			}
+		};
 
-	Dictionary<StringView, DbgSubprogram*> oldProgramMap;
-	for (auto oldMethod : primaryType->mHotReplacedMethodList)
+	if (newType != primaryType)
 	{
-		oldMethod->PopulateSubprogram();
-		if (oldMethod->mBlock.IsEmpty())
-			continue;
-		auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
-		if (symInfo != NULL)
+		// We need to keep a persistent list of hot replaced methods so we can set hot jumps
+		//  in old methods that may still be on the callstack.  These entries get removed when
+		//  we unload unused hot files in
+		while (!primaryType->mMethodList.IsEmpty())
 		{
-			oldProgramMap.TryAdd(symInfo->mName, oldMethod);
+			auto method = primaryType->mMethodList.PopFront();
+			method->PopulateSubprogram();
+			primaryType->mHotReplacedMethodList.PushFront(method);
+			mHotPrimaryTypes.Add(primaryType);
 		}
 	}
+	
+	for (auto oldMethod : primaryType->mHotReplacedMethodList)
+	{
+		_AddToHotReplacedMethodList(oldMethod);
+	}
 
 	bool setHotJumpFailed = false;
-	while (!newType->mMethodList.IsEmpty())
-	{
-		DbgSubprogram* newMethod = newType->mMethodList.PopFront();
-		if (!newMethod->mBlock.IsEmpty())
-		{
-			BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC);
 
-			newMethod->PopulateSubprogram();
+	auto _HotJump = [&](DbgSubprogram* oldMethod, DbgSubprogram* newMethod)
+		{
+			bool doHotJump = false;
 
-			auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
-			if (symInfo != NULL)
+			if (oldMethod->Equals(newMethod))
 			{
-				DbgSubprogram* oldMethod = NULL;
-				if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod))
+				doHotJump = true;
+			}
+			else
+			{
+				// When mangles match but the actual signatures don't match, that can mean that the call signature was changed
+				// and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed.
+				// When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard
+				// fail if the old code gets called to avoid confusion of "why aren't my changes working?"
+
+				// If we removed captures then we can still do the hot jump. Otherwise we have to fail...
+				doHotJump = false;
+				if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) &&
+					(oldMethod->mHasThis) && (newMethod->mHasThis))
 				{
-					bool doHotJump = false;
+					auto oldParam = oldMethod->mParams.front();
+					auto newParam = newMethod->mParams.front();
 
-					if (oldMethod->Equals(newMethod))
-					{
-						doHotJump = true;
-					}
-					else
+					if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer()))
 					{
-						// When mangles match but the actual signatures don't match, that can mean that the call signature was changed
-						// and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed.
-						// When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard
-						// fail if the old code gets called to avoid confusion of "why aren't my changes working?"
-
-						// If we removed captures then we can still do the hot jump. Otherwise we have to fail...
-						doHotJump = false;
-						if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) &&
-							(oldMethod->mHasThis) && (newMethod->mHasThis))
+						auto oldType = oldParam->mType->mTypeParam->GetPrimaryType();
+						oldType->PopulateType();
+						auto newType = newParam->mType->mTypeParam->GetPrimaryType();
+						newType->PopulateType();
+						if ((oldType->IsStruct()) && (newType->IsStruct()))
 						{
-							auto oldParam = oldMethod->mParams.front();
-							auto newParam = newMethod->mParams.front();
+							bool wasMatch = true;
 
-							if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer()))
+							auto oldMember = oldType->mMemberList.front();
+							auto newMember = newType->mMemberList.front();
+							while (newMember != NULL)
 							{
-								auto oldType = oldParam->mType->mTypeParam->GetPrimaryType();
-								oldType->PopulateType();
-								auto newType = newParam->mType->mTypeParam->GetPrimaryType();
-								newType->PopulateType();
-								if ((oldType->IsStruct()) && (newType->IsStruct()))
+								if (oldMember == NULL)
 								{
-									bool wasMatch = true;
-
-									auto oldMember = oldType->mMemberList.front();
-									auto newMember = newType->mMemberList.front();
-									while (newMember != NULL)
-									{
-										if (oldMember == NULL)
-										{
-											wasMatch = false;
-											break;
-										}
-
-										if ((oldMember->mName == NULL) || (newMember->mName == NULL))
-										{
-											wasMatch = false;
-											break;
-										}
-
-										if (strcmp(oldMember->mName, newMember->mName) != 0)
-										{
-											wasMatch = false;
-											break;
-										}
+									wasMatch = false;
+									break;
+								}
 
-										if (!oldMember->mType->Equals(newMember->mType))
-										{
-											wasMatch = false;
-											break;
-										}
+								if ((oldMember->mName == NULL) || (newMember->mName == NULL))
+								{
+									wasMatch = false;
+									break;
+								}
 
-										oldMember = oldMember->mNext;
-										newMember = newMember->mNext;
-									}
+								if (strcmp(oldMember->mName, newMember->mName) != 0)
+								{
+									wasMatch = false;
+									break;
+								}
 
-									if (wasMatch)
-										doHotJump = true;
+								if (!oldMember->mType->Equals(newMember->mType))
+								{
+									wasMatch = false;
+									break;
 								}
-							}
 
-							if (!doHotJump)
-							{
-								mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC);
-								oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid;
+								oldMember = oldMember->mNext;
+								newMember = newMember->mNext;
 							}
+
+							if (wasMatch)
+								doHotJump = true;
 						}
 					}
 
-					if (doHotJump)
+					if (!doHotJump)
 					{
-						if (!setHotJumpFailed)
-						{
-							if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
-								setHotJumpFailed = true;
-						}
-						oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
+						mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC);
+						oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid;
 					}
 				}
 			}
+
+			if (doHotJump)
+			{
+				if (!setHotJumpFailed)
+				{
+					if (mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
+					{
+						_RemoveLineInfo(oldMethod);
+					}
+					else
+						setHotJumpFailed = true;
+				}
+				oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
+			}
+		};
+
+	auto _ReplaceMethod = [&](DbgSubprogram* newMethod)
+		{
+			if (!newMethod->mBlock.IsEmpty())
+			{
+				BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC);
+
+				newMethod->PopulateSubprogram();
+
+				auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
+				if (symInfo != NULL)
+				{	
+					DbgSubprogram* oldMethod = NULL;
+					for (auto itr = oldProgramMap.TryGet(StringView(symInfo->mName)); itr != oldProgramMap.end(); ++itr)					
+					{
+						auto oldMethod = itr.GetValue();
+						_HotJump(oldMethod, newMethod);
+					}
+				}
+			}
+		};
+
+	if (newType == primaryType)
+	{
+		// In-place method swapping
+
+		auto newMethod = newType->mMethodList.front();
+		while (newMethod != NULL)
+		{
+			if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
+			{
+				// Our object file contains this function
+				_ReplaceMethod(newMethod);
+				auto nextMethod = newMethod->mNext;
+				newType->mMethodList.Remove(newMethod);
+				primaryType->mHotReplacedMethodList.PushFront(newMethod);
+				newMethod = nextMethod;				
+			}
+			else
+			{
+				_AddToHotReplacedMethodList(newMethod);
+			}			
+			newMethod = newMethod->mNext;
+		}		
+	}
+	else
+	{
+		while (!newType->mMethodList.IsEmpty())
+		{
+			DbgSubprogram* newMethod = newType->mMethodList.PopFront();
+			_ReplaceMethod(newMethod);
+			newMethod->mParentType = primaryType;
+			primaryType->mMethodList.PushBack(newMethod);
 		}
-		newMethod->mParentType = primaryType;
-		primaryType->mMethodList.PushBack(newMethod);
+	}
+}
+
+void DbgModule::HotReplaceType(DbgType* newType)
+{
+	auto linkedModule = GetLinkedModule();
+
+	newType->PopulateType();
+	DbgType* primaryType = linkedModule->GetPrimaryType(newType);
+	if (primaryType == newType)
+	{
+		// There was no previous type
+		BF_ASSERT(primaryType->mHotNewType == NULL);
+		return;
 	}
 
+	if (primaryType->mHotNewType != newType)
+	{
+		// We have already pulled in the new data from a previous new type
+		BF_ASSERT(primaryType->mHotNewType == NULL);
+		return;
+	}
+	primaryType->mHotNewType = NULL;
+
+	HotReplaceMethods(newType, primaryType);
+
 	//mDebugTarget->mSymbolMap.Get()
 
 // 	bool setHotJumpFailed = false;
@@ -6506,17 +6580,21 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind)
 			{
 				auto dbgType = *itr;
 				auto primaryType = dbgType->GetPrimaryType();
+
+				if ((primaryType->mHotNewType == NULL) && (HasHotReplacedMethods(primaryType)) && (dbgType == primaryType))
+					HotReplaceMethods(dbgType, primaryType);
+
 				if (primaryType != dbgType)
 				{
 					mHotPrimaryTypes.Remove(itr);
 					mHotPrimaryTypes.Add(primaryType);
 					didReplaceType = true;
 					break;
-				}
+				}				
 			}
 			if (!didReplaceType)
 				break;
-		}
+		}		
 
 		BF_ASSERT(mTypes.size() == 0);
 		for (int typeIdx = mStartTypeIdx; typeIdx < (int)linkedModule->mTypes.size(); typeIdx++)
@@ -6525,7 +6603,7 @@ bool DbgModule::ReadCOFF(DataStream* stream, DbgModuleKind moduleKind)
 			//if (!newType->mMethodList.IsEmpty())
 			if (!newType->mIsDeclaration)
 				HotReplaceType(newType);
-		}
+		}		
 	}
 
 	if (needHotTargetMemory != 0)

+ 4 - 2
IDEHelper/DbgModule.h

@@ -665,8 +665,8 @@ public:
 	class SubprogramRecord
 	{
 	public:
-		Array<DbgLineInfoCtx, AllocatorBump<DbgLineInfoCtx> > mContexts;
-		Array<DbgLineData, AllocatorBump<DbgLineData> > mLines;
+		Array<DbgLineInfoCtx, AllocatorBump> mContexts;
+		Array<DbgLineData, AllocatorBump> mLines;
 		int mCurContext;
 		bool mHasInlinees;
 	};
@@ -1273,6 +1273,8 @@ public:
 	void DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolveSymbolAddr, PE_SymInfo* symInfo);
 	void ParseHotTargetSections(DataStream* stream, addr_target* resovledSymbolAddrs);
 	void CommitHotTargetSections();
+	bool HasHotReplacedMethods(DbgType* type);
+	void HotReplaceMethods(DbgType* newType, DbgType* primaryType);
 	void HotReplaceType(DbgType* newType);
 	void ProcessHotSwapVariables();
 	virtual bool LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { return false; }

+ 5 - 2
IDEHelper/DebugTarget.cpp

@@ -33,6 +33,7 @@ DebugTarget::DebugTarget(WinDebugger* debugger)
 	mHotHeapAddr = 0;
 	mHotHeapReserveSize = 0;
 	mLastHotHeapCleanIdx = 0;
+	mVDataHotIdx = -1;
 	mIsEmpty = false;
 	mWasLocallyBuilt = false;
 	mCurModuleId = 0;
@@ -2489,15 +2490,17 @@ DbgBreakKind DebugTarget::GetDbgBreakKind(addr_target address, CPURegisters* reg
 	return DbgBreakKind_None;
 }
 
+#define ADDR_ROUGH(address) ((address) & ~0x3FFF)
+
 DbgModule* DebugTarget::FindDbgModuleForAddress(addr_target address)
 {
-	addr_target checkAddr = address & ~0xFFFF;
+	addr_target checkAddr = ADDR_ROUGH(address);
 	FindDbgModuleCacheEntry* valuePtr = NULL;
 	if (mFindDbgModuleCache.TryAdd(checkAddr, NULL, &valuePtr))
 	{
 		for (auto dwarf : mDbgModules)
 		{
-			if ((address >= dwarf->mImageBase) && (address < dwarf->mImageBase + dwarf->mImageSize))
+			if ((checkAddr >= ADDR_ROUGH(dwarf->mImageBase)) && (checkAddr <= ADDR_ROUGH(dwarf->mImageBase + dwarf->mImageSize)))
 			{
 				if (valuePtr->mFirst == NULL)
 				{

+ 1 - 0
IDEHelper/DebugTarget.h

@@ -49,6 +49,7 @@ public:
 	addr_target mHotHeapAddr;
 	int64 mHotHeapReserveSize;
 	int mLastHotHeapCleanIdx;
+	int mVDataHotIdx;
 	String mTargetPath;
 	DbgModule* mLaunchBinary;
 	DbgModule* mTargetBinary;

+ 1 - 1
IDEHelper/IDEHelper.vcxproj.user

@@ -20,6 +20,6 @@
     <LocalDebuggerEnvironment>_NO_DEBUG_HEAP=1</LocalDebuggerEnvironment>
   </PropertyGroup>
   <PropertyGroup>
-    <ShowAllFiles>true</ShowAllFiles>
+    <ShowAllFiles>false</ShowAllFiles>
   </PropertyGroup>
 </Project>

+ 8 - 0
IDEHelper/WinDebugger.cpp

@@ -1151,9 +1151,14 @@ void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
 
 	int startingModuleIdx = (int)mDebugTarget->mDbgModules.size();
 
+	bool hasHotVData = false;
+
 	bool failed = false;
 	for (auto fileName : objectFiles)
 	{
+		if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1))
+			hasHotVData = true;
+
 		BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str());
 		DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx);
 		if ((newBinary != NULL) && (newBinary->mFailed))
@@ -1186,6 +1191,9 @@ void WinDebugger::HotLoad(const Array<String>& objectFiles, int hotIdx)
 
 	mDebugTarget->RehupSrcFiles();
 
+	if (hasHotVData)
+		mDebugTarget->mVDataHotIdx = hotIdx;
+
 	for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++)
 	{
 		auto breakpoint = mBreakpoints[breakIdx];