PdbToXml.cs 53.3 KB
Newer Older
1
// Copyright (c) Microsoft.  All Rights Reserved.  Licensed under the Apache License, Version 2.0.  See License.txt in the project root for license information.
P
Pilchie 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml;
16 17 18
using Microsoft.CodeAnalysis;
using Microsoft.DiaSymReader;
using CDI = Microsoft.CodeAnalysis.CustomDebugInfoReader;
19
using CDIC = Microsoft.Cci.CustomDebugInfoConstants;
P
Pilchie 已提交
20 21 22 23 24 25 26 27 28 29 30 31
using PooledStringBuilder = Microsoft.CodeAnalysis.Collections.PooledStringBuilder;

namespace Roslyn.Test.PdbUtilities
{
    /// <summary>
    /// Class to write out XML for a PDB.
    /// </summary>
    public sealed class PdbToXmlConverter
    {
        // For printing integers in a standard hex format.
        private const string IntHexFormat = "0x{0:X}";

B
beep boop 已提交
32 33 34 35
        private readonly MetadataReader _metadataReader;
        private readonly TempPdbReader _pdbReader;
        private readonly PdbToXmlOptions _options;
        private readonly XmlWriter _writer;
P
Pilchie 已提交
36 37

        // Maps files to ids. 
B
beep boop 已提交
38
        private readonly Dictionary<string, int> _fileMapping = new Dictionary<string, int>();
P
Pilchie 已提交
39 40 41

        private PdbToXmlConverter(XmlWriter writer, TempPdbReader pdbReader, MetadataReader metadataReader, PdbToXmlOptions options)
        {
B
beep boop 已提交
42 43 44 45
            _pdbReader = pdbReader;
            _metadataReader = metadataReader;
            _writer = writer;
            _options = options;
P
Pilchie 已提交
46 47
        }

48
        public unsafe static string DeltaPdbToXml(Stream deltaPdb, IEnumerable<int> methodTokens)
P
Pilchie 已提交
49 50 51
        {
            var writer = new StringWriter();
            ToXml(
52 53
                writer,
                deltaPdb,
P
Pilchie 已提交
54
                metadataReaderOpt: null,
55
                options: PdbToXmlOptions.IncludeTokens,
56
                methodHandles: methodTokens.Select(token => (MethodDefinitionHandle)MetadataTokens.Handle(token)));
P
Pilchie 已提交
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76

            return writer.ToString();
        }

        public static string ToXml(Stream pdbStream, Stream peStream, PdbToXmlOptions options = PdbToXmlOptions.ResolveTokens, string methodName = null)
        {
            var writer = new StringWriter();
            ToXml(writer, pdbStream, peStream, options, methodName);
            return writer.ToString();
        }

        public static string ToXml(Stream pdbStream, byte[] peImage, PdbToXmlOptions options = PdbToXmlOptions.ResolveTokens, string methodName = null)
        {
            var writer = new StringWriter();
            ToXml(writer, pdbStream, new MemoryStream(peImage), options, methodName);
            return writer.ToString();
        }

        public unsafe static void ToXml(TextWriter xmlWriter, Stream pdbStream, Stream peStream, PdbToXmlOptions options = PdbToXmlOptions.Default, string methodName = null)
        {
A
angocke 已提交
77
            IEnumerable<MethodDefinitionHandle> methodHandles;
P
Pilchie 已提交
78 79 80 81 82 83 84
            var headers = new PEHeaders(peStream);
            byte[] metadata = new byte[headers.MetadataSize];
            peStream.Seek(headers.MetadataStartOffset, SeekOrigin.Begin);
            peStream.Read(metadata, 0, headers.MetadataSize);

            fixed (byte* metadataPtr = metadata)
            {
A
angocke 已提交
85
                var metadataReader = new MetadataReader(metadataPtr, metadata.Length);
P
Pilchie 已提交
86 87 88 89 90 91 92

                if (string.IsNullOrEmpty(methodName))
                {
                    methodHandles = metadataReader.MethodDefinitions;
                }
                else
                {
93 94 95 96 97 98
                    var matching = metadataReader.MethodDefinitions.
                        Where(methodHandle => GetQualifiedMethodName(metadataReader, methodHandle) == methodName).ToArray();

                    if (matching.Length == 0)
                    {
                        xmlWriter.WriteLine("<error>");
99
                        xmlWriter.WriteLine(string.Format("<message>No method '{0}' found in metadata.</message>", methodName));
100 101 102 103 104 105 106 107
                        xmlWriter.WriteLine("<available-methods>");

                        foreach (var methodHandle in metadataReader.MethodDefinitions)
                        {
                            xmlWriter.Write("<method><![CDATA[");
                            xmlWriter.Write(GetQualifiedMethodName(metadataReader, methodHandle));
                            xmlWriter.Write("]]></method>");
                            xmlWriter.WriteLine();
108
                        }
109 110 111 112 113 114 115 116

                        xmlWriter.WriteLine("</available-methods>");
                        xmlWriter.WriteLine("</error>");

                        return;
                    }

                    methodHandles = matching;
P
Pilchie 已提交
117 118 119 120 121 122 123 124 125 126
                }

                ToXml(xmlWriter, pdbStream, metadataReader, options, methodHandles);
            }
        }

