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

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
            {
T
tmat 已提交
142 143 144
                using (SymReader symReader = new SymReader(pdbStream, metadataReaderOpt))
                {
                    var converter = new PdbToXmlConverter(writer, symReader, metadataReaderOpt, options);
P
Pilchie 已提交
145

T
tmat 已提交
146 147
                    converter.WriteRoot(methodHandles ?? metadataReaderOpt.MethodDefinitions);
                }
P
Pilchie 已提交
148 149 150
            }
        }

A
angocke 已提交
151
        private void WriteRoot(IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
152
        {
B
beep boop 已提交
153 154
            _writer.WriteStartDocument();
            _writer.WriteStartElement("symbols");
P
Pilchie 已提交
155

T
tmat 已提交
156 157 158 159 160 161 162
            var documents = _symReader.GetDocuments();
            var documentIndex = BuildDocumentIndex(documents);

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

T
tmat 已提交
164
            if ((_options & PdbToXmlOptions.ExcludeMethods) == 0)
P
Pilchie 已提交
165
            {
T
tmat 已提交
166 167
                WriteEntryPoint();
                WriteAllMethods(methodHandles, documentIndex);
P
Pilchie 已提交
168 169 170
                WriteAllMethodSpans();
            }

B
beep boop 已提交
171
            _writer.WriteEndElement();
P
Pilchie 已提交
172 173
        }

T
tmat 已提交
174
        private void WriteAllMethods(IEnumerable<MethodDefinitionHandle> methodHandles, IReadOnlyDictionary<string, int> documentIndex)
P
Pilchie 已提交
175
        {
B
beep boop 已提交
176
            _writer.WriteStartElement("methods");
P
Pilchie 已提交
177 178 179

            foreach (var methodHandle in methodHandles)
            {
T
tmat 已提交
180
                WriteMethod(methodHandle, documentIndex);
P
Pilchie 已提交
181 182
            }

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

T
tmat 已提交
186
        private void WriteMethod(MethodDefinitionHandle methodHandle, IReadOnlyDictionary<string, int> documentIndex)
P
Pilchie 已提交
187
        {
B
beep boop 已提交
188
            int token = _metadataReader.GetToken(methodHandle);
189
            ISymUnmanagedMethod method = _symReader.GetMethod(token);
T
tmat 已提交
190 191 192 193 194 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

            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 已提交
220
            {
T
tmat 已提交
221
                // no debug info to write
P
Pilchie 已提交
222 223 224
                return;
            }

B
beep boop 已提交
225
            _writer.WriteStartElement("method");
P
Pilchie 已提交
226 227
            WriteMethodAttributes(token, isReference: false);

228
            if (cdi != null)
P
Pilchie 已提交
229
            {
230
                WriteCustomDebugInfo(cdi);
P
Pilchie 已提交
231 232
            }

T
tmat 已提交
233
            if (!sequencePoints.IsDefaultOrEmpty)
P
Pilchie 已提交
234
            {
T
tmat 已提交
235 236
                WriteSequencePoints(sequencePoints, documentIndex);
            }
P
Pilchie 已提交
237

T
tmat 已提交
238 239 240 241
            if (rootScope != null)
            {
                WriteScopes(rootScope);
            }
P
Pilchie 已提交
242

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

T
tmat 已提交
248
            _writer.WriteEndElement();
P
Pilchie 已提交
249 250 251 252 253 254 255 256
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
505

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

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

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

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

T
Tomas Matousek 已提交
525 526
                            _writer.WriteAttributeString("kind", CultureInvariantToString(synthesizedKind));
                            _writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : CultureInvariantToString(syntaxOffset));
527 528 529

                            if (badOrdinal || hasOrdinal)
                            {
T
Tomas Matousek 已提交
530
                                _writer.WriteAttributeString("ordinal", badOrdinal ? "?" : CultureInvariantToString(ordinal));
531 532 533
                            }
                        }

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

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

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

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

571 572
                    // [-1, inf)
                    methodOrdinal--;
T
Tomas Matousek 已提交
573
                    _writer.WriteElementString("methodOrdinal", CultureInvariantToString(methodOrdinal));
574 575

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

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

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

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

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

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

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

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

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

T
tmat 已提交
656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672
        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);
            }
        }

