PdbToXml.cs 58.5 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

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;
12
using System.Reflection.Metadata.Decoding;
P
Pilchie 已提交
13 14 15 16
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml;
17 18
using Microsoft.CodeAnalysis;
using Microsoft.DiaSymReader;
T
Tomas Matousek 已提交
19
using Roslyn.Utilities;
20
using CDI = Microsoft.CodeAnalysis.CustomDebugInfoReader;
21
using CDIC = Microsoft.Cci.CustomDebugInfoConstants;
22
using ImportScope = Microsoft.CodeAnalysis.ImportScope;
P
Pilchie 已提交
23 24 25 26 27 28 29 30 31 32 33 34
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 已提交
35
        private readonly MetadataReader _metadataReader;
36
        private readonly ISymUnmanagedReader _symReader;
B
beep boop 已提交
37 38
        private readonly PdbToXmlOptions _options;
        private readonly XmlWriter _writer;
P
Pilchie 已提交
39

T
tmat 已提交
40 41 42 43 44 45 46
        private static readonly XmlWriterSettings s_xmlWriterSettings = new XmlWriterSettings
        {
            Encoding = Encoding.UTF8,
            Indent = true,
            IndentChars = "  ",
            NewLineChars = "\r\n",
        };
P
Pilchie 已提交
47

48
        private PdbToXmlConverter(XmlWriter writer, ISymUnmanagedReader symReader, MetadataReader metadataReader, PdbToXmlOptions options)
P
Pilchie 已提交
49
        {
50
            _symReader = symReader;
B
beep boop 已提交
51 52 53
            _metadataReader = metadataReader;
            _writer = writer;
            _options = options;
P
Pilchie 已提交
54 55
        }

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

            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 已提交
85
            IEnumerable<MethodDefinitionHandle> methodHandles;
P
Pilchie 已提交
86 87 88 89 90 91 92
            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 已提交
93
                var metadataReader = new MetadataReader(metadataPtr, metadata.Length);
P
Pilchie 已提交
94 95 96 97 98 99 100

                if (string.IsNullOrEmpty(methodName))
                {
                    methodHandles = metadataReader.MethodDefinitions;
                }
                else
                {
101 102 103 104 105 106
                    var matching = metadataReader.MethodDefinitions.
                        Where(methodHandle => GetQualifiedMethodName(metadataReader, methodHandle) == methodName).ToArray();

                    if (matching.Length == 0)
                    {
                        xmlWriter.WriteLine("<error>");
107
                        xmlWriter.WriteLine(string.Format("<message><![CDATA[No method '{0}' found in metadata.]]></message>", methodName));
108 109 110 111 112 113 114 115
                        xmlWriter.WriteLine("<available-methods>");

                        foreach (var methodHandle in metadataReader.MethodDefinitions)
                        {
                            xmlWriter.Write("<method><![CDATA[");
                            xmlWriter.Write(GetQualifiedMethodName(metadataReader, methodHandle));
                            xmlWriter.Write("]]></method>");
                            xmlWriter.WriteLine();
116
                        }
117 118 119 120 121 122 123 124

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

                        return;
                    }

                    methodHandles = matching;
P
Pilchie 已提交
125 126 127 128 129 130 131 132 133 134
                }

                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 已提交
