PdbToXml.cs 53.7 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
using Microsoft.CodeAnalysis;
using Microsoft.DiaSymReader;
T
Tomas Matousek 已提交
18
using Roslyn.Utilities;
19
using CDI = Microsoft.CodeAnalysis.CustomDebugInfoReader;
20
using CDIC = Microsoft.Cci.CustomDebugInfoConstants;
P
Pilchie 已提交
21 22 23 24 25 26 27 28 29 30 31 32
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 已提交
33
        private readonly MetadataReader _metadataReader;
34
        private readonly ISymUnmanagedReader _symReader;
B
beep boop 已提交
35 36
        private readonly PdbToXmlOptions _options;
        private readonly XmlWriter _writer;
P
Pilchie 已提交
37 38

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

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

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

            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 已提交
78
            IEnumerable<MethodDefinitionHandle> methodHandles;
P
Pilchie 已提交
79 80 81 82 83 84 85
            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 已提交
86
                var metadataReader = new MetadataReader(metadataPtr, metadata.Length);
P
Pilchie 已提交
87 88 89 90 91 92 93

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

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

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

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

                        return;
                    }

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

                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 已提交
128
        private static void ToXml(TextWriter xmlWriter, Stream pdbStream, MetadataReader metadataReaderOpt, PdbToXmlOptions options, IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
129 130 131 132 133 134 135
        {
            Debug.Assert(pdbStream != null);
            Debug.Assert((options & PdbToXmlOptions.ResolveTokens) == 0 || metadataReaderOpt != null);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

217
                var rootScope = method.GetRootScope();
P
Pilchie 已提交
218

219 220 221
                // C# and VB compilers leave the root scope empty and put outermost lexical scope in it.
                // Don't display such empty root scope.
                if (rootScope.GetNamespaces().IsEmpty && rootScope.GetLocals().IsEmpty && rootScope.GetConstants().IsEmpty)
P
Pilchie 已提交
222
                {
223
                    foreach (ISymUnmanagedScope child in rootScope.GetScopes())
P
Pilchie 已提交
224
                    {
225
                        WriteScope(child, isRoot: false);
P
Pilchie 已提交
226 227
                    }
                }
228
                else
P
Pilchie 已提交
229
                {
230
                    WriteScope(rootScope, isRoot: true);
P
Pilchie 已提交
231 232
                }

233
                WriteAsyncInfo(method);
P
Pilchie 已提交
234 235
            }

B
beep boop 已提交
236
            _writer.WriteEndElement(); // method
P
Pilchie 已提交
237 238 239 240 241 242 243 244
        }

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

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

249
            foreach (var record in records)
P
Pilchie 已提交
250
            {
251
                if (record.Version != CDIC.CdiVersion)
P
Pilchie 已提交
252
                {
253
                    WriteUnknownCustomDebugInfo(record);
P
Pilchie 已提交
254 255 256
                }
                else
                {
257
                    switch (record.Kind)
P
Pilchie 已提交
258 259
                    {
                        case CustomDebugInfoKind.UsingInfo:
260
                            WriteUsingCustomDebugInfo(record);
P
Pilchie 已提交
261 262
                            break;
                        case CustomDebugInfoKind.ForwardInfo:
263
                            WriteForwardCustomDebugInfo(record);
P
Pilchie 已提交
264 265
                            break;
                        case CustomDebugInfoKind.ForwardToModuleInfo:
266
                            WriteForwardToModuleCustomDebugInfo(record);
P
Pilchie 已提交
267
                            break;
268
                        case CustomDebugInfoKind.StateMachineHoistedLocalScopes:
269
                            WriteStatemachineHoistedLocalScopesCustomDebugInfo(record);
P
Pilchie 已提交
270 271
                            break;
                        case CustomDebugInfoKind.ForwardIterator:
272
                            WriteForwardIteratorCustomDebugInfo(record);
P
Pilchie 已提交
273 274
                            break;
                        case CustomDebugInfoKind.DynamicLocals:
275
                            WriteDynamicLocalsCustomDebugInfo(record);
P
Pilchie 已提交
276
                            break;
277
                        case CustomDebugInfoKind.EditAndContinueLocalSlotMap:
278
                            WriteEditAndContinueLocalSlotMap(record);
279
                            break;
280 281 282
                        case CustomDebugInfoKind.EditAndContinueLambdaMap:
                            WriteEditAndContinueLambdaMap(record);
                            break;
P
Pilchie 已提交
283
                        default:
284
                            WriteUnknownCustomDebugInfo(record);
P
Pilchie 已提交
285 286 287 288 289
                            break;
                    }
                }
            }