673
        private void WriteScope(ISymUnmanagedScope scope, bool isRoot)
P
Pilchie 已提交
674
        {
675 676 677 678
            _writer.WriteStartElement(isRoot ? "rootScope" : "scope");
            _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset()));
            _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset()));

T
tmat 已提交
679
            if ((_options & PdbToXmlOptions.ExcludeNamespaces) == 0)
P
Pilchie 已提交
680
            {
T
tmat 已提交
681 682 683 684
                foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces())
                {
                    WriteNamespace(@namespace);
                }
685
            }
P
Pilchie 已提交
686

687 688 689 690 691
            WriteLocals(scope);

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

J
Jared Parsons 已提交
694
            _writer.WriteEndElement();
P
Pilchie 已提交
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
        }

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

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

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

B
beep boop 已提交
757 758
                    _writer.WriteStartElement("currentnamespace");
                    _writer.WriteAttributeString("name", target);
T
tmat 已提交
759
                    _writer.WriteEndElement();
P
Pilchie 已提交
760
                    break;
T
tmat 已提交
761

P
Pilchie 已提交
762 763 764 765
                case ImportTargetKind.DefaultNamespace:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
T
tmat 已提交
766

B
beep boop 已提交
767 768
                    _writer.WriteStartElement("defaultnamespace");
                    _writer.WriteAttributeString("name", target);
T
tmat 已提交
769
                    _writer.WriteEndElement();
P
Pilchie 已提交
770
                    break;
T
tmat 已提交
771