135
        private static void ToXml(TextWriter xmlWriter, Stream pdbStream, MetadataReader metadataReaderOpt, PdbToXmlOptions options, IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
136 137 138 139
        {
            Debug.Assert(pdbStream != null);
            Debug.Assert((options & PdbToXmlOptions.ResolveTokens) == 0 || metadataReaderOpt != null);

T
tmat 已提交
140
            using (var writer = XmlWriter.Create(xmlWriter, s_xmlWriterSettings))
P
Pilchie 已提交
141
            {
142 143 144
                var symReader = SymReaderFactory.CreateReader(pdbStream, metadataReaderOpt);

                try
T
tmat 已提交
145 146 147 148
                {
                    var converter = new PdbToXmlConverter(writer, symReader, metadataReaderOpt, options);
                    converter.WriteRoot(methodHandles ?? metadataReaderOpt.MethodDefinitions);
                }
149 150 151 152
                finally
                {
                    ((ISymUnmanagedDispose)symReader).Destroy();
                }
P
Pilchie 已提交
153 154 155
            }
        }

A
angocke 已提交
156
        private void WriteRoot(IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
157
        {
B
beep boop 已提交
158 159
            _writer.WriteStartDocument();
            _writer.WriteStartElement("symbols");
P
Pilchie 已提交
160

T
tmat 已提交
161 162 163 164 165 166 167
            var documents = _symReader.GetDocuments();
            var documentIndex = BuildDocumentIndex(documents);

            if ((_options & PdbToXmlOptions.ExcludeDocuments) == 0)
            {
                WriteDocuments(documents, documentIndex);
            }
P
Pilchie 已提交
168

T
tmat 已提交
169
            if ((_options & PdbToXmlOptions.ExcludeMethods) == 0)
P
Pilchie 已提交
170
            {
T
tmat 已提交
171 172
                WriteEntryPoint();
                WriteAllMethods(methodHandles, documentIndex);
P
Pilchie 已提交
173 174 175
                WriteAllMethodSpans();
            }

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

T
tmat 已提交
179
        private void WriteAllMethods(IEnumerable<MethodDefinitionHandle> methodHandles, IReadOnlyDictionary<string, int> documentIndex)
P
Pilchie 已提交
180
        {
B
beep boop 已提交
181
            _writer.WriteStartElement("methods");
P
Pilchie 已提交
182 183 184

            foreach (var methodHandle in methodHandles)
            {
T
tmat 已提交
185
                WriteMethod(methodHandle, documentIndex);
P
Pilchie 已提交
186 187
            }

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

T
tmat 已提交
191
        private void WriteMethod(MethodDefinitionHandle methodHandle, IReadOnlyDictionary<string, int> documentIndex)
P
Pilchie 已提交
192
        {
B
beep boop 已提交
193
            int token = _metadataReader.GetToken(methodHandle);
194
            ISymUnmanagedMethod method = _symReader.GetMethod(token);
T
tmat 已提交
195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224

            byte[] cdi = null;
            var sequencePoints = default(ImmutableArray<SymUnmanagedSequencePoint>);
            ISymUnmanagedAsyncMethod asyncMethod = null;
            ISymUnmanagedScope rootScope = null;

            if ((_options & PdbToXmlOptions.ExcludeCustomDebugInformation) == 0)
            {
                cdi = _symReader.GetCustomDebugInfoBytes(token, methodVersion: 1);
            }

            if (method != null)
            {
                if ((_options & PdbToXmlOptions.ExcludeAsyncInfo) == 0)
                {
                    asyncMethod = method.AsAsync();
                }

                if ((_options & PdbToXmlOptions.ExcludeSequencePoints) == 0)
                {
                    sequencePoints = method.GetSequencePoints();
                }

                if ((_options & PdbToXmlOptions.ExcludeScopes) == 0)
                {
                    rootScope = method.GetRootScope();
                }
            }

            if (cdi == null && sequencePoints.IsDefaultOrEmpty && rootScope == null && asyncMethod == null)
P
Pilchie 已提交
225
            {
T
tmat 已提交
226
                // no debug info to write
P
Pilchie 已提交
227 228 229
                return;
            }

B
beep boop 已提交
230
            _writer.WriteStartElement("method");
P
Pilchie 已提交
231 232
            WriteMethodAttributes(token, isReference: false);

233
            if (cdi != null)
P
Pilchie 已提交
234
            {
235
                WriteCustomDebugInfo(cdi);
P
Pilchie 已提交
236 237
            }

T
tmat 已提交
238
            if (!sequencePoints.IsDefaultOrEmpty)
P
Pilchie 已提交
239
            {
T
tmat 已提交
240 241
                WriteSequencePoints(sequencePoints, documentIndex);
            }
P
Pilchie 已提交
242

T
tmat 已提交
243 244 245 246
            if (rootScope != null)
            {
                WriteScopes(rootScope);
            }
P
Pilchie 已提交
247

T
tmat 已提交
248 249 250
            if (asyncMethod != null)
            {
                WriteAsyncInfo(asyncMethod);
P
Pilchie 已提交
251 252
            }

T
tmat 已提交
253
            _writer.WriteEndElement();
P
Pilchie 已提交
254 255 256 257 258 259 260 261
        }

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

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

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

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

        /// <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>
315
        private void WriteUnknownCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
316
        {
B
beep boop 已提交
317 318
            _writer.WriteStartElement("unknown");
            _writer.WriteAttributeString("kind", record.Kind.ToString());
T
Tomas Matousek 已提交
319
            _writer.WriteAttributeString("version", CultureInvariantToString(record.Version));
P
Pilchie 已提交
320 321 322

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

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

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

        /// <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>
340
        private void WriteUsingCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
341
        {
342
            Debug.Assert(record.Kind == CustomDebugInfoKind.UsingInfo);
P
Pilchie 已提交
343

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

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

            foreach (short importCount in counts)
            {
B
beep boop 已提交
350
                _writer.WriteStartElement("namespace");
T
Tomas Matousek 已提交
351
                _writer.WriteAttributeString("usingCount", CultureInvariantToString(importCount));
B
beep boop 已提交
352
                _writer.WriteEndElement(); //namespace
P
Pilchie 已提交
353 354
            }

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

        /// <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>
365
        private void WriteForwardCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
366
        {
367
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardInfo);
P
Pilchie 已提交
368

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

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

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

        /// <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>
385
        private void WriteForwardToModuleCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
386
        {
387
            Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardToModuleInfo);
P
Pilchie 已提交
388

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

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

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

        /// <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>
405
        private void WriteStatemachineHoistedLocalScopesCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
406
        {
407
            Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineHoistedLocalScopes);
P
Pilchie 已提交
408

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

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

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

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

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

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

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

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

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

        /// <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>
451
        private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record)
P
Pilchie 已提交
452
        {
453
            Debug.Assert(record.Kind == CustomDebugInfoKind.DynamicLocals);
P
Pilchie 已提交
454

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

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

            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 已提交
471
                _writer.WriteStartElement("bucket");
T
Tomas Matousek 已提交
472
                _writer.WriteAttributeString("flagCount", CultureInvariantToString(flagCount));
B
beep boop 已提交
473
                _writer.WriteAttributeString("flags", pooled.ToStringAndFree());
T
Tomas Matousek 已提交
474
                _writer.WriteAttributeString("slotId", CultureInvariantToString(bucket.SlotId));
B
beep boop 已提交
475 476
                _writer.WriteAttributeString("localName", bucket.Name);
                _writer.WriteEndElement(); //bucket
P
Pilchie 已提交
477 478
            }

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

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

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

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
510

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

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

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

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

T
Tomas Matousek 已提交
530 531
                            _writer.WriteAttributeString("kind", CultureInvariantToString(synthesizedKind));
                            _writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : CultureInvariantToString(syntaxOffset));
532 533 534

                            if (badOrdinal || hasOrdinal)
                            {
T
Tomas Matousek 已提交
535
                                _writer.WriteAttributeString("ordinal", badOrdinal ? "?" : CultureInvariantToString(ordinal));
536 537 538
                            }
                        }

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

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

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

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

576 577
                    // [-1, inf)
                    methodOrdinal--;
T
Tomas Matousek 已提交
578
                    _writer.WriteElementString("methodOrdinal", CultureInvariantToString(methodOrdinal));
579 580

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

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

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

T
Tomas Matousek 已提交
607
                            _writer.WriteAttributeString("offset", CultureInvariantToString(syntaxOffset + syntaxOffsetBaseline));
608 609 610
                        }
                        finally
                        {
B
beep boop 已提交
611
                            _writer.WriteEndElement();
612 613
                        }
                    }
614

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

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

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

636 637 638 639 640 641 642
                            closureOrdinal -= 2;

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

T
tmat 已提交
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
        private void WriteScopes(ISymUnmanagedScope rootScope)
        {
            // The root scope is always empty. The first scope opened by SymWriter is the child of the root scope.
            if (rootScope.GetNamespaces().IsEmpty && rootScope.GetLocals().IsEmpty && rootScope.GetConstants().IsEmpty)
            {
                foreach (ISymUnmanagedScope child in rootScope.GetScopes())
                {
                    WriteScope(child, isRoot: false);
                }
            }
            else
            {
                // This shouldn't be executed for PDBs generated via SymWriter.
                WriteScope(rootScope, isRoot: true);
            }
        }

678
        private void WriteScope(ISymUnmanagedScope scope, bool isRoot)
P
Pilchie 已提交
679
        {
680 681 682 683
            _writer.WriteStartElement(isRoot ? "rootScope" : "scope");
            _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset()));
            _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset()));