B
beep boop 已提交
290
            _writer.WriteEndElement(); //customDebugInfo
P
Pilchie 已提交
291 292 293 294 295 296 297
        }

        /// <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>
298
        private void WriteUnknownCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
299
        {
B
beep boop 已提交
300 301
            _writer.WriteStartElement("unknown");
            _writer.WriteAttributeString("kind", record.Kind.ToString());
T
Tomas Matousek 已提交
302
            _writer.WriteAttributeString("version", CultureInvariantToString(record.Version));
P
Pilchie 已提交
303 304 305

            PooledStringBuilder pooled = PooledStringBuilder.GetInstance();
            StringBuilder builder = pooled.Builder;
306
            foreach (byte b in record.Data)
P
Pilchie 已提交
307 308 309
            {
                builder.AppendFormat("{0:X2}", b);
            }
310

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

B
beep boop 已提交
313
            _writer.WriteEndElement(); //unknown
P
Pilchie 已提交
314 315 316 317 318 319 320 321 322
        }

        /// <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>
323
        private void WriteUsingCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
324
        {
325
            Debug.Assert(record.Kind == CustomDebugInfoKind.UsingInfo);
P
Pilchie 已提交
326

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

329
            ImmutableArray<short> counts = CDI.DecodeUsingRecord(record.Data);
P
Pilchie 已提交
330 331 332

            foreach (short importCount in counts)
            {
B
beep boop 已提交
333
                _writer.WriteStartElement("namespace");
T
Tomas Matousek 已提交
334
                _writer.WriteAttributeString("usingCount", CultureInvariantToString(importCount));
B
beep boop 已提交
335
                _writer.WriteEndElement(); //namespace
P
Pilchie 已提交
336 337
            }

B
beep boop 已提交
338
            _writer.WriteEndElement(); //using
P
Pilchie 已提交
339 340 341 342 343 344 345 346 347
        }

        /// <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>
348
        private void WriteForwardCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
349
        {
350
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardInfo);
P
Pilchie 已提交
351

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

354
            int token = CDI.DecodeForwardRecord(record.Data);
P
Pilchie 已提交
355 356
            WriteMethodAttributes(token, isReference: true);

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

        /// <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>
368
        private void WriteForwardToModuleCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
369
        {
370
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardToModuleInfo);
P
Pilchie 已提交
371

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

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

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

        /// <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>
388
        private void WriteStatemachineHoistedLocalScopesCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
389
        {
390
            Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineHoistedLocalScopes);
P
Pilchie 已提交
391

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

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

396
            foreach (StateMachineHoistedLocalScope scope in scopes)
P
Pilchie 已提交
397
            {
B
beep boop 已提交
398 399 400 401
                _writer.WriteStartElement("slot");
                _writer.WriteAttributeString("startOffset", AsILOffset(scope.StartOffset));
                _writer.WriteAttributeString("endOffset", AsILOffset(scope.EndOffset));
                _writer.WriteEndElement(); //bucket
P
Pilchie 已提交
402 403
            }

B
beep boop 已提交
404
            _writer.WriteEndElement();
P
Pilchie 已提交
405 406 407 408 409 410 411 412 413
        }

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

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

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

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

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

        /// <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>
434
        private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
