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 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, 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 void WriteData(Cci.BinaryWriter resourceWriter) { if (fileReference == null) { try { using (Stream stream = streamProvider()) { if (stream == null) { throw new InvalidOperationException(CodeAnalysisResources.ResourceStreamProviderShouldReturnNonNullStream); } var count = (int)(stream.Length - stream.Position); resourceWriter.WriteInt(count); var to = resourceWriter.BaseStream; var position = (int)to.Position; to.Position = (uint)(position + count); resourceWriter.Align(8); var buffer = to.Buffer; stream.Read(buffer, position, count); } } catch (Exception e) { throw new ResourceException(this.name, e); } } }
internal void SerializeCustomDebugInformation(ArrayBuilder <Cci.MemoryStream> customDebugInfo) { if (this.LocalSlots.IsDefaultOrEmpty) { return; } Cci.MemoryStream customMetadata = new Cci.MemoryStream(); Cci.BinaryWriter cmw = new Cci.BinaryWriter(customMetadata); cmw.WriteByte(4); // version cmw.WriteByte(6); // kind: EditAndContinueLocalSlotMap cmw.Align(4); // length (will be patched) uint lengthPosition = cmw.BaseStream.Position; cmw.WriteUint(0); SerializeLocalSlots(cmw); uint length = customMetadata.Position; // align with values that the reader skips while (length % 4 != 0) { cmw.WriteByte(AlignmentValue); length++; } cmw.BaseStream.Position = lengthPosition; cmw.WriteUint(length); cmw.BaseStream.Position = length; customDebugInfo.Add(customMetadata); }
/// <summary> /// Produces a serialized blob of all constant initializers. /// Nonconstat initializers are matched with a zero of corresponding size. /// </summary> private ImmutableArray <byte> GetRawData(ImmutableArray <BoundExpression> initializers) { // the initial size is a guess. // there is no point to be precise here as MemoryStream always has N + 1 storage // and will need to be trimmed regardless var stream = new Cci.MemoryStream((uint)(initializers.Length * 4)); var writer = new Cci.BinaryWriter(stream); SerializeArrayRecursive(writer, initializers); return(ImmutableArray.Create(stream.Buffer, 0, (int)stream.Position)); }
internal void SerializeLocalSlots(Cci.BinaryWriter writer) { int syntaxOffsetBaseline = -1; foreach (LocalSlotDebugInfo localSlot in this.LocalSlots) { if (localSlot.Id.SyntaxOffset < syntaxOffsetBaseline) { syntaxOffsetBaseline = localSlot.Id.SyntaxOffset; } } if (syntaxOffsetBaseline != -1) { writer.WriteByte(SyntaxOffsetBaseline); writer.WriteCompressedUInt((uint)(-syntaxOffsetBaseline)); } foreach (LocalSlotDebugInfo localSlot in this.LocalSlots) { SynthesizedLocalKind kind = localSlot.SynthesizedKind; Debug.Assert(kind <= SynthesizedLocalKind.MaxValidValueForLocalVariableSerializedToDebugInformation); if (!kind.IsLongLived()) { writer.WriteByte(0); continue; } byte b = (byte)(kind + 1); Debug.Assert((b & (1 << 7)) == 0); bool hasOrdinal = localSlot.Id.Ordinal > 0; if (hasOrdinal) { b |= 1 << 7; } writer.WriteByte(b); writer.WriteCompressedUInt((uint)(localSlot.Id.SyntaxOffset - syntaxOffsetBaseline)); if (hasOrdinal) { writer.WriteCompressedUInt((uint)localSlot.Id.Ordinal); } } }
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); }
private Cci.MemoryStream SerializeRecord(byte kind, Action<Cci.BinaryWriter> data) { Cci.MemoryStream customMetadata = new Cci.MemoryStream(); Cci.BinaryWriter cmw = new Cci.BinaryWriter(customMetadata); cmw.WriteByte(Cci.CustomDebugInfoConstants.CdiVersion); cmw.WriteByte(kind); cmw.Align(4); // length (will be patched) uint lengthPosition = cmw.BaseStream.Position; cmw.WriteUint(0); data(cmw); uint length = customMetadata.Position; cmw.BaseStream.Position = lengthPosition; cmw.WriteUint(length); cmw.BaseStream.Position = length; return customMetadata; }
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 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 void SerializeLambdaMap(Cci.BinaryWriter writer) { Debug.Assert(this.MethodOrdinal >= -1); writer.WriteCompressedUInt((uint)(this.MethodOrdinal + 1)); int syntaxOffsetBaseline = -1; foreach (ClosureDebugInfo info in this.Closures) { if (info.SyntaxOffset < syntaxOffsetBaseline) { syntaxOffsetBaseline = info.SyntaxOffset; } } foreach (LambdaDebugInfo info in this.Lambdas) { if (info.SyntaxOffset < syntaxOffsetBaseline) { syntaxOffsetBaseline = info.SyntaxOffset; } } writer.WriteCompressedUInt((uint)(-syntaxOffsetBaseline)); writer.WriteCompressedUInt((uint)this.Closures.Length); foreach (ClosureDebugInfo info in this.Closures) { writer.WriteCompressedUInt((uint)(info.SyntaxOffset - syntaxOffsetBaseline)); } foreach (LambdaDebugInfo info in this.Lambdas) { Debug.Assert(info.ClosureOrdinal >= LambdaDebugInfo.MinClosureOrdinal); Debug.Assert(info.Generation == 0); writer.WriteCompressedUInt((uint)(info.SyntaxOffset - syntaxOffsetBaseline)); writer.WriteCompressedUInt((uint)(info.ClosureOrdinal - LambdaDebugInfo.MinClosureOrdinal)); } }
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 customMetadata = new Cci.MemoryStream(); var cmw = new Cci.BinaryWriter(customMetadata); new EditAndContinueMethodDebugInformation(10, slots, closures, lambdas).SerializeLambdaMap(cmw); var bytes = customMetadata.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); }
private Cci.MemoryStream SerializeRecord(byte kind, Action <Cci.BinaryWriter> data) { Cci.MemoryStream customMetadata = new Cci.MemoryStream(); Cci.BinaryWriter cmw = new Cci.BinaryWriter(customMetadata); cmw.WriteByte(Cci.CustomDebugInfoConstants.CdiVersion); cmw.WriteByte(kind); cmw.Align(4); // length (will be patched) uint lengthPosition = cmw.BaseStream.Position; cmw.WriteUint(0); data(cmw); uint length = customMetadata.Position; cmw.BaseStream.Position = lengthPosition; cmw.WriteUint(length); cmw.BaseStream.Position = length; return(customMetadata); }
public void EditAndContinueLambdaAndClosureMap_NegativeSyntaxOffsets() { var slots = ImmutableArray<LocalSlotDebugInfo>.Empty; var closures = ImmutableArray.Create( new ClosureDebugInfo(-100, 0), new ClosureDebugInfo(10, 0), new ClosureDebugInfo(-200, 0)); var lambdas = ImmutableArray.Create( new LambdaDebugInfo(20, 1, 0), new LambdaDebugInfo(-50, 0, 0), new LambdaDebugInfo(-180, LambdaDebugInfo.StaticClosureOrdinal, 0)); 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, 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); }
private void RealizeBlocks() { // drop dead code. // We do not want to deal with unreachable code even when not optimizing. // sometimes dead code may have subtle verification violations // for example illegal fall-through in unreachable code is still illegal, // but compiler will not enforce returning from dead code. // it is easier to just remove dead code than make sure it is all valid MarkReachableBlocks(); RewriteSpecialBlocks(); DropUnreachableBlocks(); if (_optimizations == OptimizationLevel.Release && OptimizeLabels()) { // redo unreachable code elimination if some labels were optimized // as that could result in more dead code. MarkAllBlocksUnreachable(); MarkReachableBlocks(); DropUnreachableBlocks(); } // some gotos must become leaves RewriteBranchesAcrossExceptionHandlers(); // now we can compute block offsets and adjust branches while (ComputeOffsetsAndAdjustBranches()) { // if branches were optimized, we may have more unreachable code // redo unreachable code elimination and if anything was dropped redo adjusting. MarkAllBlocksUnreachable(); MarkReachableBlocks(); if (!DropUnreachableBlocks()) { // nothing was dropped, we are done adjusting break; } } // Now linearize everything with computed offsets. var realizedIlBitStream = Cci.MemoryStream.GetInstance(); var writer = new Cci.BinaryWriter(realizedIlBitStream); for (var block = leaderBlock; block != null; block = block.NextBlock) { // If the block has any IL markers, we can calculate their real IL offsets now int blockFirstMarker = block.FirstILMarker; if (blockFirstMarker >= 0) { int blockLastMarker = block.LastILMarker; Debug.Assert(blockLastMarker >= blockFirstMarker); for (int i = blockFirstMarker; i <= blockLastMarker; i++) { int blockOffset = _allocatedILMarkers[i].BlockOffset; int absoluteOffset = (int)realizedIlBitStream.Position + blockOffset; _allocatedILMarkers[i] = new ILMarker() { BlockOffset = blockOffset, AbsoluteOffset = absoluteOffset }; } } block.RegularInstructions?.WriteTo(realizedIlBitStream); switch (block.BranchCode) { case ILOpCode.Nop: break; case ILOpCode.Switch: // switch (N, t1, t2... tN) // IL ==> ILOpCode.Switch < unsigned int32 > < int32 >... < int32 > WriteOpCode(writer, ILOpCode.Switch); var switchBlock = (SwitchBlock)block; writer.WriteUint(switchBlock.BranchesCount); int switchBlockEnd = switchBlock.Start + switchBlock.TotalSize; var blockBuilder = ArrayBuilder<BasicBlock>.GetInstance(); switchBlock.GetBranchBlocks(blockBuilder); foreach (var branchBlock in blockBuilder) { writer.WriteInt(branchBlock.Start - switchBlockEnd); } blockBuilder.Free(); break; default: WriteOpCode(writer, block.BranchCode); if (block.BranchLabel != null) { int target = block.BranchBlock.Start; int curBlockEnd = block.Start + block.TotalSize; int offset = target - curBlockEnd; if (block.BranchCode.BranchOperandSize() == 1) { sbyte btOffset = (sbyte)offset; Debug.Assert(btOffset == offset); writer.WriteSbyte(btOffset); } else { writer.WriteInt(offset); } } break; } } this.RealizedIL = realizedIlBitStream.ToArray(); realizedIlBitStream.Free(); RealizeSequencePoints(); this.RealizedExceptionHandlers = _scopeManager.GetExceptionHandlerRegions(); }
private void RealizeBlocks() { // drop dead code. // We do not want to deal with unreachable code even when not optimizing. // sometimes dead code may have subtle verification violations // for example illegal fall-through in unreachable code is still illegal, // but compiler will not enforce returning from dead code. // it is easier to just remove dead code than make sure it is all valid MarkReachableBlocks(); RewriteSpecialBlocks(); DropUnreachableBlocks(); if (_optimizations == OptimizationLevel.Release && OptimizeLabels()) { // redo unreachable code elimination if some labels were optimized // as that could result in more more dead code. MarkAllBlocksUnreachable(); MarkReachableBlocks(); DropUnreachableBlocks(); } // some gotos must become leaves RewriteBranchesAcrossExceptionHandlers(); // now we can compute block offsets and adjust branches while (ComputeOffsetsAndAdjustBranches()) { // if branches were optimized, we may have more unreachable code // redo unreachable code elimination and if anything was dropped redo adjusting. MarkAllBlocksUnreachable(); MarkReachableBlocks(); if (!DropUnreachableBlocks()) { // nothing was dropped, we are done adjusting break; } } // Now linearize everything with computed offsets. var realizedIlBitStream = Cci.MemoryStream.GetInstance(); var writer = new Cci.BinaryWriter(realizedIlBitStream); for (var block = leaderBlock; block != null; block = block.NextBlock) { // If the block has any IL markers, we can calculate their real IL offsets now int blockFirstMarker = block.FirstILMarker; if (blockFirstMarker >= 0) { int blockLastMarker = block.LastILMarker; Debug.Assert(blockLastMarker >= blockFirstMarker); for (int i = blockFirstMarker; i <= blockLastMarker; i++) { int blockOffset = _allocatedILMarkers[i].BlockOffset; int absoluteOffset = (int)realizedIlBitStream.Position + blockOffset; _allocatedILMarkers[i] = new ILMarker() { BlockOffset = blockOffset, AbsoluteOffset = absoluteOffset }; } } block.RegularInstructions?.WriteTo(realizedIlBitStream); switch (block.BranchCode) { case ILOpCode.Nop: break; case ILOpCode.Switch: // switch (N, t1, t2... tN) // IL ==> ILOpCode.Switch < unsigned int32 > < int32 >... < int32 > WriteOpCode(writer, ILOpCode.Switch); var switchBlock = (SwitchBlock)block; writer.WriteUint(switchBlock.BranchesCount); int switchBlockEnd = switchBlock.Start + switchBlock.TotalSize; var blockBuilder = ArrayBuilder <BasicBlock> .GetInstance(); switchBlock.GetBranchBlocks(blockBuilder); foreach (var branchBlock in blockBuilder) { writer.WriteInt(branchBlock.Start - switchBlockEnd); } blockBuilder.Free(); break; default: WriteOpCode(writer, block.BranchCode); if (block.BranchLabel != null) { int target = block.BranchBlock.Start; int curBlockEnd = block.Start + block.TotalSize; int offset = target - curBlockEnd; if (block.BranchCode.BranchOperandSize() == 1) { sbyte btOffset = (sbyte)offset; Debug.Assert(btOffset == offset); writer.WriteSbyte(btOffset); } else { writer.WriteInt(offset); } } break; } } this.RealizedIL = realizedIlBitStream.ToArray(); realizedIlBitStream.Free(); RealizeSequencePoints(); this.RealizedExceptionHandlers = _scopeManager.GetExceptionHandlerRegions(); }
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 customMetadata = new Cci.MemoryStream(); var cmw = new Cci.BinaryWriter(customMetadata); new EditAndContinueMethodDebugInformation(10, slots, closures, lambdas).SerializeLambdaMap(cmw); var bytes = customMetadata.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, LambdaDebugInfo.StaticClosureOrdinal, 0)); 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); }
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 customMetadata = new Cci.MemoryStream(); var cmw = new Cci.BinaryWriter(customMetadata); new EditAndContinueMethodDebugInformation(123, slots, closures, lambdas).SerializeLocalSlots(cmw); var bytes = customMetadata.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); }
/// <summary> /// Produces a serialized blob of all constant initializers. /// Nonconstat initializers are matched with a zero of corresponding size. /// </summary> private ImmutableArray<byte> GetRawData(ImmutableArray<BoundExpression> initializers) { // the initial size is a guess. // there is no point to be precise here as MemoryStream always has N + 1 storage // and will need to be trimmed regardless var stream = new Cci.MemoryStream((uint)(initializers.Length * 4)); var writer = new Cci.BinaryWriter(stream); SerializeArrayRecursive(writer, initializers); return ImmutableArray.Create(stream.Buffer, 0, (int)stream.Position); }