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 @@ @@ -18,9 +18,11 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.DebugInfo;
using ICSharpCode.Decompiler.IL;
using ICSharpCode.Decompiler.Util;
@ -108,6 +110,25 @@ namespace ICSharpCode.Decompiler.CSharp @@ -108,6 +110,25 @@ namespace ICSharpCode.Decompiler.CSharp
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) {
VisitAsSequencePoint(stmt);
}
@ -331,11 +352,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -331,11 +352,13 @@ namespace ICSharpCode.Decompiler.CSharp
}
list.Add(sequencePoint);
}
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>();
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) {
// overlapping sequence point?
// delete previous sequence points that are after sequencePoint.Offset
@ -348,17 +371,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -348,17 +371,12 @@ namespace ICSharpCode.Decompiler.CSharp
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);
pos = sequencePoint.EndOffset;
}
// Add a hidden sequence point to account for the epilog of the function
if (pos < function.CodeSize) {
var hidden = new DebugInfo.SequencePoint();
hidden.Offset = pos;
@ -366,8 +384,48 @@ namespace ICSharpCode.Decompiler.CSharp @@ -366,8 +384,48 @@ namespace ICSharpCode.Decompiler.CSharp
hidden.SetHidden();
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;
}
}
return dict;
}
}

4
ICSharpCode.Decompiler/DebugInfo/AsyncDebugInfo.cs

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

2
ICSharpCode.Decompiler/DebugInfo/SequencePoint.cs

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

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

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

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

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

30
ICSharpCode.Decompiler/IL/ILReader.cs

@ -50,6 +50,10 @@ namespace ICSharpCode.Decompiler.IL @@ -50,6 +50,10 @@ namespace ICSharpCode.Decompiler.IL
public DebugInfo.IDebugInfoProvider DebugInfo { get; set; }
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>
/// Creates a new ILReader instance.
/// </summary>
@ -63,6 +67,7 @@ namespace ICSharpCode.Decompiler.IL @@ -63,6 +67,7 @@ namespace ICSharpCode.Decompiler.IL
this.module = module;
this.compilation = module.Compilation;
this.metadata = module.metadata;
this.SequencePointCandidates = new List<int>();
}
GenericContext genericContext;
@ -84,7 +89,7 @@ namespace ICSharpCode.Decompiler.IL @@ -84,7 +89,7 @@ namespace ICSharpCode.Decompiler.IL
UnionFind<ILVariable> unionFind;
List<(ILVariable, ILVariable)> stackMismatchPairs;
IEnumerable<ILVariable> stackVariables;
void Init(MethodDefinitionHandle methodDefinitionHandle, MethodBodyBlock body, GenericContext genericContext)
{
if (body == null)
@ -382,6 +387,7 @@ namespace ICSharpCode.Decompiler.IL @@ -382,6 +387,7 @@ namespace ICSharpCode.Decompiler.IL
int start = reader.Offset;
StoreStackForOffset(start, ref currentStack);
currentInstructionStart = start;
bool startedWithEmptyStack = currentStack.IsEmpty;
ILInstruction decodedInstruction;
try {
decodedInstruction = DecodeInstruction();
@ -400,6 +406,10 @@ namespace ICSharpCode.Decompiler.IL @@ -400,6 +406,10 @@ namespace ICSharpCode.Decompiler.IL
currentStack = ImmutableStack<ILVariable>.Empty;
}
}
if (IsSequencePointInstruction(decodedInstruction) || startedWithEmptyStack) {
this.SequencePointCandidates.Add(decodedInstruction.StartILOffset);
}
}
var visitor = new CollectStackVariablesVisitor(unionFind);
@ -410,6 +420,20 @@ namespace ICSharpCode.Decompiler.IL @@ -410,6 +420,20 @@ namespace ICSharpCode.Decompiler.IL
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()
{
if (stackMismatchPairs.Count == 0)
@ -509,6 +533,10 @@ namespace ICSharpCode.Decompiler.IL @@ -509,6 +533,10 @@ namespace ICSharpCode.Decompiler.IL
function.Warnings.Add("Discarded unreachable code: "
+ string.Join(", ", removedBlocks.Select(b => $"IL_{b.StartILOffset:x4}")));
}
this.SequencePointCandidates.Sort();
function.SequencePointCandidates = this.SequencePointCandidates;
function.Warnings.AddRange(Warnings);
return function;
}

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

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Collections.Immutable;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
@ -113,7 +114,7 @@ namespace ICSharpCode.Decompiler.IL @@ -113,7 +114,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
internal TypeSystem.Implementation.LocalFunctionMethod ReducedMethod;
internal DebugInfo.AsyncDebugInfo AsyncDebugInfo;
public DebugInfo.AsyncDebugInfo AsyncDebugInfo;
int ctorCallStart = int.MinValue;
@ -169,6 +170,13 @@ namespace ICSharpCode.Decompiler.IL @@ -169,6 +170,13 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
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>
/// Constructs a new ILFunction from the given metadata and with the given ILAst body.
/// </summary>

2
global.json

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

Loading…
Cancel
Save