435
        {
436
            Debug.Assert(record.Kind == CustomDebugInfoKind.DynamicLocals);
P
Pilchie 已提交
437

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

440
            var buckets = CDI.DecodeDynamicLocalsRecord(record.Data);
P
Pilchie 已提交
441 442 443 444 445 446 447 448 449 450 451 452 453

            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 已提交
454
                _writer.WriteStartElement("bucket");
T
Tomas Matousek 已提交
455
                _writer.WriteAttributeString("flagCount", CultureInvariantToString(flagCount));
B
beep boop 已提交
456
                _writer.WriteAttributeString("flags", pooled.ToStringAndFree());
T
Tomas Matousek 已提交
457
                _writer.WriteAttributeString("slotId", CultureInvariantToString(bucket.SlotId));
B
beep boop 已提交
458 459
                _writer.WriteAttributeString("localName", bucket.Name);
                _writer.WriteEndElement(); //bucket
P
Pilchie 已提交
460 461
            }

B
beep boop 已提交
462
            _writer.WriteEndElement(); //dynamicLocals
P
Pilchie 已提交
463 464
        }

465
        private unsafe void WriteEditAndContinueLocalSlotMap(CustomDebugInfoRecord record)
466
        {
467
            Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLocalSlotMap);
468

B
beep boop 已提交
469
            _writer.WriteStartElement("encLocalSlotMap");
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485
            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 已提交
486
                                _writer.WriteElementString("baseline", "?");
487 488 489 490 491 492
                                return;
                            }

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
493

B
beep boop 已提交
494
                        _writer.WriteStartElement("slot");
495 496 497 498

                        if (b == 0)
                        {
                            // short-lived temp, no info
B
beep boop 已提交
499
                            _writer.WriteAttributeString("kind", "temp");
500 501 502 503 504
                        }
                        else
                        {
                            int synthesizedKind = (b & 0x3f) - 1;
                            bool hasOrdinal = (b & (1 << 7)) != 0;
505

506 507 508 509 510 511 512
                            int syntaxOffset;
                            bool badSyntaxOffset = !blobReader.TryReadCompressedInteger(out syntaxOffset);
                            syntaxOffset += syntaxOffsetBaseline;

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

T
Tomas Matousek 已提交
513 514
                            _writer.WriteAttributeString("kind", CultureInvariantToString(synthesizedKind));
                            _writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : CultureInvariantToString(syntaxOffset));
515 516 517

                            if (badOrdinal || hasOrdinal)
                            {
T
Tomas Matousek 已提交
518
                                _writer.WriteAttributeString("ordinal", badOrdinal ? "?" : CultureInvariantToString(ordinal));
519 520 521
                            }
                        }

B
beep boop 已提交
522
                        _writer.WriteEndElement();
523 524 525 526
                    }
                }
            }
            finally
527
            {
B
beep boop 已提交
528
                _writer.WriteEndElement(); //encLocalSlotMap
529 530 531 532 533 534
            }
        }

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

B
beep boop 已提交
536
            _writer.WriteStartElement("encLambdaMap");
537 538 539
            try
            {
                if (record.Data.Length == 0)
540
                {
541 542
                    return;
                }
543

544 545 546 547 548 549 550 551 552
                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))
553
                    {
B
beep boop 已提交
554 555
                        _writer.WriteElementString("methodOrdinal", "?");
                        _writer.WriteEndElement();
556
                        return;
557 558
                    }

559 560
                    // [-1, inf)
                    methodOrdinal--;
T
Tomas Matousek 已提交
561
                    _writer.WriteElementString("methodOrdinal", CultureInvariantToString(methodOrdinal));
562 563

                    if (!blobReader.TryReadCompressedInteger(out syntaxOffsetBaseline))
564
                    {
B
beep boop 已提交
565 566
                        _writer.WriteElementString("baseline", "?");
                        _writer.WriteEndElement();
567
                        return;
568 569
                    }

570 571
                    syntaxOffsetBaseline = -syntaxOffsetBaseline;
                    if (!blobReader.TryReadCompressedInteger(out closureCount))
572
                    {
B
beep boop 已提交
573 574
                        _writer.WriteElementString("closureCount", "?");
                        _writer.WriteEndElement();
575
                        return;
576
                    }
577 578

                    for (int i = 0; i < closureCount; i++)
