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 12 13 14 15

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Xml;
16 17
using Microsoft.CodeAnalysis;
using Microsoft.DiaSymReader;
T
Tomas Matousek 已提交
18
using Roslyn.Utilities;
19
using CDI = Microsoft.CodeAnalysis.CustomDebugInfoReader;
20
using CDIC = Microsoft.Cci.CustomDebugInfoConstants;
P
Pilchie 已提交
21 22 23 24 25 26 27 28 29 30 31 32
using PooledStringBuilder = Microsoft.CodeAnalysis.Collections.PooledStringBuilder;

namespace Roslyn.Test.PdbUtilities
{
    /// <summary>
    /// Class to write out XML for a PDB.
    /// </summary>
    public sealed class PdbToXmlConverter
    {
        // For printing integers in a standard hex format.
        private const string IntHexFormat = "0x{0:X}";

B
beep boop 已提交
33
        private readonly MetadataReader _metadataReader;
34
        private readonly ISymUnmanagedReader _symReader;
B
beep boop 已提交
35 36
        private readonly PdbToXmlOptions _options;
        private readonly XmlWriter _writer;
P
Pilchie 已提交
37 38

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

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

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

            return writer.ToString();
        }

        public static string ToXml(Stream pdbStream, Stream peStream, PdbToXmlOptions options = PdbToXmlOptions.ResolveTokens, string methodName = null)
        {
            var writer = new StringWriter();
            ToXml(writer, pdbStream, peStream, options, methodName);
            return writer.ToString();
        }

        public static string ToXml(Stream pdbStream, byte[] peImage, PdbToXmlOptions options = PdbToXmlOptions.ResolveTokens, string methodName = null)
        {
            var writer = new StringWriter();
            ToXml(writer, pdbStream, new MemoryStream(peImage), options, methodName);
            return writer.ToString();
        }

        public unsafe static void ToXml(TextWriter xmlWriter, Stream pdbStream, Stream peStream, PdbToXmlOptions options = PdbToXmlOptions.Default, string methodName = null)
        {
A
angocke 已提交
78
            IEnumerable<MethodDefinitionHandle> methodHandles;
P
Pilchie 已提交
79 80 81 82 83 84 85
            var headers = new PEHeaders(peStream);
            byte[] metadata = new byte[headers.MetadataSize];
            peStream.Seek(headers.MetadataStartOffset, SeekOrigin.Begin);
            peStream.Read(metadata, 0, headers.MetadataSize);

            fixed (byte* metadataPtr = metadata)
            {
A
angocke 已提交
86
                var metadataReader = new MetadataReader(metadataPtr, metadata.Length);
P
Pilchie 已提交
87 88 89 90 91 92 93

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

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

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

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

                        return;
                    }

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

                ToXml(xmlWriter, pdbStream, metadataReader, options, methodHandles);
            }
        }

        /// <summary>
        /// Load the PDB given the parameters at the ctor and spew it out to the XmlWriter specified
        /// at the ctor.
        /// </summary>
A
angocke 已提交
128
        private static void ToXml(TextWriter xmlWriter, Stream pdbStream, MetadataReader metadataReaderOpt, PdbToXmlOptions options, IEnumerable<MethodDefinitionHandle> methodHandles)
P
Pilchie 已提交
129 130 131 132 133 134 135
        {
            Debug.Assert(pdbStream != null);
            Debug.Assert((options & PdbToXmlOptions.ResolveTokens) == 0 || metadataReaderOpt != null);

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

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

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

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

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

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

            return memoryStream.GetBuffer();
        }

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

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

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

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

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

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

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

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

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

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

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

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

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

                // TODO (tomat): Ideally this would be done in a separate test helper, not in PdbToXml.
                // verify ISymUnmanagedMethod APIs:
                var expectedSlotNames = new Dictionary<int, ImmutableArray<string>>();
220
                WriteLocals(method, expectedSlotNames);