T
tmat 已提交
684
            if ((_options & PdbToXmlOptions.ExcludeNamespaces) == 0)
P
Pilchie 已提交
685
            {
T
tmat 已提交
686 687 688 689
                foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces())
                {
                    WriteNamespace(@namespace);
                }
690
            }
P
Pilchie 已提交
691

692 693 694 695 696
            WriteLocals(scope);

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

J
Jared Parsons 已提交
699
            _writer.WriteEndElement();
P
Pilchie 已提交
700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
        }

        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;
717 718
                    var parsingSucceeded = CDI.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope);
                    Debug.Assert(parsingSucceeded);
P
Pilchie 已提交
719 720 721 722 723 724 725 726 727 728
                }
                else
                {
                    switch (rawName[0])
                    {
                        case 'U':
                        case 'A':
                        case 'X':
                        case 'Z':
                        case 'E':
729
                        case 'T':
P
Pilchie 已提交
730
                            scope = ImportScope.Unspecified;
731 732
                            if (!CDI.TryParseCSharpImportString(rawName, out alias, out externAlias, out target, out kind))
                            {
T
tmat 已提交
733
                                throw new InvalidOperationException($"Invalid import '{rawName}'");
734
                            }
P
Pilchie 已提交
735 736 737 738
                            break;

                        default:
                            externAlias = null;
739 740
                            if (!CDI.TryParseVisualBasicImportString(rawName, out alias, out target, out kind, out scope))
                            {
T
tmat 已提交
741
                                throw new InvalidOperationException($"Invalid import '{rawName}'");
742
                            }
P
Pilchie 已提交
743 744 745 746
                            break;
                    }
                }
            }
T
tmat 已提交
747
            catch (ArgumentException) when ((_options & PdbToXmlOptions.ThrowOnError) == 0)