579
                    {
B
beep boop 已提交
580
                        _writer.WriteStartElement("closure");
581 582 583 584 585
                        try
                        {
                            int syntaxOffset;
                            if (!blobReader.TryReadCompressedInteger(out syntaxOffset))
                            {
B
beep boop 已提交
586
                                _writer.WriteElementString("offset", "?");
587 588 589
                                break;
                            }

T
Tomas Matousek 已提交
590
                            _writer.WriteAttributeString("offset", CultureInvariantToString(syntaxOffset + syntaxOffsetBaseline));
591 592 593
                        }
                        finally
                        {
B
beep boop 已提交
594
                            _writer.WriteEndElement();
595 596
                        }
                    }
597

598 599
                    while (blobReader.RemainingBytes > 0)
                    {
B
beep boop 已提交
600
                        _writer.WriteStartElement("lambda");
601 602 603 604 605
                        try
                        {
                            int syntaxOffset;
                            if (!blobReader.TryReadCompressedInteger(out syntaxOffset))
                            {
B
beep boop 已提交
606
                                _writer.WriteElementString("offset", "?");
607 608
                                return;
                            }
609

T
Tomas Matousek 已提交
610
                            _writer.WriteAttributeString("offset", CultureInvariantToString(syntaxOffset + syntaxOffsetBaseline));
611

612 613 614
                            int closureOrdinal;
                            if (!blobReader.TryReadCompressedInteger(out closureOrdinal))
                            {
B
beep boop 已提交
615
                                _writer.WriteElementString("closure", "?");
616 617
                                return;
                            }
618

619 620 621 622 623 624 625
                            closureOrdinal -= 2;

                            if (closureOrdinal == -2)
                            {
                                _writer.WriteAttributeString("closure", "this");
                            }
                            else if (closureOrdinal != -1)
626
                            {
B
beep boop 已提交
627
                                _writer.WriteAttributeString("closure",
T
Tomas Matousek 已提交
628
                                    CultureInvariantToString(closureOrdinal) + (closureOrdinal >= closureCount ? " (invalid)" : ""));
629 630 631
                            }
                        }
                        finally
632
                        {
B
beep boop 已提交
633
                            _writer.WriteEndElement();
634 635 636 637
                        }
                    }
                }
            }
638 639
            finally
            {
B
beep boop 已提交
640
                _writer.WriteEndElement(); //encLocalSlotMap
641
            }
642 643
        }

644
        private void WriteScope(ISymUnmanagedScope scope, bool isRoot)
P
Pilchie 已提交
645
        {
646 647 648 649 650
            _writer.WriteStartElement(isRoot ? "rootScope" : "scope");
            _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset()));
            _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset()));

            foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces())
P
Pilchie 已提交
651
            {
652 653
                WriteNamespace(@namespace);
            }
P
Pilchie 已提交
654

655 656 657 658 659
            WriteLocals(scope);

            foreach (ISymUnmanagedScope child in scope.GetScopes())
            {
                WriteScope(child, isRoot: false);
P
Pilchie 已提交
660
            }
661 662

            _writer.WriteEndElement(); 
P
Pilchie 已提交
663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679
        }

        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;
680 681
                    var parsingSucceeded = CDI.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope);
                    Debug.Assert(parsingSucceeded);
