PdbToXml.cs 53.0 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
        private readonly MetadataReader _metadataReader;
33
        private readonly ISymUnmanagedReader _symReader;
B
beep boop 已提交
34 35
        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
        private PdbToXmlConverter(XmlWriter writer, ISymUnmanagedReader symReader, MetadataReader metadataReader, PdbToXmlOptions options)
P
Pilchie 已提交
41
        {
42
            _symReader = symReader;
B
beep boop 已提交
43 44 45
            _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
        {
            Debug.Assert(pdbStream != null);
            Debug.Assert((options & PdbToXmlOptions.ResolveTokens) == 0 || metadataReaderOpt != null);

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

135
            using (SymReader symReader = new SymReader(pdbStream))
P
Pilchie 已提交
136
            {
137
                var converter = new PdbToXmlConverter(writer, symReader, metadataReaderOpt, options);
P
Pilchie 已提交
138 139 140

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

P
Pilchie 已提交
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
            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 已提交
161
        private void WriteRoot(IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
162
        {
B
beep boop 已提交
163
            _writer.WriteStartDocument();
P
Pilchie 已提交
164

B
beep boop 已提交
165
            _writer.WriteStartElement("symbols");
P
Pilchie 已提交
166 167 168 169 170

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

B
beep boop 已提交
171
            if ((_options & PdbToXmlOptions.IncludeMethodSpans) != 0)
P
Pilchie 已提交
172 173 174 175
            {
                WriteAllMethodSpans();
            }

B
beep boop 已提交
176
            _writer.WriteEndElement();
P
Pilchie 已提交
177 178 179
        }

        // Dump all of the methods in the given ISymbolReader to the XmlWriter provided in the ctor.
A
angocke 已提交
180
        private void WriteAllMethods(IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
181
        {
B
beep boop 已提交
182
            _writer.WriteStartElement("methods");
P
Pilchie 已提交
183 184 185 186 187 188

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

B
beep boop 已提交
189
            _writer.WriteEndElement();
P
Pilchie 已提交
190 191
        }

A
angocke 已提交
192
        private void WriteMethod(MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
193
        {
B
beep boop 已提交
194
            int token = _metadataReader.GetToken(methodHandle);
P
Pilchie 已提交
195

196 197
            byte[] cdi = _symReader.GetCustomDebugInfoBytes(token, methodVersion: 1);
            ISymUnmanagedMethod method = _symReader.GetMethod(token);
198
            if (cdi == null && method == null)
P
Pilchie 已提交
199 200 201 202 203
            {
                // no debug info for the method
                return;
            }

B
beep boop 已提交
204
            _writer.WriteStartElement("method");
P
Pilchie 已提交
205 206
            WriteMethodAttributes(token, isReference: false);

207
            if (cdi != null)
P
Pilchie 已提交
208
            {
209
                WriteCustomDebugInfo(cdi);
P
Pilchie 已提交
210 211
            }

212
            if (method != null)
P
Pilchie 已提交
213
            {
214
                WriteSequencePoints(method);
P
Pilchie 已提交
215 216 217 218

                // 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>>();
219
                WriteLocals(method, expectedSlotNames);
P
Pilchie 已提交
220

221
                var actualSlotNames = method.GetLocalVariableSlots();
P
Pilchie 已提交
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239

                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++;
                }

240
                ImmutableArray<ISymUnmanagedScope> children = method.GetRootScope().GetScopes();
P
Pilchie 已提交
241 242 243 244 245
                if (children.Length != 0)
                {
                    WriteScopes(children[0]);
                }

246
                WriteAsyncInfo(method);
P
Pilchie 已提交
247 248
            }

B
beep boop 已提交
249
            _writer.WriteEndElement(); // method
P
Pilchie 已提交
250 251 252 253 254 255 256 257
        }

        /// <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)
        {
258
            var records = CustomDebugInfoReader.GetCustomDebugInfoRecords(bytes).ToArray();
259

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

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

B
beep boop 已提交
303
            _writer.WriteEndElement(); //customDebugInfo
P
Pilchie 已提交
304 305 306 307 308 309 310
        }

        /// <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>
311
        private void WriteUnknownCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
312
        {
B
beep boop 已提交
313 314 315
            _writer.WriteStartElement("unknown");
            _writer.WriteAttributeString("kind", record.Kind.ToString());
            _writer.WriteAttributeString("version", record.Version.ToString());
P
Pilchie 已提交
316 317 318

            PooledStringBuilder pooled = PooledStringBuilder.GetInstance();
            StringBuilder builder = pooled.Builder;
319
            foreach (byte b in record.Data)
P
Pilchie 已提交
320 321 322
            {
                builder.AppendFormat("{0:X2}", b);
            }
323

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

B
beep boop 已提交
326
            _writer.WriteEndElement(); //unknown
P
Pilchie 已提交
327 328 329 330 331 332 333 334 335
        }

        /// <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>
336
        private void WriteUsingCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
337
        {
338
            Debug.Assert(record.Kind == CustomDebugInfoKind.UsingInfo);
P
Pilchie 已提交
339

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

342
            ImmutableArray<short> counts = CDI.DecodeUsingRecord(record.Data);
P
Pilchie 已提交
343 344 345

            foreach (short importCount in counts)
            {
B
beep boop 已提交
346 347 348
                _writer.WriteStartElement("namespace");
                _writer.WriteAttributeString("usingCount", importCount.ToString());
                _writer.WriteEndElement(); //namespace
P
Pilchie 已提交
349 350
            }

B
beep boop 已提交
351
            _writer.WriteEndElement(); //using
P
Pilchie 已提交
352 353 354 355 356 357 358 359 360
        }

        /// <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>
361
        private void WriteForwardCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
362
        {
363
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardInfo);
P
Pilchie 已提交
364

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

367
            int token = CDI.DecodeForwardRecord(record.Data);
P
Pilchie 已提交
368 369
            WriteMethodAttributes(token, isReference: true);

B
beep boop 已提交
370
            _writer.WriteEndElement(); //forward
P
Pilchie 已提交
371 372 373 374 375 376 377 378 379 380
        }

        /// <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>
381
        private void WriteForwardToModuleCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
382
        {
383
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardToModuleInfo);
P
Pilchie 已提交
384

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

387
            int token = CDI.DecodeForwardRecord(record.Data);
P
Pilchie 已提交
388 389
            WriteMethodAttributes(token, isReference: true);

B
beep boop 已提交
390
            _writer.WriteEndElement(); //forwardToModule
P
Pilchie 已提交
391 392 393 394 395 396 397 398 399 400
        }

        /// <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>
401
        private void WriteStatemachineHoistedLocalScopesCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
402
        {
403
            Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineHoistedLocalScopes);
P
Pilchie 已提交
404

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

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

409
            foreach (StateMachineHoistedLocalScope scope in scopes)
P
Pilchie 已提交
410
            {
B
beep boop 已提交
411 412 413 414
                _writer.WriteStartElement("slot");
                _writer.WriteAttributeString("startOffset", AsILOffset(scope.StartOffset));
                _writer.WriteAttributeString("endOffset", AsILOffset(scope.EndOffset));
                _writer.WriteEndElement(); //bucket
P
Pilchie 已提交
415 416
            }

B
beep boop 已提交
417
            _writer.WriteEndElement();
P
Pilchie 已提交
418 419 420 421 422 423 424 425 426
        }

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

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

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

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

B
beep boop 已提交
437
            _writer.WriteEndElement(); //forwardIterator
P
Pilchie 已提交
438 439 440 441 442 443 444 445 446
        }

        /// <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>
447
        private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
448
        {
449
            Debug.Assert(record.Kind == CustomDebugInfoKind.DynamicLocals);
P
Pilchie 已提交
450

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

453
            var buckets = CDI.DecodeDynamicLocalsRecord(record.Data);
P
Pilchie 已提交
454 455 456 457 458 459 460 461 462 463 464 465 466

            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 已提交
467 468 469 470 471 472
                _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 已提交
473 474
            }

B
beep boop 已提交
475
            _writer.WriteEndElement(); //dynamicLocals
P
Pilchie 已提交
476 477
        }

