Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
e216509245
  1. 6
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  2. 2
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  3. 144
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  4. 1
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  5. 8
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  6. 7
      ILSpy/MainWindow.xaml.cs
  7. 108
      ILSpy/TextView/DecompilerTextView.cs
  8. 9
      ILSpy/TextView/ReferenceElementGenerator.cs

6
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -290,6 +290,12 @@ namespace Decompiler @@ -290,6 +290,12 @@ namespace Decompiler
new Ast.GotoStatement(((ILLabel)byteCode.Operand).Name)
}
};
case ILCode.TernaryOp:
return new Ast.ConditionalExpression() {
Condition = MakeBranchCondition(byteCode.Arguments[0]),
TrueExpression = (Expression)TransformExpression(byteCode.Arguments[1]),
FalseExpression = (Expression)TransformExpression(byteCode.Arguments[2]),
};
}
List<Ast.Expression> args = TransformExpressionArguments(byteCode);

2
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -57,7 +57,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -57,7 +57,7 @@ namespace ICSharpCode.Decompiler.Disassembler
{
writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction);
writer.Write(": ");
writer.Write(instruction.OpCode.Name);
writer.WriteReference(instruction.OpCode.Name, instruction.OpCode);
if(null != instruction.Operand) {
writer.Write(' ');
WriteOperand(writer, instruction.Operand);

144
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -12,6 +12,7 @@ namespace Decompiler.ControlFlow @@ -12,6 +12,7 @@ namespace Decompiler.ControlFlow
public enum ILAstOptimizationStep
{
SplitToMovableBlocks,
PeepholeOptimizations,
FindLoops,
FindConditions,
FlattenNestedMovableBlocks,
@ -33,7 +34,11 @@ namespace Decompiler.ControlFlow @@ -33,7 +34,11 @@ namespace Decompiler.ControlFlow
SplitToBasicBlocks(block);
}
OptimizeShortCircuits(method);
if (abortBeforeStep == ILAstOptimizationStep.PeepholeOptimizations) return;
AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
PeepholeOptimizations(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
@ -100,24 +105,23 @@ namespace Decompiler.ControlFlow @@ -100,24 +105,23 @@ namespace Decompiler.ControlFlow
ILNode lastNode = block.Body[i - 1];
ILNode currNode = block.Body[i];
bool added = false;
// Insert split
if (currNode is ILLabel ||
lastNode is ILTryCatchBlock ||
currNode is ILTryCatchBlock ||
(lastNode is ILExpression) && ((ILExpression)lastNode).IsBranch() ||
(currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && basicBlock.Body.Count > 0))
(currNode is ILExpression) && (((ILExpression)currNode).IsBranch() && ((ILExpression)currNode).Code.CanFallThough() && basicBlock.Body.Count > 0))
{
ILBasicBlock lastBlock = basicBlock;
basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock);
if (currNode is ILLabel) {
// Reuse the first label
// Insert as entry label
basicBlock.EntryLabel = (ILLabel)currNode;
added = true;
} else {
basicBlock.EntryLabel = new ILLabel() { Name = "Block_" + (nextBlockIndex++) };
basicBlock.Body.Add(currNode);
}
// Explicit branch from one block to other
@ -125,10 +129,20 @@ namespace Decompiler.ControlFlow @@ -125,10 +129,20 @@ namespace Decompiler.ControlFlow
if (!(lastNode is ILExpression) || ((ILExpression)lastNode).Code.CanFallThough()) {
lastBlock.FallthoughGoto = new ILExpression(ILCode.Br, basicBlock.EntryLabel);
}
} else {
basicBlock.Body.Add(currNode);
}
if (!added)
basicBlock.Body.Add(currNode);
}
}
foreach (ILBasicBlock bb in basicBlocks) {
if (bb.Body.Count > 0 &&
bb.Body.Last() is ILExpression &&
((ILExpression)bb.Body.Last()).Code == ILCode.Br)
{
Debug.Assert(bb.FallthoughGoto == null);
bb.FallthoughGoto = (ILExpression)bb.Body.Last();
bb.Body.RemoveAt(bb.Body.Count - 1);
}
}
@ -136,25 +150,6 @@ namespace Decompiler.ControlFlow @@ -136,25 +150,6 @@ namespace Decompiler.ControlFlow
return;
}
void OptimizeShortCircuits(ILBlock method)
{
AnalyseLabels(method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
bool modified;
do {
modified = false;
for (int i = 0; i < block.Body.Count;) {
if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) {
modified = true;
} else {
i++;
}
}
} while(modified);
}
}
Dictionary<ILLabel, int> labelGlobalRefCount;
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock;
@ -175,11 +170,34 @@ namespace Decompiler.ControlFlow @@ -175,11 +170,34 @@ namespace Decompiler.ControlFlow
}
}
void PeepholeOptimizations(ILBlock block)
{
bool modified;
do {
modified = false;
for (int i = 0; i < block.Body.Count;) {
if (TrySimplifyShortCircuit(block.Body, (ILBasicBlock)block.Body[i])) {
modified = true;
continue;
}
if (TrySimplifyTernaryOperator(block.Body, (ILBasicBlock)block.Body[i])) {
modified = true;
continue;
}
i++;
}
} while(modified);
}
bool IsConditionalBranch(ILBasicBlock bb, ref ILExpression branchExpr, ref ILLabel trueLabel, ref ILLabel falseLabel)
{
if (bb.Body.Count == 1) {
branchExpr = bb.Body[0] as ILExpression;
if (branchExpr != null && branchExpr.Operand is ILLabel && branchExpr.Arguments.Count > 0) {
if (branchExpr != null &&
branchExpr.Operand is ILLabel &&
branchExpr.Arguments.Count > 0 &&
branchExpr.Prefixes == null)
{
trueLabel = (ILLabel)branchExpr.Operand;
falseLabel = (ILLabel)((ILExpression)bb.FallthoughGoto).Operand;
return true;
@ -188,6 +206,71 @@ namespace Decompiler.ControlFlow @@ -188,6 +206,71 @@ namespace Decompiler.ControlFlow
return false;
}
bool IsStloc(ILBasicBlock bb, ref ILVariable locVar, ref ILExpression val, ref ILLabel fallLabel)
{
if (bb.Body.Count == 1) {
ILExpression expr = bb.Body[0] as ILExpression;
if (expr != null &&
expr.Code == ILCode.Stloc &&
expr.Prefixes == null)
{
locVar = (ILVariable)expr.Operand;
val = expr.Arguments[0];
fallLabel = (ILLabel)bb.FallthoughGoto.Operand;
return true;
}
}
return false;
}
// scope is modified if successful
bool TrySimplifyTernaryOperator(List<ILNode> scope, ILBasicBlock head)
{
Debug.Assert(scope.Contains(head));
ILExpression branchExpr = null;
ILLabel trueLabel = null;
ILLabel falseLabel = null;
ILVariable trueLocVar = null;
ILExpression trueExpr = null;
ILLabel trueFall = null;
ILVariable falseLocVar = null;
ILExpression falseExpr = null;
ILLabel falseFall = null;
if(IsConditionalBranch(head, ref branchExpr, ref trueLabel, ref falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 &&
IsStloc(labelToBasicBlock[trueLabel], ref trueLocVar, ref trueExpr, ref trueFall) &&
IsStloc(labelToBasicBlock[falseLabel], ref falseLocVar, ref falseExpr, ref falseFall) &&
trueLocVar == falseLocVar &&
trueFall == falseFall)
{
// Create the ternary expression
head.Body = new List<ILNode>() {
new ILExpression(ILCode.Stloc, trueLocVar,
new ILExpression(ILCode.TernaryOp, null,
new ILExpression(branchExpr.Code, null, branchExpr.Arguments.ToArray()),
trueExpr,
falseExpr
)
)
};
head.FallthoughGoto = new ILExpression(ILCode.Br, trueFall);
// Remove the old basic blocks
scope.Remove(labelToBasicBlock[trueLabel]);
scope.Remove(labelToBasicBlock[falseLabel]);
labelToBasicBlock.Remove(trueLabel);
labelToBasicBlock.Remove(falseLabel);
labelGlobalRefCount.Remove(trueLabel);
labelGlobalRefCount.Remove(falseLabel);
return true;
}
return false;
}
// scope is modified if successful
bool TrySimplifyShortCircuit(List<ILNode> scope, ILBasicBlock head)
{
@ -228,7 +311,8 @@ namespace Decompiler.ControlFlow @@ -228,7 +311,8 @@ namespace Decompiler.ControlFlow
head.FallthoughGoto = new ILExpression(ILCode.Br, nextFalseLabel);
// Remove the inlined branch from scope
labelGlobalRefCount[nextBasicBlock.EntryLabel] = 0;
labelGlobalRefCount.Remove(nextBasicBlock.EntryLabel);
labelToBasicBlock.Remove(nextBasicBlock.EntryLabel);
if (!scope.Remove(nextBasicBlock))
throw new Exception("Element not found");

1
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -259,6 +259,7 @@ namespace Decompiler @@ -259,6 +259,7 @@ namespace Decompiler
BrLogicAnd,
BrLogicOr,
InitArray, // Array Initializer
TernaryOp, // ?:
Pattern // used for ILAst pattern nodes
}

8
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -119,6 +119,14 @@ namespace Decompiler @@ -119,6 +119,14 @@ namespace Decompiler
InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean);
}
return null;
case ILCode.TernaryOp:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
}
return TypeWithMoreInformation(
InferTypeForExpression(expr.Arguments[1], expectedType, forceInferChildren),
InferTypeForExpression(expr.Arguments[2], expectedType, forceInferChildren)
);
#endregion
#region Variable load/store
case ILCode.Stloc:

7
ILSpy/MainWindow.xaml.cs

@ -280,6 +280,13 @@ namespace ICSharpCode.ILSpy @@ -280,6 +280,13 @@ namespace ICSharpCode.ILSpy
SelectNode(assemblyListTreeNode.FindEventNode(((EventReference)reference).Resolve()));
} else if (reference is AssemblyDefinition) {
SelectNode(assemblyListTreeNode.FindAssemblyNode((AssemblyDefinition)reference));
} else if (reference is Mono.Cecil.Cil.OpCode) {
string link = "http://msdn.microsoft.com/library/system.reflection.emit.opcodes." + ((Mono.Cecil.Cil.OpCode)reference).Code.ToString().ToLowerInvariant() + ".aspx";
try {
Process.Start(link);
} catch {
}
}
}
#endregion