P
Pilchie 已提交
682 683 684 685 686 687 688 689 690 691
                }
                else
                {
                    switch (rawName[0])
                    {
                        case 'U':
                        case 'A':
                        case 'X':
                        case 'Z':
                        case 'E':
692
                        case 'T':
P
Pilchie 已提交
693
                            scope = ImportScope.Unspecified;
694 695 696 697
                            if (!CDI.TryParseCSharpImportString(rawName, out alias, out externAlias, out target, out kind))
                            {
                                throw new InvalidOperationException(string.Format("Invalid import '{0}'", rawName));
                            }
P
Pilchie 已提交
698 699 700 701
                            break;

                        default:
                            externAlias = null;
702 703 704 705
                            if (!CDI.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope))
                            {
                                throw new InvalidOperationException(string.Format("Invalid import '{0}'", rawName));
                            }
P
Pilchie 已提交
706 707 708 709 710 711
                            break;
                    }
                }
            }
            catch (ArgumentException) // TODO: filter
            {
B
beep boop 已提交
712
                if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
713 714 715 716
                {
                    throw;
                }

B
beep boop 已提交
717 718 719
                _writer.WriteStartElement("invalid-custom-data");
                _writer.WriteAttributeString("raw", rawName);
                _writer.WriteEndElement();
P
Pilchie 已提交
720 721 722 723 724 725 726 727 728
                return;
            }

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

        private void WriteScopeAttribute(ImportScope scope)
        {
            if (scope == ImportScope.File)
            {
B
beep boop 已提交
844
                _writer.WriteAttributeString("importlevel", "file");
P
Pilchie 已提交
845 846 847
            }
            else if (scope == ImportScope.Project)
            {
B
beep boop 已提交
848
                _writer.WriteAttributeString("importlevel", "project");
P
Pilchie 已提交
849 850 851 852 853 854 855
            }
            else
            {
                Debug.Assert(scope == ImportScope.Unspecified, "Unexpected scope '" + scope + "'");
            }
        }

856
        private void WriteAsyncInfo(ISymUnmanagedMethod method)
P
Pilchie 已提交
857
        {
858 859
            var asyncMethod = method.AsAsync();
            if (asyncMethod == null)
P
Pilchie 已提交
860
            {
861 862
                return;
            }
863

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

866 867 868
            var catchOffset = asyncMethod.GetCatchHandlerILOffset();
            if (catchOffset >= 0)
            {
B
beep boop 已提交
869 870 871
                _writer.WriteStartElement("catchHandler");
                _writer.WriteAttributeString("offset", AsILOffset(catchOffset));
                _writer.WriteEndElement();
P
Pilchie 已提交
872 873
            }

B
beep boop 已提交
874
            _writer.WriteStartElement("kickoffMethod");
875
            WriteMethodAttributes(asyncMethod.GetKickoffMethod(), isReference: true);
B
beep boop 已提交
876
            _writer.WriteEndElement();
877 878

            foreach (var info in asyncMethod.GetAsyncStepInfos())
P
Pilchie 已提交
879
            {
B
beep boop 已提交
880 881 882
                _writer.WriteStartElement("await");
                _writer.WriteAttributeString("yield", AsILOffset(info.YieldOffset));
                _writer.WriteAttributeString("resume", AsILOffset(info.ResumeOffset));
883
                WriteMethodAttributes(info.ResumeMethod, isReference: true);
B
beep boop 已提交
884
                _writer.WriteEndElement();
P
Pilchie 已提交
885 886
            }

B
beep boop 已提交
887
            _writer.WriteEndElement();
P
Pilchie 已提交
888 889
        }

890
        private void WriteLocals(ISymUnmanagedScope scope)
P
Pilchie 已提交
891
        {
892
            foreach (ISymUnmanagedVariable l in scope.GetLocals())
P
Pilchie 已提交
893
            {
B
beep boop 已提交
894
                _writer.WriteStartElement("local");
895
                _writer.WriteAttributeString("name", l.GetName());
P
Pilchie 已提交
896

897 898 899 900
                // 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.
                _writer.WriteAttributeString("il_index", CultureInvariantToString(l.GetSlot()));
P
Pilchie 已提交
901

902 903 904 905
                _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset()));
                _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset()));
                _writer.WriteAttributeString("attributes", CultureInvariantToString(l.GetAttributes()));
                _writer.WriteEndElement();
P
Pilchie 已提交
906
            }
907

T
Tomas Matousek 已提交
908
            foreach (ISymUnmanagedConstant constant in scope.GetConstants())