        /// <summary>
        /// Load the PDB given the parameters at the ctor and spew it out to the XmlWriter specified
        /// at the ctor.
        /// </summary>
A
angocke 已提交
127
        private static void ToXml(TextWriter xmlWriter, Stream pdbStream, MetadataReader metadataReaderOpt, PdbToXmlOptions options, IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
128 129 130 131 132 133 134 135 136 137 138 139
        {
            Debug.Assert(pdbStream != null);
            Debug.Assert((options & PdbToXmlOptions.ResolveTokens) == 0 || metadataReaderOpt != null);

            XmlDocument doc = new XmlDocument();
            XmlWriter writer = doc.CreateNavigator().AppendChild();

            using (TempPdbReader pdbReader = TempPdbReader.Create(pdbStream))
            {
                if (pdbReader == null)
                {
                    Console.WriteLine("Error: No Symbol Reader could be initialized.");
140
                    return;
P
Pilchie 已提交
141 142 143 144 145 146
                }

                var converter = new PdbToXmlConverter(writer, pdbReader, metadataReaderOpt, options);

                converter.WriteRoot(methodHandles ?? metadataReaderOpt.MethodDefinitions);
            }
147

P
Pilchie 已提交
148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
            writer.Close();

            // Save xml to disk
            doc.Save(xmlWriter);
        }

        private static byte[] GetImage(Stream stream)
        {
            MemoryStream memoryStream = stream as MemoryStream;
            if (memoryStream == null)
            {
                memoryStream = new MemoryStream((int)stream.Length);
                stream.Position = 0;
                stream.CopyTo(memoryStream);
            }

            return memoryStream.GetBuffer();
        }

A
angocke 已提交
167
        private void WriteRoot(IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
168
        {
B
beep boop 已提交
169
            _writer.WriteStartDocument();
P
Pilchie 已提交
170

B
beep boop 已提交
171
            _writer.WriteStartElement("symbols");
P
Pilchie 已提交
172 173 174 175 176

            WriteDocList();
            WriteEntryPoint();
            WriteAllMethods(methodHandles);

B
beep boop 已提交
177
            if ((_options & PdbToXmlOptions.IncludeMethodSpans) != 0)
P
Pilchie 已提交
178 179 180 181
            {
                WriteAllMethodSpans();
            }

B
beep boop 已提交
182
            _writer.WriteEndElement();
P
Pilchie 已提交
183 184 185
        }

        // Dump all of the methods in the given ISymbolReader to the XmlWriter provided in the ctor.
A
angocke 已提交
186
        private void WriteAllMethods(IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
187
        {
B
beep boop 已提交
188
            _writer.WriteStartElement("methods");
P
Pilchie 已提交
189 190 191 192 193 194

            foreach (var methodHandle in methodHandles)
            {
                WriteMethod(methodHandle);
            }

B
beep boop 已提交
195
            _writer.WriteEndElement();
P
Pilchie 已提交
196 197
        }

A
angocke 已提交
198
        private void WriteMethod(MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
199
        {
B
beep boop 已提交
200
            int token = _metadataReader.GetToken(methodHandle);
P
Pilchie 已提交
201

B
beep boop 已提交
202 203
            byte[] cdi = _pdbReader.SymbolReader.GetCustomDebugInfoBytes(token, methodVersion: 0);
            ISymUnmanagedMethod method = _pdbReader.SymbolReader.GetMethod(token);
204
            if (cdi == null && method == null)
P
Pilchie 已提交
205 206 207 208 209
            {
                // no debug info for the method
                return;
            }

B
beep boop 已提交
210
            _writer.WriteStartElement("method");
P
Pilchie 已提交
211 212
            WriteMethodAttributes(token, isReference: false);

213
            if (cdi != null)
P
Pilchie 已提交
214
            {
215
                WriteCustomDebugInfo(cdi);
P
Pilchie 已提交
216 217
            }

218
            if (method != null)
P
Pilchie 已提交
219
            {
220
                WriteSequencePoints(method);
P
Pilchie 已提交
221 222 223 224

                // TODO (tomat): Ideally this would be done in a separate test helper, not in PdbToXml.
                // verify ISymUnmanagedMethod APIs:
                var expectedSlotNames = new Dictionary<int, ImmutableArray<string>>();
225
                WriteLocals(method, expectedSlotNames);
P
Pilchie 已提交
226

227
                var actualSlotNames = method.GetLocalVariableSlots();
P
Pilchie 已提交
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245

                Debug.Assert(actualSlotNames.Length == (expectedSlotNames.Count == 0 ? 0 : expectedSlotNames.Keys.Max() + 1));

                int i = 0;
                foreach (var slotName in actualSlotNames)
                {
                    if (slotName == null)
                    {
                        Debug.Assert(!expectedSlotNames.ContainsKey(i));
                    }
                    else
                    {
                        Debug.Assert(expectedSlotNames[i].Contains(slotName));
                    }

                    i++;
                }

246
                ImmutableArray<ISymUnmanagedScope> children = method.GetRootScope().GetScopes();
P
Pilchie 已提交
247 248 249 250 251
                if (children.Length != 0)
                {
                    WriteScopes(children[0]);
                }

252
                WriteAsyncInfo(method);
P
Pilchie 已提交
253 254
            }

B
beep boop 已提交
255
            _writer.WriteEndElement(); // method
P
Pilchie 已提交
256 257 258 259 260 261 262 263
        }

        /// <summary>
        /// Given a byte array of custom debug info, parse the array and write out XML describing
        /// its structure and contents.
        /// </summary>
        private void WriteCustomDebugInfo(byte[] bytes)
        {
264
            var records = CustomDebugInfoReader.GetCustomDebugInfoRecords(bytes).ToArray();
265

B
beep boop 已提交
266
            _writer.WriteStartElement("customDebugInfo");
P
Pilchie 已提交
267

268
            foreach (var record in records)
P
Pilchie 已提交
269
            {
270
                if (record.Version != CDIC.CdiVersion)
P
Pilchie 已提交
271
                {
272
                    WriteUnknownCustomDebugInfo(record);
P
Pilchie 已提交
273 274 275
                }
                else
                {
276
                    switch (record.Kind)
P
Pilchie 已提交
277 278
                    {
                        case CustomDebugInfoKind.UsingInfo:
279
                            WriteUsingCustomDebugInfo(record);
P
Pilchie 已提交
280 281
                            break;
                        case CustomDebugInfoKind.ForwardInfo:
282
                            WriteForwardCustomDebugInfo(record);
P
Pilchie 已提交
283 284
                            break;
                        case CustomDebugInfoKind.ForwardToModuleInfo:
285
                            WriteForwardToModuleCustomDebugInfo(record);
P
Pilchie 已提交
286
                            break;
287
                        case CustomDebugInfoKind.StateMachineHoistedLocalScopes:
288
                            WriteStatemachineHoistedLocalScopesCustomDebugInfo(record);
P
Pilchie 已提交
289 290
                            break;
                        case CustomDebugInfoKind.ForwardIterator:
291
                            WriteForwardIteratorCustomDebugInfo(record);
P
Pilchie 已提交
292 293
                            break;
                        case CustomDebugInfoKind.DynamicLocals:
294
                            WriteDynamicLocalsCustomDebugInfo(record);
P
Pilchie 已提交
295
                            break;
296
                        case CustomDebugInfoKind.EditAndContinueLocalSlotMap:
297
                            WriteEditAndContinueLocalSlotMap(record);
298
                            break;
299 300 301
                        case CustomDebugInfoKind.EditAndContinueLambdaMap:
                            WriteEditAndContinueLambdaMap(record);
                            break;
P
Pilchie 已提交
302
                        default:
303
                            WriteUnknownCustomDebugInfo(record);
P
Pilchie 已提交
304 305 306 307 308
                            break;
                    }
                }
            }

B
beep boop 已提交
309
            _writer.WriteEndElement(); //customDebugInfo
P
Pilchie 已提交
310 311 312 313 314 315 316
        }

        /// <summary>
        /// If the custom debug info is in a format that we don't understand, then we will
        /// just print a standard record header followed by the rest of the record as a
        /// single hex string.
        /// </summary>
317
        private void WriteUnknownCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
318
        {
B
beep boop 已提交
319 320 321
            _writer.WriteStartElement("unknown");
            _writer.WriteAttributeString("kind", record.Kind.ToString());
            _writer.WriteAttributeString("version", record.Version.ToString());
P
Pilchie 已提交
322 323 324

            PooledStringBuilder pooled = PooledStringBuilder.GetInstance();
            StringBuilder builder = pooled.Builder;
325
            foreach (byte b in record.Data)
P
Pilchie 已提交
326 327 328
            {
                builder.AppendFormat("{0:X2}", b);
            }
329

B
beep boop 已提交
330
            _writer.WriteAttributeString("payload", pooled.ToStringAndFree());
P
Pilchie 已提交
331

B
beep boop 已提交
332
            _writer.WriteEndElement(); //unknown
P
Pilchie 已提交
333 334 335 336 337 338 339 340 341
        }

        /// <summary>
        /// For each namespace declaration enclosing a method (innermost-to-outermost), there is a count
        /// of the number of imports in that declaration.
        /// </summary>
        /// <remarks>
        /// There's always at least one entry (for the global namespace).
        /// </remarks>
342
        private void WriteUsingCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
343
        {
344
            Debug.Assert(record.Kind == CustomDebugInfoKind.UsingInfo);
P
Pilchie 已提交
345

B
beep boop 已提交
346
            _writer.WriteStartElement("using");
P
Pilchie 已提交
347

348
            ImmutableArray<short> counts = CDI.DecodeUsingRecord(record.Data);
P
Pilchie 已提交
349 350 351

            foreach (short importCount in counts)
            {
B
beep boop 已提交
352 353 354
                _writer.WriteStartElement("namespace");
                _writer.WriteAttributeString("usingCount", importCount.ToString());
                _writer.WriteEndElement(); //namespace
P
Pilchie 已提交
355 356
            }

B
beep boop 已提交
357
            _writer.WriteEndElement(); //using
P
Pilchie 已提交
358 359 360 361 362 363 364 365 366
        }

        /// <summary>
        /// This indicates that further information can be obtained by looking at the custom debug
        /// info of another method (specified by token).
        /// </summary>
        /// <remarks>
        /// Emitting tokens makes tests more fragile.
        /// </remarks>
367
        private void WriteForwardCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
368
        {
369
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardInfo);
P
Pilchie 已提交
370

B
beep boop 已提交
371
            _writer.WriteStartElement("forward");
P
Pilchie 已提交
372

373
            int token = CDI.DecodeForwardRecord(record.Data);
P
Pilchie 已提交
374 375
            WriteMethodAttributes(token, isReference: true);

B
beep boop 已提交
376
            _writer.WriteEndElement(); //forward
P
Pilchie 已提交
377 378 379 380 381 382 383 384 385 386
        }

        /// <summary>
        /// This indicates that further information can be obtained by looking at the custom debug
        /// info of another method (specified by token).
        /// </summary>
        /// <remarks>
        /// Appears when there are extern aliases and edit-and-continue is disabled.
        /// Emitting tokens makes tests more fragile.
        /// </remarks>
387
        private void WriteForwardToModuleCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
388
        {
389
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardToModuleInfo);
P
Pilchie 已提交
390

B
beep boop 已提交
391
            _writer.WriteStartElement("forwardToModule");
P
Pilchie 已提交
392

393
            int token = CDI.DecodeForwardRecord(record.Data);
P
Pilchie 已提交
394 395
            WriteMethodAttributes(token, isReference: true);

B
beep boop 已提交
396
            _writer.WriteEndElement(); //forwardToModule
P
Pilchie 已提交
397 398 399 400 401 402 403 404 405 406
        }

        /// <summary>
        /// Appears when iterator locals have to lifted into fields.  Contains a list of buckets with
        /// start and end offsets (presumably, into IL).
        /// TODO: comment when the structure is understood.
        /// </summary>
        /// <remarks>
        /// Appears when there are locals in iterator methods.
        /// </remarks>
407
        private void WriteStatemachineHoistedLocalScopesCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
408
        {
409
            Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineHoistedLocalScopes);
P
Pilchie 已提交
410

B
beep boop 已提交
411
            _writer.WriteStartElement("hoistedLocalScopes");
P
Pilchie 已提交
412

413
            var scopes = CDI.DecodeStateMachineHoistedLocalScopesRecord(record.Data);
P
Pilchie 已提交
414

415
            foreach (StateMachineHoistedLocalScope scope in scopes)
P
Pilchie 已提交
416
            {
B
beep boop 已提交
417 418 419 420
                _writer.WriteStartElement("slot");
                _writer.WriteAttributeString("startOffset", AsILOffset(scope.StartOffset));
                _writer.WriteAttributeString("endOffset", AsILOffset(scope.EndOffset));
                _writer.WriteEndElement(); //bucket
P
Pilchie 已提交
421 422
            }

B
beep boop 已提交
423
            _writer.WriteEndElement();
P
Pilchie 已提交
424 425 426 427 428 429 430 431 432
        }

        /// <summary>
        /// Contains a name string.
        /// TODO: comment when the structure is understood.
        /// </summary>
        /// <remarks>
        /// Appears when are iterator methods.
        /// </remarks>
433
        private void WriteForwardIteratorCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
434
        {
435
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardIterator);
P
Pilchie 已提交
436

B
beep boop 已提交
437
            _writer.WriteStartElement("forwardIterator");
P
Pilchie 已提交
438

439
            string name = CDI.DecodeForwardIteratorRecord(record.Data);
P
Pilchie 已提交
440

B
beep boop 已提交
441
            _writer.WriteAttributeString("name", name);
P
Pilchie 已提交
442

B
beep boop 已提交
443
            _writer.WriteEndElement(); //forwardIterator
P
Pilchie 已提交
444 445 446 447 448 449 450 451 452
        }