P
Pilchie 已提交
748
            {
B
beep boop 已提交
749 750 751
                _writer.WriteStartElement("invalid-custom-data");
                _writer.WriteAttributeString("raw", rawName);
                _writer.WriteEndElement();
P
Pilchie 已提交
752 753 754 755 756 757 758 759 760
                return;
            }

            switch (kind)
            {
                case ImportTargetKind.CurrentNamespace:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
T
tmat 已提交
761

B
beep boop 已提交
762 763
                    _writer.WriteStartElement("currentnamespace");
                    _writer.WriteAttributeString("name", target);
T
tmat 已提交
764
                    _writer.WriteEndElement();
P
Pilchie 已提交
765
                    break;
T
tmat 已提交
766

P
Pilchie 已提交
767 768 769 770
                case ImportTargetKind.DefaultNamespace:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
T
tmat 已提交
771

B
beep boop 已提交
772 773
                    _writer.WriteStartElement("defaultnamespace");
                    _writer.WriteAttributeString("name", target);
T
tmat 已提交
774
                    _writer.WriteEndElement();
P
Pilchie 已提交
775
                    break;
T
tmat 已提交
776

P
Pilchie 已提交
777 778 779 780
                case ImportTargetKind.MethodToken:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
T
tmat 已提交
781

P
Pilchie 已提交
782
                    int token = Convert.ToInt32(target);
B
beep boop 已提交
783
                    _writer.WriteStartElement("importsforward");
P
Pilchie 已提交
784
                    WriteMethodAttributes(token, isReference: true);
T
tmat 已提交
785
                    _writer.WriteEndElement();
P
Pilchie 已提交
786
                    break;
T
tmat 已提交
787

P
Pilchie 已提交
788 789
                case ImportTargetKind.XmlNamespace:
                    Debug.Assert(externAlias == null);
T
tmat 已提交
790

B
beep boop 已提交
791 792 793
                    _writer.WriteStartElement("xmlnamespace");
                    _writer.WriteAttributeString("prefix", alias);
                    _writer.WriteAttributeString("name", target);
P
Pilchie 已提交
794
                    WriteScopeAttribute(scope);
T
tmat 已提交
795
                    _writer.WriteEndElement();
P
Pilchie 已提交
796
                    break;
T
tmat 已提交
797

P
Pilchie 已提交
798 799
                case ImportTargetKind.NamespaceOrType:
                    Debug.Assert(externAlias == null);
T
tmat 已提交
800

B
beep boop 已提交
801 802 803 804
                    _writer.WriteStartElement("alias");
                    _writer.WriteAttributeString("name", alias);
                    _writer.WriteAttributeString("target", target);
                    _writer.WriteAttributeString("kind", "namespace"); // Strange, but retaining to avoid breaking tests.
P
Pilchie 已提交
805
                    WriteScopeAttribute(scope);
T
tmat 已提交
806
                    _writer.WriteEndElement();
P
Pilchie 已提交
807
                    break;
T
tmat 已提交
808

P
Pilchie 已提交
809 810 811
                case ImportTargetKind.Namespace:
                    if (alias != null)
                    {
B
beep boop 已提交
812 813
                        _writer.WriteStartElement("alias");
                        _writer.WriteAttributeString("name", alias);
T
tmat 已提交
814 815 816 817 818
                        if (externAlias != null)
                        {
                            _writer.WriteAttributeString("qualifier", externAlias);
                        }

B
beep boop 已提交
819 820
                        _writer.WriteAttributeString("target", target);
                        _writer.WriteAttributeString("kind", "namespace");
P
Pilchie 已提交
821
                        Debug.Assert(scope == ImportScope.Unspecified); // Only C# hits this case.
T
tmat 已提交
822
                        _writer.WriteEndElement();
P
Pilchie 已提交
823 824 825
                    }
                    else
                    {
B
beep boop 已提交
826 827 828
                        _writer.WriteStartElement("namespace");
                        if (externAlias != null) _writer.WriteAttributeString("qualifier", externAlias);
                        _writer.WriteAttributeString("name", target);
P
Pilchie 已提交
829
                        WriteScopeAttribute(scope);
T
tmat 已提交
830
                        _writer.WriteEndElement();
P
Pilchie 已提交
831
                    }
T
tmat 已提交
832

P
Pilchie 已提交
833
                    break;
T
tmat 已提交
834

P
Pilchie 已提交
835 836 837 838
                case ImportTargetKind.Type:
                    Debug.Assert(externAlias == null);
                    if (alias != null)
                    {
B
beep boop 已提交
839 840 841 842
                        _writer.WriteStartElement("alias");
                        _writer.WriteAttributeString("name", alias);
                        _writer.WriteAttributeString("target", target);
                        _writer.WriteAttributeString("kind", "type");
P
Pilchie 已提交
843
                        Debug.Assert(scope == ImportScope.Unspecified); // Only C# hits this case.
T
tmat 已提交
844
                        _writer.WriteEndElement(); 
P
Pilchie 已提交
845 846 847
                    }
                    else
                    {
B
beep boop 已提交
848 849
                        _writer.WriteStartElement("type");
                        _writer.WriteAttributeString("name", target);
P
Pilchie 已提交
850
                        WriteScopeAttribute(scope);
T
tmat 已提交
851
                        _writer.WriteEndElement();
P
Pilchie 已提交
852
                    }
T
tmat 已提交
853

P
Pilchie 已提交
854
                    break;
T
tmat 已提交
855

P
Pilchie 已提交
856
                case ImportTargetKind.Assembly:
857 858
                    Debug.Assert(alias != null);
                    Debug.Assert(externAlias == null);
P
Pilchie 已提交
859 860 861
                    Debug.Assert(scope == ImportScope.Unspecified);
                    if (target == null)
                    {
B
beep boop 已提交
862 863
                        _writer.WriteStartElement("extern");
                        _writer.WriteAttributeString("alias", alias);
T
tmat 已提交
864
                        _writer.WriteEndElement();
P
Pilchie 已提交
865 866 867
                    }
                    else
                    {
B
beep boop 已提交
868 869 870
                        _writer.WriteStartElement("externinfo");
                        _writer.WriteAttributeString("alias", alias);
                        _writer.WriteAttributeString("assembly", target);
T
tmat 已提交
871
                        _writer.WriteEndElement();
P
Pilchie 已提交
872
                    }
T
tmat 已提交
873

P
Pilchie 已提交
874
                    break;
T
tmat 已提交
875

A
acasey 已提交
876 877 878
                case ImportTargetKind.Defunct:
                    Debug.Assert(alias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
B
beep boop 已提交
879 880
                    _writer.WriteStartElement("defunct");
                    _writer.WriteAttributeString("name", rawName);
T
tmat 已提交
881
                    _writer.WriteEndElement();
A
acasey 已提交
882
                    break;
T
tmat 已提交
883

P
Pilchie 已提交
884 885
                default:
                    Debug.Assert(false, "Unexpected import kind '" + kind + "'");
B
beep boop 已提交
886 887
                    _writer.WriteStartElement("unknown");
                    _writer.WriteAttributeString("name", rawName);
T
tmat 已提交
888
                    _writer.WriteEndElement();
P
Pilchie 已提交
889 890 891 892 893 894 895 896
                    break;
            }
        }

        private void WriteScopeAttribute(ImportScope scope)
        {
            if (scope == ImportScope.File)
            {
B
beep boop 已提交
897
                _writer.WriteAttributeString("importlevel", "file");
P
Pilchie 已提交
898 899 900
            }
            else if (scope == ImportScope.Project)
            {
B
beep boop 已提交
901
                _writer.WriteAttributeString("importlevel", "project");
P
Pilchie 已提交
902 903 904 905 906 907 908
            }
            else
            {
                Debug.Assert(scope == ImportScope.Unspecified, "Unexpected scope '" + scope + "'");
            }
        }