P
Pilchie 已提交
221

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

                Debug.Assert(actualSlotNames.Length == (expectedSlotNames.Count == 0 ? 0 : expectedSlotNames.Keys.Max() + 1));

                int i = 0;
                foreach (var slotName in actualSlotNames)
                {
                    if (slotName == null)
                    {
                        Debug.Assert(!expectedSlotNames.ContainsKey(i));
                    }
                    else
                    {
                        Debug.Assert(expectedSlotNames[i].Contains(slotName));
                    }

                    i++;
                }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
507

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

P
Pilchie 已提交
658 659
        private void WriteScopes(ISymUnmanagedScope scope)
        {
B
beep boop 已提交
660
            _writer.WriteStartElement("scope");
P
Pilchie 已提交
661
            {
B
beep boop 已提交
662 663
                _writer.WriteAttributeString("startOffset", AsILOffset(scope.GetStartOffset()));
                _writer.WriteAttributeString("endOffset", AsILOffset(scope.GetEndOffset()));
P
Pilchie 已提交
664 665 666 667 668 669 670 671 672 673 674 675 676
                {
                    foreach (ISymUnmanagedNamespace @namespace in scope.GetNamespaces())
                    {
                        WriteNamespace(@namespace);
                    }

                    WriteLocalsHelper(scope, slotNames: null, includeChildScopes: false);
                }
                foreach (ISymUnmanagedScope child in scope.GetScopes())
                {
                    WriteScopes(child);
                }
            }
B
beep boop 已提交
677
            _writer.WriteEndElement(); // </scope>
P
Pilchie 已提交
678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694
        }

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

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

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

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

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

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

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

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

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

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

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

        // Write all the locals in the given method out to an XML file.
        // Since the symbol store represents the locals in a recursive scope structure, we need to walk a tree.
        // Although the locals are technically a hierarchy (based off nested scopes), it's easiest for clients
        // if we present them as a linear list. We will provide the range for each local's scope so that somebody
        // could reconstruct an approximation of the scope tree. The reconstruction may not be exact.
        // (Note this would still break down if you had an empty scope nested in another scope.
        private void WriteLocals(ISymUnmanagedMethod method, Dictionary<int, ImmutableArray<string>> slotNames)
        {
B
beep boop 已提交
913
            _writer.WriteStartElement("locals");
914 915
            // If there are no locals, then this element will just be empty.
            WriteLocalsHelper(method.GetRootScope(), slotNames, includeChildScopes: true);
B
beep boop 已提交
916
            _writer.WriteEndElement();
P
Pilchie 已提交
917 918
        }

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

                    // Each local maps to a "IL Index" or "slot" number. 
                    // The index is not necessarily unique. Several locals may refer to the same slot. 
                    // It just means that the same local is known under different names inside the same or different scopes.
                    // This index is what you pass to ICorDebugILFrame::GetLocalVariable() to get
                    // a specific local variable. 
                    // NOTE: VB emits "fake" locals for resumable locals which are actually backed by fields.
                    //       These locals always map to the slot #0 which is just a valid number that is 
                    //       not used. Only scoping information is used by EE in this case.
935
                    int slot = l.GetSlot();
B
beep boop 已提交
936
                    _writer.WriteAttributeString("il_index", CultureInvariantToString(slot));
P
Pilchie 已提交
937 938 939 940 941 942 943 944 945

                    bool reusingSlot = false;

                    // collect slot names so that we can verify ISymUnmanagedReader APIs
                    if (slotNames != null)
                    {
                        ImmutableArray<string> existingNames;
                        if (slotNames.TryGetValue(slot, out existingNames))
                        {
946
                            slotNames[slot] = existingNames.Add(l.GetName());
P
Pilchie 已提交
947 948 949 950
                            reusingSlot = true;
                        }
                        else
                        {
951
                            slotNames.Add(slot, ImmutableArray.Create(l.GetName()));
P
Pilchie 已提交
952 953 954 955
                        }
                    }

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

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

