/// <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> private void WriteDynamicLocalsCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.DynamicLocals); writer.WriteStartElement("dynamicLocals"); var buckets = CDI.DecodeDynamicLocalsRecord(record.Data); 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); } writer.WriteStartElement("bucket"); writer.WriteAttributeString("flagCount", flagCount.ToString()); writer.WriteAttributeString("flags", pooled.ToStringAndFree()); writer.WriteAttributeString("slotId", bucket.SlotId.ToString()); writer.WriteAttributeString("localName", bucket.Name); writer.WriteEndElement(); //bucket } writer.WriteEndElement(); //dynamicLocals }
/// <summary> /// Contains a name string. /// TODO: comment when the structure is understood. /// </summary> /// <remarks> /// Appears when are iterator methods. /// </remarks> private void WriteForwardIteratorCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardIterator); writer.WriteStartElement("forwardIterator"); string name = CDI.DecodeForwardIteratorRecord(record.Data); writer.WriteAttributeString("name", name); writer.WriteEndElement(); //forwardIterator }
/// <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> private void WriteForwardToModuleCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.ForwardToModuleInfo); writer.WriteStartElement("forwardToModule"); int token = CDI.DecodeForwardRecord(record.Data); WriteMethodAttributes(token, isReference: true); writer.WriteEndElement(); //forwardToModule }
/// <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> private void WriteUsingCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.UsingInfo); writer.WriteStartElement("using"); ImmutableArray <short> counts = CDI.DecodeUsingRecord(record.Data); foreach (short importCount in counts) { writer.WriteStartElement("namespace"); writer.WriteAttributeString("usingCount", importCount.ToString()); writer.WriteEndElement(); //namespace } writer.WriteEndElement(); //using }
/// <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> private void WriteStatemachineHoistedLocalScopesCustomDebugInfo(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.StateMachineHoistedLocalScopes); writer.WriteStartElement("hoistedLocalScopes"); var scopes = CDI.DecodeStateMachineHoistedLocalScopesRecord(record.Data); foreach (StateMachineHoistedLocalScope scope in scopes) { writer.WriteStartElement("slot"); writer.WriteAttributeString("startOffset", AsILOffset(scope.StartOffset)); writer.WriteAttributeString("endOffset", AsILOffset(scope.EndOffset)); writer.WriteEndElement(); //bucket } writer.WriteEndElement(); }
/// <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> private void WriteUnknownCustomDebugInfo(CustomDebugInfoRecord record) { writer.WriteStartElement("unknown"); writer.WriteAttributeString("kind", record.Kind.ToString()); writer.WriteAttributeString("version", record.Version.ToString()); PooledStringBuilder pooled = PooledStringBuilder.GetInstance(); StringBuilder builder = pooled.Builder; foreach (byte b in record.Data) { builder.AppendFormat("{0:X2}", b); } writer.WriteAttributeString("payload", pooled.ToStringAndFree()); writer.WriteEndElement(); //unknown }
private unsafe void WriteEditAndContinueLocalSlotMap(CustomDebugInfoRecord record) { Debug.Assert(record.Kind == CustomDebugInfoKind.EditAndContinueLocalSlotMap); writer.WriteStartElement("encLocalSlotMap"); 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) { break; } if (b == 0xfe) { syntaxOffsetBaseline = -blobReader.ReadCompressedInteger(); writer.WriteElementString("baseline", syntaxOffsetBaseline.ToString()); continue; } writer.WriteStartElement("slot"); if (b == 0) { // short-lived temp, no info writer.WriteAttributeString("kind", "temp"); } else { int synthesizedKind = (b & 0x3f) - 1; bool hasOrdinal = (b & (1 << 7)) != 0; int syntaxOffset; bool badSyntaxOffset = !blobReader.TryReadCompressedInteger(out syntaxOffset); syntaxOffset += syntaxOffsetBaseline; int ordinal = 0; bool badOrdinal = hasOrdinal && !blobReader.TryReadCompressedInteger(out ordinal); writer.WriteAttributeString("kind", synthesizedKind.ToString()); writer.WriteAttributeString("offset", badSyntaxOffset ? "?" : syntaxOffset.ToString()); if (badOrdinal || hasOrdinal) { writer.WriteAttributeString("ordinal", badOrdinal ? "?" : ordinal.ToString()); } } writer.WriteEndElement(); } } writer.WriteEndElement(); //encLocalSlotMap }