PdbToXml.cs 55.1 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;
21
using ImportScope = Microsoft.CodeAnalysis.ImportScope;
P
Pilchie 已提交
22 23 24 25 26 27 28 29 30 31 32 33
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 已提交
34
        private readonly MetadataReader _metadataReader;
35
        private readonly ISymUnmanagedReader _symReader;
B
beep boop 已提交
36 37
        private readonly PdbToXmlOptions _options;
        private readonly XmlWriter _writer;
P
Pilchie 已提交
38 39

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

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

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

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

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

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

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

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

                        return;
                    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                            syntaxOffsetBaseline = -syntaxOffsetBaseline;
                            continue;
                        }
494

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

645
        private void WriteScope(ISymUnmanagedScope scope, bool isRoot)
P
Pilchie 已提交
646
        {
647 648 649 650 651
            _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 已提交
652
            {
653 654
                WriteNamespace(@namespace);
            }
P
Pilchie 已提交
655

656 657 658 659 660
            WriteLocals(scope);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                if (value is int &&
                    (int)value == 0 &&
                    (signature[0] == (byte)ConstantTypeCode.NullReference ||
                     signature[0] == (int)SignatureTypeCode.Object ||
                     signature[0] == (int)SignatureTypeCode.String ||
923
                     (signature.Length > 2 && signature[0] == (int)SignatureTypeCode.GenericTypeInstance && signature[1] == (byte)ConstantTypeCode.NullReference)))
P
Pilchie 已提交
924
                {
T
Tomas Matousek 已提交
925
                    _writer.WriteAttributeString("value", "null");
P
Pilchie 已提交
926

T
Tomas Matousek 已提交
927
                    if (signature[0] == (int)SignatureTypeCode.String)
P
Pilchie 已提交
928
                    {
T
Tomas Matousek 已提交
929 930 931 932 933 934 935 936
                        _writer.WriteAttributeString("type", "String");
                    }
                    else if (signature[0] == (int)SignatureTypeCode.Object)
                    {
                        _writer.WriteAttributeString("type", "Object");
                    }
                    else
                    {
937
                        _writer.WriteAttributeString("signature", FormatSignature(signature));
P
Pilchie 已提交
938
                    }
T
Tomas Matousek 已提交
939 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
                }
                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
                {
968 969 970 971 972 973 974 975 976
                    string str = value as string;
                    if (str != null)
                    {
                        _writer.WriteAttributeString("value", EscapeNonPrintableCharacters(str));
                    }
                    else
                    {
                        _writer.WriteAttributeString("value", string.Format(CultureInfo.InvariantCulture, "{0}", value));
                    }
P
Pilchie 已提交
977

T
Tomas Matousek 已提交
978 979 980 981 982
                    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))
                    {
983
                        _writer.WriteAttributeString("signature", FormatSignature(signature));
T
Tomas Matousek 已提交
984 985 986 987 988 989 990 991 992 993
                    }
                    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 已提交
994
                }
T
Tomas Matousek 已提交
995 996

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

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

        private static string EscapeNonPrintableCharacters(string str)
        {
            StringBuilder sb = new StringBuilder();

            foreach (char c in str)
            {
                bool escape;
                switch (CharUnicodeInfo.GetUnicodeCategory(c))
                {
                    case UnicodeCategory.Control:
                    case UnicodeCategory.OtherNotAssigned:
                    case UnicodeCategory.ParagraphSeparator:
                    case UnicodeCategory.Surrogate:
                        escape = true;
                        break;

                    default:
                        escape = c >= 0xFFFC;
                        break;
                }

                if (escape)
                {
                    sb.AppendFormat("\\u{0:X4}", (int)c);
                }
                else
                {
                    sb.Append(c);
                }
            }

            return sb.ToString();
        }

T
Tomas Matousek 已提交
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
        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 已提交
1082 1083 1084
        // 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).        
1085
        private void WriteSequencePoints(ISymUnmanagedMethod method)