T
Tomas Matousek 已提交
968
            foreach (ISymUnmanagedConstant constant in scope.GetConstants())
P
Pilchie 已提交
969
            {
T
Tomas Matousek 已提交
970 971 972 973
                string name = constant.GetName();
                var signature = constant.GetSignature();
                object value = constant.GetValue();

B
beep boop 已提交
974
                _writer.WriteStartElement("constant");
T
Tomas Matousek 已提交
975 976 977 978 979 980 981 982
                _writer.WriteAttributeString("name", name);

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

T
Tomas Matousek 已提交
988
                    if (signature[0] == (int)SignatureTypeCode.String)
P
Pilchie 已提交
989
                    {
T
Tomas Matousek 已提交
990 991 992 993 994 995 996 997 998 999 1000 1001 1002
                        _writer.WriteAttributeString("type", "String");
                    }
                    else if (signature[0] == (int)SignatureTypeCode.Object)
                    {
                        _writer.WriteAttributeString("type", "Object");
                    }
                    else
                    {
                        // TODO:
                        // A null reference, the type is encoded in the signature. 
                        // Ideally we would parse the signature and display the target type name. 
                        // That requires MetadataReader vNext though.
                        _writer.WriteAttributeString("signature", BitConverter.ToString(signature.ToArray()));
P
Pilchie 已提交
1003
                    }
T
Tomas Matousek 已提交
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
                }
                else if (value == null)
                {
                    // empty string
                    if (signature[0] == (byte)SignatureTypeCode.String)
                    {
                        _writer.WriteAttributeString("value", "");
                        _writer.WriteAttributeString("type", "String");
                    }
                    else
                    {
                        _writer.WriteAttributeString("value", "null");
                        _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray()));
                    }
                }
                else if (value is decimal)
                {
                    // TODO: check that the signature is a TypeRef
                    _writer.WriteAttributeString("value", ((decimal)value).ToString(CultureInfo.InvariantCulture));
                    _writer.WriteAttributeString("type", value.GetType().Name);
                }
                else if (value is double && signature[0] != (byte)SignatureTypeCode.Double)
                {
                    // TODO: check that the signature is a TypeRef
                    _writer.WriteAttributeString("value", DateTimeUtilities.ToDateTime((double)value).ToString(CultureInfo.InvariantCulture));
                    _writer.WriteAttributeString("type", "DateTime");
                }
                else
                {
                    _writer.WriteAttributeString("value", (value as string)?.Replace("\0", "U+0000") ?? string.Format(CultureInfo.InvariantCulture, "{0}", value));
P
Pilchie 已提交
1034

T
Tomas Matousek 已提交
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054
                    var runtimeType = GetConstantRuntimeType(signature);
                    if (runtimeType == null && 
                        (value is sbyte || value is byte || value is short || value is ushort ||
                         value is int || value is uint || value is long || value is ulong))
                    {
                        // TODO:
                        // Enum.
                        // Ideally we would parse the signature and display the target type name. 
                        // That requires MetadataReader vNext though.
                        _writer.WriteAttributeString("signature", BitConverter.ToString(signature.ToArray()));
                    }
                    else if (runtimeType == value.GetType())
                    {
                        _writer.WriteAttributeString("type", ((SignatureTypeCode)signature[0]).ToString());
                    }
                    else
                    {
                        _writer.WriteAttributeString("runtime-type", value.GetType().Name);
                        _writer.WriteAttributeString("unknown-signature", BitConverter.ToString(signature.ToArray()));
                    }
P
Pilchie 已提交
1055
                }
T
Tomas Matousek 已提交
1056 1057

                _writer.WriteEndElement();
P
Pilchie 已提交
1058
            }
1059

P
Pilchie 已提交
1060 1061
            if (includeChildScopes)
            {
1062
                foreach (ISymUnmanagedScope childScope in scope.GetScopes())
P
Pilchie 已提交
1063 1064 1065 1066 1067 1068
                {
                    WriteLocalsHelper(childScope, slotNames, includeChildScopes);
                }
            }
        }