T
tmat 已提交
909
        private void WriteAsyncInfo(ISymUnmanagedAsyncMethod asyncMethod)
P
Pilchie 已提交
910
        {
B
beep boop 已提交
911
            _writer.WriteStartElement("asyncInfo");
P
Pilchie 已提交
912

913 914 915
            var catchOffset = asyncMethod.GetCatchHandlerILOffset();
            if (catchOffset >= 0)
            {
B
beep boop 已提交
916 917 918
                _writer.WriteStartElement("catchHandler");
                _writer.WriteAttributeString("offset", AsILOffset(catchOffset));
                _writer.WriteEndElement();
P
Pilchie 已提交
919 920
            }

B
beep boop 已提交
921
            _writer.WriteStartElement("kickoffMethod");
922
            WriteMethodAttributes(asyncMethod.GetKickoffMethod(), isReference: true);
B
beep boop 已提交
923
            _writer.WriteEndElement();
924 925

            foreach (var info in asyncMethod.GetAsyncStepInfos())
P
Pilchie 已提交
926
            {
B
beep boop 已提交
927 928 929
                _writer.WriteStartElement("await");
                _writer.WriteAttributeString("yield", AsILOffset(info.YieldOffset));
                _writer.WriteAttributeString("resume", AsILOffset(info.ResumeOffset));
930
                WriteMethodAttributes(info.ResumeMethod, isReference: true);
B
beep boop 已提交
931
                _writer.WriteEndElement();
P
Pilchie 已提交
932 933
            }

B
beep boop 已提交
934
            _writer.WriteEndElement();
P
Pilchie 已提交
935 936
        }

937
        private void WriteLocals(ISymUnmanagedScope scope)
P
Pilchie 已提交
938
        {
939
            foreach (ISymUnmanagedVariable l in scope.GetLocals())
P
Pilchie 已提交
940
            {
B
beep boop 已提交
941
                _writer.WriteStartElement("local");
942
                _writer.WriteAttributeString("name", l.GetName());
P
Pilchie 已提交
943

944 945 946 947
                // 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 已提交
948

949 950 951 952
                _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset()));
                _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset()));
                _writer.WriteAttributeString("attributes", CultureInvariantToString(l.GetAttributes()));
                _writer.WriteEndElement();
P
Pilchie 已提交
953
            }
954

T
Tomas Matousek 已提交
955
            foreach (ISymUnmanagedConstant constant in scope.GetConstants())
