diff --git a/BeefySysLib/BeefySysLib.vcxproj b/BeefySysLib/BeefySysLib.vcxproj
index 66e34fd1..e201b14e 100644
--- a/BeefySysLib/BeefySysLib.vcxproj
+++ b/BeefySysLib/BeefySysLib.vcxproj
@@ -2184,6 +2184,7 @@ copy /y "$(OutDir)$(TargetName).lib" "$(SolutionDir)\BeefLibs\Beefy2D\dist\"
+
diff --git a/BeefySysLib/BeefySysLib.vcxproj.filters b/BeefySysLib/BeefySysLib.vcxproj.filters
index 64a87168..85fb8ba1 100644
--- a/BeefySysLib/BeefySysLib.vcxproj.filters
+++ b/BeefySysLib/BeefySysLib.vcxproj.filters
@@ -1132,6 +1132,9 @@
src\third_party\putty
+
+ src\util
+
diff --git a/BeefySysLib/util/Array.h b/BeefySysLib/util/Array.h
index 6781e333..7ef9c577 100644
--- a/BeefySysLib/util/Array.h
+++ b/BeefySysLib/util/Array.h
@@ -4,16 +4,16 @@
NS_BF_BEGIN;
-template
class AllocatorCLib
{
public:
+ template
T* allocate(intptr count)
{
return (T*)malloc(sizeof(T) * count);
}
- void deallocate(T* ptr)
+ void deallocate(void* ptr)
{
free(ptr);
}
@@ -27,9 +27,14 @@ public:
{
free(ptr);
}
+
+ bool deallocateAll()
+ {
+ return false;
+ }
};
-template >
+template
class ArrayBase : public TAlloc
{
public:
@@ -445,7 +450,7 @@ protected:
void SetBufferSize(intptr newSize)
{
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@@ -625,7 +630,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -653,7 +658,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -682,7 +687,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + count, this->mAllocSize + this->mAllocSize / 2 + 1);
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -764,7 +769,7 @@ public:
protected:
void SetBufferSize(intptr newSize)
{
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@@ -928,7 +933,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -956,7 +961,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -984,7 +989,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -1102,7 +1107,7 @@ public:
}
};
-template >
+template
class Array : public ArrayImpl::value>
{
public:
diff --git a/BeefySysLib/util/BumpAllocator.h b/BeefySysLib/util/BumpAllocator.h
index 5b2037b3..9a136fda 100644
--- a/BeefySysLib/util/BumpAllocator.h
+++ b/BeefySysLib/util/BumpAllocator.h
@@ -255,23 +255,24 @@ class BumpAllocator : public BumpAllocatorT<0x2000>
};
-template
-class AllocatorBump
+template
+class AllocatorBumpT
{
public:
BumpAllocatorT* mAlloc;
- AllocatorBump()
+ AllocatorBumpT()
{
mAlloc = NULL;
}
+ template
T* allocate(intptr count)
{
return (T*)mAlloc->AllocBytes((int)(sizeof(T) * count), alignof(T));
}
- void deallocate(T* ptr)
+ void deallocate(void* ptr)
{
}
@@ -286,26 +287,9 @@ public:
}
};
-template
-class RawAllocatorBump
+class AllocatorBump : public AllocatorBumpT<0x2000>
{
-public:
- BumpAllocatorT* mAlloc;
- RawAllocatorBump()
- {
- mAlloc = NULL;
- }
-
- void* rawAllocate(intptr size)
- {
- return mAlloc->AllocBytes(size, 16);
- }
-
- void rawDeallocate(void* ptr)
- {
-
- }
};
NS_BF_END
diff --git a/BeefySysLib/util/Deque.h b/BeefySysLib/util/Deque.h
index 096bb671..af436ca2 100644
--- a/BeefySysLib/util/Deque.h
+++ b/BeefySysLib/util/Deque.h
@@ -8,7 +8,7 @@ NS_BF_BEGIN;
#define DEQUE_IDX(i) (this->mVals[((i) + this->mOffset) % this->mAllocSize])
#define DEQUE_IDX_ON(arr, i) ((arr).mVals[((i) + (arr).mOffset) % (arr).mAllocSize])
-template >
+template
class DequeBase : public TAlloc
{
public:
@@ -370,7 +370,7 @@ protected:
void Grow(intptr newSize)
{
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@@ -707,7 +707,7 @@ class DequeImpl : public DequeBase
protected:
void Grow(intptr newSize)
{
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@@ -1043,7 +1043,7 @@ public:
}
};
-template >
+template
class Deque : public DequeImpl::value>
{
public:
diff --git a/BeefySysLib/util/Dictionary.h b/BeefySysLib/util/Dictionary.h
index 11912fd7..d9f5341e 100644
--- a/BeefySysLib/util/Dictionary.h
+++ b/BeefySysLib/util/Dictionary.h
@@ -9,7 +9,7 @@ NS_BF_BEGIN;
#ifdef NEW_DICTIONAY
-template >
+template
class Dictionary : public TAlloc
{
public:
diff --git a/BeefySysLib/util/HashSet.h b/BeefySysLib/util/HashSet.h
index 725c2876..d72ff78d 100644
--- a/BeefySysLib/util/HashSet.h
+++ b/BeefySysLib/util/HashSet.h
@@ -5,7 +5,7 @@
NS_BF_BEGIN;
-template >
+template
class HashSet : public TAlloc
{
public:
diff --git a/BeefySysLib/util/MultiDictionary.h b/BeefySysLib/util/MultiDictionary.h
new file mode 100644
index 00000000..a960f110
--- /dev/null
+++ b/BeefySysLib/util/MultiDictionary.h
@@ -0,0 +1,523 @@
+#pragma once
+
+#include "../Common.h"
+#include "Array.h"
+
+NS_BF_BEGIN;
+
+struct MultiDictionaryFuncs : AllocatorCLib
+{
+ template
+ size_t GetHash(const T& value)
+ {
+ return BeefHash()(value);
+ }
+
+ template
+ bool Matches(const T& lhs, const T& rhs)
+ {
+ return lhs == rhs;
+ }
+};
+
+template
+class MultiDictionary : public TFuncs
+{
+public:
+ struct Entry
+ {
+ TKey mKey;
+ TValue mValue;
+ int mNext;
+ int mHashCode;
+ };
+
+ struct EntryRef
+ {
+ public:
+ MultiDictionary* mSet;
+ int mIndex;
+
+ public:
+ EntryRef()
+ {
+ mSet = NULL;
+ mIndex = -1;
+ }
+
+ EntryRef(MultiDictionary* set, int index)
+ {
+ mSet = set;
+ mIndex = index;
+ }
+
+ Entry* operator*()
+ {
+ return &this->mSet->mEntries[this->mIndex];
+ }
+
+ Entry* operator->()
+ {
+ return &this->mSet->mEntries[this->mIndex];
+ }
+
+ operator bool() const
+ {
+ return this->mIndex != -1;
+ }
+ };
+
+ struct Iterator
+ {
+ public:
+ MultiDictionary* 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(newSize);
+
+ for (int i = 0; i < mCount; i++)
+ {
+ auto& newEntry = newEntries[i];
+ auto& oldEntry = mEntries[i];
+ newEntry.mHashCode = oldEntry.mHashCode;
+ newEntry.mNext = oldEntry.mNext;
+ new (&newEntry.mKey) TKey(std::move(*(TKey*)&oldEntry.mKey));
+ new (&newEntry.mValue) TValue(std::move(*(TValue*)&oldEntry.mValue));
+ }
+ for (int i = mCount; i < newSize; i++)
+ {
+ newEntries[i].mHashCode = -1;
+ }
+
+ TFuncs::deallocate(mEntries);
+
+ mEntries = newEntries;
+ mAllocSize = (int)newSize;
+ }
+
+ void FreeIdx(int entryIdx)
+ {
+ this->mEntries[entryIdx].mNext = this->mFreeList;
+ this->mFreeList = entryIdx;
+ this->mFreeCount++;
+ }
+
+ int AllocEntry()
+ {
+ int index;
+ if (this->mFreeCount > 0)
+ {
+ index = this->mFreeList;
+ this->mFreeList = this->mEntries[index].mNext;
+ this->mFreeCount--;
+ }
+ else
+ {
+ if (this->mCount == this->mAllocSize)
+ ResizeEntries();
+ index = mCount;
+ this->mCount++;
+ }
+ return index;
+ }
+
+public:
+ int* mHashHeads;
+ int mAllocSize;
+ Entry* mEntries;
+ int mFreeList;
+ int mFreeCount;
+
+ static const int cDefaultHashSize = 17;
+ int mHashSize;
+ int mCount;
+
+ MultiDictionary()
+ {
+ this->mHashHeads = NULL;
+ this->mHashSize = cDefaultHashSize;
+ this->mEntries = NULL;
+ this->mAllocSize = 0;
+ this->mCount = 0;
+ this->mFreeList = -1;
+ this->mFreeCount = 0;
+ }
+
+ ~MultiDictionary()
+ {
+ this->Clear();
+ }
+
+ void EnsureFreeCount(int wantFreeCount)
+ {
+ int freeCount = mFreeCount + (mAllocSize - mCount);
+ if (freeCount >= wantFreeCount)
+ return;
+ ResizeEntries(BF_MAX(ExpandSize(mCount), mAllocSize + wantFreeCount - freeCount));
+ }
+
+ int GetCount() const
+ {
+ return mCount - mFreeCount;
+ }
+
+ int size() const
+ {
+ return mCount - mFreeCount;
+ }
+
+ EntryRef AddRaw(int hash)
+ {
+ if (this->mHashHeads == NULL)
+ {
+ this->mHashHeads = TFuncs::allocate(mHashSize);
+ memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
+ }
+
+ int index = AllocEntry();
+ int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+ int headEntry = this->mHashHeads[hashIdx];
+
+ Entry* newEntry = &mEntries[index];
+ newEntry->mValue = T();
+ newEntry->mNext = headEntry;
+ newEntry->mHashCode = hash;
+
+ mHashHeads[hashIdx] = index;
+
+ return EntryRef(this, index);
+ }
+
+ void Add(TKey key, TValue value)
+ {
+ if (this->mHashHeads == NULL)
+ {
+ this->mHashHeads = TFuncs::allocate(mHashSize);
+ memset(this->mHashHeads, -1, sizeof(int) * mHashSize);
+ }
+
+ int index = AllocEntry();
+ int hash = TFuncs::GetHash(key);
+ int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+ int headEntry = this->mHashHeads[hashIdx];
+
+ Entry* newEntry = &mEntries[index];
+ newEntry->mKey = key;
+ newEntry->mValue = value;
+ newEntry->mNext = headEntry;
+ newEntry->mHashCode = hash;
+
+ mHashHeads[hashIdx] = index;
+ }
+
+ void AddAfter(TKey key, TValue value, Entry* afterEntry)
+ {
+ int hash = TFuncs::GetHash(key);
+ int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+ BF_ASSERT(hash == afterEntry->mHashCode);
+
+ int index = AllocEntry();
+ Entry* newEntry = &mEntries[index];
+ newEntry->mKey = key;
+ newEntry->mValue = value;
+ newEntry->mNext = afterEntry->mNext;
+ newEntry->mHashCode = hash;
+
+ afterEntry->mNext = index;
+ }
+
+ void Rehash(int newHashSize)
+ {
+ auto newHashHeads = TFuncs::allocate(newHashSize);
+ memset(newHashHeads, -1, sizeof(int) * newHashSize);
+
+ if (mHashHeads != NULL)
+ {
+ SizedArray entryList;
+ for (int hashIdx = 0; hashIdx < mHashSize; hashIdx++)
+ {
+ int checkEntryIdx = mHashHeads[hashIdx];
+ if (checkEntryIdx != -1)
+ {
+ // We want to keep elements with equal hashes in their insert order so we need to
+ // iterate through the linked list in reverse
+ entryList.Clear();
+
+ while (checkEntryIdx != -1)
+ {
+ entryList.Add(checkEntryIdx);
+ checkEntryIdx = mEntries[checkEntryIdx].mNext;
+ }
+
+ for (int i = (int)entryList.mSize - 1; i >= 0; i--)
+ {
+ int checkEntryIdx = entryList[i];
+ auto checkEntry = &mEntries[checkEntryIdx];
+ int newHashIdx = (checkEntry->mHashCode & 0x7FFFFFFF) % newHashSize;
+ checkEntry->mNext = newHashHeads[newHashIdx];
+ newHashHeads[newHashIdx] = checkEntryIdx;
+ }
+ }
+ }
+
+ TFuncs::deallocate(mHashHeads);
+ }
+ mHashHeads = newHashHeads;
+ mHashSize = newHashSize;
+ }
+
+ void CheckRehash()
+ {
+ // Make the lookup load reasonable
+ if (mHashSize < mCount)
+ {
+ this->Rehash(BF_MAX(mCount, (int)(mHashSize * 1.5f)) | 1);
+ }
+ }
+
+ template
+ bool TryGet(const TKey& key, TKey* outKey, TValue* outValue)
+ {
+ if (mHashHeads == NULL)
+ return false;
+
+ this->CheckRehash();
+
+ int hash = TFuncs::GetHash(key);
+ int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+ int checkEntryIdx = this->mHashHeads[hashIdx];
+ while (checkEntryIdx != -1)
+ {
+ Entry* checkEntry = &mEntries[checkEntryIdx];
+ if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
+ {
+ if (outKey != NULL)
+ *outKey = checkEntry->mKey;
+ if (outValue != NULL)
+ *outValue = checkEntry->mValue;
+ return true;
+ }
+ checkEntryIdx = checkEntry->mNext;
+ }
+ return false;
+ }
+
+ template
+ 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
+ bool Remove(const TKey& key)
+ {
+ if (mHashHeads == NULL)
+ return false;
+
+ this->CheckRehash();
+
+ int hash = TFuncs::GetHash(key);
+ int hashIdx = (hash & 0x7FFFFFFF) % this->mHashSize;
+
+ int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
+ int checkEntryIdx = *srcCheckEntryPtr;
+ while (checkEntryIdx != -1)
+ {
+ auto checkEntry = &mEntries[checkEntryIdx];
+ if ((checkEntry->mHashCode == hash) && (TFuncs::Matches(key, checkEntry->mKey)))
+ {
+ *srcCheckEntryPtr = checkEntry->mNext;
+ FreeIdx(checkEntryIdx);
+ return true;
+ }
+ srcCheckEntryPtr = &checkEntry->mNext;
+ checkEntryIdx = checkEntry->mNext;
+ }
+ return false;
+ }
+
+ Iterator Erase(const Iterator& itr)
+ {
+ Iterator next = itr;
+ ++next;
+
+ bool found = false;
+
+ auto entryIdx = itr.mCurEntry;
+ auto entry = &mEntries[entryIdx];
+ int hashIdx = (entry->mHashCode & 0x7FFFFFFF) % this->mHashSize;
+
+ int* srcCheckEntryPtr = &this->mHashHeads[hashIdx];
+ int checkEntryIdx = *srcCheckEntryPtr;
+ while (checkEntryIdx != -1)
+ {
+ auto checkEntry = &mEntries[checkEntryIdx];
+ if (checkEntryIdx == itr.mCurEntry)
+ {
+ *srcCheckEntryPtr = checkEntry->mNext;
+ found = true;
+ }
+ srcCheckEntryPtr = &checkEntry->mNext;
+ checkEntryIdx = checkEntry->mNext;
+ }
+
+ BF_ASSERT(found);
+ FreeIdx(entryIdx);
+
+ return next;
+ }
+
+ void Clear()
+ {
+ if (!TFuncs::deallocateAll())
+ {
+ auto itr = begin();
+ auto endItr = end();
+ while (itr != endItr)
+ {
+ auto entry = itr.mCurEntry;
+ ++itr;
+ FreeIdx(entry);
+ }
+ TFuncs::deallocate(this->mHashHeads);
+ TFuncs::deallocate(this->mEntries);
+ }
+
+ this->mHashSize = cDefaultHashSize;
+ this->mHashHeads = NULL;
+ this->mEntries = NULL;
+ this->mCount = 0;
+ }
+
+ Iterator begin()
+ {
+ return ++Iterator(this);
+ }
+
+ Iterator end()
+ {
+ Iterator itr(this);
+ itr.mCurBucket = this->mHashSize;
+ return itr;
+ }
+};
+
+NS_BF_END;
\ No newline at end of file
diff --git a/BeefySysLib/util/MultiHashSet.h b/BeefySysLib/util/MultiHashSet.h
index 79b04b11..8e6cc16d 100644
--- a/BeefySysLib/util/MultiHashSet.h
+++ b/BeefySysLib/util/MultiHashSet.h
@@ -30,7 +30,7 @@ struct MultiHashSetFuncs
}
};
-template >
+template
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(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(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(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(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;
diff --git a/BeefySysLib/util/SizedArray.h b/BeefySysLib/util/SizedArray.h
index 51071e59..772635dc 100644
--- a/BeefySysLib/util/SizedArray.h
+++ b/BeefySysLib/util/SizedArray.h
@@ -6,7 +6,7 @@
NS_BF_BEGIN;
-template >
+template
class SizedArrayBase : protected TAlloc
{
public:
@@ -378,7 +378,7 @@ protected:
void Grow(intptr newSize)
{
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@@ -533,7 +533,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -562,7 +562,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -638,7 +638,7 @@ public:
protected:
void Grow(intptr newSize)
{
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (this->mSize > 0)
@@ -761,7 +761,7 @@ public:
{
intptr newSize = this->mAllocSize + this->mAllocSize / 2 + 1;
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -790,7 +790,7 @@ public:
{
intptr newSize = BF_MAX(this->mSize + size, this->mAllocSize + this->mAllocSize / 2 + 1);
- T* newVals = TAlloc::allocate(newSize);
+ T* newVals = TAlloc::allocate(newSize);
if (this->mVals != NULL)
{
if (idx > 0) // Copy left of idx
@@ -854,14 +854,14 @@ public:
}
};
-template >
+template
class SizedArrayImpl : public SizedArrayBaseT::value>
{
public:
typedef SizedArrayBaseT::value> _Base;
};
-template >
+template
class SizedArray : public SizedArrayImpl
{
public:
diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h
index 374564dd..8e96ec9a 100644
--- a/IDEHelper/Compiler/BfModule.h
+++ b/IDEHelper/Compiler/BfModule.h
@@ -1063,8 +1063,8 @@ public:
BfIRBlock mIRHeadBlock;
BfIRBlock mIRInitBlock;
BfIRBlock mIREntryBlock;
- Array > mLocals;
- HashSet > mLocalVarSet;
+ Array mLocals;
+ HashSet mLocalVarSet;
Array mLocalMethods;
Dictionary mLocalMethodMap;
Dictionary mLocalMethodCache; // So any lambda 'capturing' and 'processing' stages use the same local method
diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h
index ae9e8494..bd9f85e8 100644
--- a/IDEHelper/Compiler/BfResolvedTypeUtils.h
+++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h
@@ -2690,11 +2690,7 @@ public:
void ReportMemory(MemReporter* memReporter);
};
-class BfResolvedTypeSetFuncs : public MultiHashSetFuncs
-{
-};
-
-class BfResolvedTypeSet : public MultiHashSet
+class BfResolvedTypeSet : public MultiHashSet
{
public:
enum BfHashFlags
@@ -2749,7 +2745,7 @@ public:
BfTypeDef* ResolveToTypeDef(BfTypeReference* typeReference, BfType** outType = NULL);
};
- class Iterator : public MultiHashSet::Iterator
+ class Iterator : public MultiHashSet::Iterator
{
public:
Iterator(MultiHashSet* set) : MultiHashSet::Iterator(set)
diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h
index d08c1b81..c3c4fa9c 100644
--- a/IDEHelper/Compiler/BfSystem.h
+++ b/IDEHelper/Compiler/BfSystem.h
@@ -1254,7 +1254,7 @@ public:
bool HasCustomAttributes();
};
-struct BfTypeDefMapFuncs : public MultiHashSetFuncs
+struct BfTypeDefMapFuncs : public AllocatorCLib
{
int GetHash(BfTypeDef* typeDef)
{
diff --git a/IDEHelper/DbgModule.cpp b/IDEHelper/DbgModule.cpp
index 90334231..68f6b699 100644
--- a/IDEHelper/DbgModule.cpp
+++ b/IDEHelper/DbgModule.cpp
@@ -21,6 +21,7 @@
#include "Compiler/BfDemangler.h"
#include "BeefySysLib/util/Hash.h"
#include "BeefySysLib/util/BeefPerf.h"
+#include "BeefySysLib/util/MultiDictionary.h"
#include "DbgSymSrv.h"
#include "MiniDumpDebugger.h"
@@ -5173,8 +5174,7 @@ void DbgModule::ParseHotTargetSections(DataStream* stream, addr_target* resolved
stream->Read(&coffReloc, sizeof(COFFRelocation));
PE_SymInfo* symInfo = (PE_SymInfo*)&mSymbolData[coffReloc.mSymbolTableIndex * 18];
- //const char* symName = mSymbolData[coffReloc.mSymbolTableIndex];
-
+
bool isStaticSymbol = symInfo->mStorageClass == COFF_SYM_CLASS_STATIC;
if (symInfo->mNameOfs[0] != 0)
@@ -5313,27 +5313,20 @@ void DbgModule::CommitHotTargetSections()
}
}
-void DbgModule::HotReplaceType(DbgType* newType)
+bool DbgModule::HasHotReplacedMethods(DbgType* type)
+{
+ for (auto newMethod : type->mMethodList)
+ {
+ if ((newMethod->mBlock.mLowPC >= mImageBase) && (newMethod->mBlock.mHighPC <= mImageBase + mImageSize))
+ return true;
+ }
+ return false;
+}
+
+void DbgModule::HotReplaceMethods(DbgType* newType, DbgType* primaryType)
{
auto linkedModule = GetLinkedModule();
- newType->PopulateType();
- DbgType* primaryType = linkedModule->GetPrimaryType(newType);
- if (primaryType == newType)
- {
- // There was no previous type
- BF_ASSERT(primaryType->mHotNewType == NULL);
- return;
- }
-
- if (primaryType->mHotNewType != newType)
- {
- // We have already pulled in the new data from a previous new type
- BF_ASSERT(primaryType->mHotNewType == NULL);
- return;
- }
- primaryType->mHotNewType = NULL;
-
primaryType->PopulateType();
linkedModule->ParseGlobalsData();
linkedModule->ParseSymbolData();
@@ -5354,183 +5347,264 @@ void DbgModule::HotReplaceType(DbgType* newType)
// Now actually remove the linedata from the defining module
HashSet checkedFiles;
- for (auto method : primaryType->mMethodList)
- {
- //method->mWasModuleHotReplaced = true;
- method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
+ //for (auto method : primaryType->mMethodList)
- if (method->mLineInfo == NULL)
- continue;
+ auto _RemoveLineInfo = [&](DbgSubprogram* method)
+ {
+ //method->mWasModuleHotReplaced = true;
+ method->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Orphaned; // May be temporarily orphaned
- //FIXME: Hot replacing lines
- DbgSrcFile* lastSrcFile = NULL;
- checkedFiles.Clear();
+ if (method->mLineInfo == NULL)
+ return;
- int prevCtx = -1;
- auto inlineRoot = method->GetRootInlineParent();
- for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++)
- {
- auto& lineData = method->mLineInfo->mLines[lineIdx];
- if (lineData.mCtxIdx != prevCtx)
+ //FIXME: Hot replacing lines
+ DbgSrcFile* lastSrcFile = NULL;
+ checkedFiles.Clear();
+
+ int prevCtx = -1;
+ auto inlineRoot = method->GetRootInlineParent();
+ for (int lineIdx = 0; lineIdx < method->mLineInfo->mLines.mSize; lineIdx++)
{
- auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx];
- auto srcFile = ctxInfo.mSrcFile;
- prevCtx = lineData.mCtxIdx;
- if (srcFile != lastSrcFile)
+ auto& lineData = method->mLineInfo->mLines[lineIdx];
+ if (lineData.mCtxIdx != prevCtx)
{
- if (checkedFiles.Add(srcFile))
+ auto ctxInfo = inlineRoot->mLineInfo->mContexts[lineData.mCtxIdx];
+ auto srcFile = ctxInfo.mSrcFile;
+ prevCtx = lineData.mCtxIdx;
+ if (srcFile != lastSrcFile)
{
- // Remove linedata for old type
- // These go into a hot-replaced list so we can still bind to them -- that is necessary because
- // we may still have old versions of this method running (and may forever, if its in a loop on some thread)
- // since we only patch entry points
- //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true);
+ if (checkedFiles.Add(srcFile))
+ {
+ // Remove linedata for old type
+ // These go into a hot-replaced list so we can still bind to them -- that is necessary because
+ // we may still have old versions of this method running (and may forever, if its in a loop on some thread)
+ // since we only patch entry points
+ //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, primaryType->mCompileUnit, true);
- //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true);
- srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true);
+ //srcFile->RemoveLines(primaryType->mCompileUnit->mDbgModule, method, true);
+ srcFile->RemoveLines(method->mCompileUnit->mDbgModule, method, true);
+ }
+
+ lastSrcFile = srcFile;
}
-
- lastSrcFile = srcFile;
}
}
- }
- }
+ };
//DbgType* primaryType = newType->GetPrimaryType();
- // We need to keep a persistent list of hot replaced methods so we can set hot jumps
- // in old methods that may still be on the callstack. These entries get removed when
- // we unload unused hot files in
- while (!primaryType->mMethodList.IsEmpty())
+ MultiDictionary oldProgramMap;
+ auto _AddToHotReplacedMethodList = [&](DbgSubprogram* oldMethod)
+ {
+ int hotIdx = oldMethod->mCompileUnit->mDbgModule->mHotIdx;
+ if ((hotIdx > 0) && (hotIdx < mDebugTarget->mVDataHotIdx))
+ {
+ // Too old
+ return;
+ }
+
+ oldMethod->PopulateSubprogram();
+ if (oldMethod->mBlock.IsEmpty())
+ return;
+ auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
+ if (symInfo != NULL)
+ {
+ oldProgramMap.Add(StringView(symInfo->mName), oldMethod);
+ }
+ };
+
+ if (newType != primaryType)
{
- auto method = primaryType->mMethodList.PopFront();
-
- method->PopulateSubprogram();
-
- primaryType->mHotReplacedMethodList.PushFront(method);
- mHotPrimaryTypes.Add(primaryType);
+ // We need to keep a persistent list of hot replaced methods so we can set hot jumps
+ // in old methods that may still be on the callstack. These entries get removed when
+ // we unload unused hot files in
+ while (!primaryType->mMethodList.IsEmpty())
+ {
+ auto method = primaryType->mMethodList.PopFront();
+ method->PopulateSubprogram();
+ primaryType->mHotReplacedMethodList.PushFront(method);
+ mHotPrimaryTypes.Add(primaryType);
+ }
}
-
- Dictionary oldProgramMap;
+
for (auto oldMethod : primaryType->mHotReplacedMethodList)
{
- oldMethod->PopulateSubprogram();
- if (oldMethod->mBlock.IsEmpty())
- continue;
- auto symInfo = mDebugTarget->mSymbolMap.Get(oldMethod->mBlock.mLowPC);
- if (symInfo != NULL)
- {
- oldProgramMap.TryAdd(symInfo->mName, oldMethod);
- }
+ _AddToHotReplacedMethodList(oldMethod);
}
bool setHotJumpFailed = false;
- while (!newType->mMethodList.IsEmpty())
- {
- DbgSubprogram* newMethod = newType->mMethodList.PopFront();
- if (!newMethod->mBlock.IsEmpty())
+
+ auto _HotJump = [&](DbgSubprogram* oldMethod, DbgSubprogram* newMethod)
{
- BfLogDbg("Hot added new method %p %s Address:%p\n", newMethod, newMethod->mName, newMethod->mBlock.mLowPC);
+ bool doHotJump = false;
- newMethod->PopulateSubprogram();
-
- auto symInfo = mDebugTarget->mSymbolMap.Get(newMethod->mBlock.mLowPC);
- if (symInfo != NULL)
+ if (oldMethod->Equals(newMethod))
{
- DbgSubprogram* oldMethod = NULL;
- if (oldProgramMap.TryGetValue(symInfo->mName, &oldMethod))
+ doHotJump = true;
+ }
+ else
+ {
+ // When mangles match but the actual signatures don't match, that can mean that the call signature was changed
+ // and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed.
+ // When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard
+ // fail if the old code gets called to avoid confusion of "why aren't my changes working?"
+
+ // If we removed captures then we can still do the hot jump. Otherwise we have to fail...
+ doHotJump = false;
+ if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) &&
+ (oldMethod->mHasThis) && (newMethod->mHasThis))
{
- bool doHotJump = false;
+ auto oldParam = oldMethod->mParams.front();
+ auto newParam = newMethod->mParams.front();
- if (oldMethod->Equals(newMethod))
+ if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer()))
{
- doHotJump = true;
- }
- else
- {
- // When mangles match but the actual signatures don't match, that can mean that the call signature was changed
- // and thus it's actually a different method and shouldn't hot jump OR it could be lambda whose captures changed.
- // When the lambda captures change, the user didn't actually enter a different signature so we want to do a hard
- // fail if the old code gets called to avoid confusion of "why aren't my changes working?"
-
- // If we removed captures then we can still do the hot jump. Otherwise we have to fail...
- doHotJump = false;
- if ((oldMethod->IsLambda()) && (oldMethod->Equals(newMethod, true)) &&
- (oldMethod->mHasThis) && (newMethod->mHasThis))
+ auto oldType = oldParam->mType->mTypeParam->GetPrimaryType();
+ oldType->PopulateType();
+ auto newType = newParam->mType->mTypeParam->GetPrimaryType();
+ newType->PopulateType();
+ if ((oldType->IsStruct()) && (newType->IsStruct()))
{
- auto oldParam = oldMethod->mParams.front();
- auto newParam = newMethod->mParams.front();
+ bool wasMatch = true;
- if ((oldParam->mType->IsPointer()) && (newParam->mType->IsPointer()))
+ auto oldMember = oldType->mMemberList.front();
+ auto newMember = newType->mMemberList.front();
+ while (newMember != NULL)
{
- auto oldType = oldParam->mType->mTypeParam->GetPrimaryType();
- oldType->PopulateType();
- auto newType = newParam->mType->mTypeParam->GetPrimaryType();
- newType->PopulateType();
- if ((oldType->IsStruct()) && (newType->IsStruct()))
+ if (oldMember == NULL)
{
- bool wasMatch = true;
-
- auto oldMember = oldType->mMemberList.front();
- auto newMember = newType->mMemberList.front();
- while (newMember != NULL)
- {
- if (oldMember == NULL)
- {
- wasMatch = false;
- break;
- }
-
- if ((oldMember->mName == NULL) || (newMember->mName == NULL))
- {
- wasMatch = false;
- break;
- }
-
- if (strcmp(oldMember->mName, newMember->mName) != 0)
- {
- wasMatch = false;
- break;
- }
-
- if (!oldMember->mType->Equals(newMember->mType))
- {
- wasMatch = false;
- break;
- }
-
- oldMember = oldMember->mNext;
- newMember = newMember->mNext;
- }
-
- if (wasMatch)
- doHotJump = true;
+ wasMatch = false;
+ break;
}
+
+ if ((oldMember->mName == NULL) || (newMember->mName == NULL))
+ {
+ wasMatch = false;
+ break;
+ }
+
+ if (strcmp(oldMember->mName, newMember->mName) != 0)
+ {
+ wasMatch = false;
+ break;
+ }
+
+ if (!oldMember->mType->Equals(newMember->mType))
+ {
+ wasMatch = false;
+ break;
+ }
+
+ oldMember = oldMember->mNext;
+ newMember = newMember->mNext;
}
- if (!doHotJump)
- {
- mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC);
- oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid;
- }
+ if (wasMatch)
+ doHotJump = true;
}
}
- if (doHotJump)
+ if (!doHotJump)
{
- if (!setHotJumpFailed)
- {
- if (!mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
- setHotJumpFailed = true;
- }
- oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
+ mDebugTarget->mDebugger->PhysSetBreakpoint(oldMethod->mBlock.mLowPC);
+ oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Invalid;
}
}
}
- }
- newMethod->mParentType = primaryType;
- primaryType->mMethodList.PushBack(newMethod);
+
+ if (doHotJump)
+ {
+ if (!setHotJumpFailed)
+ {
+ if (mDebugger->SetHotJump(oldMethod, newMethod->mBlock.mLowPC, (int)(newMethod->mBlock.mHighPC - newMethod->mBlock.mLowPC)))
+ {
+ _RemoveLineInfo(oldMethod);
+ }
+ else
+ setHotJumpFailed = true;
+ }
+ oldMethod->mHotReplaceKind = DbgSubprogram::HotReplaceKind_Replaced;
+ }
+ };
+
+ auto _ReplaceMethod = [&](DbgSubprogram* newMethod)
+ {
+ 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);
+ }
+ }
+}
+
+void DbgModule::HotReplaceType(DbgType* newType)
+{
+ auto linkedModule = GetLinkedModule();
+
+ newType->PopulateType();
+ DbgType* primaryType = linkedModule->GetPrimaryType(newType);
+ if (primaryType == newType)
+ {
+ // There was no previous type
+ BF_ASSERT(primaryType->mHotNewType == NULL);
+ return;
+ }
+
+ if (primaryType->mHotNewType != newType)
+ {
+ // We have already pulled in the new data from a previous new type
+ BF_ASSERT(primaryType->mHotNewType == NULL);
+ return;
+ }
+ primaryType->mHotNewType = NULL;
+
+ HotReplaceMethods(newType, primaryType);
//mDebugTarget->mSymbolMap.Get()
@@ -6506,17 +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)
diff --git a/IDEHelper/DbgModule.h b/IDEHelper/DbgModule.h
index 813401d8..6087ac2b 100644
--- a/IDEHelper/DbgModule.h
+++ b/IDEHelper/DbgModule.h
@@ -665,8 +665,8 @@ public:
class SubprogramRecord
{
public:
- Array > mContexts;
- Array > mLines;
+ Array mContexts;
+ Array mLines;
int mCurContext;
bool mHasInlinees;
};
@@ -1273,6 +1273,8 @@ public:
void DoReloc(DbgHotTargetSection* hotTargetSection, COFFRelocation& coffReloc, addr_target resolveSymbolAddr, PE_SymInfo* symInfo);
void ParseHotTargetSections(DataStream* stream, addr_target* resovledSymbolAddrs);
void CommitHotTargetSections();
+ bool HasHotReplacedMethods(DbgType* type);
+ void HotReplaceMethods(DbgType* newType, DbgType* primaryType);
void HotReplaceType(DbgType* newType);
void ProcessHotSwapVariables();
virtual bool LoadPDB(const String& pdbPath, uint8 wantGuid[16], int32 wantAge) { return false; }
diff --git a/IDEHelper/DebugTarget.cpp b/IDEHelper/DebugTarget.cpp
index 17eb7102..9437e741 100644
--- a/IDEHelper/DebugTarget.cpp
+++ b/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)
{
diff --git a/IDEHelper/DebugTarget.h b/IDEHelper/DebugTarget.h
index 1b5f060c..312744c7 100644
--- a/IDEHelper/DebugTarget.h
+++ b/IDEHelper/DebugTarget.h
@@ -49,6 +49,7 @@ public:
addr_target mHotHeapAddr;
int64 mHotHeapReserveSize;
int mLastHotHeapCleanIdx;
+ int mVDataHotIdx;
String mTargetPath;
DbgModule* mLaunchBinary;
DbgModule* mTargetBinary;
diff --git a/IDEHelper/IDEHelper.vcxproj.user b/IDEHelper/IDEHelper.vcxproj.user
index 1ce90950..75967093 100644
--- a/IDEHelper/IDEHelper.vcxproj.user
+++ b/IDEHelper/IDEHelper.vcxproj.user
@@ -20,6 +20,6 @@
_NO_DEBUG_HEAP=1
- true
+ false
\ No newline at end of file
diff --git a/IDEHelper/WinDebugger.cpp b/IDEHelper/WinDebugger.cpp
index 8b3f8d73..7783c148 100644
--- a/IDEHelper/WinDebugger.cpp
+++ b/IDEHelper/WinDebugger.cpp
@@ -1151,9 +1151,14 @@ void WinDebugger::HotLoad(const Array& objectFiles, int hotIdx)
int startingModuleIdx = (int)mDebugTarget->mDbgModules.size();
+ bool hasHotVData = false;
+
bool failed = false;
for (auto fileName : objectFiles)
{
+ if ((fileName.IndexOf("/vdata.") != -1) || (fileName.IndexOf("\\vdata.") != -1))
+ hasHotVData = true;
+
BfLogDbg("WinDebugger::HotLoad: %s\n", fileName.c_str());
DbgModule* newBinary = mDebugTarget->HotLoad(fileName, hotIdx);
if ((newBinary != NULL) && (newBinary->mFailed))
@@ -1186,6 +1191,9 @@ void WinDebugger::HotLoad(const Array& objectFiles, int hotIdx)
mDebugTarget->RehupSrcFiles();
+ if (hasHotVData)
+ mDebugTarget->mVDataHotIdx = hotIdx;
+
for (int breakIdx = 0; breakIdx < (int)mBreakpoints.size(); breakIdx++)
{
auto breakpoint = mBreakpoints[breakIdx];