T
Tomas Matousek 已提交
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
        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 已提交
1108 1109 1110
        // 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).        
1111
        private void WriteSequencePoints(ISymUnmanagedMethod method)
P
Pilchie 已提交
1112
        {
B
beep boop 已提交
1113
            _writer.WriteStartElement("sequencePoints");
P
Pilchie 已提交
1114

1115
            var sequencePoints = method.GetSequencePoints();
1116

P
Pilchie 已提交
1117
            // Write out sequence points
1118
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1119
            {
B
beep boop 已提交
1120 1121
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1122

1123
                if (sequencePoint.IsHidden)
P
Pilchie 已提交
1124
                {
T
TomasMatousek 已提交
1125 1126
                    if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0)
                    {
B
beep boop 已提交
1127
                        _writer.WriteAttributeString("hidden", "invalid");
T
TomasMatousek 已提交
1128 1129 1130
                    }
                    else
                    {
B
beep boop 已提交
1131
                        _writer.WriteAttributeString("hidden", XmlConvert.ToString(true));
T
TomasMatousek 已提交
1132 1133 1134 1135
                    }
                }
                else
                {
B
beep boop 已提交
1136 1137 1138 1139
                    _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 已提交
1140 1141
                }

1142
                int documentId;
B
beep boop 已提交
1143 1144
                _fileMapping.TryGetValue(sequencePoint.Document.GetName(), out documentId);
                _writer.WriteAttributeString("document", CultureInvariantToString(documentId));
P
Pilchie 已提交
1145

B
beep boop 已提交
1146
                _writer.WriteEndElement();
P
Pilchie 已提交
1147 1148
            }

B
beep boop 已提交
1149
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1150 1151 1152 1153 1154 1155
        }

        // Write all docs, and add to the m_fileMapping list.
        // Other references to docs will then just refer to this list.
        private void WriteDocList()
        {
1156
            var documents = _symReader.GetDocuments();
1157
            if (documents.Length == 0)
P
Pilchie 已提交
1158 1159 1160 1161 1162
            {
                return;
            }

            int id = 0;
B
beep boop 已提交
1163
            _writer.WriteStartElement("files");
1164
            foreach (ISymUnmanagedDocument doc in documents)
P
Pilchie 已提交
1165
            {
1166
                string name = doc.GetName();
P
Pilchie 已提交
1167 1168

                // Symbol store may give out duplicate documents. We'll fold them here
B
beep boop 已提交
1169
                if (_fileMapping.ContainsKey(name))
P
Pilchie 已提交
1170
                {
B
beep boop 已提交
1171
                    _writer.WriteComment("There is a duplicate entry for: " + name);
P
Pilchie 已提交
1172 1173 1174 1175
                    continue;
                }

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

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

B
beep boop 已提交
1180 1181 1182 1183 1184
                _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 已提交
1185

1186
                var checkSum = string.Concat(doc.GetChecksum().Select(b => string.Format("{0,2:X}", b) + ", "));
1187 1188 1189

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1190 1191
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1192 1193
                }

B
beep boop 已提交
1194
                _writer.WriteEndElement(); // file
P
Pilchie 已提交
1195
            }
B
beep boop 已提交
1196
            _writer.WriteEndElement(); // files
P
Pilchie 已提交
1197 1198 1199 1200
        }

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