        /// <summary>
        /// Contains a list of buckets, each of which contains a number of flags, a slot ID, and a name.
        /// TODO: comment when the structure is understood.
        /// </summary>
        /// <remarks>
        /// Appears when there are dynamic locals.
        /// </remarks>
453
        private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
454
        {
455
            Debug.Assert(record.Kind == CustomDebugInfoKind.DynamicLocals);
P
Pilchie 已提交
456

B
beep boop 已提交
457
            _writer.WriteStartElement("dynamicLocals");
P
Pilchie 已提交
458

459
            var buckets = CDI.DecodeDynamicLocalsRecord(record.Data);
P
Pilchie 已提交
460 461 462 463 464 465 466 467 468 469 470 471 472

            foreach (DynamicLocalBucket bucket in buckets)
            {
                ulong flags = bucket.Flags;
                int flagCount = bucket.FlagCount;

                PooledStringBuilder pooled = PooledStringBuilder.GetInstance();
                StringBuilder flagsBuilder = pooled.Builder;
                for (int f = 0; f < flagCount; f++)
                {
                    flagsBuilder.Append((flags >> f) & 1UL);
                }

B
beep boop 已提交
473 474 475 476 477 478
                _writer.WriteStartElement("bucket");
                _writer.WriteAttributeString("flagCount", flagCount.ToString());
                _writer.WriteAttributeString("flags", pooled.ToStringAndFree());
                _writer.WriteAttributeString("slotId", bucket.SlotId.ToString());
                _writer.WriteAttributeString("localName", bucket.Name);
                _writer.WriteEndElement(); //bucket
P
Pilchie 已提交
479 480
            }

B
beep boop 已提交
481
            _writer.WriteEndElement(); //dynamicLocals
P
Pilchie 已提交
482 483
        }

484
        private unsafe void WriteEditAndContinueLocalSlotMap(CustomDebugInfoRecord record)
485
        {
486
            Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLocalSlotMap);
487

B
beep boop 已提交
488
            _writer.WriteStartElement("encLocalSlotMap");
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
            try
            {
                int syntaxOffsetBaseline = -1;

                fixed (byte* compressedSlotMapPtr = &record.Data.ToArray()[0])
                {
                    var blobReader = new BlobReader(compressedSlotMapPtr, record.Data.Length);

                    while (blobReader.RemainingBytes > 0)
                    {
                        byte b = blobReader.ReadByte();

                        if (b == 0xff)
                        {
                            if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline))
                            {
B
beep boop 已提交
505
                                _writer.WriteElementString("baseline", "?");
506 507 508 509 510 511
                                return;
                            }

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
512

B
beep boop 已提交
513
                        _writer.WriteStartElement("slot");
514 515 516 517

                        if (b == 0)
                        {
                            // short-lived temp, no info
B
beep boop 已提交
518
                            _writer.WriteAttributeString("kind", "temp");
519 520 521 522 523
                        }
                        else
                        {
                            int synthesizedKind = (b & 0x3f) - 1;
                            bool hasOrdinal = (b & (1 << 7)) != 0;
524

525 526 527 528 529 530 531
                            int syntaxOffset;
                            bool badSyntaxOffset = !blobReader.TryReadCompressedInteger(out syntaxOffset);
                            syntaxOffset += syntaxOffsetBaseline;

                            int ordinal = 0;
                            bool badOrdinal = hasOrdinal && !blobReader.TryReadCompressedInteger(out ordinal);

B
beep boop 已提交
532 533
                            _writer.WriteAttributeString("kind", synthesizedKind.ToString());
                            _writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : syntaxOffset.ToString());
534 535 536

                            if (badOrdinal || hasOrdinal)
                            {
B
beep boop 已提交
537
                                _writer.WriteAttributeString("ordinal", badOrdinal ? "?" : ordinal.ToString());
538 539 540
                            }
                        }

B
beep boop 已提交
541
                        _writer.WriteEndElement();
542 543 544 545
                    }
                }
            }
            finally