P
Pilchie 已提交
909
            {
T
Tomas Matousek 已提交
910 911 912 913
                string name = constant.GetName();
                var signature = constant.GetSignature();
                object value = constant.GetValue();

B
beep boop 已提交
914
                _writer.WriteStartElement("constant");
T
Tomas Matousek 已提交
915 916 917 918 919 920 921 922
                _writer.WriteAttributeString("name", name);

                if (value is int &&
                    (int)value == 0 &&
                    (signature[0] == (byte)ConstantTypeCode.NullReference ||
                     signature[0] == (int)SignatureTypeCode.Object ||
                     signature[0] == (int)SignatureTypeCode.String ||
                     signature[0] == (int)SignatureTypeCode.GenericTypeInstance))
P
Pilchie 已提交
923
                {
T
Tomas Matousek 已提交
924 925 926
                    // TODO: 0 for enums nested in a generic class, null for reference type
                    // We need to decode the signature and see if the target type is enum.
                    _writer.WriteAttributeString("value", "null");
P
Pilchie 已提交
927

T
Tomas Matousek 已提交
928
                    if (signature[0] == (int)SignatureTypeCode.String)
P
Pilchie 已提交
929
                    {
T
Tomas Matousek 已提交
930 931 932 933 934 935 936 937 938 939 940 941 942
                        _writer.WriteAttributeString("type", "String");
                    }
                    else if (signature[0] == (int)SignatureTypeCode.Object)
                    {
                        _writer.WriteAttributeString("type", "Object");
                    }
                    else
                    {
                        // TODO:
                        // A null reference, the type is encoded in the signature. 
                        // Ideally we would parse the signature and display the target type name. 
                        // That requires MetadataReader vNext though.
                        _writer.WriteAttributeString("signature", BitConverter.ToString(signature.ToArray()));
P
Pilchie 已提交
943
                    }
T
Tomas Matousek 已提交
944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973
                }
                else if (value == null)
                {
                    // empty string
                    if (signature[0] == (byte)SignatureTypeCode.String)
                    {
                        _writer.WriteAttributeString("value", "");
                        _writer.WriteAttributeString("type", "String");
                    }
                    else
                    {
                        _writer.WriteAttributeString("value", "null");
                        _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray()));
                    }
                }
                else if (value is decimal)
                {
                    // TODO: check that the signature is a TypeRef
                    _writer.WriteAttributeString("value", ((decimal)value).ToString(CultureInfo.InvariantCulture));
                    _writer.WriteAttributeString("type", value.GetType().Name);
                }
                else if (value is double && signature[0] != (byte)SignatureTypeCode.Double)
                {
                    // TODO: check that the signature is a TypeRef
                    _writer.WriteAttributeString("value", DateTimeUtilities.ToDateTime((double)value).ToString(CultureInfo.InvariantCulture));
                    _writer.WriteAttributeString("type", "DateTime");
                }
                else
                {
                    _writer.WriteAttributeString("value", (value as string)?.Replace("\0", "U+0000") ?? string.Format(CultureInfo.InvariantCulture, "{0}", value));
P
Pilchie 已提交
974

T
Tomas Matousek 已提交
975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994
                    var runtimeType = GetConstantRuntimeType(signature);
                    if (runtimeType == null && 
                        (value is sbyte || value is byte || value is short || value is ushort ||
                         value is int || value is uint || value is long || value is ulong))
                    {
                        // TODO:
                        // Enum.
                        // Ideally we would parse the signature and display the target type name. 
                        // That requires MetadataReader vNext though.
                        _writer.WriteAttributeString("signature", BitConverter.ToString(signature.ToArray()));
                    }
                    else if (runtimeType == value.GetType())
                    {
                        _writer.WriteAttributeString("type", ((SignatureTypeCode)signature[0]).ToString());
                    }
                    else
                    {
                        _writer.WriteAttributeString("runtime-type", value.GetType().Name);
                        _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray()));
                    }
P
Pilchie 已提交
995
                }
T
Tomas Matousek 已提交
996 997

                _writer.WriteEndElement();
P
Pilchie 已提交
998 999 1000
            }
        }