P
Pilchie 已提交
772 773 774 775
                case ImportTargetKind.MethodToken:
                    Debug.Assert(alias == null);
                    Debug.Assert(externAlias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
T
tmat 已提交
776

P
Pilchie 已提交
777
                    int token = Convert.ToInt32(target);
B
beep boop 已提交
778
                    _writer.WriteStartElement("importsforward");
P
Pilchie 已提交
779
                    WriteMethodAttributes(token, isReference: true);
T
tmat 已提交
780
                    _writer.WriteEndElement();
P
Pilchie 已提交
781
                    break;
T
tmat 已提交
782

P
Pilchie 已提交
783 784
                case ImportTargetKind.XmlNamespace:
                    Debug.Assert(externAlias == null);
T
tmat 已提交
785

B
beep boop 已提交
786 787 788
                    _writer.WriteStartElement("xmlnamespace");
                    _writer.WriteAttributeString("prefix", alias);
                    _writer.WriteAttributeString("name", target);
P
Pilchie 已提交
789
                    WriteScopeAttribute(scope);
T
tmat 已提交
790
                    _writer.WriteEndElement();
P
Pilchie 已提交
791
                    break;
T
tmat 已提交
792

P
Pilchie 已提交
793 794
                case ImportTargetKind.NamespaceOrType:
                    Debug.Assert(externAlias == null);
T
tmat 已提交
795

B
beep boop 已提交
796 797 798 799
                    _writer.WriteStartElement("alias");
                    _writer.WriteAttributeString("name", alias);
                    _writer.WriteAttributeString("target", target);
                    _writer.WriteAttributeString("kind", "namespace"); // Strange, but retaining to avoid breaking tests.
P
Pilchie 已提交
800
                    WriteScopeAttribute(scope);
T
tmat 已提交
801
                    _writer.WriteEndElement();
P
Pilchie 已提交
802
                    break;
T
tmat 已提交
803

P
Pilchie 已提交
804 805 806
                case ImportTargetKind.Namespace:
                    if (alias != null)
                    {
B
beep boop 已提交
807 808
                        _writer.WriteStartElement("alias");
                        _writer.WriteAttributeString("name", alias);
T
tmat 已提交
809 810 811 812 813
                        if (externAlias != null)
                        {
                            _writer.WriteAttributeString("qualifier", externAlias);
                        }

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

P
Pilchie 已提交
828
                    break;
T
tmat 已提交
829

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

P
Pilchie 已提交
849
                    break;
T
tmat 已提交
850

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

P
Pilchie 已提交
869
                    break;
T
tmat 已提交
870

A
acasey 已提交
871 872 873
                case ImportTargetKind.Defunct:
                    Debug.Assert(alias == null);
                    Debug.Assert(scope == ImportScope.Unspecified);
B
beep boop 已提交
874 875
                    _writer.WriteStartElement("defunct");
                    _writer.WriteAttributeString("name", rawName);
T
tmat 已提交
876
                    _writer.WriteEndElement();
A
acasey 已提交
877
                    break;
T
tmat 已提交
878

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

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

T
tmat 已提交
904
        private void WriteAsyncInfo(ISymUnmanagedAsyncMethod asyncMethod)
P
Pilchie 已提交
905
        {
B
beep boop 已提交
906
            _writer.WriteStartElement("asyncInfo");
P
Pilchie 已提交
907

908 909 910
            var catchOffset = asyncMethod.GetCatchHandlerILOffset();
            if (catchOffset >= 0)
            {
B
beep boop 已提交
911 912 913
                _writer.WriteStartElement("catchHandler");
                _writer.WriteAttributeString("offset", AsILOffset(catchOffset));
                _writer.WriteEndElement();
P
Pilchie 已提交
914 915
            }

B
beep boop 已提交
916
            _writer.WriteStartElement("kickoffMethod");
917
            WriteMethodAttributes(asyncMethod.GetKickoffMethod(), isReference: true);
B
beep boop 已提交
918
            _writer.WriteEndElement();
919 920

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

B
beep boop 已提交
929
            _writer.WriteEndElement();
P
Pilchie 已提交
930 931
        }

932
        private void WriteLocals(ISymUnmanagedScope scope)
P
Pilchie 已提交
933
        {
934
            foreach (ISymUnmanagedVariable l in scope.GetLocals())
P
Pilchie 已提交
935
            {
B
beep boop 已提交
936
                _writer.WriteStartElement("local");
937
                _writer.WriteAttributeString("name", l.GetName());
P
Pilchie 已提交
938

939 940 941 942
                // 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 已提交
943

944 945 946 947
                _writer.WriteAttributeString("il_start", AsILOffset(scope.GetStartOffset()));
                _writer.WriteAttributeString("il_end", AsILOffset(scope.GetEndOffset()));
                _writer.WriteAttributeString("attributes", CultureInvariantToString(l.GetAttributes()));
                _writer.WriteEndElement();
P
Pilchie 已提交
948
            }
949

T
Tomas Matousek 已提交
950
            foreach (ISymUnmanagedConstant constant in scope.GetConstants())
P
Pilchie 已提交
951
            {
T
Tomas Matousek 已提交
952 953 954 955
                string name = constant.GetName();
                var signature = constant.GetSignature();
                object value = constant.GetValue();

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

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

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

                _writer.WriteEndElement();
P
Pilchie 已提交
1038 1039
            }
        }
1040
        
1041 1042
        private unsafe string FormatSignature(ImmutableArray<byte> signature)
        {
1043 1044 1045 1046 1047 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 1125 1126 1127 1128 1129 1130 1131 1132
            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 + "[]";
            }

            public string GetTypeFromDefinition(TypeDefinitionHandle handle)
            {
                var typeDef = _reader.GetTypeDefinition(handle);
                var name = _reader.GetString(typeDef.Name);
                return typeDef.Namespace.IsNil ? name : _reader.GetString(typeDef.Namespace) + "." + name;
            }

            public string GetTypeFromReference(TypeReferenceHandle handle)
            {
                var typeRef = _reader.GetTypeReference(handle);
                var name = _reader.GetString(typeRef.Name);
                return typeRef.Namespace.IsNil ? name : _reader.GetString(typeRef.Namespace) + "." + name;
            }
1133 1134
        }

T
Tomas Matousek 已提交
1135 1136 1137 1138 1139 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
        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 已提交
1174
        private void WriteSequencePoints(ImmutableArray<SymUnmanagedSequencePoint> sequencePoints, IReadOnlyDictionary<string, int> documentIndex)
P
Pilchie 已提交
1175
        {
T
tmat 已提交
1176
            Debug.Assert(!sequencePoints.IsDefaultOrEmpty);
1177 1178

            _writer.WriteStartElement("sequencePoints");
1179

P
Pilchie 已提交
1180
            // Write out sequence points
1181
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1182
            {
B
beep boop 已提交
1183 1184
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1185

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

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

B
beep boop 已提交
1219
                _writer.WriteEndElement();
P
Pilchie 已提交
1220 1221
            }

B
beep boop 已提交
1222
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1223 1224
        }

T
tmat 已提交
1225
        private IReadOnlyDictionary<string, int> BuildDocumentIndex(ImmutableArray<ISymUnmanagedDocument> documents)
P
Pilchie 已提交
1226
        {
T
tmat 已提交
1227 1228 1229 1230
            var index = new Dictionary<string, int>(documents.Length);

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

1234
                // Native PDB doesn't allow no-name documents. SymWriter silently ignores them.
T
tmat 已提交
1235 1236
                // 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.
1237 1238 1239 1240 1241
                if (name.Length == 0)
                {
                    continue;
                }

T
tmat 已提交
1242 1243 1244
                // 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))
1245
                {
T
tmat 已提交
1246
                    index.Add(name, id);
1247 1248
                }

T
tmat 已提交
1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264
                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 已提交
1265 1266 1267 1268
                {
                    continue;
                }

T
tmat 已提交
1269 1270 1271 1272 1273 1274
                if (!hasDocument)
                {
                    _writer.WriteStartElement("files");
                }

                hasDocument = true;
P
Pilchie 已提交
1275

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

B
beep boop 已提交
1278 1279 1280 1281 1282
                _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 已提交
1283

T
tmat 已提交
1284
                var checkSum = string.Concat(doc.GetChecksum().Select(b => $"{b,2:X}, "));
1285 1286 1287

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1288 1289
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1290 1291
                }

T
tmat 已提交
1292
                _writer.WriteEndElement();
P
Pilchie 已提交
1293
            }
