From 4347fab625661926df360c84f3fc2354a2f3b616 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sat, 14 Oct 2017 12:13:04 +0200 Subject: [PATCH] Add SequencePointBuilder. --- .../CSharp/CSharpDecompiler.cs | 14 ++ .../CSharp/SequencePointBuilder.cs | 147 ++++++++++++++++++ .../ICSharpCode.Decompiler.csproj | 2 + ICSharpCode.Decompiler/IL/SequencePoint.cs | 26 ++++ 4 files changed, 189 insertions(+) create mode 100644 ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs create mode 100644 ICSharpCode.Decompiler/IL/SequencePoint.cs diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 801b95b7c..1bba92164 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -30,6 +30,7 @@ using ICSharpCode.Decompiler.IL; using ICSharpCode.Decompiler.IL.ControlFlow; using ICSharpCode.Decompiler.IL.Transforms; using ICSharpCode.Decompiler.TypeSystem; +using ICSharpCode.Decompiler.Util; namespace ICSharpCode.Decompiler.CSharp { @@ -982,6 +983,19 @@ namespace ICSharpCode.Decompiler.CSharp } #endregion + #region Sequence Points + /// + /// Creates sequence points for the given syntax tree. + /// + /// This only works correctly when the nodes in the syntax tree have line/column information. + /// + public Dictionary> CreateSequencePoints(SyntaxTree syntaxTree) + { + SequencePointBuilder spb = new SequencePointBuilder(); + syntaxTree.AcceptVisitor(spb); + return spb.GetSequencePoints(); + } + #endregion } [Flags] diff --git a/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs new file mode 100644 index 000000000..eaf3367b7 --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/SequencePointBuilder.cs @@ -0,0 +1,147 @@ +// Copyright (c) 2017 Daniel Grunwald +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.IL; +using ICSharpCode.Decompiler.Util; + +namespace ICSharpCode.Decompiler.CSharp +{ + /// + /// Given a SyntaxTree that was output from the decompiler, constructs the list of sequence points. + /// + class SequencePointBuilder : DepthFirstAstVisitor + { + struct StatePerSequencePoint + { + internal readonly AstNode PrimaryNode; + internal readonly List Intervals; + internal ILFunction Function; + + public StatePerSequencePoint(AstNode primaryNode) + { + this.PrimaryNode = primaryNode; + this.Intervals = new List(); + this.Function = null; + } + } + + readonly List<(ILFunction, SequencePoint)> sequencePoints = new List<(ILFunction, SequencePoint)>(); + readonly HashSet mappedInstructions = new HashSet(); + readonly Stack outerStates = new Stack(); + StatePerSequencePoint current = new StatePerSequencePoint(); + + void VisitAsSequencePoint(AstNode node) + { + StartSequencePoint(node); + node.AcceptVisitor(this); + EndSequencePoint(node.StartLocation, node.EndLocation); + } + + protected override void VisitChildren(AstNode node) + { + base.VisitChildren(node); + AddToSequencePoint(node); + } + + public override void VisitBlockStatement(BlockStatement blockStatement) + { + foreach (var stmt in blockStatement.Statements) { + VisitAsSequencePoint(stmt); + } + } + + public override void VisitForStatement(ForStatement forStatement) + { + foreach (var init in forStatement.Initializers) { + VisitAsSequencePoint(init); + } + VisitAsSequencePoint(forStatement.Condition); + foreach (var inc in forStatement.Iterators) { + VisitAsSequencePoint(inc); + } + VisitAsSequencePoint(forStatement.EmbeddedStatement); + } + + /// + /// Start a new C# statement = new sequence point. + /// + void StartSequencePoint(AstNode primaryNode) + { + outerStates.Push(current); + current = new StatePerSequencePoint(primaryNode); + } + + void EndSequencePoint(TextLocation startLocation, TextLocation endLocation) + { + if (current.Intervals.Count > 0 && current.Function != null) { + sequencePoints.Add((current.Function, new SequencePoint { + Offset = current.Intervals.Select(i => i.Start).Min(), + StartLine = startLocation.Line, + StartColumn = startLocation.Column, + EndLine = endLocation.Line, + EndColumn = endLocation.Column + })); + } + current = outerStates.Pop(); + } + + void AddToSequencePoint(AstNode node) + { + foreach (var inst in node.Annotations.OfType()) { + AddToSequencePoint(inst); + } + } + + void AddToSequencePoint(ILInstruction inst) + { + if (!mappedInstructions.Add(inst)) { + // inst was already used by a nested sequence point within this sequence point + return; + } + // Add the IL range associated with this instruction to the current sequence point. + if (!inst.ILRange.IsEmpty) { + current.Intervals.Add(inst.ILRange); + current.Function = inst.Ancestors.OfType().FirstOrDefault(); + } + // Also add the child IL instructions, unless they were already processed by + // another C# expression. + foreach (var child in inst.Children) { + AddToSequencePoint(child); + } + } + + internal Dictionary> GetSequencePoints() + { + var dict = new Dictionary>(); + foreach (var (function, sequencePoint) in this.sequencePoints) { + if (!dict.TryGetValue(function, out var list)) { + dict.Add(function, list = new List()); + } + list.Add(sequencePoint); + } + foreach (var list in dict.Values) { + list.Sort((a, b) => a.Offset.CompareTo(b.Offset)); + } + return dict; + } + } +} diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index a1e17edfd..93e3c5389 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -60,6 +60,7 @@ + @@ -272,6 +273,7 @@ + diff --git a/ICSharpCode.Decompiler/IL/SequencePoint.cs b/ICSharpCode.Decompiler/IL/SequencePoint.cs new file mode 100644 index 000000000..568953c35 --- /dev/null +++ b/ICSharpCode.Decompiler/IL/SequencePoint.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace ICSharpCode.Decompiler.IL +{ + /// + /// A sequence point produced by the decompiler. + /// + public struct SequencePoint + { + /// + /// IL offset. + /// + public int Offset { get; set; } + + public int StartLine { get; set; } + public int StartColumn { get; set; } + public int EndLine { get; set; } + public int EndColumn { get; set; } + + public bool IsHidden { + get { return StartLine == 0xfeefee && StartLine == EndLine; } + } + } +}