546
            {
B
beep boop 已提交
547
                _writer.WriteEndElement(); //encLocalSlotMap
548 549 550 551 552 553
            }
        }

        private unsafe void WriteEditAndContinueLambdaMap(CustomDebugInfoRecord record)
        {
            Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLambdaMap);
554

B
beep boop 已提交
555
            _writer.WriteStartElement("encLambdaMap");
556 557 558
            try
            {
                if (record.Data.Length == 0)
559
                {
560 561
                    return;
                }
562

563 564 565 566 567 568 569 570 571
                int methodOrdinal = -1;
                int syntaxOffsetBaseline = -1;
                int closureCount;

                fixed (byte* blobPtr = &record.Data.ToArray()[0])
                {
                    var blobReader = new BlobReader(blobPtr, record.Data.Length);

                    if (!blobReader.TryReadCompressedInteger(out methodOrdinal))
572
                    {
B
beep boop 已提交
573 574
                        _writer.WriteElementString("methodOrdinal", "?");
                        _writer.WriteEndElement();
575
                        return;
576 577
                    }

578 579
                    // [-1, inf)
                    methodOrdinal--;
B
beep boop 已提交
580
                    _writer.WriteElementString("methodOrdinal", methodOrdinal.ToString());
581 582

                    if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline))
583
                    {
B
beep boop 已提交
584 585
                        _writer.WriteElementString("baseline", "?");
                        _writer.WriteEndElement();
586
                        return;
587 588
                    }

589 590
                    syntaxOffsetBaseline = -syntaxOffsetBaseline;
                    if (!blobReader.TryReadCompressedInteger(out closureCount))
591
                    {
B
beep boop 已提交
592 593
                        _writer.WriteElementString("closureCount", "?");
                        _writer.WriteEndElement();
594
                        return;
595
                    }
596 597

                    for (int i = 0; i < closureCount; i++)
598
                    {
B
beep boop 已提交
599
                        _writer.WriteStartElement("closure");
600 601 602 603 604
                        try
                        {
                            int syntaxOffset;
                            if (!blobReader.TryReadCompressedInteger(out syntaxOffset))
                            {
B
beep boop 已提交
605
                                _writer.WriteElementString("offset", "?");
606 607 608
                                break;
                            }

B
beep boop 已提交
609
                            _writer.WriteAttributeString("offset", (syntaxOffset + syntaxOffsetBaseline).ToString());
610 611 612
                        }
                        finally
                        {
B
beep boop 已提交
613
                            _writer.WriteEndElement();
614 615
                        }
                    }
616

617 618
                    while (blobReader.RemainingBytes > 0)
                    {
B
beep boop 已提交
619
                        _writer.WriteStartElement("lambda");
620 621 622 623 624
                        try
                        {
                            int syntaxOffset;
                            if (!blobReader.TryReadCompressedInteger(out syntaxOffset))
                            {
B
beep boop 已提交
625
                                _writer.WriteElementString("offset", "?");
626 627
                                return;
                            }
628

B
beep boop 已提交
629
                            _writer.WriteAttributeString("offset", (syntaxOffset + syntaxOffsetBaseline).ToString());
630

631 632 633
                            int closureOrdinal;
                            if (!blobReader.TryReadCompressedInteger(out closureOrdinal))
                            {
B
beep boop 已提交
634
                                _writer.WriteElementString("closure", "?");
635 636
                                return;
                            }
637

638 639 640 641 642 643 644
                            closureOrdinal -= 2;

                            if (closureOrdinal == -2)
                            {
                                _writer.WriteAttributeString("closure", "this");
                            }
                            else if (closureOrdinal != -1)
645
                            {
B
beep boop 已提交
646
                                _writer.WriteAttributeString("closure",
647
                                    closureOrdinal.ToString() + (closureOrdinal >= closureCount ? " (invalid)" : ""));
648 649 650
                            }
                        }
                        finally
651
                        {
B
beep boop 已提交
652
                            _writer.WriteEndElement();
653 654 655 656
                        }
                    }
                }
            }
657 658
            finally
            {
B
beep boop 已提交
659
                _writer.WriteEndElement(); //encLocalSlotMap
660
            }
661 662
        }

P
Pilchie 已提交
663 664
        private void WriteScopes(ISymUnmanagedScope scope)
        {
B
beep boop 已提交
665
            _writer.WriteStartElement("scope");
P
Pilchie 已提交
666
            {
B
beep boop 已提交
667 668
                _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset()));
                _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset()));
