Will care about internal line updates for methods in case empty lines added in method's body.
IfFailRet(pCode->CreateBreakpoint(ilNextOffset, &iCorFuncBreakpoint));\r
IfFailRet(iCorFuncBreakpoint->Activate(fbp.enabled ? TRUE : FALSE));\r
\r
- CORDB_ADDRESS modAddress;\r
- IfFailRet(entry.first->GetBaseAddress(&modAddress));\r
-\r
fbp.funcBreakpoints.emplace_back(entry.second, currentVersion, iCorFuncBreakpoint.Detach());\r
}\r
\r
return RetCode.OK;
}
+ /// <summary>
+ /// Get list of all sequence points for method.
+ /// </summary>
+ /// <param name="symbolReaderHandle">symbol reader handle returned by LoadSymbolsForModule</param>
+ /// <param name="methodToken">method token</param>
+ /// <param name="points">result - array of sequence points</param>
+ /// <param name="pointsCount">result - count of elements in array of sequence points</param>
+ /// <returns>"Ok" if information is available</returns>
+ private static RetCode GetSequencePoints(IntPtr symbolReaderHandle, int methodToken, out IntPtr points, out int pointsCount)
+ {
+ Debug.Assert(symbolReaderHandle != IntPtr.Zero);
+ var list = new List<DbgSequencePoint>();
+ pointsCount = 0;
+ points = IntPtr.Zero;
+
+ try
+ {
+ GCHandle gch = GCHandle.FromIntPtr(symbolReaderHandle);
+ MetadataReader reader = ((OpenedReader)gch.Target).Reader;
+
+ SequencePointCollection sequencePoints = GetSequencePointCollection(methodToken, reader);
+
+ foreach (SequencePoint p in sequencePoints)
+ {
+ if (p.StartLine == 0 || p.StartLine == SequencePoint.HiddenLine)
+ continue;
+
+ string fileName = reader.GetString(reader.GetDocument(p.Document).Name);
+ list.Add(new DbgSequencePoint()
+ {
+ document = Marshal.StringToBSTR(fileName),
+ startLine = p.StartLine,
+ endLine = p.EndLine,
+ startColumn = p.StartColumn,
+ endColumn = p.EndColumn,
+ offset = p.Offset
+ });
+ }
+
+ if (list.Count == 0)
+ return RetCode.Fail;
+
+ var structSize = Marshal.SizeOf<DbgSequencePoint>();
+ IntPtr allPoints = Marshal.AllocCoTaskMem(list.Count * structSize);
+ var currentPtr = allPoints;
+
+ foreach (var p in list)
+ {
+ Marshal.StructureToPtr(p, currentPtr, false);
+ currentPtr = currentPtr + structSize;
+ }
+
+ points = allPoints;
+ pointsCount = list.Count;
+ }
+ catch
+ {
+ foreach (var p in list)
+ {
+ Marshal.FreeBSTR(p.document);
+ }
+ return RetCode.Exception;
+ }
+
+ return RetCode.OK;
+ }
+
/// <summary>
/// Find IL offset for next close user code sequence point by IL offset.
/// </summary>
typedef RetCode (*GetLocalVariableNameAndScope)(PVOID, int32_t, int32_t, BSTR*, uint32_t*, uint32_t*);
typedef RetCode (*GetHoistedLocalScopes)(PVOID, int32_t, PVOID*, int32_t*);
typedef RetCode (*GetSequencePointByILOffsetDelegate)(PVOID, mdMethodDef, uint32_t, PVOID);
+typedef RetCode (*GetSequencePointsDelegate)(PVOID, mdMethodDef, PVOID*, int32_t*);
typedef RetCode (*GetNextUserCodeILOffsetDelegate)(PVOID, mdMethodDef, uint32_t, uint32_t*, int32_t*);
typedef RetCode (*GetStepRangesFromIPDelegate)(PVOID, int32_t, mdMethodDef, uint32_t*, uint32_t*);
typedef RetCode (*GetModuleMethodsRangesDelegate)(PVOID, uint32_t, PVOID, uint32_t, PVOID, PVOID*);
GetLocalVariableNameAndScope getLocalVariableNameAndScopeDelegate = nullptr;
GetHoistedLocalScopes getHoistedLocalScopesDelegate = nullptr;
GetSequencePointByILOffsetDelegate getSequencePointByILOffsetDelegate = nullptr;
+GetSequencePointsDelegate getSequencePointsDelegate = nullptr;
GetNextUserCodeILOffsetDelegate getNextUserCodeILOffsetDelegate = nullptr;
GetStepRangesFromIPDelegate getStepRangesFromIPDelegate = nullptr;
GetModuleMethodsRangesDelegate getModuleMethodsRangesDelegate = nullptr;
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetLocalVariableNameAndScope", (void **)&getLocalVariableNameAndScopeDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetHoistedLocalScopes", (void **)&getHoistedLocalScopesDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetSequencePointByILOffset", (void **)&getSequencePointByILOffsetDelegate)) &&
+ SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetSequencePoints", (void **)&getSequencePointsDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetNextUserCodeILOffset", (void **)&getNextUserCodeILOffsetDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetStepRangesFromIP", (void **)&getStepRangesFromIPDelegate)) &&
SUCCEEDED(Status = createDelegate(hostHandle, domainId, ManagedPartDllName, SymbolReaderClassName, "GetModuleMethodsRanges", (void **)&getModuleMethodsRangesDelegate)) &&
getLocalVariableNameAndScopeDelegate &&
getHoistedLocalScopesDelegate &&
getSequencePointByILOffsetDelegate &&
+ getSequencePointsDelegate &&
getNextUserCodeILOffsetDelegate &&
getStepRangesFromIPDelegate &&
getModuleMethodsRangesDelegate &&
getLocalVariableNameAndScopeDelegate = nullptr;
getHoistedLocalScopesDelegate = nullptr;
getSequencePointByILOffsetDelegate = nullptr;
+ getSequencePointsDelegate = nullptr;
getNextUserCodeILOffsetDelegate = nullptr;
getStepRangesFromIPDelegate = nullptr;
getModuleMethodsRangesDelegate = nullptr;
return retCode == RetCode::OK ? S_OK : E_FAIL;
}
+HRESULT GetSequencePoints(PVOID pSymbolReaderHandle, mdMethodDef methodToken, SequencePoint **sequencePoints, int32_t &Count)
+{
+ std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
+ if (!getSequencePointsDelegate || !pSymbolReaderHandle)
+ return E_FAIL;
+
+ RetCode retCode = getSequencePointsDelegate(pSymbolReaderHandle, methodToken, (PVOID*)sequencePoints, &Count);
+
+ return retCode == RetCode::OK ? S_OK : E_FAIL;
+}
+
HRESULT GetNextUserCodeILOffset(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG32 ilOffset, ULONG32 &ilNextOffset, bool *noUserCodeFound)
{
std::unique_lock<Utility::RWLock::Reader> read_lock(CLRrwlock.reader);
ULONG64 inMemoryPdbAddress, ULONG64 inMemoryPdbSize, VOID **ppSymbolReaderHandle);
void DisposeSymbols(PVOID pSymbolReaderHandle);
HRESULT GetSequencePointByILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, SequencePoint *sequencePoint);
+ HRESULT GetSequencePoints(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, SequencePoint **sequencePoints, int32_t &Count);
HRESULT GetNextUserCodeILOffset(PVOID pSymbolReaderHandle, mdMethodDef MethodToken, ULONG32 IlOffset, ULONG32 &ilNextOffset, bool *noUserCodeFound);
HRESULT GetNamedLocalVariableAndScope(PVOID pSymbolReaderHandle, mdMethodDef methodToken, ULONG localIndex,
WCHAR *localName, ULONG localNameLen, ULONG32 *pIlStart, ULONG32 *pIlEnd);
if (!asyncMethodSteppingInfo.awaits.empty())
asyncMethodSteppingInfo.awaits.clear();
- return m_sharedModules->GetModuleInfo(modAddress, [&](Modules::ModuleInfo &mdInfo) -> HRESULT
+ return m_sharedModules->GetModuleInfo(modAddress, [&](ModuleInfo &mdInfo) -> HRESULT
{
if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
return E_FAIL;
namespace netcoredbg
{
-Modules::ModuleInfo::~ModuleInfo() noexcept
+ModuleInfo::~ModuleInfo() noexcept
{
for (auto symbolReaderHandle : m_symbolReaderHandles)
{
std::string GetModuleFileName(ICorDebugModule *pModule);
HRESULT IsModuleHaveSameName(ICorDebugModule *pModule, const std::string &Name, bool isFullPath);
-class Modules
+struct ModuleInfo
{
-public:
+ std::vector<PVOID> m_symbolReaderHandles;
+ ToRelease<ICorDebugModule> m_iCorModule;
+ // Cache for LineUpdates data for all methods in this module (Hot Reload related).
+ method_block_updates_t m_methodBlockUpdates;
- struct ModuleInfo
+ ModuleInfo(PVOID Handle, ICorDebugModule *Module) :
+ m_iCorModule(Module)
{
- std::vector<PVOID> m_symbolReaderHandles;
- ToRelease<ICorDebugModule> m_iCorModule;
- // Cache for LineUpdates data for all methods in this module (Hot Reload related).
- method_block_updates_t m_methodBlockUpdates;
-
- ModuleInfo(PVOID Handle, ICorDebugModule *Module) :
- m_iCorModule(Module)
- {
- if (Handle == nullptr)
- return;
-
- m_symbolReaderHandles.reserve(1);
- m_symbolReaderHandles.emplace_back(Handle);
- }
-
- ModuleInfo(ModuleInfo&& other) noexcept :
- m_symbolReaderHandles(std::move(other.m_symbolReaderHandles)),
- m_iCorModule(std::move(other.m_iCorModule))
- {
- }
- ModuleInfo(const ModuleInfo&) = delete;
- ModuleInfo& operator=(ModuleInfo&&) = delete;
- ModuleInfo& operator=(const ModuleInfo&) = delete;
- ~ModuleInfo() noexcept;
- };
+ if (Handle == nullptr)
+ return;
+
+ m_symbolReaderHandles.reserve(1);
+ m_symbolReaderHandles.emplace_back(Handle);
+ }
+
+ ModuleInfo(ModuleInfo&& other) noexcept :
+ m_symbolReaderHandles(std::move(other.m_symbolReaderHandles)),
+ m_iCorModule(std::move(other.m_iCorModule))
+ {
+ }
+ ModuleInfo(const ModuleInfo&) = delete;
+ ModuleInfo& operator=(ModuleInfo&&) = delete;
+ ModuleInfo& operator=(const ModuleInfo&) = delete;
+ ~ModuleInfo() noexcept;
+};
+
+class Modules
+{
+public:
struct SequencePoint {
int32_t startLine;
return S_OK;
}
-static HRESULT LineUpdatesForMethodData(unsigned fullPathIndex, method_data_t &methodData, const std::vector<block_update_t> &blockUpdate, method_block_updates_t &methodBlockUpdates)
+HRESULT ModulesSources::LineUpdatesForMethodData(ICorDebugModule *pModule, unsigned fullPathIndex, method_data_t &methodData,
+ const std::vector<block_update_t> &blockUpdate, ModuleInfo &mdInfo)
{
+ int32_t startLineOffset = 0;
+ int32_t endLineOffset = 0;
+ std::unordered_map<std::size_t, int32_t> methodBlockOffsets;
+
for (const auto &block : blockUpdate)
{
if (block.oldLine < 0 || block.endLineOffset < 0 || methodData.endLine < 0)
return E_INVALIDARG;
+ const int32_t lineOffset = block.newLine - block.oldLine;
+
// Note, endLineOffset could be std::numeric_limits<int32_t>::max() (max line number in C# source), so, we forced to cast it first.
// Also, this is why we test that method within range and after that negate result.
- if (!(methodData.startLine >= block.oldLine &&
- (uint32_t)methodData.endLine <= (uint32_t)block.oldLine + (uint32_t)block.endLineOffset))
+ if ((uint32_t)methodData.startLine <= (uint32_t)block.oldLine + (uint32_t)block.endLineOffset && methodData.startLine >= block.oldLine)
+ {
+ startLineOffset = lineOffset;
+ }
+
+ if ((uint32_t)methodData.endLine <= (uint32_t)block.oldLine + (uint32_t)block.endLineOffset && methodData.endLine >= block.oldLine)
+ {
+ endLineOffset = lineOffset;
+ }
+
+ if ((uint32_t)methodData.startLine > (uint32_t)block.oldLine + (uint32_t)block.endLineOffset || methodData.endLine < block.oldLine)
continue;
- const int32_t codeLineOffset = block.newLine - block.oldLine;
+ // update methodBlockUpdates
- auto updateCashe = [&]() -> bool
+ auto findMethod = mdInfo.m_methodBlockUpdates.find(methodData.methodDef);
+ if (findMethod == mdInfo.m_methodBlockUpdates.end())
{
- auto findMethod = methodBlockUpdates.find(methodData.methodDef);
- if (findMethod == methodBlockUpdates.end())
- return false;
-
- for (auto &entry : findMethod->second)
+ HRESULT Status;
+ ToRelease<ICorDebugFunction> iCorFunction;
+ IfFailRet(pModule->GetFunctionFromToken(methodData.methodDef, &iCorFunction));
+ ToRelease<ICorDebugFunction2> iCorFunction2;
+ IfFailRet(iCorFunction->QueryInterface(IID_ICorDebugFunction2, (LPVOID*) &iCorFunction2));
+ ULONG32 methodVersion;
+ IfFailRet(iCorFunction2->GetVersionNumber(&methodVersion));
+
+ if (mdInfo.m_symbolReaderHandles.empty() || mdInfo.m_symbolReaderHandles.size() < methodVersion)
+ return E_FAIL;
+
+ Interop::SequencePoint *sequencePoints = nullptr;
+ int32_t count = 0;
+ if (FAILED(Status = Interop::GetSequencePoints(mdInfo.m_symbolReaderHandles[methodVersion - 1], methodData.methodDef, &sequencePoints, count)))
{
- if (fullPathIndex == entry.fullPathIndex &&
- methodData.startLine == entry.newLine &&
- methodData.endLine == entry.newLine + entry.endLineOffset)
+ if (sequencePoints)
{
- // All we need for previous stored data is change newLine, since oldLine will be the same (PDB for this method version was not changed).
- entry.newLine += codeLineOffset;
- return true;
+ for (int i = 0; i < count; i++)
+ {
+ Interop::SysFreeString(sequencePoints[i].document);
+ }
+ Interop::CoTaskMemFree(sequencePoints);
}
+
+ return Status;
}
- return false;
- };
- if (!updateCashe())
- methodBlockUpdates[methodData.methodDef].emplace_back(fullPathIndex, methodData.startLine + codeLineOffset, methodData.startLine, methodData.endLine - methodData.startLine);
- methodData.startLine += codeLineOffset;
- methodData.endLine += codeLineOffset;
+ for (int i = 0; i < count; i++)
+ {
+ unsigned index;
+ IfFailRet(GetFullPathIndex(sequencePoints[i].document, index));
+ Interop::SysFreeString(sequencePoints[i].document);
+
+ mdInfo.m_methodBlockUpdates[methodData.methodDef].emplace_back(index, sequencePoints[i].startLine, sequencePoints[i].startLine, sequencePoints[i].endLine - sequencePoints[i].startLine);
+
+ }
+
+ if (sequencePoints)
+ Interop::CoTaskMemFree(sequencePoints);
+ }
+
+ for (std::size_t i = 0; i < mdInfo.m_methodBlockUpdates[methodData.methodDef].size(); ++i)
+ {
+ auto &entry = mdInfo.m_methodBlockUpdates[methodData.methodDef][i];
+
+ if (entry.fullPathIndex != fullPathIndex ||
+ entry.newLine < block.oldLine ||
+ (uint32_t)entry.newLine + (uint32_t)entry.endLineOffset > (uint32_t)block.oldLine + (uint32_t)block.endLineOffset)
+ continue;
+
+ // Line updates file can have only one entry for each line.
+ methodBlockOffsets[i] = lineOffset;
+ }
+ }
+
+ if (!methodBlockOffsets.empty())
+ {
+ auto findMethod = mdInfo.m_methodBlockUpdates.find(methodData.methodDef);
+ assert(findMethod != mdInfo.m_methodBlockUpdates.end());
- break;
+ for (const auto &entry : methodBlockOffsets)
+ {
+ // All we need for previous stored data is change newLine, since oldLine will be the same (PDB for this method version was not changed).
+ findMethod->second[entry.first].newLine += entry.second;
+ }
}
+ if (startLineOffset == 0 && endLineOffset == 0)
+ return S_OK;
+
+ methodData.startLine += startLineOffset;
+ methodData.endLine += endLineOffset;
return S_OK;
}
HRESULT ModulesSources::UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, std::unordered_set<mdMethodDef> methodTokens,
- src_block_updates_t &srcBlockUpdates, PVOID pSymbolReaderHandle, method_block_updates_t &methodBlockUpdates)
+ src_block_updates_t &srcBlockUpdates, ModuleInfo &mdInfo)
{
std::lock_guard<std::mutex> lock(m_sourcesInfoMutex);
HRESULT Status;
std::unique_ptr<module_methods_data_t, module_methods_data_t_deleter> inputData;
- IfFailRet(GetPdbMethodsRanges(pMDImport, pSymbolReaderHandle, &methodTokens, inputData));
+ IfFailRet(GetPdbMethodsRanges(pMDImport, mdInfo.m_symbolReaderHandles.back(), &methodTokens, inputData));
struct src_update_data_t
{
{
inputMetodDefSet.insert(updateData.second.methodsData[j].methodDef);
// All sequence points related to this method were updated and provide proper lines from PDB directly.
- methodBlockUpdates.erase(updateData.second.methodsData[j].methodDef);
+ mdInfo.m_methodBlockUpdates.erase(updateData.second.methodsData[j].methodDef);
}
// Move multiMethodsData first (since this is constructors and all will be on level 0 for sure).
tmpFileMethodsData.multiMethodsData.clear();
for (auto &methodData : tmpMultiMethodsData)
{
- IfFailRet(LineUpdatesForMethodData(fullPathIndex, methodData, updateData.second.blockUpdate, methodBlockUpdates));
+ IfFailRet(LineUpdatesForMethodData(pModule, fullPathIndex, methodData, updateData.second.blockUpdate, mdInfo));
AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, methodData, 0);
}
auto findData = inputMetodDefSet.find(methodData.methodDef);
if (findData == inputMetodDefSet.end())
{
- IfFailRet(LineUpdatesForMethodData(fullPathIndex, methodData, updateData.second.blockUpdate, methodBlockUpdates));
+ IfFailRet(LineUpdatesForMethodData(pModule, fullPathIndex, methodData, updateData.second.blockUpdate, mdInfo));
AddMethodData(inputMethodsData, tmpFileMethodsData.multiMethodsData, methodData, 0);
}
}
return E_FAIL;
}
+// Note, this is breakpoint only backward correction, that will care for "closest next executable code line" in PDB stored data.
+// We can't map line from new to old PDB location, since impossible map new added line data to PDB data that don't have this line.
+// Plus, methodBlockUpdates store sequence points only data.
static void LineUpdatesBackwardCorrection(unsigned fullPathIndex, mdMethodDef methodToken, method_block_updates_t &methodBlockUpdates, int32_t &startLine)
{
auto findSourceUpdate = methodBlockUpdates.find(methodToken);
for (const auto &entry : findSourceUpdate->second)
{
if (entry.fullPathIndex != fullPathIndex ||
- entry.newLine > startLine ||
entry.newLine + entry.endLineOffset < startLine)
continue;
- startLine += entry.oldLine - entry.newLine;
+ startLine = entry.oldLine; // <- closest executable code line for requested line in old PDB data
break;
}
}
return E_FAIL;
}
- Modules::ModuleInfo *pmdInfo; // Note, pmdInfo must be covered by m_modulesInfoMutex.
+ ModuleInfo *pmdInfo; // Note, pmdInfo must be covered by m_modulesInfoMutex.
IfFailRet(pModules->GetModuleInfo(sourceData.modAddress, &pmdInfo)); // we must have it, since we loaded data from it
if (pmdInfo->m_symbolReaderHandles.empty())
continue;
{
if (lineUpdates.newLine != lineUpdates.oldLine)
{
- assert(startBlock.newLine == empty);
+ if (startBlock.newLine != empty)
+ srcBlockUpdates[updateFileData.first].emplace_back(startBlock.newLine + 1, startBlock.oldLine + 1, lineUpdates.oldLine - 1 - startBlock.oldLine);
startBlock.newLine = lineUpdates.newLine;
startBlock.oldLine = lineUpdates.oldLine;
CORDB_ADDRESS modAddress;
IfFailRet(pModule->GetBaseAddress(&modAddress));
- return pModules->GetModuleInfo(modAddress, [&](Modules::ModuleInfo &mdInfo) -> HRESULT
+ return pModules->GetModuleInfo(modAddress, [&](ModuleInfo &mdInfo) -> HRESULT
{
if (mdInfo.m_symbolReaderHandles.empty())
return E_FAIL; // Deltas could be applied for already loaded modules with PDB only.
ToRelease<IMetaDataImport> pMDImport;
IfFailRet(pMDUnknown->QueryInterface(IID_IMetaDataImport, (LPVOID*) &pMDImport));
- return UpdateSourcesCodeLinesForModule(pModule, pMDImport, methodTokens, srcBlockUpdates, pSymbolReaderHandle, mdInfo.m_methodBlockUpdates);
+ return UpdateSourcesCodeLinesForModule(pModule, pMDImport, methodTokens, srcBlockUpdates, mdInfo);
});
}
}
class Modules;
+struct ModuleInfo;
class ModulesSources
{
HRESULT GetFullPathIndex(BSTR document, unsigned &fullPathIndex);
HRESULT UpdateSourcesCodeLinesForModule(ICorDebugModule *pModule, IMetaDataImport *pMDImport, std::unordered_set<mdMethodDef> methodTokens,
- src_block_updates_t &blockUpdates, PVOID pSymbolReaderHandle, method_block_updates_t &methodBlockUpdates);
+ src_block_updates_t &blockUpdates, ModuleInfo &mdInfo);
HRESULT ResolveRelativeSourceFileName(std::string &filename);
+ HRESULT LineUpdatesForMethodData(ICorDebugModule *pModule, unsigned fullPathIndex, method_data_t &methodData,
+ const std::vector<block_update_t> &blockUpdate, ModuleInfo &mdInfo);
#ifdef WIN32
// on Windows OS, all files names converted to uppercase in containers above, but this vector hold initial full path names
// Setup breakpoints before apply delta.\r
Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 16);\r
Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 19);\r
- Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 25);\r
+ Context.EnableBreakpoint(@"__FILE__:__LINE__", @"Program.cs", 27);\r
Context.EnableFuncBreakpoint(@"__FILE__:__LINE__", "Program.HotReloadBreakpointTest1");\r
Context.EnableFuncBreakpoint(@"__FILE__:__LINE__", "Program.HotReloadBreakpointTest2");\r
\r
static void HotReloadBreakpointTest2()\r
{\r
Console.WriteLine(""Another added string."");\r
- Console.WriteLine(""One more added string.""); // line 25\r
+ int i=0;\r
+ i++;\r
+ Console.WriteLine(""One more added string.""); // line 27\r
}\r
}\r
}", @"Program.cs");\r
Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta2");\r
Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta2");\r
\r
+ Context.GetDelta(@"__FILE__:__LINE__",\r
+ @"using System;\r
+ namespace TestAppHotReload\r
+ {\r
+ class Program\r
+ {\r
+ static void Main(string[] args)\r
+ {\r
+ Console.WriteLine(""Hello World!"");\r
+ HotReloadTest();\r
+ }\r
+ static void HotReloadTest()\r
+ {\r
+ Console.WriteLine(""Updated string."");\r
+ HotReloadBreakpointTest1();\r
+ HotReloadBreakpointTest1(); // line 15\r
+ }\r
+ static void HotReloadBreakpointTest1()\r
+ {\r
+ Console.WriteLine(""Updated added string.""); // line 19\r
+ HotReloadBreakpointTest2();\r
+ }\r
+\r
+ static void HotReloadBreakpointTest2()\r
+ {\r
+ Console.WriteLine(""Another added string."");\r
+ int i=0;\r
+\r
+ i++;\r
+\r
+\r
+ Console.WriteLine(""One more added string."");\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+ Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta3");\r
+ Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta3");\r
+\r
+\r
+\r
+ Context.GetDelta(@"__FILE__:__LINE__",\r
+ @"using System;\r
+ namespace TestAppHotReload\r
+ {\r
+ class Program\r
+ {\r
+ static void Main(string[] args)\r
+ {\r
+ Console.WriteLine(""Hello World!"");\r
+ HotReloadTest();\r
+ }\r
+ static void HotReloadTest()\r
+ {\r
+ Console.WriteLine(""Updated string."");\r
+ HotReloadBreakpointTest1();\r
+ HotReloadBreakpointTest1(); // line 15\r
+ }\r
+ static void HotReloadBreakpointTest1()\r
+ {\r
+ Console.WriteLine(""Updated added string.""); // line 19\r
+ HotReloadBreakpointTest2();\r
+ }\r
+\r
+\r
+ static void HotReloadBreakpointTest2()\r
+ {\r
+ Console.WriteLine(""Another added string."");\r
+\r
+\r
+ int i=0;\r
+ i++;\r
+\r
+\r
+ Console.WriteLine(""One more added string.""); // line 33\r
+ }\r
+ }\r
+ }", @"Program.cs");\r
+ Context.WriteDeltas(@"__FILE__:__LINE__", "tmp_delta4");\r
+ Context.ApplyDeltas(@"__FILE__:__LINE__", "tmp_delta4");\r
+\r
+\r
+\r
Context.Continue(@"__FILE__:__LINE__");\r
});\r
\r
\r
Label.Checkpoint("bp_test_func_added", "bp_test_line_added", (Object context) => {\r
Context Context = (Context)context;\r
- Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 23); // Program.HotReloadBreakpointTest2\r
+ Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 25); // Program.HotReloadBreakpointTest2\r
Context.Continue(@"__FILE__:__LINE__");\r
});\r
\r
Label.Checkpoint("bp_test_line_added", "bp_test_line_not_changed", (Object context) => {\r
Context Context = (Context)context;\r
- Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 25);\r
+ Context.WasBreakpointHit(@"__FILE__:__LINE__", @"Program.cs", 29);\r
Context.Continue(@"__FILE__:__LINE__");\r
});\r
\r