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

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 40

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

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

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

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

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

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

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

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

                        return;
                    }

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

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

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

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

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

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

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

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

            return memoryStream.GetBuffer();
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

219
                var rootScope = method.GetRootScope();
P
Pilchie 已提交
220

221
                // The root scope is always empty. The first scope opened by SymWriter is the child of the root scope.
222
                if (rootScope.GetNamespaces().IsEmpty && rootScope.GetLocals().IsEmpty && rootScope.GetConstants().IsEmpty)
P
Pilchie 已提交
223
                {
224
                    foreach (ISymUnmanagedScope child in rootScope.GetScopes())
P
Pilchie 已提交
225
                    {
226
                        WriteScope(child, isRoot: false);
P
Pilchie 已提交
227 228
                    }
                }
229
                else
P
Pilchie 已提交
230
                {
231
                    // This shouldn't be executed for PDBs generated via SymWriter.
232
                    WriteScope(rootScope, isRoot: true);
P
Pilchie 已提交
233 234
                }

235
                WriteAsyncInfo(method);
P
Pilchie 已提交
236 237
            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
495

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

621 622 623 624 625 626 627
                            closureOrdinal -= 2;

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

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

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

657 658 659 660 661
            WriteLocals(scope);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

899 900 901 902
                // 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 已提交
903

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

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

B
beep boop 已提交
916
                _writer.WriteStartElement("constant");
T
Tomas Matousek 已提交
917 918 919 920 921 922 923
                _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 ||
924
                     (signature.Length > 2 && signature[0] == (int)SignatureTypeCode.GenericTypeInstance && signature[1] == (byte)ConstantTypeCode.NullReference)))
P
Pilchie 已提交
925
                {
T
Tomas Matousek 已提交
926
                    _writer.WriteAttributeString("value", "null");
P
Pilchie 已提交
927

T
Tomas Matousek 已提交
928
                    if (signature[0] == (int)SignatureTypeCode.String)
P
Pilchie 已提交
929
                    {
T
Tomas Matousek 已提交
930 931 932 933 934 935 936 937
                        _writer.WriteAttributeString("type", "String");
                    }
                    else if (signature[0] == (int)SignatureTypeCode.Object)
                    {
                        _writer.WriteAttributeString("type", "Object");
                    }
                    else
                    {
938
                        _writer.WriteAttributeString("signature", FormatSignature(signature));
P
Pilchie 已提交
939
                    }
T
Tomas Matousek 已提交
940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968
                }
                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
                {
969 970 971
                    string str = value as string;
                    if (str != null)
                    {
T
Tomas Matousek 已提交
972
                        _writer.WriteAttributeString("value", StringUtilities.EscapeNonPrintableCharacters(str));
973 974 975 976 977
                    }
                    else
                    {
                        _writer.WriteAttributeString("value", string.Format(CultureInfo.InvariantCulture, "{0}", value));
                    }
P
Pilchie 已提交
978

T
Tomas Matousek 已提交
979
                    var runtimeType = GetConstantRuntimeType(signature);
J
Jared Parsons 已提交
980
                    if (runtimeType == null &&
T
Tomas Matousek 已提交
981 982 983
                        (value is sbyte || value is byte || value is short || value is ushort ||
                         value is int || value is uint || value is long || value is ulong))
                    {
984
                        _writer.WriteAttributeString("signature", FormatSignature(signature));
T
Tomas Matousek 已提交
985 986 987 988 989 990 991 992 993 994
                    }
                    else if (runtimeType == value.GetType())
                    {
                        _writer.WriteAttributeString("type", ((SignatureTypeCode)signature[0]).ToString());
                    }
                    else
                    {
                        _writer.WriteAttributeString("runtime-type", value.GetType().Name);
                        _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray()));
                    }
P
Pilchie 已提交
995
                }
T
Tomas Matousek 已提交
996 997

                _writer.WriteEndElement();
P
Pilchie 已提交
998 999
            }
        }
1000
        
1001 1002
        private unsafe string FormatSignature(ImmutableArray<byte> signature)
        {
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 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
            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;
            }
1093 1094
        }