478
        private unsafe void WriteEditAndContinueLocalSlotMap(CustomDebugInfoRecord record)
479
        {
480
            Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLocalSlotMap);
481

B
beep boop 已提交
482
            _writer.WriteStartElement("encLocalSlotMap");
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498
            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 已提交
499
                                _writer.WriteElementString("baseline", "?");
500 501 502 503 504 505
                                return;
                            }

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
506

B
beep boop 已提交
507
                        _writer.WriteStartElement("slot");
508 509 510 511

                        if (b == 0)
                        {
                            // short-lived temp, no info
B
beep boop 已提交
512
                            _writer.WriteAttributeString("kind", "temp");
513 514 515 516 517
                        }
                        else
                        {
                            int synthesizedKind = (b & 0x3f) - 1;
                            bool hasOrdinal = (b & (1 << 7)) != 0;
518

519 520 521 522 523 524 525
                            int syntaxOffset;
                            bool badSyntaxOffset = !blobReader.TryReadCompressedInteger(out syntaxOffset);
                            syntaxOffset += syntaxOffsetBaseline;

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

B
beep boop 已提交
526 527
                            _writer.WriteAttributeString("kind", synthesizedKind.ToString());
                            _writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : syntaxOffset.ToString());
528 529 530

                            if (badOrdinal || hasOrdinal)
                            {
B
beep boop 已提交
531
                                _writer.WriteAttributeString("ordinal", badOrdinal ? "?" : ordinal.ToString());
532 533 534
                            }
                        }