108
ILSpy/TextView/DecompilerTextView.cs

@ -19,27 +19,31 @@ @@ -19,27 +19,31 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Threading;
using System.Xml;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Folding;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy.TreeNodes;
using ICSharpCode.NRefactory.Documentation;
using ILSpy.Debugger.AvalonEdit;
using ILSpy.Debugger.Bookmarks;
using ILSpy.Debugger.ToolTips;
using Microsoft.Win32;
using Mono.Cecil;
using TextEditorWeakEventManager = ILSpy.Debugger.AvalonEdit.TextEditorWeakEventManager;
namespace ICSharpCode.ILSpy.TextView
{
@ -73,11 +77,13 @@ namespace ICSharpCode.ILSpy.TextView @@ -73,11 +77,13 @@ namespace ICSharpCode.ILSpy.TextView
});
InitializeComponent();
this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference);
this.referenceElementGenerator = new ReferenceElementGenerator(this.JumpToReference, this.IsLink);
textEditor.TextArea.TextView.ElementGenerators.Add(referenceElementGenerator);
this.uiElementGenerator = new UIElementGenerator();
textEditor.TextArea.TextView.ElementGenerators.Add(uiElementGenerator);
textEditor.Options.RequireControlModifierForHyperlinkClick = false;
textEditor.TextArea.TextView.MouseHover += TextViewMouseHover;
textEditor.TextArea.TextView.MouseHoverStopped += TextViewMouseHoverStopped;
// add margin
iconMargin = new IconBarMargin();
@ -92,6 +98,92 @@ namespace ICSharpCode.ILSpy.TextView @@ -92,6 +98,92 @@ namespace ICSharpCode.ILSpy.TextView
#endregion
#region Tooltip support
ToolTip tooltip;
void TextViewMouseHoverStopped(object sender, MouseEventArgs e)
{
if (tooltip != null)
tooltip.IsOpen = false;
}
void TextViewMouseHover(object sender, MouseEventArgs e)
{
TextViewPosition? position = textEditor.TextArea.TextView.GetPosition(e.GetPosition(textEditor.TextArea.TextView) + textEditor.TextArea.TextView.ScrollOffset);
if (position == null)
return;
int offset = textEditor.Document.GetOffset(position.Value);
ReferenceSegment seg = referenceElementGenerator.References.FindSegmentsContaining(offset).FirstOrDefault();
if (seg == null)
return;
object content = GenerateTooltip(seg);
if (tooltip != null)
tooltip.IsOpen = false;
if (content != null)
tooltip = new ToolTip() { Content = content, IsOpen = true };
}
object GenerateTooltip(ReferenceSegment segment)
{
if (segment.Reference is Mono.Cecil.Cil.OpCode) {
Mono.Cecil.Cil.OpCode code = (Mono.Cecil.Cil.OpCode)segment.Reference;
string encodedName = code.Code.ToString();
string opCodeHex = code.Size > 1 ? string.Format("0x{0:x2}{1:x2}", code.Op1, code.Op2) : string.Format("0x{0:x2}", code.Op2);
string documentationFile = FindDocumentation("mscorlib.xml");
string text = "";
if (documentationFile != null){
XmlDocumentationProvider provider = new XmlDocumentationProvider(documentationFile);
string documentation = provider.GetDocumentation("F:System.Reflection.Emit.OpCodes." + encodedName);
if (documentation != null)
text = StripXml(documentation);
}
return string.Format("{0} ({1}): {2}", code.Name, opCodeHex, text);
}
return null;
}
string StripXml(string xml)
{
return Regex.Replace(xml, "</?.*>", "").Trim();
}
string FindDocumentation(string fileName)
{
string path = System.Runtime.InteropServices.RuntimeEnvironment.GetRuntimeDirectory();
List<string> names = new List<string>();
EnumerateCultures(CultureInfo.CurrentCulture, names);
names.Add("en");
names.Add("en-US");
names.Add("en-GB");
foreach (string name in names) {
string location = Path.Combine(path, name, fileName);
if (File.Exists(location))
return location;
}
path = Path.Combine(Environment.GetEnvironmentVariable("PROGRAMFILES(X86)") ?? Environment.GetEnvironmentVariable("PROGRAMFILES"), @"Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0");
string loc = Path.Combine(path, fileName);
if (File.Exists(loc))
return loc;
return null;
}
void EnumerateCultures(CultureInfo info, List<string> names)
{
while (info != null) {
names.Add(info.Name);
info = info.Parent;
if (info == info.Parent)
return;
}
}
#endregion
#region RunWithCancellation
/// <summary>
/// Switches the GUI into "waiting" mode, then calls <paramref name="taskCreation"/> to create
@ -381,6 +473,14 @@ namespace ICSharpCode.ILSpy.TextView @@ -381,6 +473,14 @@ namespace ICSharpCode.ILSpy.TextView
}
mainWindow.JumpToReference(reference);
}
/// <summary>
/// Filters all ReferenceSegments that are no real links.
/// </summary>
bool IsLink(ReferenceSegment referenceSegment)
{
return true;
}
#endregion
#region SaveToDisk