T
Tomas Matousek 已提交
1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
        private static Type GetConstantRuntimeType(ImmutableArray<byte> signature)
        {
            switch ((SignatureTypeCode)signature[0])
            {
                case SignatureTypeCode.Boolean:
                case SignatureTypeCode.Byte:
                case SignatureTypeCode.SByte:
                case SignatureTypeCode.Int16:
                    return typeof(short);

                case SignatureTypeCode.Char:
                case SignatureTypeCode.UInt16:
                    return typeof(ushort);

                case SignatureTypeCode.Int32:
                    return typeof(int);

                case SignatureTypeCode.UInt32:
                    return typeof(uint);

                case SignatureTypeCode.Int64:
                    return typeof(long);

                case SignatureTypeCode.UInt64:
                    return typeof(ulong);

                case SignatureTypeCode.Single:
                    return typeof(float);

                case SignatureTypeCode.Double:
                    return typeof(double);

                case SignatureTypeCode.String:
                    return typeof(string);
            }

            return null;
        }

P
Pilchie 已提交
1040 1041 1042
        // 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).        
1043
        private void WriteSequencePoints(ISymUnmanagedMethod method)
P
Pilchie 已提交
1044
        {
1045
            var sequencePoints = method.GetSequencePoints();
1046 1047 1048 1049 1050 1051
            if (sequencePoints.Length == 0)
            {
                return;
            }

            _writer.WriteStartElement("sequencePoints");
1052

P
Pilchie 已提交
1053
            // Write out sequence points
1054
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1055
            {
B
beep boop 已提交
1056 1057
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1058

1059
                if (sequencePoint.IsHidden)
P
Pilchie 已提交
1060
                {
T
TomasMatousek 已提交
1061 1062
                    if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0)
                    {
B
beep boop 已提交
1063
                        _writer.WriteAttributeString("hidden", "invalid");
T
TomasMatousek 已提交
1064 1065 1066
                    }
                    else
                    {
B
beep boop 已提交
1067
                        _writer.WriteAttributeString("hidden", XmlConvert.ToString(true));
T
TomasMatousek 已提交
1068 1069 1070 1071
                    }
                }
                else
                {
B
beep boop 已提交
1072 1073 1074 1075
                    _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 已提交
1076 1077
                }

1078
                int documentId;
B
beep boop 已提交
1079 1080
                _fileMapping.TryGetValue(sequencePoint.Document.GetName(), out documentId);
                _writer.WriteAttributeString("document", CultureInvariantToString(documentId));
P
Pilchie 已提交
1081

B
beep boop 已提交
1082
                _writer.WriteEndElement();
P
Pilchie 已提交
1083 1084
            }

B
beep boop 已提交
1085
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1086 1087 1088 1089 1090 1091
        }

        // Write all docs, and add to the m_fileMapping list.
        // Other references to docs will then just refer to this list.
        private void WriteDocList()
        {
1092
            var documents = _symReader.GetDocuments();
1093
            if (documents.Length == 0)
P
Pilchie 已提交
1094 1095 1096 1097 1098
            {
                return;
            }

            int id = 0;
B
beep boop 已提交
1099
            _writer.WriteStartElement("files");
1100
            foreach (ISymUnmanagedDocument doc in documents)
P
Pilchie 已提交
1101
            {
1102
                string name = doc.GetName();
P
Pilchie 已提交
1103 1104

                // Symbol store may give out duplicate documents. We'll fold them here
B
beep boop 已提交
1105
                if (_fileMapping.ContainsKey(name))
P
Pilchie 已提交
1106
                {
B
beep boop 已提交
1107
                    _writer.WriteComment("There is a duplicate entry for: " + name);
P
Pilchie 已提交
1108 1109 1110 1111
                    continue;
                }

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

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

B
beep boop 已提交
1116 1117 1118 1119 1120
                _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 已提交
1121

1122
                var checkSum = string.Concat(doc.GetChecksum().Select(b => string.Format("{0,2:X}", b) + ", "));
1123 1124 1125

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1126 1127
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1128 1129
                }

B
beep boop 已提交
1130
                _writer.WriteEndElement(); // file
P
Pilchie 已提交
1131
            }
B
beep boop 已提交
1132
            _writer.WriteEndElement(); // files
P
Pilchie 已提交
1133 1134 1135 1136
        }

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