B
beep boop 已提交
535
                        _writer.WriteEndElement();
536 537 538 539
                    }
                }
            }
            finally
540
            {
B
beep boop 已提交
541
                _writer.WriteEndElement(); //encLocalSlotMap
542 543 544 545 546 547
            }
        }

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

B
beep boop 已提交
549
            _writer.WriteStartElement("encLambdaMap");
550 551 552
            try
            {
                if (record.Data.Length == 0)
553
                {
554 555
                    return;
                }
556

557 558 559 560 561 562 563 564 565
                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))
566
                    {
B
beep boop 已提交
567 568
                        _writer.WriteElementString("methodOrdinal", "?");
                        _writer.WriteEndElement();
569
                        return;
570 571
                    }

572 573
                    // [-1, inf)
                    methodOrdinal--;
B
beep boop 已提交
574
                    _writer.WriteElementString("methodOrdinal", methodOrdinal.ToString());
575 576

                    if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline))
577
                    {
B
beep boop 已提交
578 579
                        _writer.WriteElementString("baseline", "?");
                        _writer.WriteEndElement();
580
                        return;
581 582
                    }

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

                    for (int i = 0; i < closureCount; i++)
592
                    {
B
beep boop 已提交
593
                        _writer.WriteStartElement("closure");
594 595 596 597 598
                        try
                        {
                            int syntaxOffset;
                            if (!blobReader.TryReadCompressedInteger(out syntaxOffset))
                            {
B
beep boop 已提交
599
                                _writer.WriteElementString("offset", "?");
600 601 602
                                break;
                            }

B
beep boop 已提交
603
                            _writer.WriteAttributeString("offset", (syntaxOffset + syntaxOffsetBaseline).ToString());
604 605 606
                        }
                        finally
                        {
B
beep boop 已提交
607
                            _writer.WriteEndElement();
608 609
                        }
                    }
610

611 612
                    while (blobReader.RemainingBytes > 0)
                    {
B
beep boop 已提交
613
                        _writer.WriteStartElement("lambda");
614 615 616 617 618
                        try
                        {
                            int syntaxOffset;
                            if (!blobReader.TryReadCompressedInteger(out syntaxOffset))
                            {
B
beep boop 已提交
619
                                _writer.WriteElementString("offset", "?");
620 621
                                return;
                            }
622

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

625 626 627
                            int closureOrdinal;
                            if (!blobReader.TryReadCompressedInteger(out closureOrdinal))
                            {
B
beep boop 已提交
628
                                _writer.WriteElementString("closure", "?");
629 630
                                return;
                            }
631

632 633 634 635 636 637 638
                            closureOrdinal -= 2;

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

P
Pilchie 已提交
657 658
        private void WriteScopes(ISymUnmanagedScope scope)
        {
B
beep boop 已提交
659
            _writer.WriteStartElement("scope");
P
Pilchie 已提交
660
            {
B
beep boop 已提交
661 662
                _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset()));
                _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset()));
P
Pilchie 已提交
663 664 665 666 667 668 669 670 671 672 673 674 675
                {
                    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 已提交
676
            _writer.WriteEndElement(); // </scope>
P
Pilchie 已提交
677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693
        }

        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;
