diff options
Diffstat (limited to 'src/debug/ildbsymlib/symread.cpp')
-rw-r--r-- | src/debug/ildbsymlib/symread.cpp | 2765 |
1 files changed, 2765 insertions, 0 deletions
diff --git a/src/debug/ildbsymlib/symread.cpp b/src/debug/ildbsymlib/symread.cpp new file mode 100644 index 0000000000..3fe3c8c9cc --- /dev/null +++ b/src/debug/ildbsymlib/symread.cpp @@ -0,0 +1,2765 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. +// =========================================================================== +// File: symread.cpp +// + +// =========================================================================== +#include "pch.h" +#include "symread.h" +#include "corimage.h" + +#define CODE_WITH_NO_SOURCE 0xfeefee +// ------------------------------------------------------------------------- +// SymReader class +// ------------------------------------------------------------------------- + +//----------------------------------------------------------- +// NewSymReader +// Static function used to create a new instance of SymReader +//----------------------------------------------------------- +HRESULT +SymReader::NewSymReader( + REFCLSID clsid, + void** ppObj + ) +{ + HRESULT hr = S_OK; + SymReader* pSymReader = NULL; + + _ASSERTE(IsValidCLSID(clsid)); + _ASSERTE(IsValidWritePtr(ppObj, IUnknown*)); + + if (clsid != IID_ISymUnmanagedReader) + return (E_UNEXPECTED); + + IfFalseGo(ppObj, E_INVALIDARG); + + *ppObj = NULL; + IfNullGo( pSymReader = NEW(SymReader())); + + *ppObj = pSymReader; + pSymReader->AddRef(); + pSymReader = NULL; + +ErrExit: + + RELEASE( pSymReader ); + + return hr; +} + + +//----------------------------------------------------------- +// ~SymReader +//----------------------------------------------------------- +SymReader::~SymReader() +{ + Cleanup(); +} + +//----------------------------------------------------------- +// Cleanup +// Release all memory and clear initialized data structures +// (eg. as a result of a failed Initialization attempt) +//----------------------------------------------------------- +void SymReader::Cleanup() +{ + if (m_pDocs) + { + unsigned i; + for(i = 0; i < m_pPDBInfo->m_CountOfDocuments; i++) + { + RELEASE(m_pDocs[i]); + } + } + + DELETE(m_pPDBInfo); + m_pPDBInfo = NULL; + + // If we loaded from stream, then free the memory we allocated + if (m_fInitializeFromStream) + { + DELETEARRAY(m_DataPointers.m_pBytes); + DELETEARRAY(m_DataPointers.m_pConstants); + DELETEARRAY(m_DataPointers.m_pDocuments); + DELETEARRAY(m_DataPointers.m_pMethods); + DELETEARRAY(m_DataPointers.m_pScopes); + DELETEARRAY(m_DataPointers.m_pSequencePoints); + DELETEARRAY(m_DataPointers.m_pStringsBytes); + DELETEARRAY(m_DataPointers.m_pUsings); + DELETEARRAY(m_DataPointers.m_pVars); + } + + DELETEARRAY(m_pDocs); + m_pDocs = NULL; + + RELEASE(m_pImporter); + m_pImporter = NULL; + + memset(&m_DataPointers, 0, sizeof(PDBDataPointers)); + m_szPath[0] = '\0'; +} + +//----------------------------------------------------------- +// ~QueryInterface +//----------------------------------------------------------- +HRESULT +SymReader::QueryInterface( + REFIID riid, + void **ppvObject + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(IsValidIID(riid)); + _ASSERTE(IsValidWritePtr(ppvObject, void*)); + + IfFalseGo(ppvObject, E_INVALIDARG); + if (riid == IID_ISymUnmanagedReader) + { + *ppvObject = (ISymUnmanagedReader*) this; + } + else + if (riid == IID_IUnknown) + { + *ppvObject = (IUnknown*)this; + } + else + { + *ppvObject = NULL; + hr = E_NOINTERFACE; + } + + if (*ppvObject) + { + AddRef(); + } + +ErrExit: + + return hr; +} + +static HRESULT ReadFromStream(IStream *pIStream, void *pv, ULONG cb) +{ + HRESULT hr = NOERROR; + ULONG ulBytesRead; + + IfFailGo(pIStream->Read(pv, cb, &ulBytesRead)); + if (ulBytesRead != cb) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// Initialize +// Pass in the required information to read in the debug info +// If a stream is passed in, it is used, otherwise a filename +// must be passed in +//----------------------------------------------------------- +HRESULT SymReader::Initialize( + IUnknown *importer, // Cash it to be consistent with CLR + const WCHAR* szFileName, // File name of the ildb + const WCHAR* szsearchPath, // Search Path + IStream *pIStream // IStream + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(szFileName || pIStream); + IfFalseGo(szFileName || pIStream, E_INVALIDARG ); + + _ASSERTE(!m_fInitialized); + IfFalseGo(!m_fInitialized, E_UNEXPECTED); + + // If it's passed in, we need to AddRef to be consistent the + // desktop version since ReleaseImporterFromISymUnmanagedReader (ceeload.cpp) + // assumes there's an addref + if (importer) + { + m_pImporter = importer; + m_pImporter->AddRef(); + } + + // See if we're reading from a file or stream + if (pIStream == NULL) + { + // We're initializing from a file + m_fInitializeFromStream = false; + IfFailGo(InitializeFromFile(szFileName, szsearchPath)); + } + else + { + // We're reading in from a stream + m_fInitializeFromStream = true; + IfFailGo(InitializeFromStream(pIStream)); + } + + // Note that up to this point, the data we've read in has not been validated. Since we don't trust + // our input, it's important that we don't proceed with using this data until validation has been + // successful. + IfFailGo(ValidateData()); + + +ErrExit: + // If we have not succeeded, then we need to clean up our data structures. This would allow a client to call + // Initialize again, but also ensures we can't possibly use partial or otherwise invalid (possibly + // malicious) data. + if (FAILED(hr)) + { + Cleanup(); + } + else + { + // Otherwise we are not properly initialized + m_fInitialized = true; + } + + return hr; +} + +//----------------------------------------------------------- +// Initialize the data structures by reading from the supplied stream +// Note that upon completion the data has not yet been validated for safety. +//----------------------------------------------------------- +HRESULT SymReader::InitializeFromStream( + IStream *pIStream // IStream + ) +{ + GUID GuidVersion; + BYTE *pSignature; + HRESULT hr = S_OK; + + // Reset the stream to the begining + LARGE_INTEGER li; + li.u.HighPart = 0; + li.u.LowPart = 0; + + // Make sure we're at the beginning of the stream + IfFailGo(pIStream->Seek(li, STREAM_SEEK_SET, NULL)); + + IfNullGo(pSignature = (BYTE *)_alloca(ILDB_SIGNATURE_SIZE)); + IfFailGo(ReadFromStream(pIStream, pSignature, ILDB_SIGNATURE_SIZE)); + + // Verify that we're looking at an ILDB File + if (memcmp(pSignature, ILDB_SIGNATURE, ILDB_SIGNATURE_SIZE)) + { + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + } + + IfFailGo(ReadFromStream(pIStream, &GuidVersion, sizeof(GUID))); + + SwapGuid(&GuidVersion); + + if (memcmp(&GuidVersion, &ILDB_VERSION_GUID, sizeof(GUID))) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + IfNullGo(m_pPDBInfo = NEW(PDBInfo)); + + memset(m_pPDBInfo, 0 , sizeof(PDBInfo)); + IfFailGo(ReadFromStream(pIStream, m_pPDBInfo, sizeof(PDBInfo))); + + // Swap the counts + m_pPDBInfo->ConvertEndianness(); + + if (m_pPDBInfo->m_CountOfConstants) + { + IfNullGo(m_DataPointers.m_pConstants = NEW(SymConstant[m_pPDBInfo->m_CountOfConstants])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pConstants, m_pPDBInfo->m_CountOfConstants*sizeof(SymConstant))); + } + + if (m_pPDBInfo->m_CountOfMethods) + { + IfNullGo(m_DataPointers.m_pMethods = NEW(SymMethodInfo[m_pPDBInfo->m_CountOfMethods])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pMethods, m_pPDBInfo->m_CountOfMethods*sizeof(SymMethodInfo))); + } + + if (m_pPDBInfo->m_CountOfScopes) + { + IfNullGo(m_DataPointers.m_pScopes = NEW(SymLexicalScope[m_pPDBInfo->m_CountOfScopes])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pScopes, m_pPDBInfo->m_CountOfScopes*sizeof(SymLexicalScope))); + } + + if (m_pPDBInfo->m_CountOfVars) + { + IfNullGo(m_DataPointers.m_pVars = NEW(SymVariable[m_pPDBInfo->m_CountOfVars])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pVars, m_pPDBInfo->m_CountOfVars*sizeof(SymVariable))); + } + + if (m_pPDBInfo->m_CountOfUsing) + { + IfNullGo(m_DataPointers.m_pUsings = NEW(SymUsingNamespace[m_pPDBInfo->m_CountOfUsing])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pUsings, m_pPDBInfo->m_CountOfUsing*sizeof(SymUsingNamespace))); + } + + if (m_pPDBInfo->m_CountOfSequencePoints) + { + IfNullGo(m_DataPointers.m_pSequencePoints = NEW(SequencePoint[m_pPDBInfo->m_CountOfSequencePoints])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pSequencePoints, m_pPDBInfo->m_CountOfSequencePoints*sizeof(SequencePoint))); + } + + if (m_pPDBInfo->m_CountOfDocuments) + { + IfNullGo(m_DataPointers.m_pDocuments = NEW(DocumentInfo[m_pPDBInfo->m_CountOfDocuments])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pDocuments, m_pPDBInfo->m_CountOfDocuments*sizeof(DocumentInfo))); + } + + if (m_pPDBInfo->m_CountOfBytes) + { + IfNullGo(m_DataPointers.m_pBytes = NEW(BYTE[m_pPDBInfo->m_CountOfBytes])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pBytes, m_pPDBInfo->m_CountOfBytes*sizeof(BYTE))); + } + + + if (m_pPDBInfo->m_CountOfStringBytes) + { + IfNullGo(m_DataPointers.m_pStringsBytes = NEW(BYTE[m_pPDBInfo->m_CountOfStringBytes])); + IfFailGo(ReadFromStream(pIStream, m_DataPointers.m_pStringsBytes, m_pPDBInfo->m_CountOfStringBytes)); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// ValidateData +// Checks the contents of everything in m_DataPointers (i.e. all the structures read from the file) +// to make sure it is valid. Specifically, validates that all indexes are within bounds for the +// sizes allocated. +//----------------------------------------------------------- +HRESULT SymReader::ValidateData() +{ + HRESULT hr = S_OK; + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfConstants; i++) + { + SymConstant & c = m_DataPointers.m_pConstants[i]; + IfFalseGo(c.ParentScope() < m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(c.Name() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFailGo(ValidateBytes(c.Signature(), c.SignatureSize())); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfMethods; i++) + { + // Note that start/end values may equal the count (i.e. point one past the end) because + // the end is the extent, and start can equal end to indicate no entries. + SymMethodInfo & m = m_DataPointers.m_pMethods[i]; + IfFalseGo(m.StartScopes() <= m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndScopes() <= m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartScopes() <= m.EndScopes(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartVars() <= m_pPDBInfo->m_CountOfVars, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndVars() <= m_pPDBInfo->m_CountOfVars, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartVars() <= m.EndVars(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartUsing() <= m_pPDBInfo->m_CountOfUsing, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndUsing() <= m_pPDBInfo->m_CountOfUsing, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartUsing() <= m.EndUsing(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartConstant() <= m_pPDBInfo->m_CountOfConstants, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndConstant() <= m_pPDBInfo->m_CountOfConstants, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartConstant() <= m.EndConstant(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartDocuments() <= m_pPDBInfo->m_CountOfDocuments, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndDocuments() <= m_pPDBInfo->m_CountOfDocuments, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartDocuments() <= m.EndDocuments(), HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartSequencePoints() <= m_pPDBInfo->m_CountOfSequencePoints, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.EndSequencePoints() <= m_pPDBInfo->m_CountOfSequencePoints, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(m.StartSequencePoints() <= m.EndSequencePoints(), HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfScopes; i++) + { + SymLexicalScope & s = m_DataPointers.m_pScopes[i]; + IfFalseGo((s.ParentScope() == (UINT32)-1) || (s.ParentScope() < m_pPDBInfo->m_CountOfScopes), HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfVars; i++) + { + SymVariable & v = m_DataPointers.m_pVars[i]; + IfFalseGo(v.Scope() < m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(v.Name() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFailGo(ValidateBytes(v.Signature(), v.SignatureSize())); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfUsing; i++) + { + SymUsingNamespace & u = m_DataPointers.m_pUsings[i]; + IfFalseGo(u.ParentScope() < m_pPDBInfo->m_CountOfScopes, HrFromWin32(ERROR_BAD_FORMAT)); + IfFalseGo(u.Name() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfSequencePoints; i++) + { + SequencePoint & s = m_DataPointers.m_pSequencePoints[i]; + IfFalseGo(s.Document() < m_pPDBInfo->m_CountOfDocuments, HrFromWin32(ERROR_BAD_FORMAT)); + } + + for (UINT32 i = 0; i < m_pPDBInfo->m_CountOfDocuments; i++) + { + DocumentInfo & d = m_DataPointers.m_pDocuments[i]; + IfFailGo(ValidateBytes(d.CheckSumEntry(), d.CheckSumSize())); + IfFailGo(ValidateBytes(d.SourceEntry(), d.SourceSize())); + IfFalseGo(d.UrlEntry() < m_pPDBInfo->m_CountOfStringBytes, HrFromWin32(ERROR_BAD_FORMAT)); + } + + // Nothing to validate for the m_pBytes array - each reference must validate above that it's + // length doesn't exceed the array + + // We expect all strings to be null terminated. To ensure no string operation overruns the buffer + // it sufficies to check that the buffer ends in a null character + if (m_pPDBInfo->m_CountOfStringBytes > 0) + { + IfFalseGo(m_DataPointers.m_pStringsBytes[m_pPDBInfo->m_CountOfStringBytes-1] == '\0', HrFromWin32(ERROR_BAD_FORMAT)); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// Validate a reference to the bytes array +//----------------------------------------------------------- +HRESULT SymReader::ValidateBytes(UINT32 bytesIndex, UINT32 bytesLen) +{ + S_UINT32 extent = S_UINT32(bytesIndex) + S_UINT32(bytesLen); + if (!extent.IsOverflow() && + (extent.Value() <= m_pPDBInfo->m_CountOfBytes)) + { + return S_OK; + } + + return HrFromWin32(ERROR_BAD_FORMAT); +} + +//----------------------------------------------------------- +// VerifyPEDebugInfo +// Verify that the debug info in the PE is the one we want +//----------------------------------------------------------- +HRESULT SymReader::VerifyPEDebugInfo(const WCHAR* szFileName) +{ + HRESULT hr = E_FAIL; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMapFile = INVALID_HANDLE_VALUE; + BYTE *pMod = NULL; + DWORD dwFileSize; + IMAGE_DEBUG_DIRECTORY *pDebugDir; + RSDSI *pDebugInfo; + DWORD dwUtf8Length; + DWORD dwUnicodeLength; + + // We need to change the .pdb extension to .ildb + // compatible with VS7 + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + + IMAGE_NT_HEADERS*pNT; + + hFile = WszCreateFile(szFileName, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + // Get the last error if we can + return HrFromWin32(GetLastError()); + } + + dwFileSize = GetFileSize(hFile, NULL); + if (dwFileSize < ILDB_HEADER_SIZE) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + hMapFile = WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMapFile == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + pMod = (BYTE *) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); + if (pMod == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + pNT = Cor_RtlImageNtHeader(pMod, dwFileSize); + + // If there is no DebugEntry, then just error out + if (VAL32(pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress) == 0) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + + // NOTE: This code is not secure against malformed PE files - any of the pointer additions below + // may be outside the range of memory mapped for the file. If we ever want to use this code + // on untrusted PE files, we should properly validate everything (probably by using PEDecoder). + + DWORD offset; + offset = Cor_RtlImageRvaToOffset(pNT, VAL32(pNT->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_DEBUG].VirtualAddress), dwFileSize); + if (offset == NULL) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + pDebugDir = (IMAGE_DEBUG_DIRECTORY *)(pMod + offset); + pDebugInfo = (RSDSI *)(pMod + VAL32(pDebugDir->PointerToRawData)); + + if (pDebugInfo->dwSig != VAL32(0x53445352)) // "SDSR" + { + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + } + + + // Try the returned Stored Name since it might be a fully qualified path + dwUtf8Length = VAL32(pDebugDir->SizeOfData) - sizeof(RSDSI); + dwUnicodeLength = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR) pDebugInfo->szPDB, dwUtf8Length, fullpath, COUNTOF(fullpath) - 1); + + // Make sure it's NULL terminated + _ASSERTE(dwUnicodeLength < COUNTOF(fullpath)); + fullpath[dwUnicodeLength]='\0'; + + // Replace the extension with ildb + if (_wsplitpath_s( fullpath, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (_wmakepath_s(m_szStoredSymbolName, MAX_LONGPATH, drive, dir, fname, W("ildb") )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + + // It looks valid, make sure to set the return code + hr = S_OK; +ErrExit: + if (pMod) + UnmapViewOfFile(pMod); + if (hMapFile != INVALID_HANDLE_VALUE) + CloseHandle(hMapFile); + if (hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + return hr; + +} + +//----------------------------------------------------------- +// InitializeFromFile +// Initialize the reader using the passed in file name +// Note that upon completion the data hasn't yet been validated for safety. +//----------------------------------------------------------- +HRESULT +SymReader::InitializeFromFile( + const WCHAR* szFileName, + const WCHAR* szsearchPath) +{ + HRESULT hr = S_OK; + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMapFile = INVALID_HANDLE_VALUE; + HMODULE hMod = NULL; + BYTE *CurrentOffset; + DWORD dwFileSize; + S_UINT32 dwDataSize; + GUID VersionInfo; + + _ASSERTE(szFileName); + IfFalseGo(szFileName, E_INVALIDARG ); + + IfFailGo(VerifyPEDebugInfo(szFileName)); + // We need to open the exe and check to see if the DebugInfo matches + + if (_wsplitpath_s( szFileName, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (_wmakepath_s( fullpath, _MAX_PATH, drive, dir, fname, W("ildb") )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (wcsncpy_s( m_szPath, COUNTOF(m_szPath), fullpath, _TRUNCATE) == STRUNCATE) + IfFailGo(HrFromWin32(ERROR_INSUFFICIENT_BUFFER)); + + hFile = WszCreateFile(m_szPath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + + // If the stored string is empty, don't do anything + if (m_szStoredSymbolName[0] == '\0') + { + return HrFromWin32(GetLastError()); + } + + if (_wsplitpath_s( m_szStoredSymbolName, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (_wmakepath_s( fullpath, _MAX_PATH, drive, dir, fname, W("ildb") )) + IfFailGo(HrFromWin32(ERROR_BAD_FORMAT)); + if (wcsncpy_s( m_szPath, COUNTOF(m_szPath), fullpath, _TRUNCATE) == STRUNCATE) + IfFailGo(HrFromWin32(ERROR_INSUFFICIENT_BUFFER)); + + hFile = WszCreateFile(m_szPath, + GENERIC_READ, + FILE_SHARE_READ, + NULL, + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (hFile == INVALID_HANDLE_VALUE) + { + return HrFromWin32(GetLastError()); + } + } + + dwFileSize = GetFileSize(hFile, NULL); + if (dwFileSize < ILDB_HEADER_SIZE) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + hMapFile = WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + if (hMapFile == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + hMod = (HMODULE) MapViewOfFile(hMapFile, FILE_MAP_READ, 0, 0, 0); + if (hMod == NULL) + IfFailGo(HrFromWin32(GetLastError())); + + // We've opened the file, now let's get the pertinent info + CurrentOffset = (BYTE *)hMod; + + // Verify that we're looking at an ILDB File + if (memcmp(CurrentOffset, ILDB_SIGNATURE, ILDB_SIGNATURE_SIZE)) + { + IfFailGo(E_FAIL); + } + CurrentOffset += ILDB_SIGNATURE_SIZE; + + memcpy( &VersionInfo, CurrentOffset, sizeof(GUID)); + SwapGuid( &VersionInfo ); + CurrentOffset += sizeof(GUID); + + if (memcmp(&VersionInfo, &ILDB_VERSION_GUID, sizeof(GUID))) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + IfNullGo(m_pPDBInfo = NEW(PDBInfo)); + + memcpy(m_pPDBInfo, CurrentOffset, sizeof(PDBInfo)); + + // Swap the counts + m_pPDBInfo->ConvertEndianness(); + + // Check to make sure we have enough data to be read in. + dwDataSize = S_UINT32(ILDB_HEADER_SIZE); + dwDataSize += m_pPDBInfo->m_CountOfConstants*sizeof(SymConstant); + dwDataSize += m_pPDBInfo->m_CountOfMethods * sizeof(SymMethodInfo); + dwDataSize += m_pPDBInfo->m_CountOfScopes*sizeof(SymLexicalScope); + dwDataSize += m_pPDBInfo->m_CountOfVars*sizeof(SymVariable); + dwDataSize += m_pPDBInfo->m_CountOfUsing*sizeof(SymUsingNamespace); + dwDataSize += m_pPDBInfo->m_CountOfSequencePoints*sizeof(SequencePoint); + dwDataSize += m_pPDBInfo->m_CountOfDocuments*sizeof(DocumentInfo); + dwDataSize += m_pPDBInfo->m_CountOfBytes*sizeof(BYTE); + dwDataSize += m_pPDBInfo->m_CountOfStringBytes*sizeof(BYTE); + + if (dwDataSize.IsOverflow() || dwDataSize.Value() > dwFileSize) + { + IfFailGo(HrFromWin32(ERROR_INVALID_DATA)); + } + + CurrentOffset += sizeof(PDBInfo); + + if (m_pPDBInfo->m_CountOfConstants) + { + m_DataPointers.m_pConstants = (SymConstant*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfConstants*sizeof(SymConstant)); + } + + if (m_pPDBInfo->m_CountOfMethods) + { + m_DataPointers.m_pMethods = (SymMethodInfo *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfMethods*sizeof(SymMethodInfo)); + } + + if (m_pPDBInfo->m_CountOfScopes) + { + m_DataPointers.m_pScopes = (SymLexicalScope *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfScopes*sizeof(SymLexicalScope)); + } + + if (m_pPDBInfo->m_CountOfVars) + { + m_DataPointers.m_pVars = (SymVariable *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfVars*sizeof(SymVariable)); + } + + if (m_pPDBInfo->m_CountOfUsing) + { + m_DataPointers.m_pUsings = (SymUsingNamespace *)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfUsing*sizeof(SymUsingNamespace)); + } + + if (m_pPDBInfo->m_CountOfSequencePoints) + { + m_DataPointers.m_pSequencePoints = (SequencePoint*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfSequencePoints*sizeof(SequencePoint)); + } + + if (m_pPDBInfo->m_CountOfDocuments) + { + m_DataPointers.m_pDocuments = (DocumentInfo*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfDocuments*sizeof(DocumentInfo)); + } + + if (m_pPDBInfo->m_CountOfBytes) + { + m_DataPointers.m_pBytes = (BYTE*)CurrentOffset; + CurrentOffset += (m_pPDBInfo->m_CountOfBytes*sizeof(BYTE)); + } + + if (m_pPDBInfo->m_CountOfStringBytes) + { + m_DataPointers.m_pStringsBytes = (BYTE*)CurrentOffset; + } + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetDocument +// Get the document for the passed in information +//----------------------------------------------------------- +HRESULT +SymReader::GetDocument( + __in LPWSTR wcsUrl, // URL of the document + GUID language, // Language for the file + GUID languageVendor, // Language vendor + GUID documentType, // Type of document + ISymUnmanagedDocument **ppRetVal // [out] Document + ) +{ + HRESULT hr = S_OK; + unsigned i; + SymDocument* pDoc = NULL; + WCHAR *wcsDocumentUrl = NULL; + WCHAR *wcsDocumentUrlAlloc = NULL; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(ppRetVal && wcsUrl); + IfFalseGo(ppRetVal, E_INVALIDARG); + IfFalseGo(wcsUrl, E_INVALIDARG); + + // Init Out Parameter + *ppRetVal = NULL; + + for (i = 0; i < m_pPDBInfo->m_CountOfDocuments; i++) + { + int cchName; + + // Convert the UTF8 string to Wide + cchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_DataPointers.m_pStringsBytes[m_DataPointers.m_pDocuments[i].UrlEntry()]), + -1, + 0, + NULL); + IfNullGo( wcsDocumentUrlAlloc = NEW(WCHAR[cchName]) ); + + cchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_DataPointers.m_pStringsBytes[m_DataPointers.m_pDocuments[i].UrlEntry()]), + -1, + wcsDocumentUrlAlloc, + cchName); + wcsDocumentUrl = wcsDocumentUrlAlloc; + + // Compare the url + if (wcscmp(wcsUrl, wcsDocumentUrl) == 0) + { + IfFailGo(GetDocument(i, &pDoc)); + break; + } + DELETEARRAY(wcsDocumentUrlAlloc); + wcsDocumentUrlAlloc = NULL; + } + + if (pDoc) + { + IfFailGo( pDoc->QueryInterface( IID_ISymUnmanagedDocument, + (void**) ppRetVal ) ); + } + +ErrExit: + DELETEARRAY(wcsDocumentUrlAlloc); + + RELEASE( pDoc ); + + return hr; +} + +//----------------------------------------------------------- +// GetDocuments +// Get the documents for this reader +//----------------------------------------------------------- +HRESULT +SymReader::GetDocuments( + ULONG32 cDocs, + ULONG32 *pcDocs, + ISymUnmanagedDocument *pDocs[] + ) +{ + HRESULT hr = S_OK; + unsigned cDocCount = 0; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pDocs || pcDocs); + IfFalseGo(pDocs || pcDocs, E_INVALIDARG); + + cDocs = min(cDocs, m_pPDBInfo->m_CountOfDocuments); + + for (cDocCount = 0; cDocCount < cDocs; cDocCount++) + { + if (pDocs) + { + SymDocument *pDoc; + IfFailGo(GetDocument(cDocCount, &pDoc)); + pDocs[cDocCount] = pDoc; + } + } + if (pcDocs) + { + *pcDocs = (ULONG32)m_pPDBInfo->m_CountOfDocuments; + } + +ErrExit: + if (FAILED(hr)) + { + unsigned i; + for (i = 0; i < cDocCount; i++) + { + RELEASE(pDocs[cDocCount]); + } + } + return hr; +} + +//----------------------------------------------------------- +// GetUserEntryPoint +// Get the entry point for the pe +//----------------------------------------------------------- +HRESULT +SymReader::GetUserEntryPoint( + mdMethodDef *pRetVal + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + // If it wasn't set then return E_FAIL + if (m_pPDBInfo->m_userEntryPoint == mdTokenNil) + { + hr = E_FAIL; + } + else + { + *pRetVal = m_pPDBInfo->m_userEntryPoint; + } +ErrExit: + return hr; +} + +// Compare the method token with the SymMethodInfo Entry and return the +// value expected by bsearch +int __cdecl CompareMethodToToken(const void *pMethodToken, const void *pMethodInfoEntry) +{ + mdMethodDef MethodDef = *(mdMethodDef *)pMethodToken; + SymMethodInfo *pMethodInfo = (SymMethodInfo *)pMethodInfoEntry; + + return MethodDef - pMethodInfo->MethodToken(); +} + +//----------------------------------------------------------- +// GetMethod +// Get the method for the methoddef +//----------------------------------------------------------- +HRESULT +SymReader::GetMethod( + mdMethodDef method, // MethodDef + ISymUnmanagedMethod **ppRetVal // [out] Method + ) +{ + HRESULT hr = S_OK; + UINT32 MethodEntry = 0; + SymMethodInfo *pMethodInfo; + SymMethod * pMethod = NULL; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + + pMethodInfo = (SymMethodInfo *)bsearch(&method, m_DataPointers.m_pMethods, m_pPDBInfo->m_CountOfMethods, sizeof(SymMethodInfo), CompareMethodToToken); + IfFalseGo(pMethodInfo, E_FAIL); // no matching method found + + // Found a match + MethodEntry = UINT32 (pMethodInfo - m_DataPointers.m_pMethods); + _ASSERTE(m_DataPointers.m_pMethods[MethodEntry].MethodToken() == method); + IfNullGo( pMethod = NEW(SymMethod(this, &m_DataPointers, MethodEntry)) ); + *ppRetVal = pMethod; + pMethod->AddRef(); + hr = S_OK; + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetMethodByVersion +//----------------------------------------------------------- +HRESULT +SymReader::GetMethodByVersion( + mdMethodDef method, + int version, + ISymUnmanagedMethod **ppRetVal + ) +{ + // Don't support multiple version of the same Method so just + // call GetMethod + return GetMethod(method, ppRetVal); +} + + +//----------------------------------------------------------- +// GetMethodFromDocumentPosition +//----------------------------------------------------------- +HRESULT +SymReader::GetMethodFromDocumentPosition( + ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ISymUnmanagedMethod **ppRetVal +) +{ + HRESULT hr = S_OK; + UINT32 DocumentEntry; + UINT32 Method; + UINT32 point; + SequencePoint *pSequencePointBefore; + SequencePoint *pSequencePointAfter; + bool found = false; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(document && ppRetVal); + IfFalseGo(document, E_INVALIDARG); + IfFalseGo(ppRetVal, E_INVALIDARG); + + DocumentEntry = ((SymDocument *)document)->GetDocumentEntry(); + + // Init out parameter + *ppRetVal = NULL; + + // Walk all Methods, check their Document and SequencePoints to see if it's in this doc + // and the line/column + + // This function returns the first match if more than one methods cover the specified position. + for (Method = 0; Method < m_pPDBInfo->m_CountOfMethods; Method++) + { + pSequencePointBefore = NULL; + pSequencePointAfter = NULL; + + // Walk the sequence points + for (point = m_DataPointers.m_pMethods[Method].StartSequencePoints(); + point < m_DataPointers.m_pMethods[Method].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_DataPointers.m_pSequencePoints[point].Document() == DocumentEntry) + { + // If the point is position is within the sequence point then + // we're done. + if (m_DataPointers.m_pSequencePoints[point].IsWithin(line, column)) + { + IfFailGo(GetMethod(m_DataPointers.m_pMethods[Method].MethodToken(), ppRetVal)); + found = true; + break; + } + + // If the sequence is before the point then just remember the point + if (m_DataPointers.m_pSequencePoints[point].IsUserLine() && + m_DataPointers.m_pSequencePoints[point].IsLessThan(line, column)) + { + pSequencePointBefore = &m_DataPointers.m_pSequencePoints[point]; + } + + // If the sequence is before the point then just remember the point + if (m_DataPointers.m_pSequencePoints[point].IsUserLine() && + m_DataPointers.m_pSequencePoints[point].IsGreaterThan(line, column)) + { + pSequencePointAfter = &m_DataPointers.m_pSequencePoints[point]; + } + } + } + + // If we found an exact match, we're done. + if (found) + { + break; + } + + // If we found sequence points within the method before and after + // the line/column then we may have found the method. Record the return value, but keep looking + // to see if we find an exact match. This may not actually be the right method. Iron Python, for instance, + // issues a "method" containing sequence points for all the method headers in a class. Sequence points + // in this "method" would then span the sequence points in the bodies of all but the last method. + if (pSequencePointBefore && pSequencePointAfter) + { + IfFailGo(GetMethod(m_DataPointers.m_pMethods[Method].MethodToken(), ppRetVal)); + } + } + + // This function returns E_FAIL if no match is found. + // Note that this is different from the behaviour of GetMethodsFromDocumentPosition() (see below). + if (*ppRetVal == NULL) + { + hr = E_FAIL; + } + +ErrExit: + return hr; +} + +//--------------------------------------------------------------------------------------- +// +// Return all methods with sequence points covering the specified source location. This +// is actually not as straighforward as it sounds, since we need to mimic the behaviour of +// diasymreader and PDBs here. For PDBs, diasymreader actually does two passes over the +// sequence points. It tries to find an exact match in the first pass, and if that fails, +// it'll do a second pass looking for an approximate match. An approximate match is a sequence +// point which doesn't start on the specified line but covers it. If there's an exact match, +// then it ignores all the approximate matches. In both cases, diasymreader only checks the +// start line number of the sequence point and it ignores the column number. +// +// For backward compatibility, I'm leaving GetMethodFromDocumentPosition() unchanged. +// + +HRESULT +SymReader::GetMethodsFromDocumentPosition( + ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ULONG32 cMethod, + ULONG32* pcMethod, //[Optional]: How many method actually returned + ISymUnmanagedMethod** ppRetVal + ) +{ + HRESULT hr = S_OK; + UINT32 DocumentEntry; + UINT32 Method; + UINT32 point; + + UINT CurMethod = 0; + bool found = false; + bool fExactMatch = true; + + ULONG32 maxPreLine = 0; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(document); + IfFalseGo(document, E_INVALIDARG); + + _ASSERTE((cMethod == 0) || (ppRetVal != NULL)); + IfFalseGo(cMethod == 0 || ppRetVal != NULL, E_INVALIDARG); + + // Initialize the out parameter if it has been provided. + if (pcMethod != NULL) + { + *pcMethod = 0; + } + + DocumentEntry = ((SymDocument *)document)->GetDocumentEntry(); + + // Enumerate the sequence points in two passes. + while (true) + { + // Walk all Methods, check their Document and SequencePoints to see if it's in this doc + // and the line/column + + for (Method = 0; Method < m_pPDBInfo->m_CountOfMethods; Method++) + { + found = false; + + // Walk the sequence points + for (point = m_DataPointers.m_pMethods[Method].StartSequencePoints(); + point < m_DataPointers.m_pMethods[Method].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_DataPointers.m_pSequencePoints[point].Document() == DocumentEntry) + { + // PDBs (more specifically the DIA APIs) only check the start line number and not the end line number when + // trying to determine whether a sequence point covers the specified line number. We need to match this + // behaviour here. For backward compatibility reasons, GetMethodFromDocumentPosition() is still checking + // against the entire range of a sequence point, but we should revisit that in the next release. + ULONG32 curLine = m_DataPointers.m_pSequencePoints[point].StartLine(); + + if (fExactMatch) + { + if (curLine == line) + { + found = true; + } + else if ((maxPreLine < curLine) && (curLine < line)) + { + // This is not an exact match, but let's keep track of the sequence point closest to the specified + // line. We'll use this info if we have to do a second pass. + maxPreLine = curLine; + } + } + else + { + // We are in the second pass, looking for approximate matches. + if ((maxPreLine != 0) && (maxPreLine == curLine)) + { + // Make sure the sequence point covers the specified line. + if (m_DataPointers.m_pSequencePoints[point].IsWithinLineOnly(line)) + { + found = true; + } + } + } + + // If we have found a match (whether it's exact or approximate), then save this method unless the caller is + // only interested in the number of matching methods or the array provided by the caller isn't big enough. + if (found) + { + if (CurMethod < cMethod) + { + IfFailGo(GetMethod(m_DataPointers.m_pMethods[Method].MethodToken(), &(ppRetVal[CurMethod]))); + } + CurMethod++; + break; + } + } + } + + if (found) + { + // If we have already filled out the entire array provided by the caller, then we are done. + if ((cMethod > 0) && (cMethod == CurMethod)) + { + break; + } + else + { + // Otherwise move on to the next method. + continue; + } + } + } + + // If we haven't found an exact match, then try it again looking for a sequence point covering the specified line. + if (fExactMatch && (CurMethod == 0)) + { + fExactMatch = false; + continue; + } + else + { + // If we have found an exact match, or if we have done two passes already, then bail. + break; + } + } + + // Note that unlike GetMethodFromDocumentPosition(), this function returns S_OK even if a match is not found. + if (SUCCEEDED(hr)) + { + if (pcMethod != NULL) + { + *pcMethod = CurMethod; + } + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetSymbolStoreFileName +//----------------------------------------------------------- +HRESULT +SymReader::GetSymbolStoreFileName( + ULONG32 cchName, // Length of szName + ULONG32 *pcchName, // [Optional] + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[] // [Optional] + ) +{ + _ASSERTE(m_fInitialized); + if (!m_fInitialized) + return E_UNEXPECTED; + + if (pcchName) + { + *pcchName = (ULONG32)(wcslen(m_szPath)+1); + } + + if( szName ) + { + if (wcsncpy_s( szName, cchName, m_szPath, _TRUNCATE) == STRUNCATE) + return HrFromWin32(ERROR_INSUFFICIENT_BUFFER); + } + + return NOERROR; +} + +//----------------------------------------------------------- +// GetMethodVersion +//----------------------------------------------------------- +HRESULT +SymReader::GetMethodVersion( + ISymUnmanagedMethod * pMethod, + int* pVersion + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pMethod && pVersion); + IfFalseGo( pMethod && pVersion, E_INVALIDARG); + // This symbol store only supports one version of a method + *pVersion = 0; +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetDocumentVersion +//----------------------------------------------------------- +HRESULT +SymReader::GetDocumentVersion( + ISymUnmanagedDocument* pDoc, + int* pVersion, + BOOL* pbCurrent // [Optional] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(pVersion && pDoc); + IfFalseGo(pVersion, E_INVALIDARG); + IfFalseGo(pDoc, E_INVALIDARG); + + // This symbol store only supports one version of a document + *pVersion = 0; + if (pbCurrent) + { + *pbCurrent = TRUE; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetDocument +// Return the document for the given entry +//----------------------------------------------------------- +HRESULT SymReader::GetDocument( + UINT32 DocumentEntry, + SymDocument **ppDocument) +{ + HRESULT hr = NOERROR; + + _ASSERTE(m_fInitialized); + IfFalseGo(m_fInitialized, E_UNEXPECTED); + + _ASSERTE(ppDocument); + IfFalseGo(ppDocument, E_INVALIDARG); + + _ASSERTE(DocumentEntry < m_pPDBInfo->m_CountOfDocuments); + IfFalseGo(DocumentEntry < m_pPDBInfo->m_CountOfDocuments, E_INVALIDARG); + + if (m_pDocs == NULL) + { + IfNullGo(m_pDocs = NEW(SymDocument *[m_pPDBInfo->m_CountOfDocuments])); + memset(m_pDocs, 0, m_pPDBInfo->m_CountOfDocuments * sizeof(void *)); + } + + if (m_pDocs[DocumentEntry] == NULL) + { + m_pDocs[DocumentEntry] = NEW(SymDocument(this, &m_DataPointers, m_pPDBInfo->m_CountOfMethods, DocumentEntry)); + IfNullGo(m_pDocs[DocumentEntry]); + // AddRef the table version + m_pDocs[DocumentEntry]->AddRef(); + + } + + //Set and AddRef the Out Parameter + *ppDocument = m_pDocs[DocumentEntry]; + (*ppDocument)->AddRef(); + +ErrExit: + return hr; +} + +HRESULT +SymReader::UpdateSymbolStore( + const WCHAR *filename, + IStream *pIStream + ) +{ + // This symbol store doesn't support updating the symbol store. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +HRESULT +SymReader::ReplaceSymbolStore( + const WCHAR *filename, + IStream *pIStream + ) +{ + // This symbol store doesn't support updating the symbol store. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetVariables +//----------------------------------------------------------- +HRESULT +SymReader::GetVariables( + mdToken parent, + ULONG32 cVars, + ULONG32 *pcVars, + ISymUnmanagedVariable *pVars[] + ) +{ + // + // This symbol reader doesn't support non-local variables. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetGlobalVariables +//----------------------------------------------------------- +HRESULT +SymReader::GetGlobalVariables( + ULONG32 cVars, + ULONG32 *pcVars, + ISymUnmanagedVariable *pVars[] + ) +{ + // + // This symbol reader doesn't support non-local variables. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetSymAttribute +//----------------------------------------------------------- +HRESULT +SymReader::GetSymAttribute( + mdToken parent, + __in LPWSTR name, + ULONG32 cBuffer, + ULONG32 *pcBuffer, + __out_bcount_part_opt(cBuffer, *pcBuffer) BYTE buffer[] + ) +{ + // This symbol store doesn't support attributes + // VS may query for certain attributes, but will survive without them, + // so don't ASSERT here. + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetNamespaces +//----------------------------------------------------------- +HRESULT +SymReader::GetNamespaces( + ULONG32 cNameSpaces, + ULONG32 *pcNameSpaces, + ISymUnmanagedNamespace *namespaces[] + ) +{ + // This symbol store doesn't support this + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +/* ------------------------------------------------------------------------- * + * SymDocument class + * ------------------------------------------------------------------------- */ + +HRESULT +SymDocument::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedDocument) + *ppInterface = (ISymUnmanagedDocument*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedDocument*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + + +//----------------------------------------------------------- +// GetURL +//----------------------------------------------------------- +HRESULT +SymDocument::GetURL( + ULONG32 cchUrl, // The allocated size of the buffer + ULONG32 *pcchUrl, // [optional,out] The number of characters available for return + __out_ecount_part_opt(cchUrl, *pcchUrl) WCHAR szUrl[] // [optional,out] The string buffer. + ) +{ + if (pcchUrl) + { + // Convert the UTF8 string to Wide + *pcchUrl = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pDocuments[m_DocumentEntry].UrlEntry()]), + -1, + 0, + NULL); + } + + if( szUrl ) + { + // Convert the UTF8 string to Wide + MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pDocuments[m_DocumentEntry].UrlEntry()]), + -1, + szUrl, + cchUrl); + } + return NOERROR; +} + +//----------------------------------------------------------- +// GetDocumentType +//----------------------------------------------------------- +HRESULT +SymDocument::GetDocumentType( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].DocumentType(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLanguage +//----------------------------------------------------------- +HRESULT +SymDocument::GetLanguage( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].Language(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLanguageVendor +//----------------------------------------------------------- +HRESULT +SymDocument::GetLanguageVendor( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].LanguageVendor(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetCheckSumAlgorithmId +//----------------------------------------------------------- +HRESULT +SymDocument::GetCheckSumAlgorithmId( + GUID *pRetVal + ) +{ + HRESULT hr = NOERROR; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pDocuments[m_DocumentEntry].AlgorithmId(); + SwapGuid(pRetVal); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetCheckSum +//----------------------------------------------------------- +HRESULT +SymDocument::GetCheckSum( + ULONG32 cData, // The allocated size of the buffer. + ULONG32 *pcData, // [optional] The number of bytes available for return + BYTE data[]) // [optional] The buffer to receive the checksum. +{ + BYTE *pCheckSum = &m_pData->m_pBytes[m_pData->m_pDocuments[m_DocumentEntry].CheckSumEntry()]; + ULONG32 CheckSumSize = m_pData->m_pDocuments[m_DocumentEntry].CheckSumSize(); + if (pcData) + { + *pcData = CheckSumSize; + } + if(data) + { + memcpy(data, pCheckSum, min(CheckSumSize, cData)); + } + return NOERROR; +} + +//----------------------------------------------------------- +// FindClosestLine +// Search the sequence points looking a line that is closest +// line following this one that is a sequence point +//----------------------------------------------------------- +HRESULT +SymDocument::FindClosestLine( + ULONG32 line, + ULONG32 *pRetVal + ) +{ + HRESULT hr = NOERROR; + UINT32 Method; + UINT32 point; + ULONG32 closestLine = 0; // GCC can't tell this isn't used before initialization + bool found = false; + + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + // Walk all Methods, check their Document and SequencePoints to see if it's in this doc + // and the line/column + for (Method = 0; Method < m_CountOfMethods; Method++) + { + // Walk the sequence points + for (point = m_pData->m_pMethods[Method].StartSequencePoints(); + point < m_pData->m_pMethods[Method].EndSequencePoints(); + point++) + { + SequencePoint & sp = m_pData->m_pSequencePoints[point]; + // Check to see if this sequence point is in this doc, and is at or + // after the requested line + if ((sp.Document() == m_DocumentEntry) && sp.IsUserLine()) + { + if (sp.IsWithin(line, 0) || sp.IsGreaterThan(line, 0)) + { + // This sequence point is at or after the requested line. If we haven't + // already found a "closest", or this is even closer than the one we have, + // then mark this as the best line so far. + if (!found || m_pData->m_pSequencePoints[point].StartLine() < closestLine) + { + found = true; + closestLine = m_pData->m_pSequencePoints[point].StartLine(); + } + } + } + } + } + + if (found) + { + *pRetVal = closestLine; + } + else + { + // Didn't find any lines at or after the one requested. + hr = E_FAIL; + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymDocument HasEmbeddedSource +//----------------------------------------------------------- +HRESULT +SymDocument::HasEmbeddedSource( + BOOL *pRetVal + ) +{ + // + // This symbol reader doesn't support embedded source. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymDocument GetSourceLength +//----------------------------------------------------------- +HRESULT +SymDocument::GetSourceLength( + ULONG32 *pRetVal + ) +{ + // + // This symbol reader doesn't support embedded source. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymDocument GetSourceRange +//----------------------------------------------------------- +HRESULT +SymDocument::GetSourceRange( + ULONG32 startLine, + ULONG32 startColumn, + ULONG32 endLine, + ULONG32 endColumn, + ULONG32 cSourceBytes, + ULONG32 *pcSourceBytes, + BYTE source[] + ) +{ + // + // This symbol reader doesn't support embedded source. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +/* ------------------------------------------------------------------------- * + * SymMethod class + * ------------------------------------------------------------------------- */ +HRESULT +SymMethod::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedMethod) + *ppInterface = (ISymUnmanagedMethod*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedMethod*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetToken +//----------------------------------------------------------- +HRESULT +SymMethod::GetToken( + mdMethodDef *pRetVal +) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pMethods[m_MethodEntry].MethodToken(); +ErrExit: + return hr; +} + + +//----------------------------------------------------------- +// GetSequencePointCount +//----------------------------------------------------------- +HRESULT +SymMethod::GetSequencePointCount( + ULONG32* pRetVal + ) +{ + + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + *pRetVal = (ULONG32)(m_pData->m_pMethods[m_MethodEntry].EndSequencePoints() - + m_pData->m_pMethods[m_MethodEntry].StartSequencePoints()); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetSequencePoints +//----------------------------------------------------------- +HRESULT +SymMethod::GetSequencePoints( + ULONG32 cPoints, // The size of the allocated arrays. + ULONG32* pcPoints, // [optional] The number of sequence points available for return. + ULONG32 offsets[], // [optional] + ISymUnmanagedDocument *documents[], // [Optional] + ULONG32 lines[], // [Optional] + ULONG32 columns[], // [Optional] + ULONG32 endLines[], // [Optional] + ULONG32 endColumns[] // [Optional] + ) +{ + HRESULT hr = NOERROR; + UINT32 i = 0; + ULONG32 Points = 0; + + for (i = m_pData->m_pMethods[m_MethodEntry].StartSequencePoints(); + (i < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints()); + i++, Points++) + { + if (Points < cPoints) + { + if (documents) + { + SymDocument *pDoc; + IfFailGo(m_pReader->GetDocument(m_pData->m_pSequencePoints[i].Document(), &pDoc)); + documents[Points] = pDoc; + } + + if (offsets) + { + offsets[Points] = m_pData->m_pSequencePoints[i].Offset(); + } + + if (lines) + { + lines[Points] = m_pData->m_pSequencePoints[i].StartLine(); + } + if (columns) + { + columns[Points] = m_pData->m_pSequencePoints[i].StartColumn(); + } + if (endLines) + { + endLines[Points] = m_pData->m_pSequencePoints[i].EndLine(); + } + if (endColumns) + { + endColumns[Points] = m_pData->m_pSequencePoints[i].EndColumn(); + } + } + } + + if (pcPoints) + { + *pcPoints = Points; + } + +ErrExit: + if (FAILED(hr)) + { + if (documents) + { + unsigned j; + for (j = 0; j < i; j++) + { + RELEASE(documents[i]); + } + } + } + return hr; +} + +//----------------------------------------------------------- +// GetRootScope +//----------------------------------------------------------- +HRESULT +SymMethod::GetRootScope( + ISymUnmanagedScope **ppRetVal + ) +{ + HRESULT hr = S_OK; + SymScope *pScope = NULL; + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + + // Init Out Param + *ppRetVal = NULL; + if (m_pData->m_pMethods[m_MethodEntry].EndScopes() - m_pData->m_pMethods[m_MethodEntry].StartScopes()) + { + IfNullGo(pScope = NEW(SymScope(this, m_pData, m_MethodEntry, m_pData->m_pMethods[m_MethodEntry].StartScopes()))); + pScope->AddRef(); + *ppRetVal = pScope; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetOffset +// Given a position in a document, gets the offset within the +// method that corresponds to the position. +//----------------------------------------------------------- +HRESULT +SymMethod::GetOffset( + ISymUnmanagedDocument *document, + ULONG32 line, + ULONG32 column, + ULONG32 *pRetVal + ) +{ + HRESULT hr = S_OK; + bool fFound = false; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + UINT32 point; + UINT32 DocumentEntry; + + DocumentEntry = ((SymDocument *)document)->GetDocumentEntry(); + + // Walk the sequence points + for (point = m_pData->m_pMethods[m_MethodEntry].StartSequencePoints(); + point < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_pData->m_pSequencePoints[point].Document() == DocumentEntry) + { + // Check to see if it's within the sequence point + if (m_pData->m_pSequencePoints[point].IsWithin(line, column)) + { + *pRetVal = m_pData->m_pSequencePoints[point].Offset(); + fFound = true; + break; + } + } + } + if (!fFound) + { + hr = E_FAIL; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetRanges +//----------------------------------------------------------- +HRESULT +SymMethod::GetRanges( + ISymUnmanagedDocument *pDocument, // [in] Document we're working on + ULONG32 line, // [in] The document line corresponding to the ranges. + ULONG32 column, // [in] Ignored + ULONG32 cRanges, // [in] The size of the allocated ranges[] array. + ULONG32 *pcRanges, // [out] The number of ranges available for return + ULONG32 ranges[] // [out] The range array. + ) +{ + HRESULT hr = NOERROR; + DWORD iRange = 0; + UINT32 DocumentEntry; + UINT32 point; + bool fFound = false; + + // Validate some of the parameters + _ASSERTE(pDocument && (cRanges % 2) == 0); + IfFalseGo(pDocument, E_INVALIDARG); + IfFalseGo((cRanges % 2) == 0, E_INVALIDARG); + + // Init out parameter + if (pcRanges) + { + *pcRanges=0; + } + + DocumentEntry = ((SymDocument *)pDocument)->GetDocumentEntry(); + + // Walk the sequence points + for (point = m_pData->m_pMethods[m_MethodEntry].StartSequencePoints(); + point < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints(); + point++) + { + // Check to see if this sequence point is in this doc + if (m_pData->m_pSequencePoints[point].Document() == DocumentEntry) + { + // Check to see if the line is within this sequence + // Note, to be compatible with VS7, ignore the column information + if (line >= m_pData->m_pSequencePoints[point].StartLine() && + line <= m_pData->m_pSequencePoints[point].EndLine()) + { + fFound = true; + break; + } + } + } + + if (fFound) + { + for (;point < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints(); point++) + { + + // Search through all the sequence points since line might have there + // IL spread accross multiple ranges (for loops for example) + if (m_pData->m_pSequencePoints[point].Document() == DocumentEntry && + line >= m_pData->m_pSequencePoints[point].StartLine() && + line <= m_pData->m_pSequencePoints[point].EndLine()) + { + if (iRange < cRanges) + { + ranges[iRange] = m_pData->m_pSequencePoints[point].Offset(); + } + iRange++; + if (iRange < cRanges) + { + if (point+1 < m_pData->m_pMethods[m_MethodEntry].EndSequencePoints()) + { + ranges[iRange] = m_pData->m_pSequencePoints[point+1].Offset(); + } + else + { + // Then it must be till the end of the function which is the root scope's endoffset + ranges[iRange] = m_pData->m_pScopes[m_pData->m_pMethods[m_MethodEntry].StartScopes()].EndOffset()+1; + } + } + iRange++; + } + } + if (pcRanges) + { + // If cRanges passed in, return the number + // of elements actually filled in + if (cRanges) + { + *pcRanges = min(iRange, cRanges); + } + else + { + // Otherwise return the max number + *pcRanges = iRange; + } + } + } + else + { + return E_FAIL; + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetScopeFromOffset +//----------------------------------------------------------- +HRESULT +SymMethod::GetScopeFromOffset( + ULONG32 offset, + ISymUnmanagedScope **pRetVal + ) +{ + // + // This symbol reader doesn't support this functionality + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetParameters +//----------------------------------------------------------- +HRESULT +SymMethod::GetParameters( + ULONG32 cParams, + ULONG32 *pcParams, + ISymUnmanagedVariable *params[] + ) +{ + // + // This symbol reader doesn't support parameter access. Parameters + // can be found in the normal metadata. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetNamespace +//----------------------------------------------------------- +HRESULT +SymMethod::GetNamespace( + ISymUnmanagedNamespace **ppRetVal + ) +{ + // + // This symbol reader doesn't support namespaces + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetSourceStartEnd +//----------------------------------------------------------- +HRESULT +SymMethod::GetSourceStartEnd( + ISymUnmanagedDocument *docs[2], + ULONG32 lines[2], + ULONG32 columns[2], + BOOL *pRetVal + ) +{ + // + // This symbol reader doesn't support source start/end for methods. + // + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +/* ------------------------------------------------------------------------- * + * SymScope class + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// QueryInterface +//----------------------------------------------------------- +HRESULT +SymScope::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedScope) + *ppInterface = (ISymUnmanagedScope*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedScope*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetMethod +//----------------------------------------------------------- +HRESULT +SymScope::GetMethod( + ISymUnmanagedMethod **ppRetVal + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + + *ppRetVal = m_pSymMethod; + m_pSymMethod->AddRef(); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetParent +//----------------------------------------------------------- +HRESULT +SymScope::GetParent( + ISymUnmanagedScope **ppRetVal + ) +{ + HRESULT hr = S_OK; + _ASSERTE(ppRetVal); + IfFalseGo(ppRetVal, E_INVALIDARG); + if (m_pData->m_pScopes[m_ScopeEntry].ParentScope() != (UINT32)-1) + { + IfNullGo(*ppRetVal = static_cast<ISymUnmanagedScope *>(NEW(SymScope(m_pSymMethod, m_pData, m_MethodEntry, + m_pData->m_pScopes[m_ScopeEntry].ParentScope())))); + (*ppRetVal)->AddRef(); + } + else + { + *ppRetVal = NULL; + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetChildren +//----------------------------------------------------------- +HRESULT +SymScope::GetChildren( + ULONG32 cChildren, // [optional] Number of entries in children + ULONG32 *pcChildren, // [optional, out] Number of Children available for retur + ISymUnmanagedScope *children[] // [optional] array to store children into + ) +{ + HRESULT hr = S_OK; + ULONG32 ChildrenCount = 0; + _ASSERTE(pcChildren || (children && cChildren)); + IfFalseGo((pcChildren || (children && cChildren)), E_INVALIDARG); + + if (m_pData->m_pScopes[m_ScopeEntry].HasChildren()) + { + UINT32 ScopeEntry; + for(ScopeEntry = m_pData->m_pMethods[m_MethodEntry].StartScopes(); + (ScopeEntry < m_pData->m_pMethods[m_MethodEntry].EndScopes()); + ScopeEntry++) + { + if (m_pData->m_pScopes[ScopeEntry].ParentScope() == m_ScopeEntry) + { + if (children && ChildrenCount < cChildren) + { + SymScope *pScope; + // Found a child + IfNullGo(pScope = NEW(SymScope(m_pSymMethod, m_pData, m_MethodEntry, ScopeEntry))); + children[ChildrenCount] = pScope; + pScope->AddRef(); + } + ChildrenCount++; + } + } + } + + if (pcChildren) + { + *pcChildren = ChildrenCount; + } + +ErrExit: + if (FAILED(hr) && ChildrenCount) + { + unsigned i; + for (i =0; i< ChildrenCount; i++) + { + RELEASE(children[i]); + } + } + return hr; +} + +//----------------------------------------------------------- +// GetStartOffset +//----------------------------------------------------------- +HRESULT +SymScope::GetStartOffset( + ULONG32* pRetVal + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pScopes[m_ScopeEntry].StartOffset(); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetEndOffset +//----------------------------------------------------------- +HRESULT +SymScope::GetEndOffset( + ULONG32* pRetVal + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + *pRetVal = m_pData->m_pScopes[m_ScopeEntry].EndOffset(); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLocalCount +//----------------------------------------------------------- +HRESULT +SymScope::GetLocalCount( + ULONG32 *pRetVal + ) +{ + HRESULT hr = S_OK; + ULONG32 LocalCount = 0; + _ASSERTE(pRetVal); + IfFalseGo(pRetVal, E_INVALIDARG); + + // Init out parameter + *pRetVal = 0; + if (m_pData->m_pScopes[m_ScopeEntry].HasVars()) + { + UINT32 var; + // Walk and get the locals for this Scope + for (var = m_pData->m_pMethods[m_MethodEntry].StartVars(); + var < m_pData->m_pMethods[m_MethodEntry].EndVars(); + var++) + { + if (m_pData->m_pVars[var].Scope() == m_ScopeEntry && + m_pData->m_pVars[var].IsParam() == false) + { + LocalCount++; + } + } + } + + *pRetVal = LocalCount; +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetLocals +// Input: either pcLocals or +// cLocals and pLocals +//----------------------------------------------------------- +HRESULT +SymScope::GetLocals( + ULONG32 cLocals, // [optional] available entries in pLocals + ULONG32 *pcLocals, // [optional, out] Number of locals returned + ISymUnmanagedVariable *pLocals[] // [optional] array to store locals into + ) +{ + HRESULT hr = S_OK; + + ULONG32 LocalCount = 0; + _ASSERTE(pcLocals || pLocals); + IfFalseGo(pcLocals || pLocals, E_INVALIDARG); + + if (m_pData->m_pScopes[m_ScopeEntry].HasVars()) + { + UINT32 var; + // Walk and get the locals for this Scope + for (var = m_pData->m_pMethods[m_MethodEntry].StartVars(); + var < m_pData->m_pMethods[m_MethodEntry].EndVars(); + var++) + { + if (m_pData->m_pVars[var].Scope() == m_ScopeEntry && + m_pData->m_pVars[var].IsParam() == false) + { + if (pLocals && LocalCount < cLocals) + { + SymReaderVar *pVar; + IfNullGo( pVar = NEW(SymReaderVar(this, m_pData, var))); + pLocals[LocalCount] = pVar; + pVar->AddRef(); + } + LocalCount++; + } + } + } + if (pcLocals) + { + *pcLocals = LocalCount; + } +ErrExit: + if (FAILED(hr) && LocalCount != 0) + { + unsigned i; + for (i =0; i < LocalCount; i++) + { + RELEASE(pLocals[i]); + } + } + return hr; +} + +//----------------------------------------------------------- +// GetNamespaces +// Input: either pcNameSpaces or +// cNameSpaces and pNameSpaces +//----------------------------------------------------------- +HRESULT +SymScope::GetNamespaces( + ULONG32 cNameSpaces, // [optional] number of entries pNameSpaces + ULONG32 *pcNameSpaces, // [optional, out] Maximum number of Namespace + ISymUnmanagedNamespace *pNameSpaces[] // [optinal] array to store namespaces into + ) +{ + HRESULT hr = NOERROR; + unsigned i; + UINT32 NameSpace; + unsigned NameSpaceCount = 0; + + _ASSERTE(pcNameSpaces || (pNameSpaces && cNameSpaces)); + IfFalseGo(pcNameSpaces || (pNameSpaces && cNameSpaces), E_INVALIDARG); + + for (NameSpace = m_pData->m_pMethods[m_MethodEntry].StartUsing(); + NameSpace < m_pData->m_pMethods[m_MethodEntry].EndUsing(); + NameSpace++) + { + if (m_pData->m_pUsings[NameSpace].ParentScope() == m_ScopeEntry) + { + if (pNameSpaces && (NameSpaceCount < cNameSpaces) ) + { + IfNullGo(pNameSpaces[NameSpaceCount] = NEW(SymReaderNamespace(this, m_pData, NameSpace))); + pNameSpaces[NameSpaceCount]->AddRef(); + } + NameSpaceCount++; + } + } + if (pcNameSpaces) + { + *pcNameSpaces = NameSpaceCount; + } +ErrExit: + if (FAILED(hr) && pNameSpaces) + { + for (i = 0; (i < cNameSpaces) && (i < NameSpaceCount); i++) + { + RELEASE(pNameSpaces[i]); + } + } + return hr; +} + +/* ------------------------------------------------------------------------- * + * SymReaderVar class + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// QueryInterface +//----------------------------------------------------------- +HRESULT +SymReaderVar::QueryInterface( + REFIID riid, + void **ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedVariable) + *ppInterface = (ISymUnmanagedVariable*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedVariable*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetName +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetName( + ULONG32 cchName, // [optional] Length of szName buffer + ULONG32 *pcchName, // [optional, out] Total size needed to return the name + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[] // [optional] Buffer to store the name into. + ) +{ + HRESULT hr = S_OK; + + // We must have at least one combination + _ASSERTE(pcchName || (szName && cchName)); + IfFalseGo( (pcchName || (szName && cchName)), E_INVALIDARG ); + + if (pcchName) + { + // Convert the UTF8 string to Wide + *pcchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pVars[m_VarEntry].Name()]), + -1, + 0, + NULL); + + } + if (szName) + { + // Convert the UTF8 string to Wide + MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pVars[m_VarEntry].Name()]), + -1, + szName, + cchName); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetAttributes +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAttributes( + ULONG32 *pRetVal // [out] + ) +{ + if (pRetVal == NULL) + return E_INVALIDARG; + + *pRetVal = m_pData->m_pVars[m_VarEntry].Attributes(); + return S_OK; +} + +//----------------------------------------------------------- +// GetSignature +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetSignature( + ULONG32 cSig, // Size of allocated buffer passed in (sig) + ULONG32 *pcSig, // [optional, out] Total size needed to return the signature + BYTE sig[] // [Optional] Signature + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pcSig || sig); + IfFalseGo( pcSig || sig, E_INVALIDARG ); + if (pcSig) + { + *pcSig = m_pData->m_pVars[m_VarEntry].SignatureSize(); + } + if (sig) + { + cSig = min(m_pData->m_pVars[m_VarEntry].SignatureSize(), cSig); + memcpy(sig, &m_pData->m_pBytes[m_pData->m_pVars[m_VarEntry].Signature()],cSig); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetAddressKind +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressKind( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + *pRetVal = m_pData->m_pVars[m_VarEntry].AddrKind(); +ErrExit: + return S_OK; +} + +//----------------------------------------------------------- +// GetAddressField1 +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressField1( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + + *pRetVal = m_pData->m_pVars[m_VarEntry].Addr1(); + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetAddressField2 +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressField2( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + + *pRetVal = m_pData->m_pVars[m_VarEntry].Addr2(); + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetAddressField3 +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetAddressField3( + ULONG32 *pRetVal // [out] + ) +{ + HRESULT hr = S_OK; + + _ASSERTE(pRetVal); + IfFalseGo( pRetVal, E_INVALIDARG ); + + *pRetVal = m_pData->m_pVars[m_VarEntry].Addr3(); + +ErrExit: + + return hr; +} + +//----------------------------------------------------------- +// GetStartOffset +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetStartOffset( + ULONG32 *pRetVal + ) +{ + // + // This symbol reader doesn't support variable sub-offsets. + // + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetEndOffset +//----------------------------------------------------------- +HRESULT +SymReaderVar::GetEndOffset( + ULONG32 *pRetVal + ) +{ + // + // This symbol reader doesn't support variable sub-offsets. + // + return E_NOTIMPL; +} + + +/* ------------------------------------------------------------------------- * + * SymReaderNamespace class + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// QueryInterface +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::QueryInterface( + REFIID riid, + void** ppInterface + ) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedNamespace) + *ppInterface = (ISymUnmanagedNamespace*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedNamespace*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// GetName +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::GetName( + ULONG32 cchName, // [optional] Chars available in szName + ULONG32 *pcchName, // [optional] Total size needed to return the name + __out_ecount_part_opt(cchName, *pcchName) WCHAR szName[] // [optional] Location to store the name into. + ) +{ + HRESULT hr = S_OK; + _ASSERTE(pcchName || (szName && cchName)); + IfFalseGo( (pcchName || (szName && cchName)), E_INVALIDARG ); + + if (pcchName) + { + *pcchName = (ULONG32) MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pUsings[m_NamespaceEntry].Name()]), + -1, + 0, + NULL); + } + if (szName) + { + MultiByteToWideChar(CP_UTF8, + 0, + (LPCSTR)&(m_pData->m_pStringsBytes[m_pData->m_pUsings[m_NamespaceEntry].Name()]), + -1, + szName, + cchName); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// GetNamespaces +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::GetNamespaces( + ULONG32 cNamespaces, + ULONG32 *pcNamespaces, + ISymUnmanagedNamespace* namespaces[] + ) +{ + // This symbol store doesn't support namespaces. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// GetVariables +//----------------------------------------------------------- +HRESULT +SymReaderNamespace::GetVariables( + ULONG32 cVariables, + ULONG32 *pcVariables, + ISymUnmanagedVariable *pVars[]) +{ + // This symbol store doesn't support namespaces. + _ASSERTE(!"NYI"); + return E_NOTIMPL; +} + + +/* ------------------------------------------------------------------------- * + * SequencePoint struct functions + * ------------------------------------------------------------------------- */ + +//----------------------------------------------------------- +// IsWithin - Is the point given within this sequence point +//----------------------------------------------------------- +bool SequencePoint::IsWithin( + ULONG32 line, + ULONG32 column) +{ + // If the sequence point starts on the same line + // Check the start column (if present) + if (StartLine() == line) + { + if (0 < column && StartColumn() > column) + { + return false; + } + } + + // If the sequence point ends on the same line + // Check the end column + if (EndLine() == line) + { + if (EndColumn() < column) + { + return false; + } + } + + // Make sure the line is within this sequence point + if (!((StartLine() <= line) && (EndLine() >= line))) + { + return false; + } + + // Yep it's within this sequence point + return true; + +} + +//----------------------------------------------------------- +// IsWithinLineOnly - Is the given line within this sequence point +//----------------------------------------------------------- +bool SequencePoint::IsWithinLineOnly( + ULONG32 line) +{ + return ((StartLine() <= line) && (line <= EndLine())); +} + +//----------------------------------------------------------- +// IsGreaterThan - Is the sequence point greater than the position +//----------------------------------------------------------- +bool SequencePoint::IsGreaterThan( + ULONG32 line, + ULONG32 column) +{ + return (StartLine() > line) || + (StartLine() == line && StartColumn() > column); +} + +//----------------------------------------------------------- +// IsLessThan - Is the sequence point less than the position +//----------------------------------------------------------- +bool SequencePoint::IsLessThan +( + ULONG32 line, + ULONG32 column +) +{ + return (StartLine() < line) || + (StartLine() == line && StartColumn() < column); +} + +//----------------------------------------------------------- +// IsUserLine - Is the sequence part of user code +//----------------------------------------------------------- +bool SequencePoint::IsUserLine() +{ + return StartLine() != CODE_WITH_NO_SOURCE; +} |