P
Pilchie 已提交
669 670 671 672 673 674 675 676 677 678 679 680 681
                {
                    foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces())
                    {
                        WriteNamespace(@namespace);
                    }

                    WriteLocalsHelper(scope, slotNames: null, includeChildScopes: false);
                }
                foreach (ISymUnmanagedScope child in scope.GetScopes())
                {
                    WriteScopes(child);
                }
            }
B
beep boop 已提交
682
            _writer.WriteEndElement(); // </scope>
P
Pilchie 已提交
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
        }

        private void WriteNamespace(ISymUnmanagedNamespace @namespace)
        {
            string rawName = @namespace.GetName();

            string alias;
            string externAlias;
            string target;
            ImportTargetKind kind;
            ImportScope scope;

            try
            {
                if (rawName.Length == 0)
                {
                    externAlias = null;
700 701
                    var parsingSucceeded = CDI.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope);
                    Debug.Assert(parsingSucceeded);
P
Pilchie 已提交
702 703 704 705 706 707 708 709 710 711
                }
                else
                {
                    switch (rawName[0])
                    {
                        case 'U':
                        case 'A':
                        case 'X':
                        case 'Z':
                        case 'E':
712
                        case 'T':
P
Pilchie 已提交
713
                            scope = ImportScope.Unspecified;
714 715 716 717
                            if (!CDI.TryParseCSharpImportString(rawName, out alias, out externAlias, out target, out kind))
                            {
                                throw new InvalidOperationException(string.Format("Invalid import '{0}'", rawName));
                            }
P
Pilchie 已提交
718 719 720 721
                            break;

                        default:
                            externAlias = null;
722 723 724 725
                            if (!CDI.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope))
                            {
                                throw new InvalidOperationException(string.Format("Invalid import '{0}'", rawName));
                            }
P
Pilchie 已提交
726 727 728 729 730 731
                            break;
                    }
                }
            }
            catch (ArgumentException) // TODO: filter
            {
B
beep boop 已提交
732
                if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
733 734 735 736
                {
                    throw;
                }

B
beep boop 已提交
737 738 739
                _writer.WriteStartElement("invalid-custom-data");
                _writer.WriteAttributeString("raw", rawName);
                _writer.WriteEndElement();
P
Pilchie 已提交
740 741 742 743 744 745 746 747 748
                return;
            }

            switch (kind)
            {
                case ImportTargetKind.CurrentNamespace:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
B
beep boop 已提交
749 750 751
                    _writer.WriteStartElement("currentnamespace");
                    _writer.WriteAttributeString("name", target);
                    _writer.WriteEndElement(); // </currentnamespace>
P
Pilchie 已提交
752 753 754 755 756
                    break;
                case ImportTargetKind.DefaultNamespace:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
B
beep boop 已提交
757 758 759
                    _writer.WriteStartElement("defaultnamespace");
                    _writer.WriteAttributeString("name", target);
                    _writer.WriteEndElement(); // </defaultnamespace>
P
Pilchie 已提交
760 761 762 763 764 765
                    break;
                case ImportTargetKind.MethodToken:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
                    int token = Convert.ToInt32(target);
B
beep boop 已提交
766
                    _writer.WriteStartElement("importsforward");
P
Pilchie 已提交
767
                    WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
768
                    _writer.WriteEndElement(); // </importsforward>
P
Pilchie 已提交
769 770 771
                    break;
                case ImportTargetKind.XmlNamespace:
                    Debug.Assert(externAlias == null);
B
beep boop 已提交
772 773 774
                    _writer.WriteStartElement("xmlnamespace");
                    _writer.WriteAttributeString("prefix", alias);
                    _writer.WriteAttributeString("name", target);
P
Pilchie 已提交
775
                    WriteScopeAttribute(scope);
B
beep boop 已提交
776
                    _writer.WriteEndElement(); // </xmlnamespace>
P
Pilchie 已提交
777 778 779
                    break;
                case ImportTargetKind.NamespaceOrType:
                    Debug.Assert(externAlias == null);
B
beep boop 已提交
780 781 782 783
                    _writer.WriteStartElement("alias");
                    _writer.WriteAttributeString("name", alias);
                    _writer.WriteAttributeString("target", target);
                    _writer.WriteAttributeString("kind", "namespace"); // Strange, but retaining to avoid breaking tests.
P
Pilchie 已提交
784
                    WriteScopeAttribute(scope);
B
beep boop 已提交
785
                    _writer.WriteEndElement(); // </alias>
P
Pilchie 已提交
786 787 788 789
                    break;
                case ImportTargetKind.Namespace:
                    if (alias != null)
                    {
B
beep boop 已提交
790 791 792 793 794
                        _writer.WriteStartElement("alias");
                        _writer.WriteAttributeString("name", alias);
                        if (externAlias != null) _writer.WriteAttributeString("qualifier", externAlias);
                        _writer.WriteAttributeString("target", target);
                        _writer.WriteAttributeString("kind", "namespace");
P
Pilchie 已提交
795
                        Debug.Assert(scope == ImportScope.Unspecified); // Only C# hits this case.
B
beep boop 已提交
796
                        _writer.WriteEndElement(); // </alias>
P
Pilchie 已提交
797 798 799
                    }
                    else
                    {
B
beep boop 已提交
800 801 802
                        _writer.WriteStartElement("namespace");
                        if (externAlias != null) _writer.WriteAttributeString("qualifier", externAlias);
                        _writer.WriteAttributeString("name", target);
P
Pilchie 已提交
803
                        WriteScopeAttribute(scope);
B
beep boop 已提交
804
                        _writer.WriteEndElement(); // </namespace>
P
Pilchie 已提交
805 806 807 808 809 810
                    }
                    break;
                case ImportTargetKind.Type:
                    Debug.Assert(externAlias == null);
                    if (alias != null)
                    {
B
beep boop 已提交
811 812 813 814
                        _writer.WriteStartElement("alias");
                        _writer.WriteAttributeString("name", alias);
                        _writer.WriteAttributeString("target", target);
                        _writer.WriteAttributeString("kind", "type");
P
Pilchie 已提交
815
                        Debug.Assert(scope == ImportScope.Unspecified); // Only C# hits this case.
B
beep boop 已提交
816
                        _writer.WriteEndElement(); // </alias>
P
Pilchie 已提交
817 818 819
                    }
                    else
                    {
B
beep boop 已提交
820 821
                        _writer.WriteStartElement("type");
                        _writer.WriteAttributeString("name", target);
P
Pilchie 已提交
822
                        WriteScopeAttribute(scope);
B
beep boop 已提交
823
                        _writer.WriteEndElement(); // </type>
P
Pilchie 已提交
824 825 826
                    }
                    break;
                case ImportTargetKind.Assembly:
827 828
                    Debug.Assert(alias != null);
                    Debug.Assert(externAlias == null);
P
Pilchie 已提交
829 830 831
                    Debug.Assert(scope == ImportScope.Unspecified);
                    if (target == null)
                    {
B
beep boop 已提交
832 833 834
                        _writer.WriteStartElement("extern");
                        _writer.WriteAttributeString("alias", alias);
                        _writer.WriteEndElement(); // </extern>
P
Pilchie 已提交
835 836 837
                    }
                    else
                    {
B
beep boop 已提交
838 839 840 841
                        _writer.WriteStartElement("externinfo");
                        _writer.WriteAttributeString("alias", alias);
                        _writer.WriteAttributeString("assembly", target);
                        _writer.WriteEndElement(); // </externinfo>
P
Pilchie 已提交
842 843
                    }
                    break;
A
acasey 已提交
844 845 846
                case ImportTargetKind.Defunct:
                    Debug.Assert(alias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
B
beep boop 已提交
847 848 849
                    _writer.WriteStartElement("defunct");
                    _writer.WriteAttributeString("name", rawName);
                    _writer.WriteEndElement(); // </defunct>
A
acasey 已提交
850
                    break;
P
Pilchie 已提交
851 852
                default:
                    Debug.Assert(false, "Unexpected import kind '" + kind + "'");
B
beep boop 已提交
853 854 855
                    _writer.WriteStartElement("unknown");
                    _writer.WriteAttributeString("name", rawName);
                    _writer.WriteEndElement(); // </unknown>
P
Pilchie 已提交
856 857 858 859 860 861 862 863
                    break;
            }
        }

        private void WriteScopeAttribute(ImportScope scope)
        {
            if (scope == ImportScope.File)
            {
B
beep boop 已提交
864
                _writer.WriteAttributeString("importlevel", "file");
P
Pilchie 已提交
865 866 867
            }
            else if (scope == ImportScope.Project)
            {
B
beep boop 已提交
868
                _writer.WriteAttributeString("importlevel", "project");
P
Pilchie 已提交
869 870 871 872 873 874 875
            }
            else
            {
                Debug.Assert(scope == ImportScope.Unspecified, "Unexpected scope '" + scope + "'");
            }
        }