694 695
                    var parsingSucceeded = CDI.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope);
                    Debug.Assert(parsingSucceeded);
P
Pilchie 已提交
696 697 698 699 700 701 702 703 704 705
                }
                else
                {
                    switch (rawName[0])
                    {
                        case 'U':
                        case 'A':
                        case 'X':
                        case 'Z':
                        case 'E':
706
                        case 'T':
P
Pilchie 已提交
707
                            scope = ImportScope.Unspecified;
708 709 710 711
                            if (!CDI.TryParseCSharpImportString(rawName, out alias, out externAlias, out target, out kind))
                            {
                                throw new InvalidOperationException(string.Format("Invalid import '{0}'", rawName));
                            }
P
Pilchie 已提交
712 713 714 715
                            break;

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

B
beep boop 已提交
731 732 733
                _writer.WriteStartElement("invalid-custom-data");
                _writer.WriteAttributeString("raw", rawName);
                _writer.WriteEndElement();
P
Pilchie 已提交
734 735 736 737 738 739 740 741 742
                return;
            }

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

        private void WriteScopeAttribute(ImportScope scope)
        {
            if (scope == ImportScope.File)
            {
B
beep boop 已提交
858
                _writer.WriteAttributeString("importlevel", "file");
P
Pilchie 已提交
859 860 861
            }
            else if (scope == ImportScope.Project)
            {
B
beep boop 已提交
862
                _writer.WriteAttributeString("importlevel", "project");
P
Pilchie 已提交
863 864 865 866 867 868 869
            }
            else
            {
                Debug.Assert(scope == ImportScope.Unspecified, "Unexpected scope '" + scope + "'");
            }
        }

870
        private void WriteAsyncInfo(ISymUnmanagedMethod method)
P
Pilchie 已提交
871
        {
872 873
            var asyncMethod = method.AsAsync();
            if (asyncMethod == null)
P
Pilchie 已提交
874
            {
875 876
                return;
            }
877

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

880 881 882
            var catchOffset = asyncMethod.GetCatchHandlerILOffset();
            if (catchOffset >= 0)
            {
B
beep boop 已提交
883 884 885
                _writer.WriteStartElement("catchHandler");
                _writer.WriteAttributeString("offset", AsILOffset(catchOffset));
                _writer.WriteEndElement();
P
Pilchie 已提交
886 887
            }

B
beep boop 已提交
888
            _writer.WriteStartElement("kickoffMethod");
889
            WriteMethodAttributes(asyncMethod.GetKickoffMethod(), isReference: true);
B
beep boop 已提交
890
            _writer.WriteEndElement();
891 892

            foreach (var info in asyncMethod.GetAsyncStepInfos())
P
Pilchie 已提交
893
            {
B
beep boop 已提交
894 895 896
                _writer.WriteStartElement("await");
                _writer.WriteAttributeString("yield", AsILOffset(info.YieldOffset));
                _writer.WriteAttributeString("resume", AsILOffset(info.ResumeOffset));
897
                WriteMethodAttributes(info.ResumeMethod, isReference: true);
B
beep boop 已提交
898
                _writer.WriteEndElement();
P
Pilchie 已提交
899 900
            }

B
beep boop 已提交
901
            _writer.WriteEndElement();
P
Pilchie 已提交
902 903 904 905 906 907 908 909 910 911
        }

        // 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 已提交
912
            _writer.WriteStartElement("locals");
913 914
            // If there are no locals, then this element will just be empty.
            WriteLocalsHelper(method.GetRootScope(), slotNames, includeChildScopes: true);
B
beep boop 已提交
915
            _writer.WriteEndElement();
P
Pilchie 已提交
916 917
        }

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

                    // 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.
934
                    int slot = l.GetSlot();
B
beep boop 已提交
935
                    _writer.WriteAttributeString("il_index", CultureInvariantToString(slot));
P
Pilchie 已提交
936 937 938 939 940 941 942 943 944

                    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))
                        {
945
                            slotNames[slot] = existingNames.Add(l.GetName());
P
Pilchie 已提交
946 947 948 949
                            reusingSlot = true;
                        }
                        else
                        {
950
                            slotNames.Add(slot, ImmutableArray.Create(l.GetName()));
P
Pilchie 已提交
951 952 953 954
                        }
                    }

                    // Provide scope range