1139
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1140
            {
1141
                foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1142
                {
B
beep boop 已提交
1143
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1144

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

1147
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1148
                    {
B
beep boop 已提交
1149
                        _writer.WriteStartElement("document");
1150

P
Pilchie 已提交
1151
                        int startLine, endLine;
1152
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1153

B
beep boop 已提交
1154 1155
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1156

B
beep boop 已提交
1157
                        _writer.WriteEndElement();
P
Pilchie 已提交
1158 1159
                    }

B
beep boop 已提交
1160
                    _writer.WriteEndElement();
P
Pilchie 已提交
1161 1162 1163
                }
            }

B
beep boop 已提交
1164
            _writer.WriteEndElement();
P
Pilchie 已提交
1165 1166 1167 1168 1169
        }

        // Write out a reference to the entry point method (if one exists)
        private void WriteEntryPoint()
        {
1170
            int token = _symReader.GetUserEntryPoint();
1171
            if (token != 0)
P
Pilchie 已提交
1172
            {
B
beep boop 已提交
1173
                _writer.WriteStartElement("entryPoint");
1174
                WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
1175
                _writer.WriteEndElement();
P
Pilchie 已提交
1176 1177 1178 1179 1180 1181
            }
        }

        // Write out XML snippet to refer to the given method.
        private void WriteMethodAttributes(int token, bool isReference)
        {
B
beep boop 已提交
1182
            if ((_options & PdbToXmlOptions.ResolveTokens) != 0)
P
Pilchie 已提交
1183 1184 1185 1186 1187
            {
                var handle = MetadataTokens.Handle(token);

                try
                {
A
angocke 已提交
1188
                    switch (handle.Kind)
P
Pilchie 已提交
1189
                    {
A
angocke 已提交
1190 1191
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1192 1193
                            break;

A
angocke 已提交
1194
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1195 1196 1197 1198 1199
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

                        default:
                            WriteToken(token);
B
beep boop 已提交
1200
                            _writer.WriteAttributeString("error", string.Format("Unexpected token type: {0}", handle.Kind));
P
Pilchie 已提交
1201 1202 1203 1204 1205
                            break;
                    }
                }
                catch (BadImageFormatException e) // TODO: filter
                {
B
beep boop 已提交
1206
                    if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
1207 1208 1209 1210 1211
                    {
                        throw;
                    }

                    WriteToken(token);
B
beep boop 已提交
1212
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1213 1214 1215
                }
            }

B
beep boop 已提交
1216
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1217 1218 1219 1220 1221
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1222
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1223
        {
A
angocke 已提交
1224 1225
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1226 1227 1228 1229 1230 1231 1232

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

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

A
angocke 已提交
1233
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1234
        {
B
beep boop 已提交
1235
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1236 1237

            // type name
A
angocke 已提交
1238
            var containingTypeHandle = method.GetDeclaringType();
B
beep boop 已提交
1239
            var fullName = GetFullTypeName(_metadataReader, containingTypeHandle);
P
Pilchie 已提交
1240 1241
            if (fullName != null)
            {
B
beep boop 已提交
1242
                _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName);
P
Pilchie 已提交
1243 1244 1245
            }

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

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

T
TomasMatousek 已提交
1254 1255
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1256
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1257
            }
P
Pilchie 已提交
1258 1259 1260 1261
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1262
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1263 1264

            // type name
B
beep boop 已提交
1265
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1266 1267
            if (fullName != null)
            {
B
beep boop 已提交
1268
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1269 1270 1271
            }

            // method name
B
beep boop 已提交
1272
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286
        }

        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 已提交
1287
            if (handle.Kind == HandleKind.TypeDefinition)
P
Pilchie 已提交
1288
            {
A
angocke 已提交
1289
                var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle);
P
Pilchie 已提交
1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306
                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 已提交
1307
            if (handle.Kind == HandleKind.TypeReference)
P
Pilchie 已提交
1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325
            {
                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 已提交
1326
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1327 1328 1329 1330
        }

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

1334
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1335
        {
1336
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352
        }

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