P
Pilchie 已提交
956
            {
T
Tomas Matousek 已提交
957 958 959 960
                string name = constant.GetName();
                var signature = constant.GetSignature();
                object value = constant.GetValue();

B
beep boop 已提交
961
                _writer.WriteStartElement("constant");
T
Tomas Matousek 已提交
962 963 964 965 966 967 968
                _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 ||
969
                     (signature.Length > 2 && signature[0] == (int)SignatureTypeCode.GenericTypeInstance && signature[1] == (byte)ConstantTypeCode.NullReference)))
P
Pilchie 已提交
970
                {
T
Tomas Matousek 已提交
971
                    _writer.WriteAttributeString("value", "null");
P
Pilchie 已提交
972

T
Tomas Matousek 已提交
973
                    if (signature[0] == (int)SignatureTypeCode.String)
P
Pilchie 已提交
974
                    {
T
Tomas Matousek 已提交
975 976 977 978 979 980 981 982
                        _writer.WriteAttributeString("type", "String");
                    }
                    else if (signature[0] == (int)SignatureTypeCode.Object)
                    {
                        _writer.WriteAttributeString("type", "Object");
                    }
                    else
                    {
983
                        _writer.WriteAttributeString("signature", FormatSignature(signature));
P
Pilchie 已提交
984
                    }
T
Tomas Matousek 已提交
985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013
                }
                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
                {
1014 1015 1016
                    string str = value as string;
                    if (str != null)
                    {
T
Tomas Matousek 已提交
1017
                        _writer.WriteAttributeString("value", StringUtilities.EscapeNonPrintableCharacters(str));
1018 1019 1020 1021 1022
                    }
                    else
                    {
                        _writer.WriteAttributeString("value", string.Format(CultureInfo.InvariantCulture, "{0}", value));
                    }
P
Pilchie 已提交
1023

T
Tomas Matousek 已提交
1024
                    var runtimeType = GetConstantRuntimeType(signature);
J
Jared Parsons 已提交
1025
                    if (runtimeType == null &&
T
Tomas Matousek 已提交
1026 1027 1028
                        (value is sbyte || value is byte || value is short || value is ushort ||
                         value is int || value is uint || value is long || value is ulong))
                    {
1029
                        _writer.WriteAttributeString("signature", FormatSignature(signature));
T
Tomas Matousek 已提交
1030 1031 1032 1033 1034 1035 1036 1037 1038 1039
                    }
                    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 已提交
1040
                }
T
Tomas Matousek 已提交
1041 1042

                _writer.WriteEndElement();
P
Pilchie 已提交
1043 1044
            }
        }
1045
        
1046 1047
        private unsafe string FormatSignature(ImmutableArray<byte> signature)
        {
1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121 1122 1123 1124
            fixed (byte* sigPtr = signature.ToArray())
            {
                var sigReader = new BlobReader(sigPtr, signature.Length);
                var provider = new SignatureVisualizer(_metadataReader);
                return SignatureDecoder.DecodeType(ref sigReader, provider);
            }
        }

        private sealed class SignatureVisualizer : ISignatureTypeProvider<string>
        {
            private readonly MetadataReader _reader;

            public SignatureVisualizer(MetadataReader reader)
            {
                _reader = reader;
            }

            public MetadataReader Reader => _reader;

            public string GetArrayType(string elementType, ArrayShape shape)
            {
                return elementType + "[" + new string(',', shape.Rank) + "]";
            }

            public string GetByReferenceType(string elementType)
            {
                return elementType + "&";  
            }

            public string GetFunctionPointerType(MethodSignature<string> signature)
            {
                // TODO:
                return "method-ptr"; 
            }

            public string GetGenericInstance(string genericType, ImmutableArray<string> typeArguments)
            {
                // using {} since the result is embedded in XML
                return genericType + "{" + string.Join(", ", typeArguments) + "}";
            }

            public string GetGenericMethodParameter(int index)
            {
                return "!!" + index;
            }

            public string GetGenericTypeParameter(int index)
            {
                return "!" + index;
            }

            public string GetModifiedType(string unmodifiedType, ImmutableArray<CustomModifier<string>> customModifiers)
            {
                return string.Join(" ", customModifiers.Select(mod => (mod.IsRequired ? "modreq(" : "modopt(") + mod.Type + ")")) + 
                    unmodifiedType;
            }

            public string GetPinnedType(string elementType)
            {
                return "pinned " + elementType;
            }

            public string GetPointerType(string elementType)
            {
                return elementType + "*";
            }

            public string GetPrimitiveType(PrimitiveTypeCode typeCode)
            {
                return typeCode.ToString();
            }

            public string GetSZArrayType(string elementType)
            {
                return elementType + "[]";
            }

1125
            public string GetTypeFromDefinition(TypeDefinitionHandle handle, bool? isValueType)
1126 1127 1128 1129 1130 1131
            {
                var typeDef = _reader.GetTypeDefinition(handle);
                var name = _reader.GetString(typeDef.Name);
                return typeDef.Namespace.IsNil ? name : _reader.GetString(typeDef.Namespace) + "." + name;
            }

1132
            public string GetTypeFromReference(TypeReferenceHandle handle, bool? isValueType)
1133 1134 1135 1136 1137
            {
                var typeRef = _reader.GetTypeReference(handle);
                var name = _reader.GetString(typeRef.Name);
                return typeRef.Namespace.IsNil ? name : _reader.GetString(typeRef.Namespace) + "." + name;
            }
1138 1139
        }

