diff options
author | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
---|---|---|
committer | dotnet-bot <dotnet-bot@microsoft.com> | 2015-01-30 14:14:42 -0800 |
commit | ef1e2ab328087c61a6878c1e84f4fc5d710aebce (patch) | |
tree | dee1bbb89e9d722e16b0d1485e3cdd1b6c8e2cfa /src/debug/ildbsymlib/symwrite.cpp | |
download | coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.gz coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.tar.bz2 coreclr-ef1e2ab328087c61a6878c1e84f4fc5d710aebce.zip |
Initial commit to populate CoreCLR repo
[tfs-changeset: 1407945]
Diffstat (limited to 'src/debug/ildbsymlib/symwrite.cpp')
-rw-r--r-- | src/debug/ildbsymlib/symwrite.cpp | 1554 |
1 files changed, 1554 insertions, 0 deletions
diff --git a/src/debug/ildbsymlib/symwrite.cpp b/src/debug/ildbsymlib/symwrite.cpp new file mode 100644 index 0000000000..16ae07c566 --- /dev/null +++ b/src/debug/ildbsymlib/symwrite.cpp @@ -0,0 +1,1554 @@ +// +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// +// =========================================================================== +// File: symwrite.cpp +// + +// +// Note: The various SymWriter_* and SymDocumentWriter_* are entry points +// called via PInvoke from the managed symbol wrapper used by managed languages +// to emit debug information (such as jscript) +// =========================================================================== + +#include "pch.h" +#include "symwrite.h" + + +// ------------------------------------------------------------------------- +// SymWriter class +// ------------------------------------------------------------------------- + +// This is a COM object which is called both directly from the runtime, and from managed code +// via PInvoke (CoreSymWrapper) and IJW (ISymWrapper). This is an unusual pattern, and it's not +// clear exactly how best to address it. Eg., should we be using BEGIN_EXTERNAL_ENTRYPOINT +// macros? Conceptually this is just a drop-in replacement for diasymreader.dll, and could +// live in a different DLL instead of being statically linked into the runtime. But since it +// relies on utilcode (and actually gets the runtime utilcode, not the nohost utilcode like +// other external tools), it does have some properties of runtime code. +// + +//----------------------------------------------------------- +// NewSymWriter +// Static function used to create a new instance of SymWriter +//----------------------------------------------------------- +HRESULT SymWriter::NewSymWriter(const GUID& id, void **object) +{ + if (id != IID_ISymUnmanagedWriter) + return (E_UNEXPECTED); + + SymWriter *writer = NEW(SymWriter()); + + if (writer == NULL) + return (E_OUTOFMEMORY); + + *object = (ISymUnmanagedWriter*)writer; + writer->AddRef(); + + return (S_OK); +} + +//----------------------------------------------------------- +// SymWriter Constuctor +//----------------------------------------------------------- +SymWriter::SymWriter() : + m_refCount(0), + m_openMethodToken(mdMethodDefNil), + m_LargestMethodToken(mdMethodDefNil), + m_pmethod(NULL), + m_currentScope(k_noScope), + m_hFile(NULL), + m_pIStream(NULL), + m_pStringPool(NULL), + m_closed( false ), + m_sortLines (false), + m_sortMethodEntries(false) +{ + memset(m_szPath, 0, sizeof(m_szPath)); + memset(&ModuleLevelInfo, 0, sizeof(PDBInfo)); +} + +//----------------------------------------------------------- +// SymWriter QI +//----------------------------------------------------------- +COM_METHOD SymWriter::QueryInterface(REFIID riid, void **ppInterface) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedWriter ) + *ppInterface = (ISymUnmanagedWriter*)this; + /* ROTORTODO: Pretend that we do not implement ISymUnmanagedWriter2 to prevent C# compiler from using it. + This is against COM rules since ISymUnmanagedWriter3 inherits from ISymUnmanagedWriter2. + else if (riid == IID_ISymUnmanagedWriter2 ) + *ppInterface = (ISymUnmanagedWriter2*)this; + */ + else if (riid == IID_ISymUnmanagedWriter3 ) + *ppInterface = (ISymUnmanagedWriter3*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedWriter*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter Destructor +//----------------------------------------------------------- +SymWriter::~SymWriter() +{ + // Note that this must be thread-safe - it may be invoked on the finalizer thread + // But since this dtor can only be invoked when all references have been released, + // no other threads can be manipulating the writer. + // Ideally we'd probably just add locking to all methods, but this is low-priority + // because diasymreader.dll isn't thread-safe and so we need to ensure the CLR's use + // of these interfaces are properly syncrhonized. + if ( !m_closed ) + Close(); + RELEASE(m_pIStream); + DELETE(m_pStringPool); +} + +//----------------------------------------------------------- +// SymWriter Initialize the SymWriter +//----------------------------------------------------------- +COM_METHOD SymWriter::Initialize +( + IUnknown *emitter, // Emitter (IMetaData Emit/Import) - unused by ILDB + const WCHAR *szFilename, // FileName of the exe we're creating + IStream *pIStream, // Stream to store into + BOOL fFullBuild // Is this a full build or an incremental build +) +{ + HRESULT hr = S_OK; + + // Incremental compile not implemented in Rotor + _ASSERTE(fFullBuild); + + if (emitter == NULL) + return E_INVALIDARG; + + if (pIStream != NULL) + { + m_pIStream = pIStream; + pIStream->AddRef(); + } + else + { + if (szFilename == NULL) + { + IfFailRet(E_INVALIDARG); + } + } + + m_pStringPool = NEW(StgStringPool()); + IfFailRet(m_pStringPool->InitNew()); + + if (szFilename != NULL) + { + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + _wsplitpath_s( szFilename, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 ); + _wmakepath_s( fullpath, COUNTOF(fullpath), drive, dir, fname, W("ildb") ); + if (wcsncpy_s( m_szPath, COUNTOF(m_szPath), fullpath, _TRUNCATE) == STRUNCATE) + return HrFromWin32(ERROR_INSUFFICIENT_BUFFER); + } + + // Note that we don't need the emitter - ILDB is agnostic to the module metadata. + + return hr; +} + +//----------------------------------------------------------- +// SymWriter Initialize2 the SymWriter +// Delegate to Initialize then use the szFullPathName param +//----------------------------------------------------------- +COM_METHOD SymWriter::Initialize2 +( + IUnknown *emitter, // Emitter (IMetaData Emit/Import) + const WCHAR *szTempPath, // Location of the file + IStream *pIStream, // Stream to store into + BOOL fFullBuild, // Full build or not + const WCHAR *szFullPathName // Final destination of the ildb +) +{ + HRESULT hr = S_OK; + IfFailGo( Initialize( emitter, szTempPath, pIStream, fFullBuild ) ); + // We don't need the final location of the ildb + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter GetorCreateDocument +// creates a new symbol document writer for a specified source +// Arguments: +// input: wcsUrl - The source file name +// output: ppRetVal - The new document writer +// Return Value: hr - S_OK if success, OOM otherwise +//----------------------------------------------------------- +HRESULT SymWriter::GetOrCreateDocument( + const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter +) +{ + ULONG UrlEntry; + DWORD strLength = WszWideCharToMultiByte(CP_UTF8, 0, wcsUrl, -1, 0, 0, 0, 0); + LPSTR multiByteURL = (LPSTR) new char [strLength+1]; + HRESULT hr = S_OK; + + if (multiByteURL == NULL) + { + return E_OUTOFMEMORY; + } + + WszWideCharToMultiByte(CP_UTF8, 0, wcsUrl, -1, multiByteURL, strLength+1, 0, 0); + + if (m_pStringPool->FindString(multiByteURL, &UrlEntry) == S_FALSE) // no file of that name has been seen before + { + hr = CreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal); + } + else // we already have a writer for this file + { + UINT32 docInfo = 0; + + CRITSEC_COOKIE cs = ClrCreateCriticalSection(CrstLeafLock, CRST_DEFAULT); + + ClrEnterCriticalSection(cs); + + while ((docInfo < m_MethodInfo.m_documents.count()) && (m_MethodInfo.m_documents[docInfo].UrlEntry() != UrlEntry)) + { + docInfo++; + } + + if (docInfo == m_MethodInfo.m_documents.count()) // something went wrong and we didn't find the writer + { + hr = CreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal); + } + else + { + *ppRetVal = m_MethodInfo.m_documents[docInfo].DocumentWriter(); + (*ppRetVal)->AddRef(); + } + ClrLeaveCriticalSection(cs); + } + + delete [] multiByteURL; + return hr; + +} // SymWriter::GetOrCreateDocument + +//----------------------------------------------------------- +// SymWriter CreateDocument +// creates a new symbol document writer for a specified source +// Arguments: +// input: wcsUrl - The source file name +// output: ppRetVal - The new document writer +// Return Value: hr - S_OK if success, OOM otherwise +//----------------------------------------------------------- +HRESULT SymWriter::CreateDocument(const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter +) + +{ + DocumentInfo* pDocument = NULL; + SymDocumentWriter *sdw = NULL; + UINT32 DocumentEntry; + ULONG UrlEntry; + HRESULT hr = NOERROR; + + DocumentEntry = m_MethodInfo.m_documents.count(); + IfNullGo(pDocument = m_MethodInfo.m_documents.next()); + memset(pDocument, 0, sizeof(DocumentInfo)); + + // Create the new document writer. + sdw = NEW(SymDocumentWriter(DocumentEntry, this)); + IfNullGo(sdw); + + pDocument->SetLanguage(*pLanguage); + pDocument->SetLanguageVendor(*pLanguageVendor); + pDocument->SetDocumentType(*pDocumentType); + pDocument->SetDocumentWriter(sdw); + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + hr = m_pStringPool->AddStringW(wcsUrl, (UINT32 *)&UrlEntry); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + + pDocument->SetUrlEntry(UrlEntry); + + // Pass out the new ISymUnmanagedDocumentWriter. + sdw->AddRef(); + *ppRetVal = (ISymUnmanagedDocumentWriter*)sdw; + sdw = NULL; + +ErrExit: + DELETE(sdw); + return hr; +} + +//----------------------------------------------------------- +// SymWriter DefineDocument +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineDocument( + const WCHAR *wcsUrl, // Document name + const GUID *pLanguage, // What Language we're compiling + const GUID *pLanguageVendor, // What vendor + const GUID *pDocumentType, // Type + ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter +) +{ + HRESULT hr = NOERROR; + + IfFalseGo(wcsUrl, E_INVALIDARG); + IfFalseGo(pLanguage, E_INVALIDARG); + IfFalseGo(pLanguageVendor, E_INVALIDARG); + IfFalseGo(pDocumentType, E_INVALIDARG); + IfFalseGo(ppRetVal, E_INVALIDARG); + + // Init out parameter + *ppRetVal = NULL; + + hr = GetOrCreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal); +ErrExit: + return hr; +} + + +//----------------------------------------------------------- +// SymWriter SetDocumentSrc +//----------------------------------------------------------- +HRESULT SymWriter::SetDocumentSrc( + UINT32 DocumentEntry, + DWORD SourceSize, + BYTE* pSource +) +{ + DocumentInfo* pDocument = NULL; + HRESULT hr = S_OK; + + IfFalseGo( SourceSize == 0 || pSource, E_INVALIDARG); + IfFalseGo( DocumentEntry < m_MethodInfo.m_documents.count(), E_INVALIDARG); + + pDocument = &m_MethodInfo.m_documents[DocumentEntry]; + + if (pSource) + { + UINT32 i; + IfFalseGo( m_MethodInfo.m_bytes.grab(SourceSize, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], pSource, SourceSize); + pDocument->SetSourceEntry(i); + pDocument->SetSourceSize(SourceSize); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter SetDocumentCheckSum +//----------------------------------------------------------- +HRESULT SymWriter::SetDocumentCheckSum( + UINT32 DocumentEntry, + GUID AlgorithmId, + DWORD CheckSumSize, + BYTE* pCheckSum +) +{ + DocumentInfo* pDocument = NULL; + HRESULT hr = S_OK; + + IfFalseGo( CheckSumSize == 0 || pCheckSum, E_INVALIDARG); + IfFalseGo( DocumentEntry < m_MethodInfo.m_documents.count(), E_INVALIDARG); + + pDocument = &m_MethodInfo.m_documents[DocumentEntry]; + + if (pCheckSum) + { + UINT32 i; + IfFalseGo( m_MethodInfo.m_bytes.grab(CheckSumSize, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], pCheckSum, CheckSumSize); + pDocument->SetCheckSumEntry(i); + pDocument->SetCheckSymSize(CheckSumSize); + } + + pDocument->SetAlgorithmId(AlgorithmId); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter SetUserEntryPoint +//----------------------------------------------------------- +COM_METHOD SymWriter::SetUserEntryPoint(mdMethodDef entryMethod) +{ + HRESULT hr = S_OK; + + // Make sure that an entry point hasn't already been set. + if (ModuleLevelInfo.m_userEntryPoint == 0) + ModuleLevelInfo.m_userEntryPoint = entryMethod; + + return hr; +} + +//----------------------------------------------------------- +// SymWriter OpenMethod +// Get ready to get information about a new method +//----------------------------------------------------------- +COM_METHOD SymWriter::OpenMethod(mdMethodDef method) +{ + HRESULT hr = S_OK; + + // We can only have one open method at a time. + if (m_openMethodToken != mdMethodDefNil) + return E_INVALIDARG; + + m_LargestMethodToken = max(method, m_LargestMethodToken); + + if (m_LargestMethodToken != method) + { + m_sortMethodEntries = true; + // Check to see if we're trying to open a method we've already done + unsigned i; + for (i = 0; i < m_MethodInfo.m_methods.count(); i++) + { + if (m_MethodInfo.m_methods[i].MethodToken() == method) + { + return E_INVALIDARG; + } + } + } + + // Remember the token for this method. + m_openMethodToken = method; + + IfNullGo( m_pmethod = m_MethodInfo.m_methods.next() ); + m_pmethod->SetMethodToken(m_openMethodToken); + m_pmethod->SetStartScopes(m_MethodInfo.m_scopes.count()); + m_pmethod->SetStartVars(m_MethodInfo.m_vars.count()); + m_pmethod->SetStartUsing(m_MethodInfo.m_usings.count()); + m_pmethod->SetStartConstant(m_MethodInfo.m_constants.count()); + m_pmethod->SetStartDocuments(m_MethodInfo.m_documents.count()); + m_pmethod->SetStartSequencePoints(m_MethodInfo.m_auxSequencePoints.count()); + + // By default assume the lines are inserted in the correct order + m_sortLines = false; + + // Initialize the maximum scope end offset for this method + m_maxScopeEnd = 1; + + // Open the implicit root scope for the method + _ASSERTE(m_currentScope == k_noScope); + + IfFailRet(OpenScope(0, NULL)); + + _ASSERTE(m_currentScope != k_noScope); + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::OpenMethod2( + mdMethodDef method, + ULONG32 isect, + ULONG32 offset) +{ + // This symbol writer doesn't support section offsets + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// compareAuxLines +// Used to sort SequencePoint +//----------------------------------------------------------- +int __cdecl SequencePoint::compareAuxLines(const void *elem1, const void *elem2 ) +{ + SequencePoint* p1 = (SequencePoint*)elem1; + SequencePoint* p2 = (SequencePoint*)elem2; + return p1->Offset() - p2->Offset(); +} + +//----------------------------------------------------------- +// SymWriter CloseMethod +// We're done with this function, write it out. +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseMethod() +{ + HRESULT hr = S_OK; + UINT32 CountOfSequencePoints; + + // Must have an open method. + if (m_openMethodToken == mdMethodDefNil) + return E_UNEXPECTED; + + // All scopes up to the root must have been closed (and the root must not have been closed). + _ASSERTE(m_currentScope != k_noScope); + if (m_MethodInfo.m_scopes[m_currentScope].ParentScope() != k_noScope) + return E_FAIL; + + // Close the implicit root scope using the largest end offset we've seen in this method, or 1 if none. + IfFailRet(CloseScopeInternal(m_maxScopeEnd)); + + m_pmethod->SetEndScopes(m_MethodInfo.m_scopes.count()); + m_pmethod->SetEndVars(m_MethodInfo.m_vars.count()); + m_pmethod->SetEndUsing(m_MethodInfo.m_usings.count()); + m_pmethod->SetEndConstant(m_MethodInfo.m_constants.count()); + m_pmethod->SetEndDocuments(m_MethodInfo.m_documents.count()); + m_pmethod->SetEndSequencePoints(m_MethodInfo.m_auxSequencePoints.count()); + + CountOfSequencePoints = m_pmethod->EndSequencePoints() - m_pmethod->StartSequencePoints(); + // Write any sequence points. + if (CountOfSequencePoints > 0 ) { + // sort the sequence points + if ( m_sortLines ) + { + qsort(&m_MethodInfo.m_auxSequencePoints[m_pmethod->StartSequencePoints()], + CountOfSequencePoints, + sizeof( SequencePoint ), + SequencePoint::compareAuxLines ); + } + } + + // All done with this method. + m_openMethodToken = mdMethodDefNil; + + return hr; +} + +//----------------------------------------------------------- +// SymWriter DefineSequencePoints +// Define the sequence points for this function +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineSequencePoints( + ISymUnmanagedDocumentWriter *document, // + ULONG32 spCount, // Count of sequence points + ULONG32 offsets[], // Offsets + ULONG32 lines[], // Beginning Lines + ULONG32 columns[], // [optional] Columns + ULONG32 endLines[], // [optional] End Lines + ULONG32 endColumns[] // [optional] End Columns +) +{ + HRESULT hr = S_OK; + DWORD docnum; + + // We must have a document, offsets, and lines. + IfFalseGo(document && offsets && lines, E_INVALIDARG); + // Must have some sequence points + IfFalseGo(spCount != 0, E_INVALIDARG); + // Must have an open method. + IfFalseGo(m_openMethodToken != mdMethodDefNil, E_INVALIDARG); + + // Remember that we've loaded the sequence points and + // which document they were for. + docnum = (DWORD)((SymDocumentWriter *)document)->GetDocumentEntry();; + + // if sets of lines have been inserted out-of-order, remember to sort when emitting + if ( m_MethodInfo.m_auxSequencePoints.count() > 0 && m_MethodInfo.m_auxSequencePoints[ m_MethodInfo.m_auxSequencePoints.count()-1 ].Offset() > offsets[0] ) + m_sortLines = true; + + // Copy the incomming arrays into the internal format. + + for ( UINT32 i = 0; i < spCount; i++) + { + SequencePoint * paux; + IfNullGo(paux = m_MethodInfo.m_auxSequencePoints.next()); + paux->SetOffset(offsets[i]); + paux->SetStartLine(lines[i]); + paux->SetStartColumn(columns ? columns[i] : 0); + // If no endLines specified, assume same as start + paux->SetEndLine(endLines ? endLines[i] : lines[i]); + paux->SetEndColumn(endColumns ? endColumns[i]: 0); + paux->SetDocument(docnum); + } + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter OpenScope +// Open a new scope for this function +//----------------------------------------------------------- +COM_METHOD SymWriter::OpenScope(ULONG32 startOffset, ULONG32 *scopeID) +{ + HRESULT hr = S_OK; + + // Make sure the startOffset is within the current scope. + if ((m_currentScope != k_noScope) && + (unsigned int)startOffset < m_MethodInfo.m_scopes[m_currentScope].StartOffset()) + return E_INVALIDARG; + + // Fill in the new scope. + UINT32 newScope = m_MethodInfo.m_scopes.count(); + + // Make sure that adding 1 below won't overflow (although "next" should fail much + // sooner if we were anywhere near close enough). + if (newScope >= UINT_MAX) + return E_UNEXPECTED; + + SymLexicalScope *sc; + IfNullGo( sc = m_MethodInfo.m_scopes.next()); + sc->SetParentScope(m_currentScope); // parent is the current scope. + sc->SetStartOffset(startOffset); + sc->SetHasChildren(FALSE); + sc->SetHasVars(FALSE); + sc->SetEndOffset(0); + + // The current scope has a child now. + if (m_currentScope != k_noScope) + m_MethodInfo.m_scopes[m_currentScope].SetHasChildren(TRUE); + + // The new scope is now the current scope. + m_currentScope = newScope; + _ASSERTE(m_currentScope != k_noScope); + + // Pass out the "scope id", which is a _1_ based id for the scope. + if (scopeID) + *scopeID = m_currentScope + 1; + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter CloseScope +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseScope( + ULONG32 endOffset // Closing offset of scope +) +{ + // This API can only be used to close explicit user scopes. + // The implicit root scope is only closed internally by CloseMethod. + if ((m_currentScope == k_noScope) || (m_MethodInfo.m_scopes[m_currentScope].ParentScope() == k_noScope)) + return E_FAIL; + + HRESULT hr = CloseScopeInternal(endOffset); + + _ASSERTE(m_currentScope != k_noScope); + + return hr; +} + +//----------------------------------------------------------- +// CloseScopeInternal +// Implementation for ISymUnmanagedWriter::CloseScope but can be called even to +// close the implicit root scope. +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseScopeInternal( + ULONG32 endOffset // Closing offset of scope +) +{ + _ASSERTE(m_currentScope != k_noScope); + + // Capture the end offset + m_MethodInfo.m_scopes[m_currentScope].SetEndOffset(endOffset); + + // The current scope is now the parent scope. + m_currentScope = m_MethodInfo.m_scopes[m_currentScope].ParentScope(); + + // Update the maximum scope end offset for this method + if (endOffset > m_maxScopeEnd) + m_maxScopeEnd = endOffset; + + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter SetScopeRange +// Set the Start/End Offset for this scope +//----------------------------------------------------------- +COM_METHOD SymWriter::SetScopeRange( + ULONG32 scopeID, // ID for the scope + ULONG32 startOffset, // Start Offset + ULONG32 endOffset // End Offset +) +{ + if (scopeID <= 0) + return E_INVALIDARG; + + if (scopeID > m_MethodInfo.m_scopes.count() ) + return E_INVALIDARG; + + // Remember the new start and end offsets. Also remember that the + // scopeID is _1_ based!!! + SymLexicalScope *sc = &(m_MethodInfo.m_scopes[scopeID - 1]); + sc->SetStartOffset(startOffset); + sc->SetEndOffset(endOffset); + + // Update the maximum scope end offset for this method + if (endOffset > m_maxScopeEnd) + m_maxScopeEnd = endOffset; + + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter DefineLocalVariable +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineLocalVariable( + const WCHAR *name, // Name of the variable + ULONG32 attributes, // Attributes for the var + ULONG32 cSig, // Signature for the variable + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3, + ULONG32 startOffset, ULONG32 endOffset) +{ + HRESULT hr = S_OK; + ULONG NameEntry; + + // We must have a current scope. + if (m_currentScope == k_noScope) + return E_FAIL; + + // We must have a name and a signature. + if (!name || !signature) + return E_INVALIDARG; + + if (cSig == 0) + return E_INVALIDARG; + + // Make a new local variable and copy the data. + SymVariable *var; + IfNullGo( var = m_MethodInfo.m_vars.next()); + var->SetIsParam(FALSE); + var->SetAttributes(attributes); + var->SetAddrKind(addrKind); + var->SetIsHidden(attributes & VAR_IS_COMP_GEN); + var->SetAddr1(addr1); + var->SetAddr2(addr2); + var->SetAddr3(addr3); + + + // Length of the sig? + ULONG32 sigLen; + sigLen = cSig; + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(name, (UINT32 *)&NameEntry); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + var->SetName(NameEntry); + + // Copy the signature + // Note that we give this back exactly as-is, but callers typically remove any calling + // convention prefix. + UINT32 i; + IfFalseGo(m_MethodInfo.m_bytes.grab(sigLen, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], signature, sigLen); + var->SetSignature(i); + var->SetSignatureSize(sigLen); + + // This var is in the current scope + var->SetScope(m_currentScope); + m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE); + + var->SetStartOffset(startOffset); + var->SetEndOffset(endOffset); + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::DefineLocalVariable2( + const WCHAR *name, + ULONG32 attributes, + mdSignature sigToken, + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3, + ULONG32 startOffset, ULONG32 endOffset) +{ + // This symbol writer doesn't support definiting signatures via tokens + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymWriter DefineParameter +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineParameter( + const WCHAR *name, // Param name + ULONG32 attributes, // Attribute for the parameter + ULONG32 sequence, + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + HRESULT hr = S_OK; + ULONG NameEntry; + + // We must have a method. + if (m_openMethodToken == mdMethodDefNil) + return E_INVALIDARG; + + // We must have a name. + if (!name) + return E_INVALIDARG; + + SymVariable *var; + IfNullGo( var = m_MethodInfo.m_vars.next()); + var->SetIsParam(TRUE); + var->SetAttributes(attributes); + var->SetAddrKind(addrKind); + var->SetIsHidden(attributes & VAR_IS_COMP_GEN); + var->SetAddr1(addr1); + var->SetAddr2(addr2); + var->SetAddr3(addr3); + var->SetSequence(sequence); + + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(name, (UINT32 *)&NameEntry); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + var->SetName(NameEntry); + + // This var is in the current scope + if (m_currentScope != k_noScope) + m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE); + + var->SetStartOffset(0); + var->SetEndOffset(0); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// verifyConstTypes +// Verify that the type is a type we support +//----------------------------------------------------------- +static bool verifyConstTypes( DWORD vt ) +{ + switch ( vt ) { + case VT_UI8: + case VT_I8: + case VT_I4: + case VT_UI1: // value < LF_NUMERIC + case VT_I2: + case VT_R4: + case VT_R8: + case VT_BOOL: // value < LF_NUMERIC + case VT_DATE: + case VT_BSTR: + case VT_I1: + case VT_UI2: + case VT_UI4: + case VT_INT: + case VT_UINT: + case VT_DECIMAL: + return true; + } + return false; +} + +//----------------------------------------------------------- +// SymWriter DefineConstant +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineConstant( + const WCHAR __RPC_FAR *name, + VARIANT value, + ULONG32 cSig, + unsigned char __RPC_FAR signature[]) +{ + HRESULT hr = S_OK; + ULONG ValueBstr = 0; + ULONG Name; + + // currently we only support local constants + + // We must have a method. + if (m_openMethodToken == mdMethodDefNil) + return E_INVALIDARG; + + // We must have a name and signature. + IfFalseGo(name, E_INVALIDARG); + IfFalseGo(signature, E_INVALIDARG); + IfFalseGo(cSig > 0, E_INVALIDARG); + + // + // Support byref decimal values + // + if ( (V_VT(&value)) == ( VT_BYREF | VT_DECIMAL ) ) { + if ( V_DECIMALREF(&value) == NULL ) + return E_INVALIDARG; + V_DECIMAL(&value) = *V_DECIMALREF(&value); + V_VT(&value) = VT_DECIMAL; + } + + // we only support non-ref constants + if ( ( V_VT(&value) & VT_BYREF ) != 0 ) + return E_INVALIDARG; + + if ( !verifyConstTypes( V_VT(&value) ) ) + return E_INVALIDARG; + + // If it's a BSTR, we need to persist the Bstr as an entry into + // the stringpool + if (V_VT(&value) == VT_BSTR) + { + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the bstrValue. + hr = m_pStringPool->AddStringW(V_BSTR(&value), (UINT32 *)&ValueBstr); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + V_BSTR(&value) = NULL; + } + + SymConstant *con; + IfNullGo( con = m_MethodInfo.m_constants.next()); + con->SetValue(value, ValueBstr); + + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(name, (UINT32 *)&Name); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + con->SetName(Name); + + // Copy the signature + UINT32 i; + IfFalseGo(m_MethodInfo.m_bytes.grab(cSig, &i), E_OUTOFMEMORY); + memcpy(&m_MethodInfo.m_bytes[i], signature, cSig); + con->SetSignature(i); + con->SetSignatureSize(cSig); + + // This const is in the current scope + con->SetParentScope(m_currentScope); + m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE); + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::DefineConstant2( + const WCHAR *name, + VARIANT value, + mdSignature sigToken) +{ + // This symbol writer doesn't support definiting signatures via tokens + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymWriter Abort +//----------------------------------------------------------- +COM_METHOD SymWriter::Abort(void) +{ + m_closed = true; + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter DefineField +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineField( + mdTypeDef parent, + const WCHAR *name, + ULONG32 attributes, + ULONG32 csig, + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + // This symbol store doesn't support extra random variable + // definitions. + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter DefineGlobalVariable +//----------------------------------------------------------- +COM_METHOD SymWriter::DefineGlobalVariable( + const WCHAR *name, + ULONG32 attributes, + ULONG32 csig, + BYTE signature[], + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + // This symbol writer doesn't support global variables + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +COM_METHOD SymWriter::DefineGlobalVariable2( + const WCHAR *name, + ULONG32 attributes, + mdSignature sigToken, + ULONG32 addrKind, + ULONG32 addr1, ULONG32 addr2, ULONG32 addr3) +{ + // This symbol writer doesn't support global variables + _ASSERTE(FALSE); + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// compareMethods +// Used to sort method entries +//----------------------------------------------------------- +int __cdecl SymMethodInfo::compareMethods(const void *elem1, const void *elem2 ) +{ + SymMethodInfo* p1 = (SymMethodInfo*)elem1; + SymMethodInfo* p2 = (SymMethodInfo*)elem2; + return p1->MethodToken() - p2->MethodToken(); +} + +//----------------------------------------------------------- +// SymWriter Close +//----------------------------------------------------------- +COM_METHOD SymWriter::Close() +{ + HRESULT hr = Commit(); + m_closed = true; + for (UINT32 docInfo = 0; docInfo < m_MethodInfo.m_documents.count(); docInfo++) + { + m_MethodInfo.m_documents[docInfo].SetDocumentWriter(NULL); + } + return hr; +} + +//----------------------------------------------------------- +// SymWriter Commit +//----------------------------------------------------------- +COM_METHOD SymWriter::Commit(void) +{ + // Sort the entries if need be + if (m_sortMethodEntries) + { + // First remap any tokens we need to + if (m_MethodMap.count()) + { + unsigned i; + for (i = 0; i< m_MethodMap.count(); i++) + { + m_MethodInfo.m_methods[m_MethodMap[i].MethodEntry].SetMethodToken(m_MethodMap[i].m_MethodToken); + } + } + + // Now sort the array + qsort(&m_MethodInfo.m_methods[0], + m_MethodInfo.m_methods.count(), + sizeof( SymMethodInfo ), + SymMethodInfo::compareMethods ); + m_sortMethodEntries = false; + } + return WritePDB(); +} + +//----------------------------------------------------------- +// SymWriter SetSymAttribute +//----------------------------------------------------------- +COM_METHOD SymWriter::SetSymAttribute( + mdToken parent, + const WCHAR *name, + ULONG32 cData, + BYTE data[]) +{ + // Setting attributes on the symbol isn't supported + + // ROTORTODO: #156785 in PS + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter OpenNamespace +//----------------------------------------------------------- +COM_METHOD SymWriter::OpenNamespace(const WCHAR *name) +{ + // This symbol store doesn't support namespaces. + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// SymWriter OpenNamespace +//----------------------------------------------------------- +COM_METHOD SymWriter::CloseNamespace() +{ + // This symbol store doesn't support namespaces. + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter UsingNamespace +// Add a Namespace to the list of namespace for this method +//----------------------------------------------------------- +COM_METHOD SymWriter::UsingNamespace(const WCHAR *fullName) +{ + HRESULT hr = S_OK; + ULONG Name; + + // We must have a current scope. + if (m_currentScope == k_noScope) + return E_FAIL; + + // We must have a name. + if (!fullName) + return E_INVALIDARG; + + + SymUsingNamespace *use; + IfNullGo( use = m_MethodInfo.m_usings.next()); + + // stack check needed to call back into utilcode + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO(); + // Copy the name. + hr = m_pStringPool->AddStringW(fullName, (UINT32 *)&Name); + END_SO_INTOLERANT_CODE; + IfFailGo(hr); + use->SetName(Name); + + use->SetParentScope(m_currentScope); + +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter SetMethodSourceRange +//----------------------------------------------------------- +COM_METHOD SymWriter::SetMethodSourceRange( + ISymUnmanagedDocumentWriter *startDoc, + ULONG32 startLine, + ULONG32 startColumn, + ISymUnmanagedDocumentWriter *endDoc, + ULONG32 endLine, + ULONG32 endColumn) +{ + // This symbol store doesn't support source ranges. + return E_NOTIMPL; +} + +//----------------------------------------------------------- +// UnicodeToUTF8 +// Translate the Unicode string to a UTF8 string +// Return the length in UTF8 of the Unicode string +// Including NULL terminator +//----------------------------------------------------------- +inline int WINAPI UnicodeToUTF8( + LPCWSTR pUni, // Unicode string + __out_bcount_opt(cbUTF) PSTR pUTF8, // [optional, out] Buffer for UTF8 string + int cbUTF // length of UTF8 buffer +) +{ + // Pass in the length including the NULL terminator + int cchSrc = (int)wcslen(pUni)+1; + return WideCharToMultiByte(CP_UTF8, 0, pUni, cchSrc, pUTF8, cbUTF, NULL, NULL); +} + +//----------------------------------------------------------- +// SymWriter GetDebugCVInfo +// Get the size and potentially the debug info +//----------------------------------------------------------- +COM_METHOD SymWriter::GetDebugCVInfo( + DWORD cbBuf, // [optional] Size of buf + DWORD *pcbBuf, // [out] Size needed for the DebugInfo + BYTE buf[]) // [optional, out] Buffer for DebugInfo +{ + + if ( m_szPath == NULL || *m_szPath == 0 ) + return E_UNEXPECTED; + + // We need to change the .ildb extension to .pdb to be + // compatible with VS7 + wchar_t fullpath[_MAX_PATH]; + wchar_t drive[_MAX_DRIVE]; + wchar_t dir[_MAX_DIR]; + wchar_t fname[_MAX_FNAME]; + if (_wsplitpath_s( m_szPath, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 )) + return E_FAIL; + if (_wmakepath_s( fullpath, COUNTOF(fullpath), drive, dir, fname, W("pdb") )) + return E_FAIL; + + // Get UTF-8 string size, including the Null Terminator + int Utf8Length = UnicodeToUTF8( fullpath, NULL, 0 ); + if (Utf8Length < 0 ) + return HRESULT_FROM_GetLastError(); + + DWORD dwSize = sizeof(RSDSI) + DWORD(Utf8Length); + + // If the caller is just checking for the size + if ( cbBuf == 0 && pcbBuf != NULL ) + { + *pcbBuf = dwSize; + return S_OK; + } + + if (cbBuf < dwSize) + { + return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); + } + + if ( buf == NULL ) + { + return E_INVALIDARG; + } + + RSDSI* pRsdsi = (RSDSI*)buf; + pRsdsi->dwSig = VAL32(0x53445352); // "SDSR"; + pRsdsi->guidSig = ILDB_VERSION_GUID; + SwapGuid(&(pRsdsi->guidSig)); + // Age of 0 represent VC6.0 format so make sure it's 1 + pRsdsi->age = VAL32(1); + UnicodeToUTF8( fullpath, pRsdsi->szPDB, Utf8Length ); + if ( pcbBuf ) + *pcbBuf = dwSize; + return S_OK; +} + +//----------------------------------------------------------- +// SymWriter GetDebugInfo +// Get the size and potentially the debug info +//----------------------------------------------------------- +COM_METHOD SymWriter::GetDebugInfo( + IMAGE_DEBUG_DIRECTORY *pIDD, // [out] IDD to fill in + DWORD cData, // [optional] size of data + DWORD *pcData, // [optional, out] return needed size for DebugInfo + BYTE data[]) // [optional] Buffer to store into +{ + HRESULT hr = S_OK; + if ( cData == 0 && pcData != NULL ) + { + // just checking for the size + return GetDebugCVInfo( 0, pcData, NULL ); + } + + if ( pIDD == NULL ) + return E_INVALIDARG; + + DWORD cTheData = 0; + IfFailGo( GetDebugCVInfo( cData, &cTheData, data ) ); + + memset( pIDD, 0, sizeof( *pIDD ) ); + pIDD->Type = VAL32(IMAGE_DEBUG_TYPE_CODEVIEW); + pIDD->SizeOfData = VAL32(cTheData); + + if ( pcData ) { + *pcData = cTheData; + } + +ErrExit: + return hr; +} + +COM_METHOD SymWriter::RemapToken(mdToken oldToken, mdToken newToken) +{ + HRESULT hr = NOERROR; + if (oldToken != newToken) + { + // We only care about methods + if ((TypeFromToken(oldToken) == mdtMethodDef) || + (TypeFromToken(newToken) == mdtMethodDef)) + { + // Make sure they are both methods + _ASSERTE(TypeFromToken(newToken) == mdtMethodDef); + _ASSERTE(TypeFromToken(oldToken) == mdtMethodDef); + + // Make sure we sort before saving + m_sortMethodEntries = true; + + // Check to see if we're trying to map a token we know about + unsigned i; + for (i = 0; i < m_MethodInfo.m_methods.count(); i++) + { + if (m_MethodInfo.m_methods[i].MethodToken() == oldToken) + { + // Remember the map, we need to actually do the actual + // mapping later because we might already have a function + // with a token 'newToken' + SymMap *pMethodMap; + IfNullGo( pMethodMap = m_MethodMap.next() ); + pMethodMap->m_MethodToken = newToken; + pMethodMap->MethodEntry = i; + break; + } + } + } + } +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter Write +// Write the information to a file or to a stream +//----------------------------------------------------------- +COM_METHOD SymWriter::Write(void *pData, DWORD SizeOfData) +{ + HRESULT hr = NOERROR; + DWORD NumberOfBytesWritten = 0; + if (m_pIStream) + { + IfFailGo(m_pIStream->Write(pData, + SizeOfData, + &NumberOfBytesWritten)); + } + else + { + // Write out a signature to recognize that we're an ildb + if (!WriteFile(m_hFile, pData, SizeOfData, &NumberOfBytesWritten, NULL)) + return HrFromWin32(GetLastError()); + } + _ASSERTE(NumberOfBytesWritten == SizeOfData); +ErrExit: + return hr; +} + +//----------------------------------------------------------- +// SymWriter WriteStringPool +// Write the information to a file or to a stream +//----------------------------------------------------------- +COM_METHOD SymWriter::WriteStringPool() +{ + IStream *pIStream = NULL; + BYTE *pStreamMem = NULL; + + HRESULT hr = NOERROR; + if (m_pIStream) + { + IfFailGo(m_pStringPool->PersistToStream(m_pIStream)); + } + else + { + LARGE_INTEGER disp = { {0, 0} }; + DWORD NumberOfBytes; + DWORD SizeOfData; + STATSTG statStg; + + IfFailGo(CreateStreamOnHGlobal(NULL, + TRUE, + &pIStream)); + + IfFailGo(m_pStringPool->PersistToStream(pIStream)); + + IfFailGo(pIStream->Stat(&statStg, STATFLAG_NONAME)); + SizeOfData = statStg.cbSize.u.LowPart; + + IfFailGo(pIStream->Seek(disp, STREAM_SEEK_SET, NULL)); + + pStreamMem = NEW(BYTE[SizeOfData]); + IfFailGo(pIStream->Read(pStreamMem, SizeOfData, &NumberOfBytes)); + + if (!WriteFile(m_hFile, pStreamMem, SizeOfData, &NumberOfBytes, NULL)) + return HrFromWin32(GetLastError()); + + _ASSERTE(NumberOfBytes == SizeOfData); + + } +ErrExit: + RELEASE(pIStream); + DELETEARRAY(pStreamMem); + return hr; +} + +//----------------------------------------------------------- +// SymWriter WritePDB +// Write the PDB information to a file or to a stream +//----------------------------------------------------------- +COM_METHOD SymWriter::WritePDB() +{ + + HRESULT hr = NOERROR; + GUID ildb_guid = ILDB_VERSION_GUID; + + // Make sure the ModuleLevelInfo is set + ModuleLevelInfo.m_CountOfVars = VAL32(m_MethodInfo.m_vars.count()); + ModuleLevelInfo.m_CountOfBytes = VAL32(m_MethodInfo.m_bytes.count()); + ModuleLevelInfo.m_CountOfUsing = VAL32(m_MethodInfo.m_usings.count()); + ModuleLevelInfo.m_CountOfScopes = VAL32(m_MethodInfo.m_scopes.count()); + ModuleLevelInfo.m_CountOfMethods = VAL32(m_MethodInfo.m_methods.count()); + if (m_pStringPool) + { + DWORD dwSaveSize; + IfFailGo(m_pStringPool->GetSaveSize((UINT32 *)&dwSaveSize)); + ModuleLevelInfo.m_CountOfStringBytes = VAL32(dwSaveSize); + } + else + { + ModuleLevelInfo.m_CountOfStringBytes = 0; + } + ModuleLevelInfo.m_CountOfConstants = VAL32(m_MethodInfo.m_constants.count()); + ModuleLevelInfo.m_CountOfDocuments = VAL32(m_MethodInfo.m_documents.count()); + ModuleLevelInfo.m_CountOfSequencePoints = VAL32(m_MethodInfo.m_auxSequencePoints.count()); + + // Open the file + if (m_pIStream == NULL) + { + // We need to open the output file. + m_hFile = WszCreateFile(m_szPath, + GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + NULL); + + if (m_hFile == INVALID_HANDLE_VALUE) + { + IfFailGo(HrFromWin32(GetLastError())); + } + } + else + { + // We're writing to a stream. Make sure we're at the beginning + // (eg. if this is being called more than once). + // Note that technically we should probably call SetSize to truncate the + // stream to ensure we don't leave reminants of the previous contents + // at the end of the new stream. But with our current CGrowableStream + // implementation, this would have a big performance impact (causing us to + // do linear growth and lots of reallocations at every write). We only + // ever add data to a symbol writer (don't remove anything), and so subsequent + // streams should always get larger. Regardless, ILDB supports trailing garbage + // without a problem (we used to always have the remainder of a page at the end + // of the stream), and so this is not an issue of correctness. + LARGE_INTEGER pos0; + pos0.QuadPart = 0; + IfFailGo(m_pIStream->Seek(pos0, STREAM_SEEK_SET, NULL)); + } + +#if _DEBUG + // We need to make sure the Variant entry in the constants is 8 byte + // aligned so make sure everything up to the there is aligned correctly + if ((ILDB_SIGNATURE_SIZE % 8) || + (sizeof(PDBInfo) % 8) || + (sizeof(GUID) % 8)) + { + _ASSERTE(!"We need to safe the data in an aligned format"); + } +#endif + + // Write out a signature to recognize that we're an ildb + IfFailGo(Write((void *)ILDB_SIGNATURE, ILDB_SIGNATURE_SIZE)); + // Write out a guid representing the version + SwapGuid(&ildb_guid); + IfFailGo(Write((void *)&ildb_guid, sizeof(GUID))); + + // Now we need to write the Project level + IfFailGo(Write(&ModuleLevelInfo, sizeof(PDBInfo))); + + // Now we have to write out each array as appropriate + IfFailGo(Write(m_MethodInfo.m_constants.m_array, sizeof(SymConstant) * m_MethodInfo.m_constants.count())); + + // These members are all 4 byte aligned + IfFailGo(Write(m_MethodInfo.m_methods.m_array, sizeof(SymMethodInfo) * m_MethodInfo.m_methods.count())); + IfFailGo(Write(m_MethodInfo.m_scopes.m_array, sizeof(SymLexicalScope) * m_MethodInfo.m_scopes.count())); + IfFailGo(Write(m_MethodInfo.m_vars.m_array, sizeof(SymVariable) * m_MethodInfo.m_vars.count())); + IfFailGo(Write(m_MethodInfo.m_usings.m_array, sizeof(SymUsingNamespace) * m_MethodInfo.m_usings.count())); + IfFailGo(Write(m_MethodInfo.m_auxSequencePoints.m_array, sizeof(SequencePoint) * m_MethodInfo.m_auxSequencePoints.count())); + IfFailGo(Write(m_MethodInfo.m_documents.m_array, sizeof(DocumentInfo) * m_MethodInfo.m_documents.count())); + IfFailGo(Write(m_MethodInfo.m_bytes.m_array, sizeof(BYTE) * m_MethodInfo.m_bytes.count())); + IfFailGo(WriteStringPool()); + +ErrExit: + if (m_hFile) + CloseHandle(m_hFile); + return hr; +} + +/* ------------------------------------------------------------------------- * + * SymDocumentWriter class + * ------------------------------------------------------------------------- */ +SymDocumentWriter::SymDocumentWriter( + UINT32 DocumentEntry, + SymWriter *pEmitter +) : + m_refCount ( 0 ), + m_DocumentEntry ( DocumentEntry ), + m_pEmitter( pEmitter ) +{ + _ASSERTE(pEmitter); + m_pEmitter->AddRef(); +} + +SymDocumentWriter::~SymDocumentWriter() +{ + // Note that this must be thread-safe - it may be invoked on the finalizer thread + RELEASE(m_pEmitter); +} + +COM_METHOD SymDocumentWriter::QueryInterface(REFIID riid, void **ppInterface) +{ + if (ppInterface == NULL) + return E_INVALIDARG; + + if (riid == IID_ISymUnmanagedDocumentWriter) + *ppInterface = (ISymUnmanagedDocumentWriter*)this; + else if (riid == IID_IUnknown) + *ppInterface = (IUnknown*)(ISymUnmanagedDocumentWriter*)this; + else + { + *ppInterface = NULL; + return E_NOINTERFACE; + } + + AddRef(); + return S_OK; +} + +//----------------------------------------------------------- +// SymDocumentWriter SetSource +//----------------------------------------------------------- +COM_METHOD SymDocumentWriter::SetSource(ULONG32 sourceSize, + BYTE source[]) +{ + return m_pEmitter->SetDocumentSrc(m_DocumentEntry, sourceSize, source); +} + +//----------------------------------------------------------- +// SymDocumentWriter SetCheckSum +//----------------------------------------------------------- +COM_METHOD SymDocumentWriter::SetCheckSum(GUID algorithmId, + ULONG32 checkSumSize, + BYTE checkSum[]) +{ + return m_pEmitter->SetDocumentCheckSum(m_DocumentEntry, algorithmId, checkSumSize, checkSum); +} + + +//----------------------------------------------------------- +// DocumentInfo SetDocumentWriter +//----------------------------------------------------------- +// Set the pointer to the SymDocumentWriter instance corresponding to this instance of DocumentInfo +// An argument of NULL will call Release +// Arguments +// input: pDoc - pointer to the associated SymDocumentWriter or NULL + +void DocumentInfo::SetDocumentWriter(SymDocumentWriter * pDoc) +{ + if (m_pDocumentWriter != NULL) + { + m_pDocumentWriter->Release(); + } + m_pDocumentWriter = pDoc; + if (m_pDocumentWriter != NULL) + { + pDoc->AddRef(); + } +} |