P
Pilchie 已提交
1086
        {
1087
            var sequencePoints = method.GetSequencePoints();
1088 1089 1090 1091 1092 1093
            if (sequencePoints.Length == 0)
            {
                return;
            }

            _writer.WriteStartElement("sequencePoints");
1094

P
Pilchie 已提交
1095
            // Write out sequence points
1096
            foreach (var sequencePoint in sequencePoints)
P
Pilchie 已提交
1097
            {
B
beep boop 已提交
1098 1099
                _writer.WriteStartElement("entry");
                _writer.WriteAttributeString("offset", AsILOffset(sequencePoint.Offset));
P
Pilchie 已提交
1100

1101
                if (sequencePoint.IsHidden)
P
Pilchie 已提交
1102
                {
T
TomasMatousek 已提交
1103 1104
                    if (sequencePoint.StartLine != sequencePoint.EndLine || sequencePoint.StartColumn != 0 || sequencePoint.EndColumn != 0)
                    {
B
beep boop 已提交
1105
                        _writer.WriteAttributeString("hidden", "invalid");
T
TomasMatousek 已提交
1106 1107 1108
                    }
                    else
                    {
B
beep boop 已提交
1109
                        _writer.WriteAttributeString("hidden", XmlConvert.ToString(true));
T
TomasMatousek 已提交
1110 1111 1112 1113
                    }
                }
                else
                {
B
beep boop 已提交
1114 1115 1116 1117
                    _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 已提交
1118 1119
                }

1120
                int documentId;
B
beep boop 已提交
1121 1122
                _fileMapping.TryGetValue(sequencePoint.Document.GetName(), out documentId);
                _writer.WriteAttributeString("document", CultureInvariantToString(documentId));
P
Pilchie 已提交
1123

B
beep boop 已提交
1124
                _writer.WriteEndElement();
P
Pilchie 已提交
1125 1126
            }

B
beep boop 已提交
1127
            _writer.WriteEndElement(); // sequencepoints
P
Pilchie 已提交
1128 1129 1130 1131 1132 1133 1134
        }

        // 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;
1135
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1136
            {
1137
                string name = doc.GetName();
P
Pilchie 已提交
1138

1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
                // 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 已提交
1151
                // Symbol store may give out duplicate documents. We'll fold them here
B
beep boop 已提交
1152
                if (_fileMapping.ContainsKey(name))
P
Pilchie 已提交
1153
                {
B
beep boop 已提交
1154
                    _writer.WriteComment("There is a duplicate entry for: " + name);
P
Pilchie 已提交
1155 1156 1157 1158
                    continue;
                }

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

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

B
beep boop 已提交
1163 1164 1165 1166 1167
                _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 已提交
1168

1169
                var checkSum = string.Concat(doc.GetChecksum().Select(b => string.Format("{0,2:X}", b) + ", "));
1170 1171 1172

                if (!string.IsNullOrEmpty(checkSum))
                {
B
beep boop 已提交
1173 1174
                    _writer.WriteAttributeString("checkSumAlgorithmId", doc.GetHashAlgorithm().ToString());
                    _writer.WriteAttributeString("checkSum", checkSum);
P
Pilchie 已提交
1175 1176
                }

B
beep boop 已提交
1177
                _writer.WriteEndElement(); // file
P
Pilchie 已提交
1178
            }
1179 1180 1181 1182 1183

            if (id > 0)
            {
                _writer.WriteEndElement(); // files
            }
P
Pilchie 已提交
1184 1185 1186 1187
        }

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

1190
            foreach (ISymUnmanagedDocument doc in _symReader.GetDocuments())
P
Pilchie 已提交
1191
            {
1192
                foreach (ISymUnmanagedMethod method in _symReader.GetMethodsInDocument(doc))
P
Pilchie 已提交
1193
                {
B
beep boop 已提交
1194
                    _writer.WriteStartElement("method");
P
Pilchie 已提交
1195

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

1198
                    foreach (var methodDocument in method.GetDocumentsForMethod())
P
Pilchie 已提交
1199
                    {
B
beep boop 已提交
1200
                        _writer.WriteStartElement("document");
1201

P
Pilchie 已提交
1202
                        int startLine, endLine;
1203
                        method.GetSourceExtentInDocument(methodDocument, out startLine, out endLine);
P
Pilchie 已提交
1204

B
beep boop 已提交
1205 1206
                        _writer.WriteAttributeString("startLine", startLine.ToString());
                        _writer.WriteAttributeString("endLine", endLine.ToString());
P
Pilchie 已提交
1207

B
beep boop 已提交
1208
                        _writer.WriteEndElement();
P
Pilchie 已提交
1209 1210
                    }

B
beep boop 已提交
1211
                    _writer.WriteEndElement();
P
Pilchie 已提交
1212 1213 1214
                }
            }

B
beep boop 已提交
1215
            _writer.WriteEndElement();