T
Tomas Matousek 已提交
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178
        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;
        }

T
tmat 已提交
1179
        private void WriteSequencePoints(ImmutableArray<SymUnmanagedSequencePoint> sequencePoints, IReadOnlyDictionary<string, int> documentIndex)
P
Pilchie 已提交
1180
        {
T
tmat 已提交
1181
            Debug.Assert(!sequencePoints.IsDefaultOrEmpty);
1182 1183

            _writer.WriteStartElement("sequencePoints");
1184

P
Pilchie 已提交
1185
            // Write out sequence points
1186
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1187
            {
B
beep boop 已提交
1188 1189
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1190

1191
                if (sequencePoint.IsHidden)
P
Pilchie 已提交
1192
                {
T
TomasMatousek 已提交
1193 1194
                    if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0)
                    {
B
beep boop 已提交
1195
                        _writer.WriteAttributeString("hidden", "invalid");
T
TomasMatousek 已提交
1196 1197 1198
                    }
                    else
                    {
B
beep boop 已提交
1199
                        _writer.WriteAttributeString("hidden", XmlConvert.ToString(true));
T
TomasMatousek 已提交
1200 1201 1202 1203
                    }
                }
                else
                {
B
beep boop 已提交
1204 1205 1206 1207
                    _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 已提交
1208 1209
                }

1210
                int documentId;
T
tmat 已提交
1211
                string documentName = sequencePoint.Document.GetName();
T
tmat 已提交
1212
                if (documentName.Length > 0)
T
tmat 已提交
1213
                {
T
tmat 已提交
1214 1215 1216 1217 1218 1219 1220 1221
                    if (documentIndex.TryGetValue(documentName, out documentId))
                    {
                        _writer.WriteAttributeString("document", CultureInvariantToString(documentId));
                    }
                    else
                    {
                        _writer.WriteAttributeString("document", "?");
                    }
T
tmat 已提交
1222
                }
P
Pilchie 已提交
1223

B
beep boop 已提交
1224
                _writer.WriteEndElement();
P
Pilchie 已提交
1225 1226
            }

B
beep boop 已提交
1227
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1228 1229
        }

T
tmat 已提交
1230
        private IReadOnlyDictionary<string, int> BuildDocumentIndex(ImmutableArray<ISymUnmanagedDocument> documents)
P
Pilchie 已提交
1231
        {
T
tmat 已提交
1232 1233 1234 1235
            var index = new Dictionary<string, int>(documents.Length);

            int id = 1;
            foreach (var document in documents)
P
Pilchie 已提交
1236
            {
T
tmat 已提交
1237
                string name = document.GetName();
P
Pilchie 已提交
1238

1239
                // Native PDB doesn't allow no-name documents. SymWriter silently ignores them.
T
tmat 已提交
1240 1241
                // In Portable PDB all methods must be contained in a document, whose name may be empty. 
                // Skip such documents - like they were never in the document table.
1242 1243 1244 1245 1246
                if (name.Length == 0)
                {
                    continue;
                }

T
tmat 已提交
1247 1248 1249
                // Skip adding dups into the index, but increment id so that we 
                // can tell what methods are referring to the duplicate.
                if (!index.ContainsKey(name))
1250
                {
T
tmat 已提交
1251
                    index.Add(name, id);
1252 1253
                }

T
tmat 已提交
1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269
                id++;
            }

            return index;
        }

        private void WriteDocuments(ImmutableArray<ISymUnmanagedDocument> documents, IReadOnlyDictionary<string, int> documentIndex)
        {
            bool hasDocument = false;

            foreach (var doc in documents)
            {
                string name = doc.GetName();

                int id;
                if (!documentIndex.TryGetValue(name, out id))
P
Pilchie 已提交
1270 1271 1272 1273
                {
                    continue;
                }

T
tmat 已提交
1274 1275 1276 1277 1278 1279
                if (!hasDocument)
                {
                    _writer.WriteStartElement("files");
                }

                hasDocument = true;
P
Pilchie 已提交
1280

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

B
beep boop 已提交
1283 1284 1285 1286 1287
                _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 已提交
1288

T
tmat 已提交
1289
                var checkSum = string.Concat(doc.GetChecksum().Select(b => $"{b,2:X}, "));
1290 1291 1292

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1293 1294
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1295 1296
                }

T
tmat 已提交
1297
                _writer.WriteEndElement();
P
Pilchie 已提交
1298
            }
1299

T
tmat 已提交
1300
            if (hasDocument)
1301
            {
T
tmat 已提交
1302
                _writer.WriteEndElement();
1303
            }