1294

T
tmat 已提交
1295
            if (hasDocument)
1296
            {
T
tmat 已提交
1297
                _writer.WriteEndElement();
1298
            }
P
Pilchie 已提交
1299 1300 1301 1302
        }

        private void WriteAllMethodSpans()
        {
T
tmat 已提交
1303 1304 1305 1306 1307
            if ((_options & PdbToXmlOptions.IncludeMethodSpans) == 0)
            {
                return;
            }

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

1310
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1311
            {
1312
                foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1313
                {
B
beep boop 已提交
1314
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1315

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

1318
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1319
                    {
B
beep boop 已提交
1320
                        _writer.WriteStartElement("document");
1321

P
Pilchie 已提交
1322
                        int startLine, endLine;
1323
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1324

B
beep boop 已提交
1325 1326
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1327

B
beep boop 已提交
1328
                        _writer.WriteEndElement();
P
Pilchie 已提交
1329 1330
                    }

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

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

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

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

                try
                {
A
angocke 已提交
1359
                    switch (handle.Kind)
P
Pilchie 已提交
1360
                    {
A
angocke 已提交
1361 1362
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1363 1364
                            break;

A
angocke 已提交
1365
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1366 1367 1368 1369 1370
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

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

                    WriteToken(token);
B
beep boop 已提交
1383
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1384 1385 1386
                }
            }

B
beep boop 已提交
1387
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1388 1389 1390 1391 1392
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1393
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1394
        {
A
angocke 已提交
1395 1396
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1397 1398 1399 1400 1401 1402 1403

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

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

A
angocke 已提交
1404
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1405
        {
B
beep boop 已提交
1406
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1407 1408

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

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

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

T
TomasMatousek 已提交
1425 1426
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1427
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1428
            }
P
Pilchie 已提交
1429 1430 1431 1432
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1433
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1434 1435

            // type name
B
beep boop 已提交
1436
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1437 1438
            if (fullName != null)
            {
B
beep boop 已提交
1439
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1440 1441 1442
            }

            // method name
B
beep boop 已提交
1443
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1444 1445 1446 1447 1448 1449 1450
        }

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

1451
        private static string GetFullTypeName(MetadataReader metadataReader, EntityHandle handle)
P
Pilchie 已提交
1452 1453 1454 1455 1456 1457
        {
            if (handle.IsNil)
            {
                return null;
            }

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

1493
#region Utils
P
Pilchie 已提交
1494 1495 1496

        private void WriteToken(int token)
        {
B
beep boop 已提交
1497
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1498 1499 1500 1501
        }

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

1505
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1506
        {
1507
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1508 1509 1510 1511 1512 1513 1514
        }

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

1515
#endregion
P
Pilchie 已提交
1516 1517
    }
}