P
Pilchie 已提交
1216 1217 1218 1219 1220
        }

        // Write out a reference to the entry point method (if one exists)
        private void WriteEntryPoint()
        {
1221
            int token = _symReader.GetUserEntryPoint();
1222
            if (token != 0)
P
Pilchie 已提交
1223
            {
B
beep boop 已提交
1224
                _writer.WriteStartElement("entryPoint");
1225
                WriteMethodAttributes(token, isReference: true);
B
beep boop 已提交
1226
                _writer.WriteEndElement();
P
Pilchie 已提交
1227 1228 1229 1230 1231 1232
            }
        }

        // Write out XML snippet to refer to the given method.
        private void WriteMethodAttributes(int token, bool isReference)
        {
B
beep boop 已提交
1233
            if ((_options & PdbToXmlOptions.ResolveTokens) != 0)
P
Pilchie 已提交
1234 1235 1236 1237 1238
            {
                var handle = MetadataTokens.Handle(token);

                try
                {
A
angocke 已提交
1239
                    switch (handle.Kind)
P
Pilchie 已提交
1240
                    {
A
angocke 已提交
1241 1242
                        case HandleKind.MethodDefinition:
                            WriteResolvedToken((MethodDefinitionHandle)handle, isReference);
P
Pilchie 已提交
1243 1244
                            break;

A
angocke 已提交
1245
                        case HandleKind.MemberReference:
P
Pilchie 已提交
1246 1247 1248 1249 1250
                            WriteResolvedToken((MemberReferenceHandle)handle);
                            break;

                        default:
                            WriteToken(token);
B
beep boop 已提交
1251
                            _writer.WriteAttributeString("error", string.Format("Unexpected token type: {0}", handle.Kind));
P
Pilchie 已提交
1252 1253 1254 1255 1256
                            break;
                    }
                }
                catch (BadImageFormatException e) // TODO: filter
                {
B
beep boop 已提交
1257
                    if ((_options & PdbToXmlOptions.ThrowOnError) != 0)
P
Pilchie 已提交
1258 1259 1260 1261 1262
                    {
                        throw;
                    }

                    WriteToken(token);
B
beep boop 已提交
1263
                    _writer.WriteAttributeString("metadata-error", e.Message);
P
Pilchie 已提交
1264 1265 1266
                }
            }

B
beep boop 已提交
1267
            if ((_options & PdbToXmlOptions.IncludeTokens) != 0)
P
Pilchie 已提交
1268 1269 1270 1271 1272
            {
                WriteToken(token);
            }
        }

A
angocke 已提交
1273
        private static string GetQualifiedMethodName(MetadataReader metadataReader, MethodDefinitionHandle methodHandle)
P
Pilchie 已提交
1274
        {
A
angocke 已提交
1275 1276
            var method = metadataReader.GetMethodDefinition(methodHandle);
            var containingTypeHandle = method.GetDeclaringType();
P
Pilchie 已提交
1277 1278 1279 1280 1281 1282 1283

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

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

A
angocke 已提交
1284
        private void WriteResolvedToken(MethodDefinitionHandle methodHandle, bool isReference)
P
Pilchie 已提交
1285
        {
B
beep boop 已提交
1286
            var method = _metadataReader.GetMethodDefinition(methodHandle);
P
Pilchie 已提交
1287 1288

            // type name
A
angocke 已提交
1289
            var containingTypeHandle = method.GetDeclaringType();
B
beep boop 已提交
1290
            var fullName = GetFullTypeName(_metadataReader, containingTypeHandle);
P
Pilchie 已提交
1291 1292
            if (fullName != null)
            {
B
beep boop 已提交
1293
                _writer.WriteAttributeString(isReference ? "declaringType" : "containingType", fullName);
P
Pilchie 已提交
1294 1295 1296
            }

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

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

T
TomasMatousek 已提交
1305 1306
            if (parameterNames.Length > 0)
            {
B
beep boop 已提交
1307
                _writer.WriteAttributeString("parameterNames", string.Join(", ", parameterNames));
T
TomasMatousek 已提交
1308
            }
P
Pilchie 已提交
1309 1310 1311 1312
        }

        private void WriteResolvedToken(MemberReferenceHandle memberRefHandle)
        {
B
beep boop 已提交
1313
            var memberRef = _metadataReader.GetMemberReference(memberRefHandle);
P
Pilchie 已提交
1314 1315

            // type name
B
beep boop 已提交
1316
            string fullName = GetFullTypeName(_metadataReader, memberRef.Parent);
P
Pilchie 已提交
1317 1318
            if (fullName != null)
            {
B
beep boop 已提交
1319
                _writer.WriteAttributeString("declaringType", fullName);
P
Pilchie 已提交
1320 1321 1322
            }

            // method name
B
beep boop 已提交
1323
            _writer.WriteAttributeString("methodName", _metadataReader.GetString(memberRef.Name));
P
Pilchie 已提交
1324 1325 1326 1327 1328 1329 1330
        }

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

1331
        private static string GetFullTypeName(MetadataReader metadataReader, EntityHandle handle)
P
Pilchie 已提交
1332 1333 1334 1335 1336 1337
        {
            if (handle.IsNil)
            {
                return null;
            }

A
angocke 已提交
1338
            if (handle.Kind == HandleKind.TypeDefinition)
P
Pilchie 已提交
1339
            {
A
angocke 已提交
1340
                var type = metadataReader.GetTypeDefinition((TypeDefinitionHandle)handle);
P
Pilchie 已提交
1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357
                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 已提交
1358
            if (handle.Kind == HandleKind.TypeReference)
P
Pilchie 已提交
1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376
            {
                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 已提交
1377
            _writer.WriteAttributeString("token", AsToken(token));
P
Pilchie 已提交
1378 1379 1380 1381
        }

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

1385
        internal static string AsILOffset(int i)
P
Pilchie 已提交
1386
        {
1387
            return string.Format(CultureInfo.InvariantCulture, "0x{0:x}", i);
P
Pilchie 已提交
1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403
        }

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