Browse Source

Expand the range of sequence points out the closest empty ilstack

or implicit sequence point without creating overlapping sequence points.
If such a location cannot be found do, nothing. Fill in the
gaps with hidden sequence points.

Also emit a sequence point for
the prolog to account for seqeunce point there emitted by the C#
compiler. Without this, the debugger can stop there on a step in
using the original pdb, then decompile resulting in a no-code at this
location failure.
pull/1967/head
Jackson Davis 5 years ago
parent
commit
6ab1f98fa3
  1. 78
      ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs
  2. 4
      ICSharpCode.Decompiler/DebugInfo/AsyncDebugInfo.cs
  3. 2
      ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs
  4. 1
      ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs
  5. 1
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs
  6. 30
      ICSharpCode.Decompiler/IL/ILReader.cs
  7. 10
      ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs
  8. 2
      global.json

78
ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs

@ -18,9 +18,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax; using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Util; using ICSharpCode.Decompiler.Util;
@ -108,6 +110,25 @@ namespace ICSharpCode.Decompiler.CSharp
public override void VisitBlockStatement(BlockStatement blockStatement) public override void VisitBlockStatement(BlockStatement blockStatement)
{ {
ILInstruction blockContainer = blockStatement.Annotations.OfType<ILInstruction>().FirstOrDefault();
if (blockContainer != null) {
StartSequencePoint(blockStatement.LBraceToken);
int intervalStart = blockContainer.ILRanges.First().Start;
// The end will be set to the first sequence point candidate location before the first statement of the function when the seqeunce point is adjusted
int intervalEnd = intervalStart + 1;
Interval interval = new Interval(intervalStart, intervalEnd);
List<Interval> intervals = new List<Interval>();
intervals.Add(interval);
current.Intervals.AddRange(intervals);
current.Function = blockContainer.Ancestors.OfType<ILFunction>().FirstOrDefault();
EndSequencePoint(blockStatement.LBraceToken.StartLocation, blockStatement.LBraceToken.EndLocation);
}
else {
// Ideally, we'd be able to address this case. Blocks that are not the top-level function block have no ILInstruction annotations. It isn't clear to me how to determine the il range.
// For now, do not add the opening brace sequence in this case.
}
foreach (var stmt in blockStatement.Statements) { foreach (var stmt in blockStatement.Statements) {
VisitAsSequencePoint(stmt); VisitAsSequencePoint(stmt);
} }
@ -331,11 +352,13 @@ namespace ICSharpCode.Decompiler.CSharp
} }
list.Add(sequencePoint); list.Add(sequencePoint);
} }
foreach (var (function, list) in dict.ToList()) { foreach (var (function, list) in dict.ToList()) {
// For each function, sort sequence points and fix overlaps+gaps // For each function, sort sequence points and fix overlaps
var newList = new List<DebugInfo.SequencePoint>(); var newList = new List<DebugInfo.SequencePoint>();
int pos = 0; int pos = 0;
foreach (var sequencePoint in list.OrderBy(sp => sp.Offset).ThenBy(sp => sp.EndOffset)) { IOrderedEnumerable<DebugInfo.SequencePoint> currFunctionSequencePoints = list.OrderBy(sp => sp.Offset).ThenBy(sp => sp.EndOffset);
foreach (DebugInfo.SequencePoint sequencePoint in currFunctionSequencePoints) {
if (sequencePoint.Offset < pos) { if (sequencePoint.Offset < pos) {
// overlapping sequence point? // overlapping sequence point?
// delete previous sequence points that are after sequencePoint.Offset // delete previous sequence points that are after sequencePoint.Offset
@ -348,17 +371,12 @@ namespace ICSharpCode.Decompiler.CSharp
newList[newList.Count - 1] = last; newList[newList.Count - 1] = last;
} }
} }
} else if (sequencePoint.Offset > pos) {
// insert hidden sequence point in the gap.
var hidden = new DebugInfo.SequencePoint();
hidden.Offset = pos;
hidden.EndOffset = sequencePoint.Offset;
hidden.SetHidden();
newList.Add(hidden);
} }
newList.Add(sequencePoint); newList.Add(sequencePoint);
pos = sequencePoint.EndOffset; pos = sequencePoint.EndOffset;
} }
// Add a hidden sequence point to account for the epilog of the function
if (pos < function.CodeSize) { if (pos < function.CodeSize) {
var hidden = new DebugInfo.SequencePoint(); var hidden = new DebugInfo.SequencePoint();
hidden.Offset = pos; hidden.Offset = pos;
@ -366,8 +384,48 @@ namespace ICSharpCode.Decompiler.CSharp
hidden.SetHidden(); hidden.SetHidden();
newList.Add(hidden); newList.Add(hidden);
} }
List<int> sequencePointCandidates = function.SequencePointCandidates;
int currSPCandidateIndex = 0;
for (int i = 0; i < newList.Count - 1; i++) {
DebugInfo.SequencePoint currSequencePoint = newList[i];
DebugInfo.SequencePoint nextSequencePoint = newList[i + 1];
// Adjust the end offset of the current sequence point to the closest sequence point candidate
// but do not create an overlapping sequence point. Moving the start of the current sequence
// point is not required as it is 0 for the first sequence point and is moved during the last
// iteration for all others.
while (currSPCandidateIndex < sequencePointCandidates.Count &&
sequencePointCandidates[currSPCandidateIndex] < currSequencePoint.EndOffset) {
currSPCandidateIndex++;
}
if (currSPCandidateIndex < sequencePointCandidates.Count && sequencePointCandidates[currSPCandidateIndex] <= nextSequencePoint.Offset) {
currSequencePoint.EndOffset = sequencePointCandidates[currSPCandidateIndex];
}
// Adjust the start offset of the next sequence point to the closest previous sequence point candidate
// but do not create an overlapping sequence point.
while (currSPCandidateIndex < sequencePointCandidates.Count &&
sequencePointCandidates[currSPCandidateIndex] < nextSequencePoint.Offset) {
currSPCandidateIndex++;
}
if (currSPCandidateIndex < sequencePointCandidates.Count && sequencePointCandidates[currSPCandidateIndex - 1] >= currSequencePoint.EndOffset) {
nextSequencePoint.Offset = sequencePointCandidates[currSPCandidateIndex - 1];
currSPCandidateIndex--;
}
// Fill in any gaps with a hidden sequence point
if (currSequencePoint.EndOffset != nextSequencePoint.Offset) {
SequencePoint newSP = new SequencePoint() { Offset = currSequencePoint.EndOffset, EndOffset = nextSequencePoint.Offset };
newSP.SetHidden();
newList.Insert(++i, newSP);
}
}
dict[function] = newList; dict[function] = newList;
} }
return dict; return dict;
} }
} }