876
        private void WriteAsyncInfo(ISymUnmanagedMethod method)
P
Pilchie 已提交
877
        {
878 879
            var asyncMethod = method.AsAsync();
            if (asyncMethod == null)
P
Pilchie 已提交
880
            {
881 882
                return;
            }
883

B
beep boop 已提交
884
            _writer.WriteStartElement("asyncInfo");
P
Pilchie 已提交
885

886 887 888
            var catchOffset = asyncMethod.GetCatchHandlerILOffset();
            if (catchOffset >= 0)
            {
B
beep boop 已提交
889 890 891
                _writer.WriteStartElement("catchHandler");
                _writer.WriteAttributeString("offset", AsILOffset(catchOffset));
                _writer.WriteEndElement();
P
Pilchie 已提交
892 893
            }

B
beep boop 已提交
894
            _writer.WriteStartElement("kickoffMethod");
895
            WriteMethodAttributes(asyncMethod.GetKickoffMethod(), isReference: true);
B
beep boop 已提交
896
            _writer.WriteEndElement();
897 898

            foreach (var info in asyncMethod.GetAsyncStepInfos())
P
Pilchie 已提交
899
            {
B
beep boop 已提交
900 901 902
                _writer.WriteStartElement("await");
                _writer.WriteAttributeString("yield", AsILOffset(info.YieldOffset));
                _writer.WriteAttributeString("resume", AsILOffset(info.ResumeOffset));
903
                WriteMethodAttributes(info.ResumeMethod, isReference: true);
B
beep boop 已提交
904
                _writer.WriteEndElement();
P
Pilchie 已提交
905 906
            }

B
beep boop 已提交
907
            _writer.WriteEndElement();
P
Pilchie 已提交
908 909 910 911 912 913 914 915 916 917
        }

        // Write all the locals in the given method out to an XML file.
        // Since the symbol store represents the locals in a recursive scope structure, we need to walk a tree.
        // Although the locals are technically a hierarchy (based off nested scopes), it's easiest for clients
        // if we present them as a linear list. We will provide the range for each local's scope so that somebody
        // could reconstruct an approximation of the scope tree. The reconstruction may not be exact.
        // (Note this would still break down if you had an empty scope nested in another scope.
        private void WriteLocals(ISymUnmanagedMethod method, Dictionary<int, ImmutableArray<string>> slotNames)
        {
B
beep boop 已提交
918
            _writer.WriteStartElement("locals");
919 920
            // If there are no locals, then this element will just be empty.
            WriteLocalsHelper(method.GetRootScope(), slotNames, includeChildScopes: true);
B
beep boop 已提交
921
            _writer.WriteEndElement();
P
Pilchie 已提交
922 923
        }

924
        private void WriteLocalsHelper(ISymUnmanagedScope scope, Dictionary<int, ImmutableArray<string>> slotNames, bool includeChildScopes)
P
Pilchie 已提交
925
        {
926
            foreach (ISymUnmanagedVariable l in scope.GetLocals())
P
Pilchie 已提交
927
            {
B
beep boop 已提交
928
                _writer.WriteStartElement("local");
P
Pilchie 已提交
929
                {
B
beep boop 已提交
930
                    _writer.WriteAttributeString("name", l.GetName());
P
Pilchie 已提交
931 932 933 934 935 936 937 938 939

                    // Each local maps to a "IL Index" or "slot" number. 
                    // The index is not necessarily unique. Several locals may refer to the same slot. 
                    // It just means that the same local is known under different names inside the same or different scopes.
                    // This index is what you pass to ICorDebugILFrame::GetLocalVariable() to get
                    // a specific local variable. 
                    // NOTE: VB emits "fake" locals for resumable locals which are actually backed by fields.
                    //       These locals always map to the slot #0 which is just a valid number that is 
                    //       not used. Only scoping information is used by EE in this case.
940
                    int slot = l.GetSlot();
B
beep boop 已提交
941
                    _writer.WriteAttributeString("il_index", CultureInvariantToString(slot));
P
Pilchie 已提交
942 943 944 945 946 947 948 949 950

                    bool reusingSlot = false;

                    // collect slot names so that we can verify ISymUnmanagedReader APIs
                    if (slotNames != null)
                    {
                        ImmutableArray<string> existingNames;
                        if (slotNames.TryGetValue(slot, out existingNames))
                        {
951
                            slotNames[slot] = existingNames.Add(l.GetName());
P
Pilchie 已提交
952 953 954 955
                            reusingSlot = true;
                        }
                        else
                        {
956
                            slotNames.Add(slot, ImmutableArray.Create(l.GetName()));
P
Pilchie 已提交
957 958 959 960
                        }
                    }

                    // Provide scope range
B
beep boop 已提交
961 962 963
                    _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset()));
                    _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset()));
                    _writer.WriteAttributeString("attributes", l.GetAttributes().ToString());
P
Pilchie 已提交
964 965 966

                    if (reusingSlot)
                    {
B
beep boop 已提交
967
                        _writer.WriteAttributeString("reusingslot", reusingSlot.ToString(CultureInfo.InvariantCulture));
P
Pilchie 已提交
968 969
                    }
                }