B
beep boop 已提交
955 956 957
                    _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset()));
                    _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset()));
                    _writer.WriteAttributeString("attributes", l.GetAttributes().ToString());
P
Pilchie 已提交
958 959 960

                    if (reusingSlot)
                    {
B
beep boop 已提交
961
                        _writer.WriteAttributeString("reusingslot", reusingSlot.ToString(CultureInfo.InvariantCulture));
P
Pilchie 已提交
962 963
                    }
                }
B
beep boop 已提交
964
                _writer.WriteEndElement(); // </local>
P
Pilchie 已提交
965
            }
966

967
            foreach (ISymUnmanagedConstant c in scope.GetConstants())
P
Pilchie 已提交
968 969 970
            {
                // Note: We can retrieve constant tokens by saving it into signature blob
                // in our implementation of IMetadataImport.GetSigFromToken.
B
beep boop 已提交
971
                _writer.WriteStartElement("constant");
P
Pilchie 已提交
972
                {
B
beep boop 已提交
973
                    _writer.WriteAttributeString("name", c.GetName());
974

P
Pilchie 已提交
975 976 977 978
                    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
979
                    // problematic characters/sequences with their hexadecimal equivalents, like U+0000, etc...
P
Pilchie 已提交
980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003
                    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 已提交
1004 1005
                    _writer.WriteAttributeString("value", value.ToString());
                    _writer.WriteAttributeString("type", typeName);
P
Pilchie 已提交
1006
                }
B
beep boop 已提交
1007
                _writer.WriteEndElement(); // </constant>
P
Pilchie 已提交
1008
            }
1009

P
Pilchie 已提交
1010 1011
            if (includeChildScopes)
            {
1012
                foreach (ISymUnmanagedScope childScope in scope.GetScopes())
P
Pilchie 已提交
1013 1014 1015 1016 1017 1018 1019 1020 1021
                {
                    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).        
1022
        private void WriteSequencePoints(ISymUnmanagedMethod method)
P
Pilchie 已提交
1023
        {
B
beep boop 已提交
1024
            _writer.WriteStartElement("sequencePoints");
P
Pilchie 已提交
1025

1026
            var sequencePoints = method.GetSequencePoints();
1027

P
Pilchie 已提交
1028
            // Write out sequence points
1029
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1030
            {
B
beep boop 已提交
1031 1032
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1033

1034
                if (sequencePoint.IsHidden)
P
Pilchie 已提交
1035
                {
T
TomasMatousek 已提交
1036 1037
                    if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0)
                    {
B
beep boop 已提交
1038
                        _writer.WriteAttributeString("hidden", "invalid");
T
TomasMatousek 已提交
1039 1040 1041
                    }
                    else
                    {
B
beep boop 已提交
1042
                        _writer.WriteAttributeString("hidden", XmlConvert.ToString(true));
T
TomasMatousek 已提交
1043 1044 1045 1046
                    }
                }
                else
                {
B
beep boop 已提交
1047 1048 1049 1050
                    _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 已提交
1051 1052
                }

1053
                int documentId;
B
beep boop 已提交
1054 1055
                _fileMapping.TryGetValue(sequencePoint.Document.GetName(), out documentId);
                _writer.WriteAttributeString("document", CultureInvariantToString(documentId));
P
Pilchie 已提交
1056

B
beep boop 已提交
1057
                _writer.WriteEndElement();
P
Pilchie 已提交
1058 1059
            }

B
beep boop 已提交
1060
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1061 1062 1063 1064 1065 1066
        }

        // Write all docs, and add to the m_fileMapping list.
        // Other references to docs will then just refer to this list.
        private void WriteDocList()
        {
1067
            var documents = _symReader.GetDocuments();
1068
            if (documents.Length == 0)
P
Pilchie 已提交
1069 1070 1071 1072 1073
            {
                return;
            }

            int id = 0;
B
beep boop 已提交
1074
            _writer.WriteStartElement("files");
1075
            foreach (ISymUnmanagedDocument doc in documents)
P
Pilchie 已提交
1076
            {
1077
                string name = doc.GetName();
P
Pilchie 已提交
1078 1079

                // Symbol store may give out duplicate documents. We'll fold them here
B
beep boop 已提交
1080
                if (_fileMapping.ContainsKey(name))
P
Pilchie 已提交
1081
                {
B
beep boop 已提交
1082
                    _writer.WriteComment("There is a duplicate entry for: " + name);
P
Pilchie 已提交
1083 1084 1085 1086
                    continue;
                }

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

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

B
beep boop 已提交
1091 1092 1093 1094 1095
                _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 已提交
1096

1097
                var checkSum = string.Concat(doc.GetChecksum().Select(b => string.Format("{0,2:X}", b) + ", "));
1098 1099 1100

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1101 1102
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1103 1104
                }

B
beep boop 已提交
1105
                _writer.WriteEndElement(); // file
P
Pilchie 已提交
1106
            }
