1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4// ===========================================================================
5// File: symwrite.cpp
6//
7
8//
9// Note: The various SymWriter_* and SymDocumentWriter_* are entry points
10// called via PInvoke from the managed symbol wrapper used by managed languages
11// to emit debug information (such as jscript)
12// ===========================================================================
13
14#include "pch.h"
15#include "symwrite.h"
16
17
18// -------------------------------------------------------------------------
19// SymWriter class
20// -------------------------------------------------------------------------
21
22// This is a COM object which is called both directly from the runtime, and from managed code
23// via PInvoke (CoreSymWrapper) and IJW (ISymWrapper). This is an unusual pattern, and it's not
24// clear exactly how best to address it. Eg., should we be using BEGIN_EXTERNAL_ENTRYPOINT
25// macros? Conceptually this is just a drop-in replacement for diasymreader.dll, and could
26// live in a different DLL instead of being statically linked into the runtime. But since it
27// relies on utilcode (and actually gets the runtime utilcode, not the nohost utilcode like
28// other external tools), it does have some properties of runtime code.
29//
30
31//-----------------------------------------------------------
32// NewSymWriter
33// Static function used to create a new instance of SymWriter
34//-----------------------------------------------------------
35HRESULT SymWriter::NewSymWriter(const GUID& id, void **object)
36{
37 if (id != IID_ISymUnmanagedWriter)
38 return (E_UNEXPECTED);
39
40 SymWriter *writer = NEW(SymWriter());
41
42 if (writer == NULL)
43 return (E_OUTOFMEMORY);
44
45 *object = (ISymUnmanagedWriter*)writer;
46 writer->AddRef();
47
48 return (S_OK);
49}
50
51//-----------------------------------------------------------
52// SymWriter Constuctor
53//-----------------------------------------------------------
54SymWriter::SymWriter() :
55 m_refCount(0),
56 m_openMethodToken(mdMethodDefNil),
57 m_LargestMethodToken(mdMethodDefNil),
58 m_pmethod(NULL),
59 m_currentScope(k_noScope),
60 m_hFile(NULL),
61 m_pIStream(NULL),
62 m_pStringPool(NULL),
63 m_closed( false ),
64 m_sortLines (false),
65 m_sortMethodEntries(false)
66{
67 memset(m_szPath, 0, sizeof(m_szPath));
68 memset(&ModuleLevelInfo, 0, sizeof(PDBInfo));
69}
70
71//-----------------------------------------------------------
72// SymWriter QI
73//-----------------------------------------------------------
74COM_METHOD SymWriter::QueryInterface(REFIID riid, void **ppInterface)
75{
76 if (ppInterface == NULL)
77 return E_INVALIDARG;
78
79 if (riid == IID_ISymUnmanagedWriter )
80 *ppInterface = (ISymUnmanagedWriter*)this;
81 else if (riid == IID_ISymUnmanagedWriter2 )
82 *ppInterface = (ISymUnmanagedWriter2*)this;
83 else if (riid == IID_ISymUnmanagedWriter3 )
84 *ppInterface = (ISymUnmanagedWriter3*)this;
85 else if (riid == IID_IUnknown)
86 *ppInterface = (IUnknown*)(ISymUnmanagedWriter*)this;
87 else
88 {
89 *ppInterface = NULL;
90 return E_NOINTERFACE;
91 }
92
93 AddRef();
94 return S_OK;
95}
96
97//-----------------------------------------------------------
98// SymWriter Destructor
99//-----------------------------------------------------------
100SymWriter::~SymWriter()
101{
102 // Note that this must be thread-safe - it may be invoked on the finalizer thread
103 // But since this dtor can only be invoked when all references have been released,
104 // no other threads can be manipulating the writer.
105 // Ideally we'd probably just add locking to all methods, but this is low-priority
106 // because diasymreader.dll isn't thread-safe and so we need to ensure the CLR's use
107 // of these interfaces are properly syncrhonized.
108 if ( !m_closed )
109 Close();
110 RELEASE(m_pIStream);
111 DELETE(m_pStringPool);
112}
113
114//-----------------------------------------------------------
115// SymWriter Initialize the SymWriter
116//-----------------------------------------------------------
117COM_METHOD SymWriter::Initialize
118(
119 IUnknown *emitter, // Emitter (IMetaData Emit/Import) - unused by ILDB
120 const WCHAR *szFilename, // FileName of the exe we're creating
121 IStream *pIStream, // Stream to store into
122 BOOL fFullBuild // Is this a full build or an incremental build
123)
124{
125 HRESULT hr = S_OK;
126
127 // Incremental compile not implemented
128 _ASSERTE(fFullBuild);
129
130 if (emitter == NULL)
131 return E_INVALIDARG;
132
133 if (pIStream != NULL)
134 {
135 m_pIStream = pIStream;
136 pIStream->AddRef();
137 }
138 else
139 {
140 if (szFilename == NULL)
141 {
142 IfFailRet(E_INVALIDARG);
143 }
144 }
145
146 m_pStringPool = NEW(StgStringPool());
147 IfFailRet(m_pStringPool->InitNew());
148
149 if (szFilename != NULL)
150 {
151 wchar_t fullpath[_MAX_PATH];
152 wchar_t drive[_MAX_DRIVE];
153 wchar_t dir[_MAX_DIR];
154 wchar_t fname[_MAX_FNAME];
155 _wsplitpath_s( szFilename, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 );
156 _wmakepath_s( fullpath, COUNTOF(fullpath), drive, dir, fname, W("ildb") );
157 if (wcsncpy_s( m_szPath, COUNTOF(m_szPath), fullpath, _TRUNCATE) == STRUNCATE)
158 return HrFromWin32(ERROR_INSUFFICIENT_BUFFER);
159 }
160
161 // Note that we don't need the emitter - ILDB is agnostic to the module metadata.
162
163 return hr;
164}
165
166//-----------------------------------------------------------
167// SymWriter Initialize2 the SymWriter
168// Delegate to Initialize then use the szFullPathName param
169//-----------------------------------------------------------
170COM_METHOD SymWriter::Initialize2
171(
172 IUnknown *emitter, // Emitter (IMetaData Emit/Import)
173 const WCHAR *szTempPath, // Location of the file
174 IStream *pIStream, // Stream to store into
175 BOOL fFullBuild, // Full build or not
176 const WCHAR *szFullPathName // Final destination of the ildb
177)
178{
179 HRESULT hr = S_OK;
180 IfFailGo( Initialize( emitter, szTempPath, pIStream, fFullBuild ) );
181 // We don't need the final location of the ildb
182
183ErrExit:
184 return hr;
185}
186
187//-----------------------------------------------------------
188// SymWriter GetorCreateDocument
189// creates a new symbol document writer for a specified source
190// Arguments:
191// input: wcsUrl - The source file name
192// output: ppRetVal - The new document writer
193// Return Value: hr - S_OK if success, OOM otherwise
194//-----------------------------------------------------------
195HRESULT SymWriter::GetOrCreateDocument(
196 const WCHAR *wcsUrl, // Document name
197 const GUID *pLanguage, // What Language we're compiling
198 const GUID *pLanguageVendor, // What vendor
199 const GUID *pDocumentType, // Type
200 ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter
201)
202{
203 ULONG UrlEntry;
204 DWORD strLength = WszWideCharToMultiByte(CP_UTF8, 0, wcsUrl, -1, 0, 0, 0, 0);
205 LPSTR multiByteURL = (LPSTR) new char [strLength+1];
206 HRESULT hr = S_OK;
207
208 if (multiByteURL == NULL)
209 {
210 return E_OUTOFMEMORY;
211 }
212
213 WszWideCharToMultiByte(CP_UTF8, 0, wcsUrl, -1, multiByteURL, strLength+1, 0, 0);
214
215 if (m_pStringPool->FindString(multiByteURL, &UrlEntry) == S_FALSE) // no file of that name has been seen before
216 {
217 hr = CreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal);
218 }
219 else // we already have a writer for this file
220 {
221 UINT32 docInfo = 0;
222
223 CRITSEC_COOKIE cs = ClrCreateCriticalSection(CrstLeafLock, CRST_DEFAULT);
224
225 ClrEnterCriticalSection(cs);
226
227 while ((docInfo < m_MethodInfo.m_documents.count()) && (m_MethodInfo.m_documents[docInfo].UrlEntry() != UrlEntry))
228 {
229 docInfo++;
230 }
231
232 if (docInfo == m_MethodInfo.m_documents.count()) // something went wrong and we didn't find the writer
233 {
234 hr = CreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal);
235 }
236 else
237 {
238 *ppRetVal = m_MethodInfo.m_documents[docInfo].DocumentWriter();
239 (*ppRetVal)->AddRef();
240 }
241 ClrLeaveCriticalSection(cs);
242 }
243
244 delete [] multiByteURL;
245 return hr;
246
247} // SymWriter::GetOrCreateDocument
248
249//-----------------------------------------------------------
250// SymWriter CreateDocument
251// creates a new symbol document writer for a specified source
252// Arguments:
253// input: wcsUrl - The source file name
254// output: ppRetVal - The new document writer
255// Return Value: hr - S_OK if success, OOM otherwise
256//-----------------------------------------------------------
257HRESULT SymWriter::CreateDocument(const WCHAR *wcsUrl, // Document name
258 const GUID *pLanguage, // What Language we're compiling
259 const GUID *pLanguageVendor, // What vendor
260 const GUID *pDocumentType, // Type
261 ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter
262)
263
264{
265 DocumentInfo* pDocument = NULL;
266 SymDocumentWriter *sdw = NULL;
267 UINT32 DocumentEntry;
268 ULONG UrlEntry;
269 HRESULT hr = NOERROR;
270
271 DocumentEntry = m_MethodInfo.m_documents.count();
272 IfNullGo(pDocument = m_MethodInfo.m_documents.next());
273 memset(pDocument, 0, sizeof(DocumentInfo));
274
275 // Create the new document writer.
276 sdw = NEW(SymDocumentWriter(DocumentEntry, this));
277 IfNullGo(sdw);
278
279 pDocument->SetLanguage(*pLanguage);
280 pDocument->SetLanguageVendor(*pLanguageVendor);
281 pDocument->SetDocumentType(*pDocumentType);
282 pDocument->SetDocumentWriter(sdw);
283
284 // stack check needed to call back into utilcode
285 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO();
286 hr = m_pStringPool->AddStringW(wcsUrl, (UINT32 *)&UrlEntry);
287 END_SO_INTOLERANT_CODE;
288 IfFailGo(hr);
289
290 pDocument->SetUrlEntry(UrlEntry);
291
292 // Pass out the new ISymUnmanagedDocumentWriter.
293 sdw->AddRef();
294 *ppRetVal = (ISymUnmanagedDocumentWriter*)sdw;
295 sdw = NULL;
296
297ErrExit:
298 DELETE(sdw);
299 return hr;
300}
301
302//-----------------------------------------------------------
303// SymWriter DefineDocument
304//-----------------------------------------------------------
305COM_METHOD SymWriter::DefineDocument(
306 const WCHAR *wcsUrl, // Document name
307 const GUID *pLanguage, // What Language we're compiling
308 const GUID *pLanguageVendor, // What vendor
309 const GUID *pDocumentType, // Type
310 ISymUnmanagedDocumentWriter **ppRetVal // [out] Created DocumentWriter
311)
312{
313 HRESULT hr = NOERROR;
314
315 IfFalseGo(wcsUrl, E_INVALIDARG);
316 IfFalseGo(pLanguage, E_INVALIDARG);
317 IfFalseGo(pLanguageVendor, E_INVALIDARG);
318 IfFalseGo(pDocumentType, E_INVALIDARG);
319 IfFalseGo(ppRetVal, E_INVALIDARG);
320
321 // Init out parameter
322 *ppRetVal = NULL;
323
324 hr = GetOrCreateDocument(wcsUrl, pLanguage, pLanguageVendor, pDocumentType, ppRetVal);
325ErrExit:
326 return hr;
327}
328
329
330//-----------------------------------------------------------
331// SymWriter SetDocumentSrc
332//-----------------------------------------------------------
333HRESULT SymWriter::SetDocumentSrc(
334 UINT32 DocumentEntry,
335 DWORD SourceSize,
336 BYTE* pSource
337)
338{
339 DocumentInfo* pDocument = NULL;
340 HRESULT hr = S_OK;
341
342 IfFalseGo( SourceSize == 0 || pSource, E_INVALIDARG);
343 IfFalseGo( DocumentEntry < m_MethodInfo.m_documents.count(), E_INVALIDARG);
344
345 pDocument = &m_MethodInfo.m_documents[DocumentEntry];
346
347 if (pSource)
348 {
349 UINT32 i;
350 IfFalseGo( m_MethodInfo.m_bytes.grab(SourceSize, &i), E_OUTOFMEMORY);
351 memcpy(&m_MethodInfo.m_bytes[i], pSource, SourceSize);
352 pDocument->SetSourceEntry(i);
353 pDocument->SetSourceSize(SourceSize);
354 }
355
356ErrExit:
357 return hr;
358}
359
360//-----------------------------------------------------------
361// SymWriter SetDocumentCheckSum
362//-----------------------------------------------------------
363HRESULT SymWriter::SetDocumentCheckSum(
364 UINT32 DocumentEntry,
365 GUID AlgorithmId,
366 DWORD CheckSumSize,
367 BYTE* pCheckSum
368)
369{
370 DocumentInfo* pDocument = NULL;
371 HRESULT hr = S_OK;
372
373 IfFalseGo( CheckSumSize == 0 || pCheckSum, E_INVALIDARG);
374 IfFalseGo( DocumentEntry < m_MethodInfo.m_documents.count(), E_INVALIDARG);
375
376 pDocument = &m_MethodInfo.m_documents[DocumentEntry];
377
378 if (pCheckSum)
379 {
380 UINT32 i;
381 IfFalseGo( m_MethodInfo.m_bytes.grab(CheckSumSize, &i), E_OUTOFMEMORY);
382 memcpy(&m_MethodInfo.m_bytes[i], pCheckSum, CheckSumSize);
383 pDocument->SetCheckSumEntry(i);
384 pDocument->SetCheckSymSize(CheckSumSize);
385 }
386
387 pDocument->SetAlgorithmId(AlgorithmId);
388
389ErrExit:
390 return hr;
391}
392
393//-----------------------------------------------------------
394// SymWriter SetUserEntryPoint
395//-----------------------------------------------------------
396COM_METHOD SymWriter::SetUserEntryPoint(mdMethodDef entryMethod)
397{
398 HRESULT hr = S_OK;
399
400 // Make sure that an entry point hasn't already been set.
401 if (ModuleLevelInfo.m_userEntryPoint == 0)
402 ModuleLevelInfo.m_userEntryPoint = entryMethod;
403
404 return hr;
405}
406
407//-----------------------------------------------------------
408// SymWriter OpenMethod
409// Get ready to get information about a new method
410//-----------------------------------------------------------
411COM_METHOD SymWriter::OpenMethod(mdMethodDef method)
412{
413 HRESULT hr = S_OK;
414
415 // We can only have one open method at a time.
416 if (m_openMethodToken != mdMethodDefNil)
417 return E_INVALIDARG;
418
419 m_LargestMethodToken = max(method, m_LargestMethodToken);
420
421 if (m_LargestMethodToken != method)
422 {
423 m_sortMethodEntries = true;
424 // Check to see if we're trying to open a method we've already done
425 unsigned i;
426 for (i = 0; i < m_MethodInfo.m_methods.count(); i++)
427 {
428 if (m_MethodInfo.m_methods[i].MethodToken() == method)
429 {
430 return E_INVALIDARG;
431 }
432 }
433 }
434
435 // Remember the token for this method.
436 m_openMethodToken = method;
437
438 IfNullGo( m_pmethod = m_MethodInfo.m_methods.next() );
439 m_pmethod->SetMethodToken(m_openMethodToken);
440 m_pmethod->SetStartScopes(m_MethodInfo.m_scopes.count());
441 m_pmethod->SetStartVars(m_MethodInfo.m_vars.count());
442 m_pmethod->SetStartUsing(m_MethodInfo.m_usings.count());
443 m_pmethod->SetStartConstant(m_MethodInfo.m_constants.count());
444 m_pmethod->SetStartDocuments(m_MethodInfo.m_documents.count());
445 m_pmethod->SetStartSequencePoints(m_MethodInfo.m_auxSequencePoints.count());
446
447 // By default assume the lines are inserted in the correct order
448 m_sortLines = false;
449
450 // Initialize the maximum scope end offset for this method
451 m_maxScopeEnd = 1;
452
453 // Open the implicit root scope for the method
454 _ASSERTE(m_currentScope == k_noScope);
455
456 IfFailRet(OpenScope(0, NULL));
457
458 _ASSERTE(m_currentScope != k_noScope);
459
460ErrExit:
461 return hr;
462}
463
464COM_METHOD SymWriter::OpenMethod2(
465 mdMethodDef method,
466 ULONG32 isect,
467 ULONG32 offset)
468{
469 // This symbol writer doesn't support section offsets
470 _ASSERTE(FALSE);
471 return E_NOTIMPL;
472}
473
474//-----------------------------------------------------------
475// compareAuxLines
476// Used to sort SequencePoint
477//-----------------------------------------------------------
478int __cdecl SequencePoint::compareAuxLines(const void *elem1, const void *elem2 )
479{
480 SequencePoint* p1 = (SequencePoint*)elem1;
481 SequencePoint* p2 = (SequencePoint*)elem2;
482 return p1->Offset() - p2->Offset();
483}
484
485//-----------------------------------------------------------
486// SymWriter CloseMethod
487// We're done with this function, write it out.
488//-----------------------------------------------------------
489COM_METHOD SymWriter::CloseMethod()
490{
491 HRESULT hr = S_OK;
492 UINT32 CountOfSequencePoints;
493
494 // Must have an open method.
495 if (m_openMethodToken == mdMethodDefNil)
496 return E_UNEXPECTED;
497
498 // All scopes up to the root must have been closed (and the root must not have been closed).
499 _ASSERTE(m_currentScope != k_noScope);
500 if (m_MethodInfo.m_scopes[m_currentScope].ParentScope() != k_noScope)
501 return E_FAIL;
502
503 // Close the implicit root scope using the largest end offset we've seen in this method, or 1 if none.
504 IfFailRet(CloseScopeInternal(m_maxScopeEnd));
505
506 m_pmethod->SetEndScopes(m_MethodInfo.m_scopes.count());
507 m_pmethod->SetEndVars(m_MethodInfo.m_vars.count());
508 m_pmethod->SetEndUsing(m_MethodInfo.m_usings.count());
509 m_pmethod->SetEndConstant(m_MethodInfo.m_constants.count());
510 m_pmethod->SetEndDocuments(m_MethodInfo.m_documents.count());
511 m_pmethod->SetEndSequencePoints(m_MethodInfo.m_auxSequencePoints.count());
512
513 CountOfSequencePoints = m_pmethod->EndSequencePoints() - m_pmethod->StartSequencePoints();
514 // Write any sequence points.
515 if (CountOfSequencePoints > 0 ) {
516 // sort the sequence points
517 if ( m_sortLines )
518 {
519 qsort(&m_MethodInfo.m_auxSequencePoints[m_pmethod->StartSequencePoints()],
520 CountOfSequencePoints,
521 sizeof( SequencePoint ),
522 SequencePoint::compareAuxLines );
523 }
524 }
525
526 // All done with this method.
527 m_openMethodToken = mdMethodDefNil;
528
529 return hr;
530}
531
532//-----------------------------------------------------------
533// SymWriter DefineSequencePoints
534// Define the sequence points for this function
535//-----------------------------------------------------------
536COM_METHOD SymWriter::DefineSequencePoints(
537 ISymUnmanagedDocumentWriter *document, //
538 ULONG32 spCount, // Count of sequence points
539 ULONG32 offsets[], // Offsets
540 ULONG32 lines[], // Beginning Lines
541 ULONG32 columns[], // [optional] Columns
542 ULONG32 endLines[], // [optional] End Lines
543 ULONG32 endColumns[] // [optional] End Columns
544)
545{
546 HRESULT hr = S_OK;
547 DWORD docnum;
548
549 // We must have a document, offsets, and lines.
550 IfFalseGo(document && offsets && lines, E_INVALIDARG);
551 // Must have some sequence points
552 IfFalseGo(spCount != 0, E_INVALIDARG);
553 // Must have an open method.
554 IfFalseGo(m_openMethodToken != mdMethodDefNil, E_INVALIDARG);
555
556 // Remember that we've loaded the sequence points and
557 // which document they were for.
558 docnum = (DWORD)((SymDocumentWriter *)document)->GetDocumentEntry();;
559
560 // if sets of lines have been inserted out-of-order, remember to sort when emitting
561 if ( m_MethodInfo.m_auxSequencePoints.count() > 0 && m_MethodInfo.m_auxSequencePoints[ m_MethodInfo.m_auxSequencePoints.count()-1 ].Offset() > offsets[0] )
562 m_sortLines = true;
563
564 // Copy the incomming arrays into the internal format.
565
566 for ( UINT32 i = 0; i < spCount; i++)
567 {
568 SequencePoint * paux;
569 IfNullGo(paux = m_MethodInfo.m_auxSequencePoints.next());
570 paux->SetOffset(offsets[i]);
571 paux->SetStartLine(lines[i]);
572 paux->SetStartColumn(columns ? columns[i] : 0);
573 // If no endLines specified, assume same as start
574 paux->SetEndLine(endLines ? endLines[i] : lines[i]);
575 paux->SetEndColumn(endColumns ? endColumns[i]: 0);
576 paux->SetDocument(docnum);
577 }
578
579ErrExit:
580 return hr;
581}
582
583//-----------------------------------------------------------
584// SymWriter OpenScope
585// Open a new scope for this function
586//-----------------------------------------------------------
587COM_METHOD SymWriter::OpenScope(ULONG32 startOffset, ULONG32 *scopeID)
588{
589 HRESULT hr = S_OK;
590
591 // Make sure the startOffset is within the current scope.
592 if ((m_currentScope != k_noScope) &&
593 (unsigned int)startOffset < m_MethodInfo.m_scopes[m_currentScope].StartOffset())
594 return E_INVALIDARG;
595
596 // Fill in the new scope.
597 UINT32 newScope = m_MethodInfo.m_scopes.count();
598
599 // Make sure that adding 1 below won't overflow (although "next" should fail much
600 // sooner if we were anywhere near close enough).
601 if (newScope >= UINT_MAX)
602 return E_UNEXPECTED;
603
604 SymLexicalScope *sc;
605 IfNullGo( sc = m_MethodInfo.m_scopes.next());
606 sc->SetParentScope(m_currentScope); // parent is the current scope.
607 sc->SetStartOffset(startOffset);
608 sc->SetHasChildren(FALSE);
609 sc->SetHasVars(FALSE);
610 sc->SetEndOffset(0);
611
612 // The current scope has a child now.
613 if (m_currentScope != k_noScope)
614 m_MethodInfo.m_scopes[m_currentScope].SetHasChildren(TRUE);
615
616 // The new scope is now the current scope.
617 m_currentScope = newScope;
618 _ASSERTE(m_currentScope != k_noScope);
619
620 // Pass out the "scope id", which is a _1_ based id for the scope.
621 if (scopeID)
622 *scopeID = m_currentScope + 1;
623
624ErrExit:
625 return hr;
626}
627
628//-----------------------------------------------------------
629// SymWriter CloseScope
630//-----------------------------------------------------------
631COM_METHOD SymWriter::CloseScope(
632 ULONG32 endOffset // Closing offset of scope
633)
634{
635 // This API can only be used to close explicit user scopes.
636 // The implicit root scope is only closed internally by CloseMethod.
637 if ((m_currentScope == k_noScope) || (m_MethodInfo.m_scopes[m_currentScope].ParentScope() == k_noScope))
638 return E_FAIL;
639
640 HRESULT hr = CloseScopeInternal(endOffset);
641
642 _ASSERTE(m_currentScope != k_noScope);
643
644 return hr;
645}
646
647//-----------------------------------------------------------
648// CloseScopeInternal
649// Implementation for ISymUnmanagedWriter::CloseScope but can be called even to
650// close the implicit root scope.
651//-----------------------------------------------------------
652COM_METHOD SymWriter::CloseScopeInternal(
653 ULONG32 endOffset // Closing offset of scope
654)
655{
656 _ASSERTE(m_currentScope != k_noScope);
657
658 // Capture the end offset
659 m_MethodInfo.m_scopes[m_currentScope].SetEndOffset(endOffset);
660
661 // The current scope is now the parent scope.
662 m_currentScope = m_MethodInfo.m_scopes[m_currentScope].ParentScope();
663
664 // Update the maximum scope end offset for this method
665 if (endOffset > m_maxScopeEnd)
666 m_maxScopeEnd = endOffset;
667
668 return S_OK;
669}
670
671//-----------------------------------------------------------
672// SymWriter SetScopeRange
673// Set the Start/End Offset for this scope
674//-----------------------------------------------------------
675COM_METHOD SymWriter::SetScopeRange(
676 ULONG32 scopeID, // ID for the scope
677 ULONG32 startOffset, // Start Offset
678 ULONG32 endOffset // End Offset
679)
680{
681 if (scopeID <= 0)
682 return E_INVALIDARG;
683
684 if (scopeID > m_MethodInfo.m_scopes.count() )
685 return E_INVALIDARG;
686
687 // Remember the new start and end offsets. Also remember that the
688 // scopeID is _1_ based!!!
689 SymLexicalScope *sc = &(m_MethodInfo.m_scopes[scopeID - 1]);
690 sc->SetStartOffset(startOffset);
691 sc->SetEndOffset(endOffset);
692
693 // Update the maximum scope end offset for this method
694 if (endOffset > m_maxScopeEnd)
695 m_maxScopeEnd = endOffset;
696
697 return S_OK;
698}
699
700//-----------------------------------------------------------
701// SymWriter DefineLocalVariable
702//-----------------------------------------------------------
703COM_METHOD SymWriter::DefineLocalVariable(
704 const WCHAR *name, // Name of the variable
705 ULONG32 attributes, // Attributes for the var
706 ULONG32 cSig, // Signature for the variable
707 BYTE signature[],
708 ULONG32 addrKind,
709 ULONG32 addr1, ULONG32 addr2, ULONG32 addr3,
710 ULONG32 startOffset, ULONG32 endOffset)
711{
712 HRESULT hr = S_OK;
713 ULONG NameEntry;
714
715 // We must have a current scope.
716 if (m_currentScope == k_noScope)
717 return E_FAIL;
718
719 // We must have a name and a signature.
720 if (!name || !signature)
721 return E_INVALIDARG;
722
723 if (cSig == 0)
724 return E_INVALIDARG;
725
726 // Make a new local variable and copy the data.
727 SymVariable *var;
728 IfNullGo( var = m_MethodInfo.m_vars.next());
729 var->SetIsParam(FALSE);
730 var->SetAttributes(attributes);
731 var->SetAddrKind(addrKind);
732 var->SetIsHidden(attributes & VAR_IS_COMP_GEN);
733 var->SetAddr1(addr1);
734 var->SetAddr2(addr2);
735 var->SetAddr3(addr3);
736
737
738 // Length of the sig?
739 ULONG32 sigLen;
740 sigLen = cSig;
741
742 // stack check needed to call back into utilcode
743 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO();
744 // Copy the name.
745 hr = m_pStringPool->AddStringW(name, (UINT32 *)&NameEntry);
746 END_SO_INTOLERANT_CODE;
747 IfFailGo(hr);
748 var->SetName(NameEntry);
749
750 // Copy the signature
751 // Note that we give this back exactly as-is, but callers typically remove any calling
752 // convention prefix.
753 UINT32 i;
754 IfFalseGo(m_MethodInfo.m_bytes.grab(sigLen, &i), E_OUTOFMEMORY);
755 memcpy(&m_MethodInfo.m_bytes[i], signature, sigLen);
756 var->SetSignature(i);
757 var->SetSignatureSize(sigLen);
758
759 // This var is in the current scope
760 var->SetScope(m_currentScope);
761 m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE);
762
763 var->SetStartOffset(startOffset);
764 var->SetEndOffset(endOffset);
765
766ErrExit:
767 return hr;
768}
769
770COM_METHOD SymWriter::DefineLocalVariable2(
771 const WCHAR *name,
772 ULONG32 attributes,
773 mdSignature sigToken,
774 ULONG32 addrKind,
775 ULONG32 addr1, ULONG32 addr2, ULONG32 addr3,
776 ULONG32 startOffset, ULONG32 endOffset)
777{
778 // This symbol writer doesn't support definiting signatures via tokens
779 _ASSERTE(FALSE);
780 return E_NOTIMPL;
781}
782
783//-----------------------------------------------------------
784// SymWriter DefineParameter
785//-----------------------------------------------------------
786COM_METHOD SymWriter::DefineParameter(
787 const WCHAR *name, // Param name
788 ULONG32 attributes, // Attribute for the parameter
789 ULONG32 sequence,
790 ULONG32 addrKind,
791 ULONG32 addr1, ULONG32 addr2, ULONG32 addr3)
792{
793 HRESULT hr = S_OK;
794 ULONG NameEntry;
795
796 // We must have a method.
797 if (m_openMethodToken == mdMethodDefNil)
798 return E_INVALIDARG;
799
800 // We must have a name.
801 if (!name)
802 return E_INVALIDARG;
803
804 SymVariable *var;
805 IfNullGo( var = m_MethodInfo.m_vars.next());
806 var->SetIsParam(TRUE);
807 var->SetAttributes(attributes);
808 var->SetAddrKind(addrKind);
809 var->SetIsHidden(attributes & VAR_IS_COMP_GEN);
810 var->SetAddr1(addr1);
811 var->SetAddr2(addr2);
812 var->SetAddr3(addr3);
813 var->SetSequence(sequence);
814
815
816 // stack check needed to call back into utilcode
817 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO();
818 // Copy the name.
819 hr = m_pStringPool->AddStringW(name, (UINT32 *)&NameEntry);
820 END_SO_INTOLERANT_CODE;
821 IfFailGo(hr);
822 var->SetName(NameEntry);
823
824 // This var is in the current scope
825 if (m_currentScope != k_noScope)
826 m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE);
827
828 var->SetStartOffset(0);
829 var->SetEndOffset(0);
830
831ErrExit:
832 return hr;
833}
834
835//-----------------------------------------------------------
836// verifyConstTypes
837// Verify that the type is a type we support
838//-----------------------------------------------------------
839static bool verifyConstTypes( DWORD vt )
840{
841 switch ( vt ) {
842 case VT_UI8:
843 case VT_I8:
844 case VT_I4:
845 case VT_UI1: // value < LF_NUMERIC
846 case VT_I2:
847 case VT_R4:
848 case VT_R8:
849 case VT_BOOL: // value < LF_NUMERIC
850 case VT_DATE:
851 case VT_BSTR:
852 case VT_I1:
853 case VT_UI2:
854 case VT_UI4:
855 case VT_INT:
856 case VT_UINT:
857 case VT_DECIMAL:
858 return true;
859 }
860 return false;
861}
862
863//-----------------------------------------------------------
864// SymWriter DefineConstant
865//-----------------------------------------------------------
866COM_METHOD SymWriter::DefineConstant(
867 const WCHAR __RPC_FAR *name,
868 VARIANT value,
869 ULONG32 cSig,
870 unsigned char __RPC_FAR signature[])
871{
872 HRESULT hr = S_OK;
873 ULONG ValueBstr = 0;
874 ULONG Name;
875
876 // currently we only support local constants
877
878 // We must have a method.
879 if (m_openMethodToken == mdMethodDefNil)
880 return E_INVALIDARG;
881
882 // We must have a name and signature.
883 IfFalseGo(name, E_INVALIDARG);
884 IfFalseGo(signature, E_INVALIDARG);
885 IfFalseGo(cSig > 0, E_INVALIDARG);
886
887 //
888 // Support byref decimal values
889 //
890 if ( (V_VT(&value)) == ( VT_BYREF | VT_DECIMAL ) ) {
891 if ( V_DECIMALREF(&value) == NULL )
892 return E_INVALIDARG;
893 V_DECIMAL(&value) = *V_DECIMALREF(&value);
894 V_VT(&value) = VT_DECIMAL;
895 }
896
897 // we only support non-ref constants
898 if ( ( V_VT(&value) & VT_BYREF ) != 0 )
899 return E_INVALIDARG;
900
901 if ( !verifyConstTypes( V_VT(&value) ) )
902 return E_INVALIDARG;
903
904 // If it's a BSTR, we need to persist the Bstr as an entry into
905 // the stringpool
906 if (V_VT(&value) == VT_BSTR)
907 {
908 // stack check needed to call back into utilcode
909 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO();
910 // Copy the bstrValue.
911 hr = m_pStringPool->AddStringW(V_BSTR(&value), (UINT32 *)&ValueBstr);
912 END_SO_INTOLERANT_CODE;
913 IfFailGo(hr);
914 V_BSTR(&value) = NULL;
915 }
916
917 SymConstant *con;
918 IfNullGo( con = m_MethodInfo.m_constants.next());
919 con->SetValue(value, ValueBstr);
920
921
922 // stack check needed to call back into utilcode
923 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO();
924 // Copy the name.
925 hr = m_pStringPool->AddStringW(name, (UINT32 *)&Name);
926 END_SO_INTOLERANT_CODE;
927 IfFailGo(hr);
928 con->SetName(Name);
929
930 // Copy the signature
931 UINT32 i;
932 IfFalseGo(m_MethodInfo.m_bytes.grab(cSig, &i), E_OUTOFMEMORY);
933 memcpy(&m_MethodInfo.m_bytes[i], signature, cSig);
934 con->SetSignature(i);
935 con->SetSignatureSize(cSig);
936
937 // This const is in the current scope
938 con->SetParentScope(m_currentScope);
939 m_MethodInfo.m_scopes[m_currentScope].SetHasVars(TRUE);
940
941ErrExit:
942 return hr;
943}
944
945COM_METHOD SymWriter::DefineConstant2(
946 const WCHAR *name,
947 VARIANT value,
948 mdSignature sigToken)
949{
950 // This symbol writer doesn't support definiting signatures via tokens
951 _ASSERTE(FALSE);
952 return E_NOTIMPL;
953}
954
955//-----------------------------------------------------------
956// SymWriter Abort
957//-----------------------------------------------------------
958COM_METHOD SymWriter::Abort(void)
959{
960 m_closed = true;
961 return S_OK;
962}
963
964//-----------------------------------------------------------
965// SymWriter DefineField
966//-----------------------------------------------------------
967COM_METHOD SymWriter::DefineField(
968 mdTypeDef parent,
969 const WCHAR *name,
970 ULONG32 attributes,
971 ULONG32 csig,
972 BYTE signature[],
973 ULONG32 addrKind,
974 ULONG32 addr1, ULONG32 addr2, ULONG32 addr3)
975{
976 // This symbol store doesn't support extra random variable
977 // definitions.
978 return S_OK;
979}
980
981//-----------------------------------------------------------
982// SymWriter DefineGlobalVariable
983//-----------------------------------------------------------
984COM_METHOD SymWriter::DefineGlobalVariable(
985 const WCHAR *name,
986 ULONG32 attributes,
987 ULONG32 csig,
988 BYTE signature[],
989 ULONG32 addrKind,
990 ULONG32 addr1, ULONG32 addr2, ULONG32 addr3)
991{
992 // This symbol writer doesn't support global variables
993 _ASSERTE(FALSE);
994 return E_NOTIMPL;
995}
996
997COM_METHOD SymWriter::DefineGlobalVariable2(
998 const WCHAR *name,
999 ULONG32 attributes,
1000 mdSignature sigToken,
1001 ULONG32 addrKind,
1002 ULONG32 addr1, ULONG32 addr2, ULONG32 addr3)
1003{
1004 // This symbol writer doesn't support global variables
1005 _ASSERTE(FALSE);
1006 return E_NOTIMPL;
1007}
1008
1009//-----------------------------------------------------------
1010// compareMethods
1011// Used to sort method entries
1012//-----------------------------------------------------------
1013int __cdecl SymMethodInfo::compareMethods(const void *elem1, const void *elem2 )
1014{
1015 SymMethodInfo* p1 = (SymMethodInfo*)elem1;
1016 SymMethodInfo* p2 = (SymMethodInfo*)elem2;
1017 return p1->MethodToken() - p2->MethodToken();
1018}
1019
1020//-----------------------------------------------------------
1021// SymWriter Close
1022//-----------------------------------------------------------
1023COM_METHOD SymWriter::Close()
1024{
1025 HRESULT hr = Commit();
1026 m_closed = true;
1027 for (UINT32 docInfo = 0; docInfo < m_MethodInfo.m_documents.count(); docInfo++)
1028 {
1029 m_MethodInfo.m_documents[docInfo].SetDocumentWriter(NULL);
1030 }
1031 return hr;
1032}
1033
1034//-----------------------------------------------------------
1035// SymWriter Commit
1036//-----------------------------------------------------------
1037COM_METHOD SymWriter::Commit(void)
1038{
1039 // Sort the entries if need be
1040 if (m_sortMethodEntries)
1041 {
1042 // First remap any tokens we need to
1043 if (m_MethodMap.count())
1044 {
1045 unsigned i;
1046 for (i = 0; i< m_MethodMap.count(); i++)
1047 {
1048 m_MethodInfo.m_methods[m_MethodMap[i].MethodEntry].SetMethodToken(m_MethodMap[i].m_MethodToken);
1049 }
1050 }
1051
1052 // Now sort the array
1053 qsort(&m_MethodInfo.m_methods[0],
1054 m_MethodInfo.m_methods.count(),
1055 sizeof( SymMethodInfo ),
1056 SymMethodInfo::compareMethods );
1057 m_sortMethodEntries = false;
1058 }
1059 return WritePDB();
1060}
1061
1062//-----------------------------------------------------------
1063// SymWriter SetSymAttribute
1064//-----------------------------------------------------------
1065COM_METHOD SymWriter::SetSymAttribute(
1066 mdToken parent,
1067 const WCHAR *name,
1068 ULONG32 cData,
1069 BYTE data[])
1070{
1071 // Setting attributes on the symbol isn't supported
1072 return S_OK;
1073}
1074
1075//-----------------------------------------------------------
1076// SymWriter OpenNamespace
1077//-----------------------------------------------------------
1078COM_METHOD SymWriter::OpenNamespace(const WCHAR *name)
1079{
1080 // This symbol store doesn't support namespaces.
1081 return E_NOTIMPL;
1082}
1083
1084//-----------------------------------------------------------
1085// SymWriter OpenNamespace
1086//-----------------------------------------------------------
1087COM_METHOD SymWriter::CloseNamespace()
1088{
1089 // This symbol store doesn't support namespaces.
1090 return S_OK;
1091}
1092
1093//-----------------------------------------------------------
1094// SymWriter UsingNamespace
1095// Add a Namespace to the list of namespace for this method
1096//-----------------------------------------------------------
1097COM_METHOD SymWriter::UsingNamespace(const WCHAR *fullName)
1098{
1099 HRESULT hr = S_OK;
1100 ULONG Name;
1101
1102 // We must have a current scope.
1103 if (m_currentScope == k_noScope)
1104 return E_FAIL;
1105
1106 // We must have a name.
1107 if (!fullName)
1108 return E_INVALIDARG;
1109
1110
1111 SymUsingNamespace *use;
1112 IfNullGo( use = m_MethodInfo.m_usings.next());
1113
1114 // stack check needed to call back into utilcode
1115 BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD_FORCE_SO();
1116 // Copy the name.
1117 hr = m_pStringPool->AddStringW(fullName, (UINT32 *)&Name);
1118 END_SO_INTOLERANT_CODE;
1119 IfFailGo(hr);
1120 use->SetName(Name);
1121
1122 use->SetParentScope(m_currentScope);
1123
1124ErrExit:
1125 return hr;
1126}
1127
1128//-----------------------------------------------------------
1129// SymWriter SetMethodSourceRange
1130//-----------------------------------------------------------
1131COM_METHOD SymWriter::SetMethodSourceRange(
1132 ISymUnmanagedDocumentWriter *startDoc,
1133 ULONG32 startLine,
1134 ULONG32 startColumn,
1135 ISymUnmanagedDocumentWriter *endDoc,
1136 ULONG32 endLine,
1137 ULONG32 endColumn)
1138{
1139 // This symbol store doesn't support source ranges.
1140 return E_NOTIMPL;
1141}
1142
1143//-----------------------------------------------------------
1144// UnicodeToUTF8
1145// Translate the Unicode string to a UTF8 string
1146// Return the length in UTF8 of the Unicode string
1147// Including NULL terminator
1148//-----------------------------------------------------------
1149inline int WINAPI UnicodeToUTF8(
1150 LPCWSTR pUni, // Unicode string
1151 __out_bcount_opt(cbUTF) PSTR pUTF8, // [optional, out] Buffer for UTF8 string
1152 int cbUTF // length of UTF8 buffer
1153)
1154{
1155 // Pass in the length including the NULL terminator
1156 int cchSrc = (int)wcslen(pUni)+1;
1157 return WideCharToMultiByte(CP_UTF8, 0, pUni, cchSrc, pUTF8, cbUTF, NULL, NULL);
1158}
1159
1160//-----------------------------------------------------------
1161// SymWriter GetDebugCVInfo
1162// Get the size and potentially the debug info
1163//-----------------------------------------------------------
1164COM_METHOD SymWriter::GetDebugCVInfo(
1165 DWORD cbBuf, // [optional] Size of buf
1166 DWORD *pcbBuf, // [out] Size needed for the DebugInfo
1167 BYTE buf[]) // [optional, out] Buffer for DebugInfo
1168{
1169
1170 if ( m_szPath == NULL || *m_szPath == 0 )
1171 return E_UNEXPECTED;
1172
1173 // We need to change the .ildb extension to .pdb to be
1174 // compatible with VS7
1175 wchar_t fullpath[_MAX_PATH];
1176 wchar_t drive[_MAX_DRIVE];
1177 wchar_t dir[_MAX_DIR];
1178 wchar_t fname[_MAX_FNAME];
1179 if (_wsplitpath_s( m_szPath, drive, COUNTOF(drive), dir, COUNTOF(dir), fname, COUNTOF(fname), NULL, 0 ))
1180 return E_FAIL;
1181 if (_wmakepath_s( fullpath, COUNTOF(fullpath), drive, dir, fname, W("pdb") ))
1182 return E_FAIL;
1183
1184 // Get UTF-8 string size, including the Null Terminator
1185 int Utf8Length = UnicodeToUTF8( fullpath, NULL, 0 );
1186 if (Utf8Length < 0 )
1187 return HRESULT_FROM_GetLastError();
1188
1189 DWORD dwSize = sizeof(RSDSI) + DWORD(Utf8Length);
1190
1191 // If the caller is just checking for the size
1192 if ( cbBuf == 0 && pcbBuf != NULL )
1193 {
1194 *pcbBuf = dwSize;
1195 return S_OK;
1196 }
1197
1198 if (cbBuf < dwSize)
1199 {
1200 return HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
1201 }
1202
1203 if ( buf == NULL )
1204 {
1205 return E_INVALIDARG;
1206 }
1207
1208 RSDSI* pRsdsi = (RSDSI*)buf;
1209 pRsdsi->dwSig = VAL32(0x53445352); // "SDSR";
1210 pRsdsi->guidSig = ILDB_VERSION_GUID;
1211 SwapGuid(&(pRsdsi->guidSig));
1212 // Age of 0 represent VC6.0 format so make sure it's 1
1213 pRsdsi->age = VAL32(1);
1214 UnicodeToUTF8( fullpath, pRsdsi->szPDB, Utf8Length );
1215 if ( pcbBuf )
1216 *pcbBuf = dwSize;
1217 return S_OK;
1218}
1219
1220//-----------------------------------------------------------
1221// SymWriter GetDebugInfo
1222// Get the size and potentially the debug info
1223//-----------------------------------------------------------
1224COM_METHOD SymWriter::GetDebugInfo(
1225 IMAGE_DEBUG_DIRECTORY *pIDD, // [out] IDD to fill in
1226 DWORD cData, // [optional] size of data
1227 DWORD *pcData, // [optional, out] return needed size for DebugInfo
1228 BYTE data[]) // [optional] Buffer to store into
1229{
1230 HRESULT hr = S_OK;
1231 if ( cData == 0 && pcData != NULL )
1232 {
1233 // just checking for the size
1234 return GetDebugCVInfo( 0, pcData, NULL );
1235 }
1236
1237 if ( pIDD == NULL )
1238 return E_INVALIDARG;
1239
1240 DWORD cTheData = 0;
1241 IfFailGo( GetDebugCVInfo( cData, &cTheData, data ) );
1242
1243 memset( pIDD, 0, sizeof( *pIDD ) );
1244 pIDD->Type = VAL32(IMAGE_DEBUG_TYPE_CODEVIEW);
1245 pIDD->SizeOfData = VAL32(cTheData);
1246
1247 if ( pcData ) {
1248 *pcData = cTheData;
1249 }
1250
1251ErrExit:
1252 return hr;
1253}
1254
1255COM_METHOD SymWriter::RemapToken(mdToken oldToken, mdToken newToken)
1256{
1257 HRESULT hr = NOERROR;
1258 if (oldToken != newToken)
1259 {
1260 // We only care about methods
1261 if ((TypeFromToken(oldToken) == mdtMethodDef) ||
1262 (TypeFromToken(newToken) == mdtMethodDef))
1263 {
1264 // Make sure they are both methods
1265 _ASSERTE(TypeFromToken(newToken) == mdtMethodDef);
1266 _ASSERTE(TypeFromToken(oldToken) == mdtMethodDef);
1267
1268 // Make sure we sort before saving
1269 m_sortMethodEntries = true;
1270
1271 // Check to see if we're trying to map a token we know about
1272 unsigned i;
1273 for (i = 0; i < m_MethodInfo.m_methods.count(); i++)
1274 {
1275 if (m_MethodInfo.m_methods[i].MethodToken() == oldToken)
1276 {
1277 // Remember the map, we need to actually do the actual
1278 // mapping later because we might already have a function
1279 // with a token 'newToken'
1280 SymMap *pMethodMap;
1281 IfNullGo( pMethodMap = m_MethodMap.next() );
1282 pMethodMap->m_MethodToken = newToken;
1283 pMethodMap->MethodEntry = i;
1284 break;
1285 }
1286 }
1287 }
1288 }
1289ErrExit:
1290 return hr;
1291}
1292
1293//-----------------------------------------------------------
1294// SymWriter Write
1295// Write the information to a file or to a stream
1296//-----------------------------------------------------------
1297COM_METHOD SymWriter::Write(void *pData, DWORD SizeOfData)
1298{
1299 HRESULT hr = NOERROR;
1300 DWORD NumberOfBytesWritten = 0;
1301 if (m_pIStream)
1302 {
1303 IfFailGo(m_pIStream->Write(pData,
1304 SizeOfData,
1305 &NumberOfBytesWritten));
1306 }
1307 else
1308 {
1309 // Write out a signature to recognize that we're an ildb
1310 if (!WriteFile(m_hFile, pData, SizeOfData, &NumberOfBytesWritten, NULL))
1311 return HrFromWin32(GetLastError());
1312 }
1313 _ASSERTE(NumberOfBytesWritten == SizeOfData);
1314ErrExit:
1315 return hr;
1316}
1317
1318//-----------------------------------------------------------
1319// SymWriter WriteStringPool
1320// Write the information to a file or to a stream
1321//-----------------------------------------------------------
1322COM_METHOD SymWriter::WriteStringPool()
1323{
1324 IStream *pIStream = NULL;
1325 BYTE *pStreamMem = NULL;
1326
1327 HRESULT hr = NOERROR;
1328 if (m_pIStream)
1329 {
1330 IfFailGo(m_pStringPool->PersistToStream(m_pIStream));
1331 }
1332 else
1333 {
1334 LARGE_INTEGER disp = { {0, 0} };
1335 DWORD NumberOfBytes;
1336 DWORD SizeOfData;
1337 STATSTG statStg;
1338
1339 IfFailGo(CreateStreamOnHGlobal(NULL,
1340 TRUE,
1341 &pIStream));
1342
1343 IfFailGo(m_pStringPool->PersistToStream(pIStream));
1344
1345 IfFailGo(pIStream->Stat(&statStg, STATFLAG_NONAME));
1346 SizeOfData = statStg.cbSize.u.LowPart;
1347
1348 IfFailGo(pIStream->Seek(disp, STREAM_SEEK_SET, NULL));
1349
1350 pStreamMem = NEW(BYTE[SizeOfData]);
1351 IfFailGo(pIStream->Read(pStreamMem, SizeOfData, &NumberOfBytes));
1352
1353 if (!WriteFile(m_hFile, pStreamMem, SizeOfData, &NumberOfBytes, NULL))
1354 return HrFromWin32(GetLastError());
1355
1356 _ASSERTE(NumberOfBytes == SizeOfData);
1357
1358 }
1359ErrExit:
1360 RELEASE(pIStream);
1361 DELETEARRAY(pStreamMem);
1362 return hr;
1363}
1364
1365//-----------------------------------------------------------
1366// SymWriter WritePDB
1367// Write the PDB information to a file or to a stream
1368//-----------------------------------------------------------
1369COM_METHOD SymWriter::WritePDB()
1370{
1371
1372 HRESULT hr = NOERROR;
1373 GUID ildb_guid = ILDB_VERSION_GUID;
1374
1375 // Make sure the ModuleLevelInfo is set
1376 ModuleLevelInfo.m_CountOfVars = VAL32(m_MethodInfo.m_vars.count());
1377 ModuleLevelInfo.m_CountOfBytes = VAL32(m_MethodInfo.m_bytes.count());
1378 ModuleLevelInfo.m_CountOfUsing = VAL32(m_MethodInfo.m_usings.count());
1379 ModuleLevelInfo.m_CountOfScopes = VAL32(m_MethodInfo.m_scopes.count());
1380 ModuleLevelInfo.m_CountOfMethods = VAL32(m_MethodInfo.m_methods.count());
1381 if (m_pStringPool)
1382 {
1383 DWORD dwSaveSize;
1384 IfFailGo(m_pStringPool->GetSaveSize((UINT32 *)&dwSaveSize));
1385 ModuleLevelInfo.m_CountOfStringBytes = VAL32(dwSaveSize);
1386 }
1387 else
1388 {
1389 ModuleLevelInfo.m_CountOfStringBytes = 0;
1390 }
1391 ModuleLevelInfo.m_CountOfConstants = VAL32(m_MethodInfo.m_constants.count());
1392 ModuleLevelInfo.m_CountOfDocuments = VAL32(m_MethodInfo.m_documents.count());
1393 ModuleLevelInfo.m_CountOfSequencePoints = VAL32(m_MethodInfo.m_auxSequencePoints.count());
1394
1395 // Open the file
1396 if (m_pIStream == NULL)
1397 {
1398 // We need to open the output file.
1399 m_hFile = WszCreateFile(m_szPath,
1400 GENERIC_WRITE,
1401 0,
1402 NULL,
1403 CREATE_ALWAYS,
1404 FILE_ATTRIBUTE_NORMAL,
1405 NULL);
1406
1407 if (m_hFile == INVALID_HANDLE_VALUE)
1408 {
1409 IfFailGo(HrFromWin32(GetLastError()));
1410 }
1411 }
1412 else
1413 {
1414 // We're writing to a stream. Make sure we're at the beginning
1415 // (eg. if this is being called more than once).
1416 // Note that technically we should probably call SetSize to truncate the
1417 // stream to ensure we don't leave reminants of the previous contents
1418 // at the end of the new stream. But with our current CGrowableStream
1419 // implementation, this would have a big performance impact (causing us to
1420 // do linear growth and lots of reallocations at every write). We only
1421 // ever add data to a symbol writer (don't remove anything), and so subsequent
1422 // streams should always get larger. Regardless, ILDB supports trailing garbage
1423 // without a problem (we used to always have the remainder of a page at the end
1424 // of the stream), and so this is not an issue of correctness.
1425 LARGE_INTEGER pos0;
1426 pos0.QuadPart = 0;
1427 IfFailGo(m_pIStream->Seek(pos0, STREAM_SEEK_SET, NULL));
1428 }
1429
1430#if _DEBUG
1431 // We need to make sure the Variant entry in the constants is 8 byte
1432 // aligned so make sure everything up to the there is aligned correctly
1433 if ((ILDB_SIGNATURE_SIZE % 8) ||
1434 (sizeof(PDBInfo) % 8) ||
1435 (sizeof(GUID) % 8))
1436 {
1437 _ASSERTE(!"We need to safe the data in an aligned format");
1438 }
1439#endif
1440
1441 // Write out a signature to recognize that we're an ildb
1442 IfFailGo(Write((void *)ILDB_SIGNATURE, ILDB_SIGNATURE_SIZE));
1443 // Write out a guid representing the version
1444 SwapGuid(&ildb_guid);
1445 IfFailGo(Write((void *)&ildb_guid, sizeof(GUID)));
1446
1447 // Now we need to write the Project level
1448 IfFailGo(Write(&ModuleLevelInfo, sizeof(PDBInfo)));
1449
1450 // Now we have to write out each array as appropriate
1451 IfFailGo(Write(m_MethodInfo.m_constants.m_array, sizeof(SymConstant) * m_MethodInfo.m_constants.count()));
1452
1453 // These members are all 4 byte aligned
1454 IfFailGo(Write(m_MethodInfo.m_methods.m_array, sizeof(SymMethodInfo) * m_MethodInfo.m_methods.count()));
1455 IfFailGo(Write(m_MethodInfo.m_scopes.m_array, sizeof(SymLexicalScope) * m_MethodInfo.m_scopes.count()));
1456 IfFailGo(Write(m_MethodInfo.m_vars.m_array, sizeof(SymVariable) * m_MethodInfo.m_vars.count()));
1457 IfFailGo(Write(m_MethodInfo.m_usings.m_array, sizeof(SymUsingNamespace) * m_MethodInfo.m_usings.count()));
1458 IfFailGo(Write(m_MethodInfo.m_auxSequencePoints.m_array, sizeof(SequencePoint) * m_MethodInfo.m_auxSequencePoints.count()));
1459 IfFailGo(Write(m_MethodInfo.m_documents.m_array, sizeof(DocumentInfo) * m_MethodInfo.m_documents.count()));
1460 IfFailGo(Write(m_MethodInfo.m_bytes.m_array, sizeof(BYTE) * m_MethodInfo.m_bytes.count()));
1461 IfFailGo(WriteStringPool());
1462
1463ErrExit:
1464 if (m_hFile)
1465 CloseHandle(m_hFile);
1466 return hr;
1467}
1468
1469/* ------------------------------------------------------------------------- *
1470 * SymDocumentWriter class
1471 * ------------------------------------------------------------------------- */
1472SymDocumentWriter::SymDocumentWriter(
1473 UINT32 DocumentEntry,
1474 SymWriter *pEmitter
1475) :
1476 m_refCount ( 0 ),
1477 m_DocumentEntry ( DocumentEntry ),
1478 m_pEmitter( pEmitter )
1479{
1480 _ASSERTE(pEmitter);
1481 m_pEmitter->AddRef();
1482}
1483
1484SymDocumentWriter::~SymDocumentWriter()
1485{
1486 // Note that this must be thread-safe - it may be invoked on the finalizer thread
1487 RELEASE(m_pEmitter);
1488}
1489
1490COM_METHOD SymDocumentWriter::QueryInterface(REFIID riid, void **ppInterface)
1491{
1492 if (ppInterface == NULL)
1493 return E_INVALIDARG;
1494
1495 if (riid == IID_ISymUnmanagedDocumentWriter)
1496 *ppInterface = (ISymUnmanagedDocumentWriter*)this;
1497 else if (riid == IID_IUnknown)
1498 *ppInterface = (IUnknown*)(ISymUnmanagedDocumentWriter*)this;
1499 else
1500 {
1501 *ppInterface = NULL;
1502 return E_NOINTERFACE;
1503 }
1504
1505 AddRef();
1506 return S_OK;
1507}
1508
1509//-----------------------------------------------------------
1510// SymDocumentWriter SetSource
1511//-----------------------------------------------------------
1512COM_METHOD SymDocumentWriter::SetSource(ULONG32 sourceSize,
1513 BYTE source[])
1514{
1515 return m_pEmitter->SetDocumentSrc(m_DocumentEntry, sourceSize, source);
1516}
1517
1518//-----------------------------------------------------------
1519// SymDocumentWriter SetCheckSum
1520//-----------------------------------------------------------
1521COM_METHOD SymDocumentWriter::SetCheckSum(GUID algorithmId,
1522 ULONG32 checkSumSize,
1523 BYTE checkSum[])
1524{
1525 return m_pEmitter->SetDocumentCheckSum(m_DocumentEntry, algorithmId, checkSumSize, checkSum);
1526}
1527
1528
1529//-----------------------------------------------------------
1530// DocumentInfo SetDocumentWriter
1531//-----------------------------------------------------------
1532// Set the pointer to the SymDocumentWriter instance corresponding to this instance of DocumentInfo
1533// An argument of NULL will call Release
1534// Arguments
1535// input: pDoc - pointer to the associated SymDocumentWriter or NULL
1536
1537void DocumentInfo::SetDocumentWriter(SymDocumentWriter * pDoc)
1538{
1539 if (m_pDocumentWriter != NULL)
1540 {
1541 m_pDocumentWriter->Release();
1542 }
1543 m_pDocumentWriter = pDoc;
1544 if (m_pDocumentWriter != NULL)
1545 {
1546 pDoc->AddRef();
1547 }
1548}
1549