B
beep boop 已提交
970
                _writer.WriteEndElement(); // </local>
P
Pilchie 已提交
971
            }
972

973
            foreach (ISymUnmanagedConstant c in scope.GetConstants())
P
Pilchie 已提交
974 975 976
            {
                // Note: We can retrieve constant tokens by saving it into signature blob
                // in our implementation of IMetadataImport.GetSigFromToken.
B
beep boop 已提交
977
                _writer.WriteStartElement("constant");
P
Pilchie 已提交
978
                {
B
beep boop 已提交
979
                    _writer.WriteAttributeString("name", c.GetName());
980

P
Pilchie 已提交
981 982 983 984
                    object value = c.GetValue();
                    string typeName = value.GetType().Name;

                    // certain Unicode characters will give Xml writers fits...in order to avoid this, we'll replace
985
                    // problematic characters/sequences with their hexadecimal equivalents, like U+0000, etc...
P
Pilchie 已提交
986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
                    var chars = value as string;
                    if (chars != null)
                    {
                        PooledStringBuilder pooled = PooledStringBuilder.GetInstance();
                        var valueWithPlaceholders = pooled.Builder;
                        foreach (var ch in chars)
                        {
                            // if we end up with more, we can add them here
                            if (0 == (int)ch)
                            {
                                valueWithPlaceholders.AppendFormat("U+{0:X4}", (int)ch);
                            }
                            else
                            {
                                valueWithPlaceholders.Append(ch);
                            }
                        }
                        if (valueWithPlaceholders.Length > chars.Length)
                        {
                            value = valueWithPlaceholders.ToString();
                        }
                        pooled.Free();
                    }

B
beep boop 已提交
1010 1011
                    _writer.WriteAttributeString("value", value.ToString());
                    _writer.WriteAttributeString("type", typeName);
P
Pilchie 已提交
1012
                }
B
beep boop 已提交
1013
                _writer.WriteEndElement(); // </constant>
P
Pilchie 已提交
1014
            }
1015

P
Pilchie 已提交
1016 1017
            if (includeChildScopes)
            {
1018
                foreach (ISymUnmanagedScope childScope in scope.GetScopes())
P
Pilchie 已提交
1019 1020 1021 1022 1023 1024 1025 1026 1027
                {
                    WriteLocalsHelper(childScope, slotNames, includeChildScopes);
                }
            }
        }

        // Write the sequence points for the given method
        // Sequence points are the map between IL offsets and source lines.
        // A single method could span multiple files (use C#'s #line directive to see for yourself).        
1028
        private void WriteSequencePoints(ISymUnmanagedMethod method)
P
Pilchie 已提交
1029
        {
B
beep boop 已提交
1030
            _writer.WriteStartElement("sequencePoints");
P
Pilchie 已提交
1031

1032
            var sequencePoints = method.GetSequencePoints();
1033

P
Pilchie 已提交
1034
            // Write out sequence points
1035
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1036
            {
B
beep boop 已提交
1037 1038
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1039

1040
                if (sequencePoint.IsHidden)
P
Pilchie 已提交
1041
                {
T
TomasMatousek 已提交
1042 1043
                    if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0)
                    {
B
beep boop 已提交
1044
                        _writer.WriteAttributeString("hidden", "invalid");
T
TomasMatousek 已提交
1045 1046 1047
                    }
                    else
                    {
B
beep boop 已提交
1048
                        _writer.WriteAttributeString("hidden", XmlConvert.ToString(true));
T
TomasMatousek 已提交
1049 1050 1051 1052
                    }
                }
                else
                {
B
beep boop 已提交
1053 1054 1055 1056
                    _writer.WriteAttributeString("startLine", CultureInvariantToString(sequencePoint.StartLine));
                    _writer.WriteAttributeString("startColumn", CultureInvariantToString(sequencePoint.StartColumn));
                    _writer.WriteAttributeString("endLine", CultureInvariantToString(sequencePoint.EndLine));
                    _writer.WriteAttributeString("endColumn", CultureInvariantToString(sequencePoint.EndColumn));
P
Pilchie 已提交
1057 1058
                }

1059
                int documentId;
B
beep boop 已提交
1060 1061
                _fileMapping.TryGetValue(sequencePoint.Document.GetName(), out documentId);
                _writer.WriteAttributeString("document", CultureInvariantToString(documentId));
P
Pilchie 已提交
1062

B
beep boop 已提交
1063
                _writer.WriteEndElement();
P
Pilchie 已提交
1064 1065
            }

B
beep boop 已提交
1066
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1067 1068 1069 1070 1071 1072
        }

        // Write all docs, and add to the m_fileMapping list.
        // Other references to docs will then just refer to this list.
        private void WriteDocList()
        {
B
beep boop 已提交
1073
            var documents = _pdbReader.SymbolReader.GetDocuments();
1074
            if (documents.Length == 0)
P
Pilchie 已提交
1075 1076 1077 1078 1079
            {
                return;
            }

            int id = 0;
B
beep boop 已提交
1080
            _writer.WriteStartElement("files");
1081
            foreach (ISymUnmanagedDocument doc in documents)
P
Pilchie 已提交
1082
            {
1083
                string name = doc.GetName();
P
Pilchie 已提交
1084 1085

                // Symbol store may give out duplicate documents. We'll fold them here
B
beep boop 已提交
1086
                if (_fileMapping.ContainsKey(name))
P
Pilchie 已提交
1087
                {
B
beep boop 已提交
1088
                    _writer.WriteComment("There is a duplicate entry for: " + name);
P
Pilchie 已提交
1089 1090 1091 1092
                    continue;
                }

                id++;
B
beep boop 已提交
1093
                _fileMapping.Add(name, id);
P
Pilchie 已提交
1094

B
beep boop 已提交
1095
                _writer.WriteStartElement("file");
P
Pilchie 已提交
1096

B
beep boop 已提交
1097 1098 1099 1100 1101
                _writer.WriteAttributeString("id", CultureInvariantToString(id));
                _writer.WriteAttributeString("name", name);
                _writer.WriteAttributeString("language", doc.GetLanguage().ToString());
                _writer.WriteAttributeString("languageVendor", doc.GetLanguageVendor().ToString());
                _writer.WriteAttributeString("documentType", doc.GetDocumentType().ToString());
P
Pilchie 已提交
1102

1103
                var checkSum = string.Concat(doc.GetChecksum().Select(b => string.Format("{0,2:X}", b) + ", "));
1104 1105 1106

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1107 1108
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1109 1110
                }

B
beep boop 已提交
1111
                _writer.WriteEndElement(); // file
P
Pilchie 已提交
1112
            }
B
beep boop 已提交
1113
            _writer.WriteEndElement(); // files