T
Tomas Matousek 已提交
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 1133
        private static Type GetConstantRuntimeType(ImmutableArray<byte> signature)
        {
            switch ((SignatureTypeCode)signature[0])
            {
                case SignatureTypeCode.Boolean:
                case SignatureTypeCode.Byte:
                case SignatureTypeCode.SByte:
                case SignatureTypeCode.Int16:
                    return typeof(short);

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

                case SignatureTypeCode.Int32:
                    return typeof(int);

                case SignatureTypeCode.UInt32:
                    return typeof(uint);

                case SignatureTypeCode.Int64:
                    return typeof(long);

                case SignatureTypeCode.UInt64:
                    return typeof(ulong);

                case SignatureTypeCode.Single:
                    return typeof(float);

                case SignatureTypeCode.Double:
                    return typeof(double);

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

            return null;
        }

P
Pilchie 已提交
1134 1135 1136
        // Write the sequence points for the given method
        // Sequence points are the map between IL offsets and source lines.
        // A single method could span multiple files (use C#'s #line directive to see for yourself).        
1137
        private void WriteSequencePoints(ISymUnmanagedMethod method)
P
Pilchie 已提交
1138
        {
1139
            var sequencePoints = method.GetSequencePoints();
1140 1141 1142 1143 1144 1145
            if (sequencePoints.Length == 0)
            {
                return;
            }

            _writer.WriteStartElement("sequencePoints");
1146

P
Pilchie 已提交
1147
            // Write out sequence points
1148
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1149
            {
B
beep boop 已提交
1150 1151
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1152

1153
                if (sequencePoint.IsHidden)
P
Pilchie 已提交
1154
                {
T
TomasMatousek 已提交
1155 1156
                    if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0)
                    {
B
beep boop 已提交
1157
                        _writer.WriteAttributeString("hidden", "invalid");
T
TomasMatousek 已提交
1158 1159 1160
                    }
                    else
                    {
B
beep boop 已提交
1161
                        _writer.WriteAttributeString("hidden", XmlConvert.ToString(true));
T
TomasMatousek 已提交
1162 1163 1164 1165
                    }
                }
                else
                {
B
beep boop 已提交
1166 1167 1168 1169
                    _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 已提交
1170 1171
                }

1172
                int documentId;
B
beep boop 已提交
1173 1174
                _fileMapping.TryGetValue(sequencePoint.Document.GetName(), out documentId);
                _writer.WriteAttributeString("document", CultureInvariantToString(documentId));
P
Pilchie 已提交
1175

B
beep boop 已提交
1176
                _writer.WriteEndElement();
P
Pilchie 已提交
1177 1178
            }

B
beep boop 已提交
1179
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1180 1181 1182 1183 1184 1185 1186
        }

        // Write all docs, and add to the m_fileMapping list.
        // Other references to docs will then just refer to this list.
        private void WriteDocList()
        {
            int id = 0;
1187
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1188
            {
1189
                string name = doc.GetName();
P
Pilchie 已提交
1190

1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
                // Native PDB doesn't allow no-name documents. SymWriter silently ignores them.
                // In Portable PDB all methods must be contained in a document, whose name may be empty. Skip such document.
                if (name.Length == 0)
                {
                    continue;
                }

                if (id == 0)
                {
                    _writer.WriteStartElement("files");
                }

P
Pilchie 已提交
1203
                // Symbol store may give out duplicate documents. We'll fold them here
B
beep boop 已提交
1204
                if (_fileMapping.ContainsKey(name))
P
Pilchie 已提交
1205
                {
B
beep boop 已提交
1206
                    _writer.WriteComment("There is a duplicate entry for: " + name);
P
Pilchie 已提交
1207 1208 1209 1210
                    continue;
                }

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

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

B
beep boop 已提交
1215 1216 1217 1218 1219
                _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 已提交
1220

1221
                var checkSum = string.Concat(doc.GetChecksum().Select(b => string.Format("{0,2:X}", b) + ", "));
1222 1223 1224

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1225 1226
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1227 1228
                }

B
beep boop 已提交
1229
                _writer.WriteEndElement(); // file
P
Pilchie 已提交
1230
            }
1231 1232 1233 1234 1235

            if (id > 0)
            {
                _writer.WriteEndElement(); // files
            }
P
Pilchie 已提交
1236 1237 1238 1239
        }

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