B
beep boop 已提交
1107
            _writer.WriteEndElement(); // files
P
Pilchie 已提交
1108 1109 1110 1111
        }

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

1114
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1115
            {
1116
                foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1117
                {
B
beep boop 已提交
1118
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1119

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

1122
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1123
                    {
B
beep boop 已提交
1124
                        _writer.WriteStartElement("document");
1125

P
Pilchie 已提交
1126
                        int startLine, endLine;
1127
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1128

B
beep boop 已提交
1129 1130
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1131

B
beep boop 已提交
1132
                        _writer.WriteEndElement();
P
Pilchie 已提交
1133 1134
                    }

B
beep boop 已提交
1135
                    _writer.WriteEndElement();
P
Pilchie 已提交
1136 1137 1138
                }
            }

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

        // Write out a reference to the entry point method (if one exists)
        private void WriteEntryPoint()
        {
1145
            int token = _symReader.GetUserEntryPoint();
1146
            if (token != 0)
P
Pilchie 已提交
1147
            {
B
beep boop 已提交
1148
                _writer.WriteStartElement("entryPoint");
1149
                WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
1150
                _writer.WriteEndElement();
P
Pilchie 已提交
1151 1152 1153 1154 1155 1156
            }
        }

        // Write out XML snippet to refer to the given method.
        private void WriteMethodAttributes(int token, bool isReference)
        {
B
beep boop 已提交
1157
            if ((_options & PdbToXmlOptions.ResolveTokens) != 0)
P
Pilchie 已提交
1158 1159 1160 1161 1162
            {
                var handle = MetadataTokens.Handle(token);

                try
                {
A
angocke 已提交
1163
                    switch (handle.Kind)
P
Pilchie 已提交
1164
                    {
A
angocke 已提交
1165 1166
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1167 1168
                            break;

A
angocke 已提交
1169
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1170 1171 1172 1173 1174
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

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

                    WriteToken(token);
B
beep boop 已提交
1187
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1188 1189 1190
                }
            }

B
beep boop 已提交
1191
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1192 1193 1194 1195 1196
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1197
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1198
        {
A
angocke 已提交
1199 1200
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1201 1202 1203 1204 1205 1206 1207

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

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

A
angocke 已提交
1208
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1209
        {
B
beep boop 已提交
1210
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1211 1212

            // type name
A
angocke 已提交
1213
            var containingTypeHandle = method.GetDeclaringType();
B
beep boop 已提交
1214
            var fullName = GetFullTypeName(_metadataReader, containingTypeHandle);
P
Pilchie 已提交
1215 1216
            if (fullName != null)
            {
B
beep boop 已提交
1217
                _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName);
P
Pilchie 已提交
1218 1219 1220
            }

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

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

T
TomasMatousek 已提交
1229 1230
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1231
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1232
            }
P
Pilchie 已提交
1233 1234 1235 1236
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1237
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1238 1239

            // type name
B
beep boop 已提交
1240
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1241 1242
            if (fullName != null)
            {
B
beep boop 已提交
1243
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1244 1245 1246
            }

            // method name
B
beep boop 已提交
1247
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261
        }

        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 已提交
1262
            if (handle.Kind == HandleKind.TypeDefinition)
P
Pilchie 已提交
1263
            {
A
angocke 已提交
1264
                var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle);
P
Pilchie 已提交
1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281
                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 已提交
1282
            if (handle.Kind == HandleKind.TypeReference)
P
Pilchie 已提交
1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300
            {
                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 已提交
1301
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1302 1303 1304 1305
        }

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

1309
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1310
        {
1311
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327
        }

        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
    }
}