9
ILSpy/TextView/ReferenceElementGenerator.cs

@ -29,17 +29,21 @@ namespace ICSharpCode.ILSpy.TextView @@ -29,17 +29,21 @@ namespace ICSharpCode.ILSpy.TextView
sealed class ReferenceElementGenerator : VisualLineElementGenerator
{
Action<ReferenceSegment> referenceClicked;
Predicate<ReferenceSegment> isLink;
/// <summary>
/// The collection of references (hyperlinks).
/// </summary>
public TextSegmentCollection<ReferenceSegment> References { get; set; }
public ReferenceElementGenerator(Action<ReferenceSegment> referenceClicked)
public ReferenceElementGenerator(Action<ReferenceSegment> referenceClicked, Predicate<ReferenceSegment> isLink)
{
if (referenceClicked == null)
throw new ArgumentNullException("referenceClicked");
if (isLink == null)
throw new ArgumentNullException("isLink");
this.referenceClicked = referenceClicked;
this.isLink = isLink;
}
public override int GetFirstInterestedOffset(int startOffset)
@ -56,6 +60,9 @@ namespace ICSharpCode.ILSpy.TextView @@ -56,6 +60,9 @@ namespace ICSharpCode.ILSpy.TextView
if (this.References == null)
return null;
foreach (var segment in this.References.FindSegmentsContaining(offset)) {
// skip all non-links
if (!isLink(segment))
continue;
// ensure that hyperlinks don't span several lines (VisualLineElements can't contain line breaks)
int endOffset = Math.Min(segment.EndOffset, CurrentContext.VisualLine.LastDocumentLine.EndOffset);
// don't create hyperlinks with length 0

Loading…
Cancel
Save