internal static void SerializeCustomDebugInformation( ref CustomDebugInfoEncoder encoder, EditAndContinueMethodDebugInformation debugInfo ) { // PERF: note that we pass debugInfo as explicit parameter // that is intentional to avoid capturing debugInfo as that // would result in a lot of delegate allocations here that are otherwise can be avoided. if (!debugInfo.LocalSlots.IsDefaultOrEmpty) { encoder.AddRecord( CustomDebugInfoKind.EditAndContinueLocalSlotMap, debugInfo, (info, builder) => info.SerializeLocalSlots(builder) ); } if (!debugInfo.Lambdas.IsDefaultOrEmpty) { encoder.AddRecord( CustomDebugInfoKind.EditAndContinueLambdaMap, debugInfo, (info, builder) => info.SerializeLambdaMap(builder) ); } }
private static PooledBlobBuilder SerializeRecord( byte kind, EditAndContinueMethodDebugInformation debugInfo, Action <EditAndContinueMethodDebugInformation, BlobBuilder> recordSerializer) { var cmw = PooledBlobBuilder.GetInstance(); cmw.WriteByte(CDI.CdiVersion); cmw.WriteByte(kind); cmw.WriteByte(0); // alignment size and length (will be patched) var alignmentSizeAndLengthWriter = new BlobWriter(cmw.ReserveBytes(sizeof(byte) + sizeof(uint))); recordSerializer(debugInfo, cmw); int length = cmw.Count; int alignedLength = 4 * ((length + 3) / 4); byte alignmentSize = (byte)(alignedLength - length); cmw.WriteBytes(0, alignmentSize); // fill in alignment size and length: alignmentSizeAndLengthWriter.WriteByte(alignmentSize); alignmentSizeAndLengthWriter.WriteUInt32((uint)alignedLength); return(cmw); }
public static unsafe EditAndContinueMethodDebugInformation GetEncMethodDebugInfo(this ISymUnmanagedReader3 symReader, MethodDefinitionHandle handle) { const int S_OK = 0; if (symReader is ISymUnmanagedReader4 symReader4) { int hr = symReader4.GetPortableDebugMetadata(out byte *metadata, out int size); Marshal.ThrowExceptionForHR(hr); if (hr == S_OK) { var pdbReader = new MetadataReader(metadata, size); ImmutableArray <byte> GetCdiBytes(Guid kind) => TryGetCustomDebugInformation(pdbReader, handle, kind, out var info) ? pdbReader.GetBlobContent(info.Value) : default(ImmutableArray <byte>); return(EditAndContinueMethodDebugInformation.Create( compressedSlotMap: GetCdiBytes(PortableCustomDebugInfoKinds.EncLocalSlotMap), compressedLambdaMap: GetCdiBytes(PortableCustomDebugInfoKinds.EncLambdaAndClosureMap))); } } var cdi = CustomDebugInfoUtilities.GetCustomDebugInfoBytes(symReader, handle, methodVersion: 1); if (cdi == null) { return(EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), default(ImmutableArray <byte>))); } return(GetEncMethodDebugInfo(cdi)); }
public void EditAndContinueLambdaAndClosureMap_NegativeSyntaxOffsets() { var slots = ImmutableArray <LocalSlotDebugInfo> .Empty; var closures = ImmutableArray.Create( new ClosureDebugInfo(-100), new ClosureDebugInfo(10), new ClosureDebugInfo(-200)); var lambdas = ImmutableArray.Create( new LambdaDebugInfo(20, 1), new LambdaDebugInfo(-50, 0), new LambdaDebugInfo(-180, -1)); var customMetadata = new Cci.MemoryStream(); var cmw = new Cci.BinaryWriter(customMetadata); new EditAndContinueMethodDebugInformation(0x7b, slots, closures, lambdas).SerializeLambdaMap(cmw); var bytes = customMetadata.ToImmutableArray(); AssertEx.Equal(new byte[] { 0x7C, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x02, 0x80, 0x96, 0x01, 0x14, 0x00 }, bytes); var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), bytes); AssertEx.Equal(closures, deserialized.Closures); AssertEx.Equal(lambdas, deserialized.Lambdas); }
public void EditAndContinueLambdaAndClosureMap_NegativeSyntaxOffsets() { var slots = ImmutableArray <LocalSlotDebugInfo> .Empty; var closures = ImmutableArray.Create( new ClosureDebugInfo(-100, new DebugId(0, 0)), new ClosureDebugInfo(10, new DebugId(1, 0)), new ClosureDebugInfo(-200, new DebugId(2, 0))); var lambdas = ImmutableArray.Create( new LambdaDebugInfo(20, new DebugId(0, 0), 1), new LambdaDebugInfo(-50, new DebugId(1, 0), 0), new LambdaDebugInfo(-180, new DebugId(2, 0), LambdaDebugInfo.StaticClosureOrdinal)); var cmw = new BlobBuilder(); new EditAndContinueMethodDebugInformation(0x7b, slots, closures, lambdas).SerializeLambdaMap(cmw); var bytes = cmw.ToImmutableArray(); AssertEx.Equal(new byte[] { 0x7C, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x03, 0x80, 0x96, 0x02, 0x14, 0x01 }, bytes); var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), bytes); AssertEx.Equal(closures, deserialized.Closures); AssertEx.Equal(lambdas, deserialized.Lambdas); }
public static EditAndContinueMethodDebugInformation GetEncMethodDebugInfo(byte[] customDebugInfoBlob) { var localSlots = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfoBlob, CustomDebugInfoKind.EditAndContinueLocalSlotMap); var lambdaMap = CustomDebugInfoReader.TryGetCustomDebugInfoRecord(customDebugInfoBlob, CustomDebugInfoKind.EditAndContinueLambdaMap); return(EditAndContinueMethodDebugInformation.Create(localSlots, lambdaMap)); }
public void EditAndContinueLambdaAndClosureMap_NoLambdas() { // should not happen in practice, but EditAndContinueMethodDebugInformation should handle it just fine var slots = ImmutableArray <LocalSlotDebugInfo> .Empty; var closures = ImmutableArray <ClosureDebugInfo> .Empty; var lambdas = ImmutableArray <LambdaDebugInfo> .Empty; var cmw = new BlobBuilder(); new EditAndContinueMethodDebugInformation( 10, slots, closures, lambdas ).SerializeLambdaMap(cmw); var bytes = cmw.ToImmutableArray(); AssertEx.Equal(new byte[] { 0x0B, 0x01, 0x00 }, bytes); var deserialized = EditAndContinueMethodDebugInformation.Create( default(ImmutableArray <byte>), bytes ); AssertEx.Equal(closures, deserialized.Closures); AssertEx.Equal(lambdas, deserialized.Lambdas); }
public void EditAndContinueLambdaAndClosureMap_NoClosures() { var slots = ImmutableArray <LocalSlotDebugInfo> .Empty; var closures = ImmutableArray <ClosureDebugInfo> .Empty; var lambdas = ImmutableArray.Create( new LambdaDebugInfo(20, new DebugId(0, 0), LambdaDebugInfo.StaticClosureOrdinal) ); var cmw = new BlobBuilder(); new EditAndContinueMethodDebugInformation( -1, slots, closures, lambdas ).SerializeLambdaMap(cmw); var bytes = cmw.ToImmutableArray(); AssertEx.Equal(new byte[] { 0x00, 0x01, 0x00, 0x15, 0x01 }, bytes); var deserialized = EditAndContinueMethodDebugInformation.Create( default(ImmutableArray <byte>), bytes ); AssertEx.Equal(closures, deserialized.Closures); AssertEx.Equal(lambdas, deserialized.Lambdas); }
public static EditAndContinueMethodDebugInformation GetEncMethodDebugInfo( byte[] customDebugInfoBlob ) { return(EditAndContinueMethodDebugInformation.Create( CustomDebugInfoUtilities.GetEditAndContinueLocalSlotMapRecord(customDebugInfoBlob), CustomDebugInfoUtilities.GetEditAndContinueLambdaMapRecord(customDebugInfoBlob) )); }
public static IEnumerable <string> InspectClosures( this EditAndContinueMethodDebugInformation debugInfo ) => debugInfo.Closures.IsDefault ? null : debugInfo.Closures.Select( c => $"Offset={c.SyntaxOffset} Id={c.ClosureId.Generation}#{c.ClosureId.Ordinal}" );
public static IEnumerable <string> InspectLambdas( this EditAndContinueMethodDebugInformation debugInfo ) => debugInfo.Lambdas.IsDefault ? null : debugInfo.Lambdas.Select( l => $"Offset={l.SyntaxOffset} Id={l.LambdaId.Generation}#{l.LambdaId.Ordinal} Closure={l.ClosureOrdinal}" );
public static IEnumerable <string> InspectLocalSlots( this EditAndContinueMethodDebugInformation debugInfo ) => debugInfo.LocalSlots.IsDefault ? null : debugInfo.LocalSlots.Select( s => $"Offset={s.Id.SyntaxOffset} Ordinal={s.Id.Ordinal} Kind={s.SynthesizedKind}" );
/// <summary> /// Match local declarations to names to generate a map from /// declaration to local slot. The names are indexed by slot and the /// assumption is that declarations are in the same order as slots. /// </summary> private static ImmutableArray <EncLocalInfo> CreateLocalSlotMap( EditAndContinueMethodDebugInformation methodEncInfo, ImmutableArray <LocalInfo <TypeSymbol> > slotMetadata ) { var result = new EncLocalInfo[slotMetadata.Length]; var localSlots = methodEncInfo.LocalSlots; if (!localSlots.IsDefault) { // In case of corrupted PDB or metadata, these lengths might not match. // Let's guard against such case. int slotCount = Math.Min(localSlots.Length, slotMetadata.Length); var map = new Dictionary <EncLocalInfo, int>(); for (int slotIndex = 0; slotIndex < slotCount; slotIndex++) { var slot = localSlots[slotIndex]; if (slot.SynthesizedKind.IsLongLived()) { var metadata = slotMetadata[slotIndex]; // We do not emit custom modifiers on locals so ignore the // previous version of the local if it had custom modifiers. if (metadata.CustomModifiers.IsDefaultOrEmpty) { var local = new EncLocalInfo( slot, (Cci.ITypeReference)metadata.Type.GetCciAdapter(), metadata.Constraints, metadata.SignatureOpt ); map.Add(local, slotIndex); } } } foreach (var pair in map) { result[pair.Value] = pair.Key; } } // Populate any remaining locals that were not matched to source. for (int i = 0; i < result.Length; i++) { if (result[i].IsDefault) { result[i] = new EncLocalInfo(slotMetadata[i].SignatureOpt); } } return(ImmutableArray.Create(result)); }
public static EditAndContinueMethodDebugInformation GetEncMethodDebugInfo(this ISymUnmanagedReader3 symReader, MethodDefinitionHandle handle) { var cdi = CustomDebugInfoUtilities.GetCustomDebugInfoBytes(symReader, handle, methodVersion: 1); if (cdi == null) { return(EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), default(ImmutableArray <byte>))); } return(GetEncMethodDebugInfo(cdi)); }
// internal for testing internal static void SerializeCustomDebugInformation(EditAndContinueMethodDebugInformation debugInfo, ArrayBuilder <BlobBuilder> customDebugInfo) { if (!debugInfo.LocalSlots.IsDefaultOrEmpty) { customDebugInfo.Add(SerializeRecord(CDI.CdiKindEditAndContinueLocalSlotMap, debugInfo.SerializeLocalSlots)); } if (!debugInfo.Lambdas.IsDefaultOrEmpty) { customDebugInfo.Add(SerializeRecord(CDI.CdiKindEditAndContinueLambdaMap, debugInfo.SerializeLambdaMap)); } }
public void EditAndContinueLocalSlotMap_NegativeSyntaxOffsets() { var slots = ImmutableArray.Create( new LocalSlotDebugInfo(SynthesizedLocalKind.UserDefined, new LocalDebugId(-1, 10)), new LocalSlotDebugInfo( SynthesizedLocalKind.TryAwaitPendingCaughtException, new LocalDebugId(-20000, 10) ) ); var closures = ImmutableArray <ClosureDebugInfo> .Empty; var lambdas = ImmutableArray <LambdaDebugInfo> .Empty; var cmw = new BlobBuilder(); new EditAndContinueMethodDebugInformation( 123, slots, closures, lambdas ).SerializeLocalSlots(cmw); var bytes = cmw.ToImmutableArray(); AssertEx.Equal( new byte[] { 0xFF, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A }, bytes ); var deserialized = EditAndContinueMethodDebugInformation.Create( bytes, default(ImmutableArray <byte>) ).LocalSlots; AssertEx.Equal(slots, deserialized); }
public override EditAndContinueMethodDebugInformation GetDebugInfo( MethodDefinitionHandle methodHandle ) => EditAndContinueMethodDebugInformation.Create( compressedSlotMap: GetCdiBytes( methodHandle, PortableCustomDebugInfoKinds.EncLocalSlotMap ), compressedLambdaMap: GetCdiBytes( methodHandle, PortableCustomDebugInfoKinds.EncLambdaAndClosureMap ) );
protected override ImmutableArray <EncLocalInfo> GetLocalSlotMapFromMetadata( StandaloneSignatureHandle handle, EditAndContinueMethodDebugInformation debugInfo ) { Debug.Assert(!handle.IsNil); var localInfos = _metadataDecoder.GetLocalsOrThrow(handle); var result = CreateLocalSlotMap(debugInfo, localInfos); Debug.Assert(result.Length == localInfos.Length); return(result); }
public override EditAndContinueMethodDebugInformation GetDebugInfo( MethodDefinitionHandle methodHandle ) { var methodToken = MetadataTokens.GetToken(methodHandle); byte[] debugInfo; try { debugInfo = _symReader.GetCustomDebugInfo(methodToken, _version); } catch (ArgumentOutOfRangeException) { // Sometimes the debugger returns the HRESULT for ArgumentOutOfRangeException, rather than E_FAIL, // for methods without custom debug info (https://github.com/dotnet/roslyn/issues/4138). debugInfo = null; } catch (Exception e) when(FatalError.ReportAndCatch(e)) // likely a bug in the compiler/debugger { throw new InvalidDataException(e.Message, e); } try { ImmutableArray <byte> localSlots, lambdaMap; if (debugInfo != null) { localSlots = CustomDebugInfoReader.TryGetCustomDebugInfoRecord( debugInfo, CustomDebugInfoKind.EditAndContinueLocalSlotMap ); lambdaMap = CustomDebugInfoReader.TryGetCustomDebugInfoRecord( debugInfo, CustomDebugInfoKind.EditAndContinueLambdaMap ); } else { localSlots = lambdaMap = default; } return(EditAndContinueMethodDebugInformation.Create(localSlots, lambdaMap)); } catch (InvalidOperationException e) when(FatalError.ReportAndCatch(e)) // likely a bug in the compiler/debugger { // TODO: CustomDebugInfoReader should throw InvalidDataException throw new InvalidDataException(e.Message, e); } }
public void EditAndContinueLocalSlotMap_NegativeSyntaxOffsets() { var slots = ImmutableArray.Create( new LocalSlotDebugInfo(SynthesizedLocalKind.UserDefined, new LocalDebugId(-1, 10)), new LocalSlotDebugInfo(SynthesizedLocalKind.TryAwaitPendingCaughtException, new LocalDebugId(-20000, 10))); var customMetadata = new Cci.MemoryStream(); var cmw = new Cci.BinaryWriter(customMetadata); new EditAndContinueMethodDebugInformation(slots).SerializeLocalSlots(cmw); var bytes = customMetadata.ToImmutableArray(); AssertEx.Equal(new byte[] { 0xFE, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A }, bytes); var deserialized = EditAndContinueMethodDebugInformation.Create(bytes).LocalSlots; AssertEx.Equal(slots, deserialized); }
public void UncompressSlotMap1() { using (new EnsureEnglishUICulture()) { var e = Assert.Throws <InvalidDataException>(() => EditAndContinueMethodDebugInformation.Create(ImmutableArray.Create(new byte[] { 0x01, 0x68, 0xff }), ImmutableArray <byte> .Empty)); Assert.Equal("Invalid data at offset 3: 01-68-FF*", e.Message); e = Assert.Throws <InvalidDataException>(() => EditAndContinueMethodDebugInformation.Create(ImmutableArray.Create(new byte[] { 0x01, 0x68, 0xff, 0xff, 0xff, 0xff }), ImmutableArray <byte> .Empty)); Assert.Equal("Invalid data at offset 3: 01-68-FF*FF-FF-FF", e.Message); e = Assert.Throws <InvalidDataException>(() => EditAndContinueMethodDebugInformation.Create(ImmutableArray.Create(new byte[] { 0xff, 0xff, 0xff, 0xff }), ImmutableArray <byte> .Empty)); Assert.Equal("Invalid data at offset 1: FF*FF-FF-FF", e.Message); byte[] largeData = new byte[10000]; largeData[400] = 0xff; largeData[401] = 0xff; largeData[402] = 0xff; largeData[403] = 0xff; largeData[404] = 0xff; largeData[405] = 0xff; e = Assert.Throws <InvalidDataException>(() => EditAndContinueMethodDebugInformation.Create(ImmutableArray.Create(largeData), ImmutableArray <byte> .Empty)); Assert.Equal( "Invalid data at offset 401: 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-FF*FF-FF-FF-FF-FF-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-" + "00-00-00-00-00-00-00-00-00-00-00...", e.Message); } }
public void EditAndContinueLambdaAndClosureMap_NoClosures() { var slots = ImmutableArray <LocalSlotDebugInfo> .Empty; var closures = ImmutableArray <ClosureDebugInfo> .Empty; var lambdas = ImmutableArray.Create(new LambdaDebugInfo(20, LambdaDebugInfo.StaticClosureOrdinal)); var customMetadata = new Cci.MemoryStream(); var cmw = new Cci.BinaryWriter(customMetadata); new EditAndContinueMethodDebugInformation(-1, slots, closures, lambdas).SerializeLambdaMap(cmw); var bytes = customMetadata.ToImmutableArray(); AssertEx.Equal(new byte[] { 0x00, 0x01, 0x00, 0x15, 0x01 }, bytes); var deserialized = EditAndContinueMethodDebugInformation.Create(default(ImmutableArray <byte>), bytes); AssertEx.Equal(closures, deserialized.Closures); AssertEx.Equal(lambdas, deserialized.Lambdas); }
// internal for testing internal static void SerializeCustomDebugInformation(EditAndContinueMethodDebugInformation debugInfo, ArrayBuilder <PooledBlobBuilder> customDebugInfo) { // PERF: note that we pass debugInfo as explicit parameter // that is intentional to avoid capturing debugInfo as that // would result in a lot of delegate allocations here that are otherwise can be avoided. if (!debugInfo.LocalSlots.IsDefaultOrEmpty) { customDebugInfo.Add( SerializeRecord( CDI.CdiKindEditAndContinueLocalSlotMap, debugInfo, (info, builder) => info.SerializeLocalSlots(builder))); } if (!debugInfo.Lambdas.IsDefaultOrEmpty) { customDebugInfo.Add( SerializeRecord( CDI.CdiKindEditAndContinueLambdaMap, debugInfo, (info, builder) => info.SerializeLambdaMap(builder))); } }
public void EncCdiAlignment() { var slots = ImmutableArray.Create( new LocalSlotDebugInfo(SynthesizedLocalKind.UserDefined, new LocalDebugId(-1, 10)), new LocalSlotDebugInfo(SynthesizedLocalKind.TryAwaitPendingCaughtException, new LocalDebugId(-20000, 10))); var closures = ImmutableArray.Create( new ClosureDebugInfo(-100, new DebugId(0, 0)), new ClosureDebugInfo(10, new DebugId(1, 0)), new ClosureDebugInfo(-200, new DebugId(2, 0))); var lambdas = ImmutableArray.Create( new LambdaDebugInfo(20, new DebugId(0, 0), 1), new LambdaDebugInfo(-50, new DebugId(1, 0), 0), new LambdaDebugInfo(-180, new DebugId(2, 0), LambdaDebugInfo.StaticClosureOrdinal)); var debugInfo = new EditAndContinueMethodDebugInformation(1, slots, closures, lambdas); var records = new ArrayBuilder <Cci.PooledBlobBuilder>(); Cci.CustomDebugInfoWriter.SerializeCustomDebugInformation(debugInfo, records); var cdi = Cci.CustomDebugInfoWriter.SerializeCustomDebugMetadata(records); Assert.Equal(2, records.Count); AssertEx.Equal(new byte[] { 0x04, // version 0x06, // record kind 0x00, 0x02, // alignment size // aligned record size 0x18, 0x00, 0x00, 0x00, // payload (4B aligned) 0xFF, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A, 0x00, 0x00 }, records[0].ToArray()); AssertEx.Equal(new byte[] { 0x04, // version 0x07, // record kind 0x00, 0x00, // alignment size // aligned record size 0x18, 0x00, 0x00, 0x00, // payload (4B aligned) 0x02, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x03, 0x80, 0x96, 0x02, 0x14, 0x01 }, records[1].ToArray()); var deserialized = CustomDebugInfoReader.GetCustomDebugInfoRecords(cdi).ToArray(); Assert.Equal(CustomDebugInfoKind.EditAndContinueLocalSlotMap, deserialized[0].Kind); Assert.Equal(4, deserialized[0].Version); Assert.Equal(new byte[] { 0xFF, 0xC0, 0x00, 0x4E, 0x20, 0x81, 0xC0, 0x00, 0x4E, 0x1F, 0x0A, 0x9A, 0x00, 0x0A }, deserialized[0].Data); Assert.Equal(CustomDebugInfoKind.EditAndContinueLambdaMap, deserialized[1].Kind); Assert.Equal(4, deserialized[1].Version); Assert.Equal(new byte[] { 0x02, 0x80, 0xC8, 0x03, 0x64, 0x80, 0xD2, 0x00, 0x80, 0xDC, 0x03, 0x80, 0x96, 0x02, 0x14, 0x01 }, deserialized[1].Data); }
public byte[] SerializeMethodDebugInfo(EmitContext context, IMethodBody methodBody, int methodToken, bool emitEncInfo, bool suppressNewCustomDebugInfo, out bool emitExternNamespaces) { emitExternNamespaces = false; // CONSIDER: this may not be the same "first" method as in Dev10, but // it shouldn't matter since all methods will still forward to a method // containing the appropriate information. if (_methodBodyWithModuleInfo == null) { // This module level information could go on every method (and does in // the edit-and-continue case), but - as an optimization - we'll just // put it on the first method we happen to encounter and then put a // reference to the first method's token in every other method (so they // can find the information). if (context.Module.GetAssemblyReferenceAliases(context).Any()) { _methodTokenWithModuleInfo = methodToken; _methodBodyWithModuleInfo = methodBody; emitExternNamespaces = true; } } ArrayBuilder <PooledBlobBuilder> customDebugInfo = ArrayBuilder <PooledBlobBuilder> .GetInstance(); SerializeIteratorClassMetadata(methodBody, customDebugInfo); // NOTE: This is an attempt to match Dev10's apparent behavior. For iterator methods (i.e. the method // that appears in source, not the synthesized ones), Dev10 only emits the ForwardIterator and IteratorLocal // custom debug info (e.g. there will be no information about the usings that were in scope). // NOTE: There seems to be an unusual behavior in ISymUnmanagedWriter where, if all the methods in a type are // iterator methods, no custom debug info is emitted for any method. Adding a single non-iterator // method causes the custom debug info to be produced for all methods (including the iterator methods). // Since we are making the same ISymUnmanagedWriter calls as Dev10, we see the same behavior (i.e. this // is not a regression). if (methodBody.StateMachineTypeName == null) { SerializeNamespaceScopeMetadata(context, methodBody, customDebugInfo); SerializeStateMachineLocalScopes(methodBody, customDebugInfo); } if (!suppressNewCustomDebugInfo) { SerializeDynamicLocalInfo(methodBody, customDebugInfo); SerializeTupleElementNames(methodBody, customDebugInfo); if (emitEncInfo) { EditAndContinueMethodDebugInformation encMethodInfo = MetadataWriter.GetEncMethodDebugInfo(methodBody); SerializeCustomDebugInformation(encMethodInfo, customDebugInfo); } } byte[] result = SerializeCustomDebugMetadata(customDebugInfo); foreach (PooledBlobBuilder builder in customDebugInfo) { builder.Free(); } customDebugInfo.Free(); return(result); }
public byte[] SerializeMethodDebugInfo(IModule module, IMethodBody methodBody, uint methodToken, bool isEncDelta, bool suppressNewCustomDebugInfo, out bool emitExternNamespaces) { emitExternNamespaces = false; // CONSIDER: this may not be the same "first" method as in Dev10, but // it shouldn't matter since all methods will still forward to a method // containing the appropriate information. if (_methodBodyWithModuleInfo == null) //UNDONE: || edit-and-continue { // This module level information could go on every method (and does in // the edit-and-continue case), but - as an optimization - we'll just // put it on the first method we happen to encounter and then put a // reference to the first method's token in every other method (so they // can find the information). if (module.ExternNamespaces.Any()) { _methodTokenWithModuleInfo = methodToken; _methodBodyWithModuleInfo = methodBody; emitExternNamespaces = true; } } var customDebugInfo = ArrayBuilder <MemoryStream> .GetInstance(); SerializeIteratorClassMetadata(methodBody, customDebugInfo); // NOTE: This is an attempt to match Dev10's apparent behavior. For iterator methods (i.e. the method // that appears in source, not the synthesized ones), Dev10 only emits the ForwardIterator and IteratorLocal // custom debug info (e.g. there will be no information about the usings that were in scope). // NOTE: There seems to be an unusual behavior in ISymUnmanagedWriter where, if all the methods in a type are // iterator methods, no custom debug info is emitted for any method. Adding a single non-iterator // method causes the custom debug info to be produced for all methods (including the iterator methods). // Since we are making the same ISymUnmanagedWriter calls as Dev10, we see the same behavior (i.e. this // is not a regression). if (methodBody.StateMachineTypeName == null) { SerializeNamespaceScopeMetadata(methodBody, customDebugInfo); SerializeStateMachineLocalScopes(methodBody, customDebugInfo); } if (!suppressNewCustomDebugInfo) { SerializeDynamicLocalInfo(methodBody, customDebugInfo); // delta doesn't need this information - we use information recorded by previous generation emit if (!isEncDelta) { ImmutableArray <LocalSlotDebugInfo> encLocalSlots; // Kickoff method of a state machine (async/iterator method) doens't have any interesting locals, // so we use its EnC method debug info to store information about locals hoisted to the state machine. var encSlotInfo = methodBody.StateMachineHoistedLocalSlots; if (encSlotInfo.IsDefault) { encLocalSlots = GetLocalSlotDebugInfos(methodBody.LocalVariables); } else { encLocalSlots = GetLocalSlotDebugInfos(encSlotInfo); } var encMethodInfo = new EditAndContinueMethodDebugInformation(methodBody.MethodOrdinal, encLocalSlots, methodBody.ClosureDebugInfo, methodBody.LambdaDebugInfo); encMethodInfo.SerializeCustomDebugInformation(customDebugInfo); } } byte[] result = SerializeCustomDebugMetadata(customDebugInfo); customDebugInfo.Free(); return(result); }
public static int GetMethodOrdinal(this EditAndContinueMethodDebugInformation debugInfo) => debugInfo.MethodOrdinal;
protected override ImmutableArray <EncLocalInfo> TryGetLocalSlotMapFromMetadata(MethodDefinitionHandle handle, EditAndContinueMethodDebugInformation debugInfo) { ImmutableArray <LocalInfo <TypeSymbol> > slotMetadata; if (!_metadataDecoder.TryGetLocals(handle, out slotMetadata)) { return(default(ImmutableArray <EncLocalInfo>)); } var result = CreateLocalSlotMap(debugInfo, slotMetadata); Debug.Assert(result.Length == slotMetadata.Length); return(result); }