4
ICSharpCode.Decompiler/DebugInfo/AsyncDebugInfo.cs

@ -5,7 +5,7 @@ using System.Reflection.Metadata.Ecma335;
namespace ICSharpCode.Decompiler.DebugInfo namespace ICSharpCode.Decompiler.DebugInfo
{ {
readonly struct AsyncDebugInfo public readonly struct AsyncDebugInfo
{ {
public readonly int CatchHandlerOffset; public readonly int CatchHandlerOffset;
public readonly ImmutableArray<Await> Awaits; public readonly ImmutableArray<Await> Awaits;
@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.DebugInfo
} }
} }
internal BlobBuilder BuildBlob(MethodDefinitionHandle moveNext) public BlobBuilder BuildBlob(MethodDefinitionHandle moveNext)
{ {
BlobBuilder blob = new BlobBuilder(); BlobBuilder blob = new BlobBuilder();
blob.WriteUInt32((uint)CatchHandlerOffset); blob.WriteUInt32((uint)CatchHandlerOffset);

2
ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs

@ -25,7 +25,7 @@ namespace ICSharpCode.Decompiler.DebugInfo
/// A sequence point read from a PDB file or produced by the decompiler. /// A sequence point read from a PDB file or produced by the decompiler.
/// </summary> /// </summary>
[DebuggerDisplay("SequencePoint IL_{Offset,h}-IL_{EndOffset,h}, {StartLine}:{StartColumn}-{EndLine}:{EndColumn}, IsHidden={IsHidden}")] [DebuggerDisplay("SequencePoint IL_{Offset,h}-IL_{EndOffset,h}, {StartLine}:{StartColumn}-{EndLine}:{EndColumn}, IsHidden={IsHidden}")]
public struct SequencePoint public class SequencePoint
{ {
/// <summary> /// <summary>
/// IL start offset. /// IL start offset.

1
ICSharpCode.Decompiler/IL/ControlFlow/AsyncAwaitDecompiler.cs

@ -819,6 +819,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
function.Body = mainTryCatch.TryBlock; function.Body = mainTryCatch.TryBlock;
function.AsyncReturnType = underlyingReturnType; function.AsyncReturnType = underlyingReturnType;
function.MoveNextMethod = moveNextFunction.Method; function.MoveNextMethod = moveNextFunction.Method;
function.SequencePointCandidates = moveNextFunction.SequencePointCandidates;
function.CodeSize = moveNextFunction.CodeSize; function.CodeSize = moveNextFunction.CodeSize;
function.IsIterator = IsAsyncEnumerator; function.IsIterator = IsAsyncEnumerator;
moveNextFunction.Variables.Clear(); moveNextFunction.Variables.Clear();

1
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -532,6 +532,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
ILFunction moveNextFunction = CreateILAst(moveNextMethod, context); ILFunction moveNextFunction = CreateILAst(moveNextMethod, context);
function.MoveNextMethod = moveNextFunction.Method; function.MoveNextMethod = moveNextFunction.Method;
function.SequencePointCandidates = moveNextFunction.SequencePointCandidates;
function.CodeSize = moveNextFunction.CodeSize; function.CodeSize = moveNextFunction.CodeSize;
// Copy-propagate temporaries holding a copy of 'this'. // Copy-propagate temporaries holding a copy of 'this'.

30
ICSharpCode.Decompiler/IL/ILReader.cs

@ -50,6 +50,10 @@ namespace ICSharpCode.Decompiler.IL
public DebugInfo.IDebugInfoProvider DebugInfo { get; set; } public DebugInfo.IDebugInfoProvider DebugInfo { get; set; }
public List<string> Warnings { get; } = new List<string>(); public List<string> Warnings { get; } = new List<string>();
// List of candidate locations for sequence points. Includes empty il stack locations, any nop instructions, and the instruction following
// a call instruction.
public List<int> SequencePointCandidates { get; private set; }
/// <summary> /// <summary>
/// Creates a new ILReader instance. /// Creates a new ILReader instance.
/// </summary> /// </summary>
@ -63,6 +67,7 @@ namespace ICSharpCode.Decompiler.IL
this.module = module; this.module = module;
this.compilation = module.Compilation; this.compilation = module.Compilation;
this.metadata = module.metadata; this.metadata = module.metadata;
this.SequencePointCandidates = new List<int>();
} }
GenericContext genericContext; GenericContext genericContext;
@ -84,7 +89,7 @@ namespace ICSharpCode.Decompiler.IL
UnionFind<ILVariable> unionFind; UnionFind<ILVariable> unionFind;
List<(ILVariable, ILVariable)> stackMismatchPairs; List<(ILVariable, ILVariable)> stackMismatchPairs;
IEnumerable<ILVariable> stackVariables; IEnumerable<ILVariable> stackVariables;
void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, GenericContext genericContext) void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, GenericContext genericContext)
{ {
if (body == null) if (body == null)
@ -382,6 +387,7 @@ namespace ICSharpCode.Decompiler.IL
int start = reader.Offset; int start = reader.Offset;
StoreStackForOffset(start, ref currentStack); StoreStackForOffset(start, ref currentStack);
currentInstructionStart = start; currentInstructionStart = start;
bool startedWithEmptyStack = currentStack.IsEmpty;
ILInstruction decodedInstruction; ILInstruction decodedInstruction;
try { try {
decodedInstruction = DecodeInstruction(); decodedInstruction = DecodeInstruction();
@ -400,6 +406,10 @@ namespace ICSharpCode.Decompiler.IL
currentStack = ImmutableStack<ILVariable>.Empty; currentStack = ImmutableStack<ILVariable>.Empty;
} }
} }
if (IsSequencePointInstruction(decodedInstruction) || startedWithEmptyStack) {
this.SequencePointCandidates.Add(decodedInstruction.StartILOffset);
}
} }
var visitor = new CollectStackVariablesVisitor(unionFind); var visitor = new CollectStackVariablesVisitor(unionFind);
@ -410,6 +420,20 @@ namespace ICSharpCode.Decompiler.IL
InsertStackAdjustments(); InsertStackAdjustments();
} }
private bool IsSequencePointInstruction(ILInstruction instruction)
{
if (instruction.OpCode == OpCode.Nop ||
(this.instructionBuilder.Count > 0 &&
this.instructionBuilder.Last().OpCode == OpCode.Call ||
this.instructionBuilder.Last().OpCode == OpCode.CallIndirect ||
this.instructionBuilder.Last().OpCode == OpCode.CallVirt)) {
return true;
} else {
return false;
}
}
void InsertStackAdjustments() void InsertStackAdjustments()
{ {
if (stackMismatchPairs.Count == 0) if (stackMismatchPairs.Count == 0)
@ -509,6 +533,10 @@ namespace ICSharpCode.Decompiler.IL
function.Warnings.Add("Discarded unreachable code: " function.Warnings.Add("Discarded unreachable code: "
+ string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}"))); + string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}")));
} }
this.SequencePointCandidates.Sort();
function.SequencePointCandidates = this.SequencePointCandidates;
function.Warnings.AddRange(Warnings); function.Warnings.AddRange(Warnings);
return function; return function;
} }

