summaryrefslogtreecommitdiff
path: root/src/debug/ildbsymlib/symwrite.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/debug/ildbsymlib/symwrite.cpp')
-rw-r--r--src/debug/ildbsymlib/symwrite.cpp1553
1 files changed, 1553 insertions, 0 deletions
diff --git a/src/debug/ildbsymlib/symwrite.cpp b/src/debug/ildbsymlib/symwrite.cpp
new file mode 100644
index 0000000000..c09fd37498
--- /dev/null
+++ b/src/debug/ildbsymlib/symwrite.cpp
@@ -0,0 +1,1553 @@
+// 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: 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();
+ }
+}