P
Pilchie 已提交
1304 1305 1306 1307
        }

        private void WriteAllMethodSpans()
        {
T
tmat 已提交
1308 1309 1310 1311 1312
            if ((_options & PdbToXmlOptions.IncludeMethodSpans) == 0)
            {
                return;
            }

B
beep boop 已提交
1313
            _writer.WriteStartElement("method-spans");
P
Pilchie 已提交
1314

1315
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1316
            {
1317
                foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1318
                {
B
beep boop 已提交
1319
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1320

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

1323
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1324
                    {
B
beep boop 已提交
1325
                        _writer.WriteStartElement("document");
1326

P
Pilchie 已提交
1327
                        int startLine, endLine;
1328
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1329

B
beep boop 已提交
1330 1331
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1332

B
beep boop 已提交
1333
                        _writer.WriteEndElement();
P
Pilchie 已提交
1334 1335
                    }

B
beep boop 已提交
1336
                    _writer.WriteEndElement();
P
Pilchie 已提交
1337 1338 1339
                }
            }

B
beep boop 已提交
1340
            _writer.WriteEndElement();
P
Pilchie 已提交
1341 1342 1343 1344 1345
        }

        // Write out a reference to the entry point method (if one exists)
        private void WriteEntryPoint()
        {
1346
            int token = _symReader.GetUserEntryPoint();
1347
            if (token != 0)
P
Pilchie 已提交
1348
            {
B
beep boop 已提交
1349
                _writer.WriteStartElement("entryPoint");
1350
                WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
1351
                _writer.WriteEndElement();
P
Pilchie 已提交
1352 1353 1354 1355 1356 1357
            }
        }

        // Write out XML snippet to refer to the given method.
        private void WriteMethodAttributes(int token, bool isReference)
        {
B
beep boop 已提交
1358
            if ((_options & PdbToXmlOptions.ResolveTokens) != 0)
P
Pilchie 已提交
1359 1360 1361 1362 1363
            {
                var handle = MetadataTokens.Handle(token);

                try
                {
A
angocke 已提交
1364
                    switch (handle.Kind)
P
Pilchie 已提交
1365
                    {
A
angocke 已提交
1366 1367
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1368 1369
                            break;

A
angocke 已提交
1370
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1371 1372 1373 1374 1375
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

                        default:
                            WriteToken(token);
T
tmat 已提交
1376
                            _writer.WriteAttributeString("error", $"Unexpected token type: {handle.Kind}");
P
Pilchie 已提交
1377 1378 1379 1380 1381
                            break;
                    }
                }
                catch (BadImageFormatException e) // TODO: filter
                {
B
beep boop 已提交
1382
                    if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
1383 1384 1385 1386 1387
                    {
                        throw;
                    }

                    WriteToken(token);
B
beep boop 已提交
1388
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1389 1390 1391
                }
            }

B
beep boop 已提交
1392
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1393 1394 1395 1396 1397
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1398
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1399
        {
A
angocke 已提交
1400 1401
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1402 1403 1404 1405 1406 1407 1408

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

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

A
angocke 已提交
1409
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1410
        {
B
beep boop 已提交
1411
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1412 1413

            // type name
A
angocke 已提交
1414
            var containingTypeHandle = method.GetDeclaringType();
B
beep boop 已提交
1415
            var fullName = GetFullTypeName(_metadataReader, containingTypeHandle);
P
Pilchie 已提交
1416 1417
            if (fullName != null)
            {
B
beep boop 已提交
1418
                _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName);
P
Pilchie 已提交
1419 1420 1421
            }

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

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

T
TomasMatousek 已提交
1430 1431
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1432
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1433
            }
P
Pilchie 已提交
1434 1435 1436 1437
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1438
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1439 1440

            // type name
B
beep boop 已提交
1441
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1442 1443
            if (fullName != null)
            {
B
beep boop 已提交
1444
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1445 1446 1447
            }

            // method name
B
beep boop 已提交
1448
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1449 1450 1451 1452 1453 1454 1455
        }

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

1456
        private static string GetFullTypeName(MetadataReader metadataReader, EntityHandle handle)
P
Pilchie 已提交
1457 1458 1459 1460 1461 1462
        {
            if (handle.IsNil)
            {
                return null;
            }

A
angocke 已提交
1463
            if (handle.Kind == HandleKind.TypeDefinition)
P
Pilchie 已提交
1464
            {
A
angocke 已提交
1465
                var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle);
P
Pilchie 已提交
1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482
                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 已提交
1483
            if (handle.Kind == HandleKind.TypeReference)
P
Pilchie 已提交
1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497
            {
                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)));
        }

1498
#region Utils
P
Pilchie 已提交
1499 1500 1501

        private void WriteToken(int token)
        {
B
beep boop 已提交
1502
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1503 1504 1505 1506
        }

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

1510
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1511
        {
1512
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1513 1514 1515 1516 1517 1518 1519
        }

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

1520
#endregion
P
Pilchie 已提交
1521 1522
    }
}