Files
ben marsh cf183af67c Integrating live coding feature (aka Live++) into UE4.
Allows fast iteration of C++ changes without restarting the application. To use, select the "Live Coding (Experimental)" mode from the drop down menu next to the editor's compile button, or type "LiveCoding" into the console for a monolithic build. Press Ctrl+Alt+F11 to find changes and compile.

Changes vs standalone Live++ version:

* UBT is used to execute builds. This allows standard UE4 adaptive unity mode, allows us to reuse object files when we do regular builds, supports using any build executor allowed by UBT (XGE, SNDBS, etc..).
* Adding new source files is supported.
* Custom visualizer for FNames is supported via a weakly linked symbol in a static library (Engine/Extras/NatvisHelpers).
* Settings are exposed in the editor's project settings dialog.
* Standalone application has been rewritten as a Slate app ("LiveCodingConsole"). There is an additional option to start the program as hidden, where it will not be visible until Ctrl+Alt+F11 is hit. Similarly, closing the window will hide it instead of closing the application.
* Does not require a standalone licensed version of Live++.

Known issues:

* Does not currently support class layout changes / object reinstancing

#rb none
[FYI] Marc.Audy, Stefan.Boberg, Nick.Penwarden
#jira

#ROBOMERGE-SOURCE: CL 5304722 in //UE4/Release-4.22/...
#ROBOMERGE-BOT: RELEASE (Release-4.22 -> Main)

[CL 5309051 by ben marsh in Main branch]
2019-03-05 18:49:25 -05:00

368 lines
7.3 KiB
C++