P
Pilchie 已提交
1114 1115 1116 1117
        }

        private void WriteAllMethodSpans()
        {
B
beep boop 已提交
1118
            _writer.WriteStartElement("method-spans");
P
Pilchie 已提交
1119

B
beep boop 已提交
1120
            foreach (ISymUnmanagedDocument doc in _pdbReader.SymbolReader.GetDocuments())
P
Pilchie 已提交
1121
            {
B
beep boop 已提交
1122
                foreach (ISymUnmanagedMethod method in _pdbReader.SymbolReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1123
                {
B
beep boop 已提交
1124
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1125

1126
                    WriteMethodAttributes(method.GetToken(), isReference: true);
P
Pilchie 已提交
1127

1128
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1129
                    {
B
beep boop 已提交
1130
                        _writer.WriteStartElement("document");
1131

P
Pilchie 已提交
1132
                        int startLine, endLine;
1133
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1134

B
beep boop 已提交
1135 1136
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1137

B
beep boop 已提交
1138
                        _writer.WriteEndElement();
P
Pilchie 已提交
1139 1140
                    }

B
beep boop 已提交
1141
                    _writer.WriteEndElement();
P
Pilchie 已提交
1142 1143 1144
                }
            }

B
beep boop 已提交
1145
            _writer.WriteEndElement();
P
Pilchie 已提交
1146 1147 1148 1149 1150
        }

        // Write out a reference to the entry point method (if one exists)
        private void WriteEntryPoint()
        {
B
beep boop 已提交
1151
            int token = _pdbReader.SymbolReader.GetUserEntryPoint();
1152
            if (token != 0)
P
Pilchie 已提交
1153
            {
B
beep boop 已提交
1154
                _writer.WriteStartElement("entryPoint");
1155
                WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
1156
                _writer.WriteEndElement();
P
Pilchie 已提交
1157 1158 1159 1160 1161 1162
            }
        }

        // Write out XML snippet to refer to the given method.
        private void WriteMethodAttributes(int token, bool isReference)
        {
B
beep boop 已提交
1163
            if ((_options & PdbToXmlOptions.ResolveTokens) != 0)
P
Pilchie 已提交
1164 1165 1166 1167 1168
            {
                var handle = MetadataTokens.Handle(token);

                try
                {
A
angocke 已提交
1169
                    switch (handle.Kind)
P
Pilchie 已提交
1170
                    {
A
angocke 已提交
1171 1172
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1173 1174
                            break;

A
angocke 已提交
1175
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1176 1177 1178 1179 1180
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

                        default:
                            WriteToken(token);
B
beep boop 已提交
1181
                            _writer.WriteAttributeString("error", string.Format("Unexpected token type: {0}", handle.Kind));
P
Pilchie 已提交
1182 1183 1184 1185 1186
                            break;
                    }
                }
                catch (BadImageFormatException e) // TODO: filter
                {
B
beep boop 已提交
1187
                    if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
1188 1189 1190 1191 1192
                    {
                        throw;
                    }

                    WriteToken(token);
B
beep boop 已提交
1193
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1194 1195 1196
                }
            }

B
beep boop 已提交
1197
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1198 1199 1200 1201 1202
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1203
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1204
        {
A
angocke 已提交
1205 1206
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1207 1208 1209 1210 1211 1212 1213

            string fullTypeName = GetFullTypeName(metadataReader, containingTypeHandle);
            string methodName = metadataReader.GetString(method.Name);

            return fullTypeName != null ? fullTypeName + "." + methodName : methodName;
        }

A
angocke 已提交
1214
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1215
        {
B
beep boop 已提交
1216
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1217 1218

            // type name
A
angocke 已提交
1219
            var containingTypeHandle = method.GetDeclaringType();
B
beep boop 已提交
1220
            var fullName = GetFullTypeName(_metadataReader, containingTypeHandle);
P
Pilchie 已提交
1221 1222
            if (fullName != null)
            {
B
beep boop 已提交
1223
                _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName);
P
Pilchie 已提交
1224 1225 1226
            }

            // method name
B
beep boop 已提交
1227
            _writer.WriteAttributeString(isReference ? "methodName" : "name", _metadataReader.GetString(method.Name));
P
Pilchie 已提交
1228 1229

            // parameters:
T
TomasMatousek 已提交
1230
            var parameterNames = (from paramHandle in method.GetParameters()
B
beep boop 已提交
1231
                                  let parameter = _metadataReader.GetParameter(paramHandle)
T
TomasMatousek 已提交
1232
                                  where parameter.SequenceNumber > 0 // exclude return parameter
B
beep boop 已提交
1233
                                  select parameter.Name.IsNil ? "?" : _metadataReader.GetString(parameter.Name)).ToArray();
P
Pilchie 已提交
1234

T
TomasMatousek 已提交
1235 1236
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1237
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1238
            }
P
Pilchie 已提交
1239 1240 1241 1242
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1243
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1244 1245

            // type name
B
beep boop 已提交
1246
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1247 1248
            if (fullName != null)
            {
B
beep boop 已提交
1249
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1250 1251 1252
            }

            // method name
B
beep boop 已提交
1253
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267
        }

        private static bool IsNested(TypeAttributes flags)
        {
            return (flags & ((TypeAttributes)0x00000006)) != 0;
        }

        private static string GetFullTypeName(MetadataReader metadataReader, Handle handle)
        {
            if (handle.IsNil)
            {
                return null;
            }

A
angocke 已提交
1268
            if (handle.Kind == HandleKind.TypeDefinition)
P
Pilchie 已提交
1269
            {
A
angocke 已提交
1270
                var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle);
P
Pilchie 已提交
1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287
                string name = metadataReader.GetString(type.Name);

                while (IsNested(type.Attributes))
                {
                    var enclosingType = metadataReader.GetTypeDefinition(type.GetDeclaringType());
                    name = metadataReader.GetString(enclosingType.Name) + "+" + name;
                    type = enclosingType;
                }

                if (type.Namespace.IsNil)
                {
                    return name;
                }

                return metadataReader.GetString(type.Namespace) + "." + name;
            }

A
angocke 已提交
1288
            if (handle.Kind == HandleKind.TypeReference)
P
Pilchie 已提交
1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
            {
                var typeRef = metadataReader.GetTypeReference((TypeReferenceHandle)handle);
                string name = metadataReader.GetString(typeRef.Name);
                if (typeRef.Namespace.IsNil)
                {
                    return name;
                }

                return metadataReader.GetString(typeRef.Namespace) + "." + name;
            }

            return string.Format("<unexpected token kind: {0}>", AsToken(metadataReader.GetToken(handle)));
        }

        #region Utils

        private void WriteToken(int token)
        {
B
beep boop 已提交
1307
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1308 1309 1310 1311
        }

        internal static string AsToken(int i)
        {
1312
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1313 1314
        }

1315
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1316
        {
1317
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 1333
        }

        internal static string CultureInvariantToString(int input)
        {
            return input.ToString(CultureInfo.InvariantCulture);
        }

        internal static void Error(string message)
        {
            Console.WriteLine("Error: {0}", message);
            Debug.Assert(false, message);
        }

        #endregion
    }
}