1242
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1243
            {
1244
                foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1245
                {
B
beep boop 已提交
1246
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1247

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

1250
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1251
                    {
B
beep boop 已提交
1252
                        _writer.WriteStartElement("document");
1253

P
Pilchie 已提交
1254
                        int startLine, endLine;
1255
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1256

B
beep boop 已提交
1257 1258
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1259

B
beep boop 已提交
1260
                        _writer.WriteEndElement();
P
Pilchie 已提交
1261 1262
                    }

B
beep boop 已提交
1263
                    _writer.WriteEndElement();
P
Pilchie 已提交
1264 1265 1266
                }
            }

B
beep boop 已提交
1267
            _writer.WriteEndElement();
P
Pilchie 已提交
1268 1269 1270 1271 1272
        }

        // Write out a reference to the entry point method (if one exists)
        private void WriteEntryPoint()
        {
1273
            int token = _symReader.GetUserEntryPoint();
1274
            if (token != 0)
P
Pilchie 已提交
1275
            {
B
beep boop 已提交
1276
                _writer.WriteStartElement("entryPoint");
1277
                WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
1278
                _writer.WriteEndElement();
P
Pilchie 已提交
1279 1280 1281 1282 1283 1284
            }
        }

        // Write out XML snippet to refer to the given method.
        private void WriteMethodAttributes(int token, bool isReference)
        {
B
beep boop 已提交
1285
            if ((_options & PdbToXmlOptions.ResolveTokens) != 0)
P
Pilchie 已提交
1286 1287 1288 1289 1290
            {
                var handle = MetadataTokens.Handle(token);

                try
                {
A
angocke 已提交
1291
                    switch (handle.Kind)
P
Pilchie 已提交
1292
                    {
A
angocke 已提交
1293 1294
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1295 1296
                            break;

A
angocke 已提交
1297
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1298 1299 1300 1301 1302
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

                        default:
                            WriteToken(token);
B
beep boop 已提交
1303
                            _writer.WriteAttributeString("error", string.Format("Unexpected token type: {0}", handle.Kind));
P
Pilchie 已提交
1304 1305 1306 1307 1308
                            break;
                    }
                }
                catch (BadImageFormatException e) // TODO: filter
                {
B
beep boop 已提交
1309
                    if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
1310 1311 1312 1313 1314
                    {
                        throw;
                    }

                    WriteToken(token);
B
beep boop 已提交
1315
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1316 1317 1318
                }
            }

B
beep boop 已提交
1319
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1320 1321 1322 1323 1324
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1325
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1326
        {
A
angocke 已提交
1327 1328
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1329 1330 1331 1332 1333 1334 1335

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

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

A
angocke 已提交
1336
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1337
        {
B
beep boop 已提交
1338
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1339 1340

            // type name
A
angocke 已提交
1341
            var containingTypeHandle = method.GetDeclaringType();
B
beep boop 已提交
1342
            var fullName = GetFullTypeName(_metadataReader, containingTypeHandle);
P
Pilchie 已提交
1343 1344
            if (fullName != null)
            {
B
beep boop 已提交
1345
                _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName);
P
Pilchie 已提交
1346 1347 1348
            }

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

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

T
TomasMatousek 已提交
1357 1358
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1359
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1360
            }
P
Pilchie 已提交
1361 1362 1363 1364
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1365
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1366 1367

            // type name
B
beep boop 已提交
1368
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1369 1370
            if (fullName != null)
            {
B
beep boop 已提交
1371
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1372 1373 1374
            }

            // method name
B
beep boop 已提交
1375
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1376 1377 1378 1379 1380 1381 1382
        }

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

1383
        private static string GetFullTypeName(MetadataReader metadataReader, EntityHandle handle)
P
Pilchie 已提交
1384 1385 1386 1387 1388 1389
        {
            if (handle.IsNil)
            {
                return null;
            }

A
angocke 已提交
1390
            if (handle.Kind == HandleKind.TypeDefinition)
P
Pilchie 已提交
1391
            {
A
angocke 已提交
1392
                var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle);
P
Pilchie 已提交
1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409
                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 已提交
1410
            if (handle.Kind == HandleKind.TypeReference)
P
Pilchie 已提交
1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424
            {
                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)));
        }

1425
#region Utils
P
Pilchie 已提交
1426 1427 1428

        private void WriteToken(int token)
        {
B
beep boop 已提交
1429
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1430 1431 1432 1433
        }

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

1437
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1438
        {
1439
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452
        }

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

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

1453
#endregion
P
Pilchie 已提交
1454 1455
    }
}