// Copyright 2011-2019 Molecular Matters GmbH, all rights reserved.
#include "LC_DiaUtil.h"
namespace detail
{
template <typename T>
static T* FindEnumerator(IDiaSession* session)
{
IDiaEnumTables* enumTables = nullptr;
if (session->getEnumTables(&enumTables) != S_OK)
{
return nullptr;
}
T* enumerator = nullptr;
IDiaTable* table = nullptr;
ULONG fetchedCount = 0ul;
while ((enumTables->Next(1, &table, &fetchedCount) == S_OK) && (fetchedCount == 1ul))
{
// there is only one table that matches the given IID, grab it
const HRESULT hr = table->QueryInterface(__uuidof(T), reinterpret_cast<void**>(&enumerator));
table->Release();
if (hr == S_OK)
{
// found the table
break;
}
}
enumTables->Release();
return enumerator;
}
template <typename T, typename S>
static void FetchFromEnumerator(T* enumerator, types::vector<S*>* symbols)
{
symbols->reserve(32u);
ULONG symbolsFetched = 0u;
S* symbol = nullptr;
while ((enumerator->Next(1u, &symbol, &symbolsFetched) == S_OK) && (symbolsFetched == 1u))
{
symbols->push_back(symbol);
}
}
}
dia::SymbolName dia::GetSymbolName(IDiaSymbol* symbol)
{
BSTR str = {};
const HRESULT hr = symbol->get_name(&str);
if (hr != S_OK)
{
str = ::SysAllocString(L"");
}
return SymbolName(str);
}
dia::SymbolName dia::GetSymbolUndecoratedName(IDiaSymbol* symbol)
{
BSTR str = {};
const HRESULT hr = symbol->get_undecoratedName(&str);
if (hr != S_OK)
{
str = ::SysAllocString(L"");
}
return SymbolName(str);
}
dia::SymbolName dia::GetSymbolLibraryName(IDiaSymbol* symbol)
{
BSTR str = {};
const HRESULT hr = symbol->get_libraryName(&str);
if (hr != S_OK)
{
str = ::SysAllocString(L"");
}
return SymbolName(str);
}
dia::SymbolName dia::GetSymbolFilename(IDiaSourceFile* symbol)
{
BSTR str = {};
const HRESULT hr = symbol->get_fileName(&str);
if (hr != S_OK)
{
str = ::SysAllocString(L"");
}
return SymbolName(str);
}
dia::Variant dia::GetSymbolEnvironmentOption(IDiaSymbol* environment)
{
return Variant(environment);
}
uint32_t dia::GetSymbolRVA(IDiaSymbol* symbol)
{
DWORD rva = 0u;
symbol->get_relativeVirtualAddress(&rva);
return rva;
}
uint32_t dia::GetSymbolSize(IDiaSymbol* symbol)
{
ULONGLONG result = 0ull;
symbol->get_length(&result);
return static_cast<uint32_t>(result);
}
uint32_t dia::GetSymbolOffset(IDiaSymbol* symbol)
{
LONG result = 0;
symbol->get_offset(&result);
return static_cast<uint32_t>(result);
}
bool dia::IsFunction(IDiaSymbol* symbol)
{
BOOL isFunction = Windows::FALSE;
symbol->get_function(&isFunction);
return (isFunction != Windows::FALSE);
}
IDiaSymbol* dia::GetTypeSymbol(IDiaSymbol* symbol)
{
IDiaSymbol* typeSymbol = nullptr;
symbol->get_type(&typeSymbol);
return typeSymbol;
}
IDiaSymbol* dia::GetParent(IDiaSymbol* symbol)
{
IDiaSymbol* parent = nullptr;
symbol->get_lexicalParent(&parent);
return parent;
}
IDiaSymbol* dia::GetSymbolById(IDiaSession* session, uint32_t id)
{
IDiaSymbol* symbol = nullptr;
session->symbolById(id, &symbol);
return symbol;
}
bool dia::WasCompiledWithLTCG(IDiaSymbol* compilandDetail)
{
BOOL isLTCG = Windows::FALSE;
compilandDetail->get_isLTCG(&isLTCG);
return (isLTCG != 0);
}
bool dia::WasCompiledWithHotpatch(IDiaSymbol* compilandDetail)
{
BOOL isHotpatch = Windows::FALSE;
compilandDetail->get_isHotpatchable(&isHotpatch);
return (isHotpatch != 0);
}
types::vector<IDiaSymbol*> dia::GatherChildSymbols(IDiaSymbol* parent, enum SymTagEnum symTag)
{
types::vector<IDiaSymbol*> symbols;
IDiaEnumSymbols* enumSymbols = nullptr;
if (parent->findChildren(symTag, NULL, nsNone, &enumSymbols) == S_OK)
{
detail::FetchFromEnumerator(enumSymbols, &symbols);
enumSymbols->Release();
}
return symbols;
}
types::vector<IDiaSourceFile*> dia::GatherCompilandFiles(IDiaSession* session, IDiaSymbol* compiland)
{
types::vector<IDiaSourceFile*> symbols;
IDiaEnumSourceFiles* enumFiles = nullptr;
if (session->findFile(compiland, NULL, nsNone, &enumFiles) == S_OK)
{
detail::FetchFromEnumerator(enumFiles, &symbols);
enumFiles->Release();
}
return symbols;
}
IDiaEnumSectionContribs* dia::FindSectionContributionsEnumerator(IDiaSession* session)
{
return detail::FindEnumerator<IDiaEnumSectionContribs>(session);
}
IDiaSymbol* dia::FindSymbolByRVA(IDiaSession* session, uint32_t rva)
{
IDiaSymbol* diaSymbol = nullptr;
long displacement = 0;
const HRESULT hr = session->findSymbolByRVAEx(rva, SymTagNull, &diaSymbol, &displacement);
if ((hr == S_OK) && (displacement == 0))
{
// found an exact match
return diaSymbol;
}
if (diaSymbol)
{
diaSymbol->Release();
}
return nullptr;
}
IDiaSymbol* dia::FindFunctionByRva(IDiaSession* session, uint32_t rva)
{
IDiaSymbol* diaSymbol = nullptr;
long displacement = 0;
// look for functions first, this includes private/static functions.
// if no symbol can be found, try public symbols next. this is needed to find symbols in stripped PDBs such as KernelBase.dll.
// as a last resort, try finding ANY symbol.
const enum SymTagEnum tagsToTry[3] = { SymTagFunction, SymTagPublicSymbol, SymTagNull };
for (unsigned int i = 0u; i < 3u; ++i)
{
const HRESULT hr = session->findSymbolByRVAEx(rva, tagsToTry[i], &diaSymbol, &displacement);
if (hr == S_OK)
{
return diaSymbol;
}
}
return nullptr;
}
IDiaSymbol* dia::FindLabelByRva(IDiaSession* session, uint32_t rva)
{
IDiaSymbol* diaSymbol = nullptr;
long displacement = 0;
const HRESULT hr = session->findSymbolByRVAEx(rva, SymTagLabel, &diaSymbol, &displacement);
if ((hr == S_OK) && (displacement == 0))
{
// found an exact match
return diaSymbol;
}
if (diaSymbol)
{
diaSymbol->Release();
}
return nullptr;
}
uint32_t dia::FindLineNumberByRVA(IDiaSession* session, uint32_t rva)
{
IDiaEnumLineNumbers* enumLineNumbers = nullptr;
// the longest instruction is 16 bytes, so there is no need to fetch lines for more than 16 instruction bytes
const HRESULT hr = session->findLinesByRVA(rva, 16u, &enumLineNumbers);
if ((hr == S_OK) && (enumLineNumbers))
{
IDiaLineNumber* line = nullptr;
DWORD fetchedCount = 0u;
if (SUCCEEDED(enumLineNumbers->Next(1, &line, &fetchedCount)) && (fetchedCount == 1))
{
DWORD lineNumber = 0u;
line->get_lineNumber(&lineNumber);
line->Release();
enumLineNumbers->Release();
return lineNumber;
}
enumLineNumbers->Release();
}
if (enumLineNumbers)
{
enumLineNumbers->Release();
}
return 0u;
}
dia::SymbolName dia::FindSourceFileByRVA(IDiaSession* session, uint32_t rva)
{
IDiaEnumLineNumbers* enumLineNumbers = nullptr;
// the longest instruction is 16 bytes, so there is no need to fetch lines for more than 16 instruction bytes
const HRESULT hr = session->findLinesByRVA(rva, 16u, &enumLineNumbers);
if ((hr == S_OK) && (enumLineNumbers))
{
IDiaLineNumber* line = nullptr;
DWORD fetchedCount = 0u;
if (SUCCEEDED(enumLineNumbers->Next(1, &line, &fetchedCount)) && (fetchedCount == 1))
{
IDiaSourceFile* sourceFile = nullptr;
line->get_sourceFile(&sourceFile);
if (sourceFile)
{
dia::SymbolName filename = dia::GetSymbolFilename(sourceFile);
sourceFile->Release();
line->Release();
enumLineNumbers->Release();
return filename;
}
line->Release();
}
enumLineNumbers->Release();
}
if (enumLineNumbers)
{
enumLineNumbers->Release();
}
return SymbolName(nullptr);
}