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 | //----------------------------------------------------------- |
35 | HRESULT 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 | //----------------------------------------------------------- |
54 | SymWriter::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 | //----------------------------------------------------------- |
74 | COM_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 | //----------------------------------------------------------- |
100 | SymWriter::~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 | //----------------------------------------------------------- |
117 | COM_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 | //----------------------------------------------------------- |
170 | COM_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 | |
183 | ErrExit: |
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 | //----------------------------------------------------------- |
195 | HRESULT 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 | //----------------------------------------------------------- |
257 | HRESULT 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 | |
297 | ErrExit: |
298 | DELETE(sdw); |
299 | return hr; |
300 | } |
301 | |
302 | //----------------------------------------------------------- |
303 | // SymWriter DefineDocument |
304 | //----------------------------------------------------------- |
305 | COM_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); |
325 | ErrExit: |
326 | return hr; |
327 | } |
328 | |
329 | |
330 | //----------------------------------------------------------- |
331 | // SymWriter SetDocumentSrc |
332 | //----------------------------------------------------------- |
333 | HRESULT 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 | |
356 | ErrExit: |
357 | return hr; |
358 | } |
359 | |
360 | //----------------------------------------------------------- |
361 | // SymWriter SetDocumentCheckSum |
362 | //----------------------------------------------------------- |
363 | HRESULT 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 | |
389 | ErrExit: |
390 | return hr; |
391 | } |
392 | |
393 | //----------------------------------------------------------- |
394 | // SymWriter SetUserEntryPoint |
395 | //----------------------------------------------------------- |
396 | COM_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 | //----------------------------------------------------------- |
411 | COM_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 | |
460 | ErrExit: |
461 | return hr; |
462 | } |
463 | |
464 | COM_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 | //----------------------------------------------------------- |
478 | int __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 | //----------------------------------------------------------- |
489 | COM_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 | //----------------------------------------------------------- |
536 | COM_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 | |
579 | ErrExit: |
580 | return hr; |
581 | } |
582 | |
583 | //----------------------------------------------------------- |
584 | // SymWriter OpenScope |
585 | // Open a new scope for this function |
586 | //----------------------------------------------------------- |
587 | COM_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 | |
624 | ErrExit: |
625 | return hr; |
626 | } |
627 | |
628 | //----------------------------------------------------------- |
629 | // SymWriter CloseScope |
630 | //----------------------------------------------------------- |
631 | COM_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 | //----------------------------------------------------------- |
652 | COM_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 | //----------------------------------------------------------- |
675 | COM_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 | //----------------------------------------------------------- |
703 | COM_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 | |
766 | ErrExit: |
767 | return hr; |
768 | } |
769 | |
770 | COM_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 | //----------------------------------------------------------- |
786 | COM_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 | |
831 | ErrExit: |
832 | return hr; |
833 | } |
834 | |
835 | //----------------------------------------------------------- |
836 | // verifyConstTypes |
837 | // Verify that the type is a type we support |
838 | //----------------------------------------------------------- |
839 | static 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 | //----------------------------------------------------------- |
866 | COM_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 | |
941 | ErrExit: |
942 | return hr; |
943 | } |
944 | |
945 | COM_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 | //----------------------------------------------------------- |
958 | COM_METHOD SymWriter::Abort(void) |
959 | { |
960 | m_closed = true; |
961 | return S_OK; |
962 | } |
963 | |
964 | //----------------------------------------------------------- |
965 | // SymWriter DefineField |
966 | //----------------------------------------------------------- |
967 | COM_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 | //----------------------------------------------------------- |
984 | COM_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 | |
997 | COM_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 | //----------------------------------------------------------- |
1013 | int __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 | //----------------------------------------------------------- |
1023 | COM_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 | //----------------------------------------------------------- |
1037 | COM_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 | //----------------------------------------------------------- |
1065 | COM_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 | //----------------------------------------------------------- |
1078 | COM_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 | //----------------------------------------------------------- |
1087 | COM_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 | //----------------------------------------------------------- |
1097 | COM_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 | |
1124 | ErrExit: |
1125 | return hr; |
1126 | } |
1127 | |
1128 | //----------------------------------------------------------- |
1129 | // SymWriter SetMethodSourceRange |
1130 | //----------------------------------------------------------- |
1131 | COM_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 | //----------------------------------------------------------- |
1149 | inline 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 | //----------------------------------------------------------- |
1164 | COM_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 | //----------------------------------------------------------- |
1224 | COM_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 | |
1251 | ErrExit: |
1252 | return hr; |
1253 | } |
1254 | |
1255 | COM_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 | } |
1289 | ErrExit: |
1290 | return hr; |
1291 | } |
1292 | |
1293 | //----------------------------------------------------------- |
1294 | // SymWriter Write |
1295 | // Write the information to a file or to a stream |
1296 | //----------------------------------------------------------- |
1297 | COM_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); |
1314 | ErrExit: |
1315 | return hr; |
1316 | } |
1317 | |
1318 | //----------------------------------------------------------- |
1319 | // SymWriter WriteStringPool |
1320 | // Write the information to a file or to a stream |
1321 | //----------------------------------------------------------- |
1322 | COM_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 | } |
1359 | ErrExit: |
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 | //----------------------------------------------------------- |
1369 | COM_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 | |
1463 | ErrExit: |
1464 | if (m_hFile) |
1465 | CloseHandle(m_hFile); |
1466 | return hr; |
1467 | } |
1468 | |
1469 | /* ------------------------------------------------------------------------- * |
1470 | * SymDocumentWriter class |
1471 | * ------------------------------------------------------------------------- */ |
1472 | SymDocumentWriter::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 | |
1484 | SymDocumentWriter::~SymDocumentWriter() |
1485 | { |
1486 | // Note that this must be thread-safe - it may be invoked on the finalizer thread |
1487 | RELEASE(m_pEmitter); |
1488 | } |
1489 | |
1490 | COM_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 | //----------------------------------------------------------- |
1512 | COM_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 | //----------------------------------------------------------- |
1521 | COM_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 | |
1537 | void 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 | |