1203
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1204
            {
1205
                foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1206
                {
B
beep boop 已提交
1207
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1208

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

1211
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1212
                    {
B
beep boop 已提交
1213
                        _writer.WriteStartElement("document");
1214

P
Pilchie 已提交
1215
                        int startLine, endLine;
1216
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1217

B
beep boop 已提交
1218 1219
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1220

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

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

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

        // Write out a reference to the entry point method (if one exists)
        private void WriteEntryPoint()
        {
1234
            int token = _symReader.GetUserEntryPoint();
1235
            if (token != 0)
P
Pilchie 已提交
1236
            {
B
beep boop 已提交
1237
                _writer.WriteStartElement("entryPoint");
1238
                WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
1239
                _writer.WriteEndElement();
P
Pilchie 已提交
1240 1241 1242 1243 1244 1245
            }
        }

        // Write out XML snippet to refer to the given method.
        private void WriteMethodAttributes(int token, bool isReference)
        {
B
beep boop 已提交
1246
            if ((_options & PdbToXmlOptions.ResolveTokens) != 0)
P
Pilchie 已提交
1247 1248 1249 1250 1251
            {
                var handle = MetadataTokens.Handle(token);

                try
                {
A
angocke 已提交
1252
                    switch (handle.Kind)
P
Pilchie 已提交
1253
                    {
A
angocke 已提交
1254 1255
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1256 1257
                            break;

A
angocke 已提交
1258
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1259 1260 1261 1262 1263
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

                        default:
                            WriteToken(token);
B
beep boop 已提交
1264
                            _writer.WriteAttributeString("error", string.Format("Unexpected token type: {0}", handle.Kind));
P
Pilchie 已提交
1265 1266 1267 1268 1269
                            break;
                    }
                }
                catch (BadImageFormatException e) // TODO: filter
                {
B
beep boop 已提交
1270
                    if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
1271 1272 1273 1274 1275
                    {
                        throw;
                    }

                    WriteToken(token);
B
beep boop 已提交
1276
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1277 1278 1279
                }
            }

B
beep boop 已提交
1280
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1281 1282 1283 1284 1285
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1286
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1287
        {
A
angocke 已提交
1288 1289
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1290 1291 1292 1293 1294 1295 1296

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

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

A
angocke 已提交
1297
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1298
        {
B
beep boop 已提交
1299
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1300 1301

            // type name
A
angocke 已提交
1302
            var containingTypeHandle = method.GetDeclaringType();
B
beep boop 已提交
1303
            var fullName = GetFullTypeName(_metadataReader, containingTypeHandle);
P
Pilchie 已提交
1304 1305
            if (fullName != null)
            {
B
beep boop 已提交
1306
                _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName);
P
Pilchie 已提交
1307 1308 1309
            }

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

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

T
TomasMatousek 已提交
1318 1319
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1320
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1321
            }
P
Pilchie 已提交
1322 1323 1324 1325
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1326
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1327 1328

            // type name
B
beep boop 已提交
1329
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1330 1331
            if (fullName != null)
            {
B
beep boop 已提交
1332
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1333 1334 1335
            }

            // method name
B
beep boop 已提交
1336
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350
        }

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

        private static string GetFullTypeName(MetadataReader metadataReader, Handle handle)
        {
            if (handle.IsNil)
            {
                return null;
            }

A
angocke 已提交
1351
            if (handle.Kind == HandleKind.TypeDefinition)
P
Pilchie 已提交
1352
            {
A
angocke 已提交
1353
                var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle);
P
Pilchie 已提交
1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370
                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 已提交
1371
            if (handle.Kind == HandleKind.TypeReference)
P
Pilchie 已提交
1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389
            {
                var typeRef = metadataReader.GetTypeReference((TypeReferenceHandle)handle);
                string name = metadataReader.GetString(typeRef.Name);
                if (typeRef.Namespace.IsNil)
                {
                    return name;
                }

                return metadataReader.GetString(typeRef.Namespace) + "." + name;
            }

            return string.Format("<unexpected token kind: {0}>", AsToken(metadataReader.GetToken(handle)));
        }

        #region Utils

        private void WriteToken(int token)
        {
B
beep boop 已提交
1390
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1391 1392 1393 1394
        }

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

1398
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1399
        {
1400
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1401 1402 1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416
        }

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

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

        #endregion
    }
}