10
ICSharpCode.Decompiler/IL/Instructions/ILFunction.cs

@ -20,6 +20,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Linq; using System.Linq;
using System.Collections.Immutable;
using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem; using ICSharpCode.Decompiler.TypeSystem;
@ -113,7 +114,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
internal TypeSystem.Implementation.LocalFunctionMethod ReducedMethod; internal TypeSystem.Implementation.LocalFunctionMethod ReducedMethod;
internal DebugInfo.AsyncDebugInfo AsyncDebugInfo; public DebugInfo.AsyncDebugInfo AsyncDebugInfo;
int ctorCallStart = int.MinValue; int ctorCallStart = int.MinValue;
@ -169,6 +170,13 @@ namespace ICSharpCode.Decompiler.IL
/// </summary> /// </summary>
public readonly IReadOnlyList<IParameter> Parameters; public readonly IReadOnlyList<IParameter> Parameters;
/// <summary>
/// List of candidate locations for sequence points. Includes any offset
/// where the stack is empty, nop instructions, and the instruction following
/// a call instruction
/// </summary>
public List<int> SequencePointCandidates { get; set; }
/// <summary> /// <summary>
/// Constructs a new ILFunction from the given metadata and with the given ILAst body. /// Constructs a new ILFunction from the given metadata and with the given ILAst body.
/// </summary> /// </summary>

2
global.json

@ -3,6 +3,6 @@
"MSBuild.Sdk.Extras": "2.0.54" "MSBuild.Sdk.Extras": "2.0.54"
}, },
"sdk": { "sdk": {
"version": "3.1.100" "version": "3.2.000"
} }
} }

Loading…
Cancel
Save