Browse Source

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

pull/191/merge
Eusebiu Marcu 15 years ago
parent
commit
aad4001cdb
  1. 107
      Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs
  2. 2
      Decompiler/.gitignore
  3. 31
      Decompiler/tests/QuickSort/AssemblyInfo.cs
  4. 49
      Decompiler/tests/QuickSort/Program.cs
  5. 36
      Decompiler/tests/QuickSort/QuickSort.csproj
  6. 18
      Decompiler/tests/QuickSort/QuickSort.sln
  7. BIN
      Decompiler/tests/QuickSort/bin/Debug/QuickSort.exe
  8. BIN
      Decompiler/tests/QuickSort/bin/Debug/QuickSort.pdb
  9. BIN
      Decompiler/tests/Stack/StackTests.exe
  10. 135
      Decompiler/tests/Stack/StackTests.il
  11. 52
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  12. 303
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  13. 3
      ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
  14. 42
      ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
  15. 130
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  16. 56
      ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs
  17. 71
      ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs
  18. 36
      ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  19. 49
      ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs
  20. 28
      ICSharpCode.Decompiler/Ast/Transforms/SimplifyTypeReferences.cs
  21. 50
      ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  22. 11
      ICSharpCode.Decompiler/CecilExtensions.cs
  23. 9
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  24. 22
      ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  25. 137
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  26. 63
      ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  27. 39
      ILSpy/CSharpLanguage.cs
  28. 3
      ILSpy/ILSpy.csproj
  29. 8
      ILSpy/Language.cs
  30. 1
      ILSpy/MainWindow.xaml
  31. 2
      NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstComparer.cs

107
Debugger/ILSpy.Debugger/AvalonEdit/IconBarMargin.cs

@ -25,10 +25,8 @@ using System.Windows.Media; @@ -25,10 +25,8 @@ using System.Windows.Media;
using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.Decompiler.Disassembler;
using ILSpy.Debugger.Bookmarks;
using ILSpy.Debugger.Services;
using ILSpy.Debugger.ToolTips;
using Mono.Cecil;
namespace ILSpy.Debugger.AvalonEdit
@ -74,7 +72,7 @@ namespace ILSpy.Debugger.AvalonEdit @@ -74,7 +72,7 @@ namespace ILSpy.Debugger.AvalonEdit
// create a dictionary line number => first bookmark
Dictionary<int, BookmarkBase> bookmarkDict = new Dictionary<int, BookmarkBase>();
foreach (var bm in BookmarkManager.Bookmarks) {
if (IconBarMargin.CurrentType == null || bm.TypeName != IconBarMargin.CurrentType.FullName)
if (CurrentType == null || bm.TypeName != CurrentType.FullName)
continue;
int line = bm.LineNumber;
@ -88,10 +86,44 @@ namespace ILSpy.Debugger.AvalonEdit @@ -88,10 +86,44 @@ namespace ILSpy.Debugger.AvalonEdit
BookmarkBase bm;
if (bookmarkDict.TryGetValue(lineNumber, out bm)) {
Rect rect = new Rect(0, PixelSnapHelpers.Round(line.VisualTop - textView.VerticalOffset, pixelSize.Height), 16, 16);
drawingContext.DrawImage(bm.Image, rect);
if (dragDropBookmark == bm && dragStarted)
drawingContext.PushOpacity(0.5);
drawingContext.DrawImage(bm.Image, rect);
if (dragDropBookmark == bm && dragStarted)
drawingContext.Pop();
}
}
}
if (dragDropBookmark != null && dragStarted) {
Rect rect = new Rect(0, PixelSnapHelpers.Round(dragDropCurrentPoint - 8, pixelSize.Height), 16, 16);
drawingContext.DrawImage(dragDropBookmark.Image, rect);
}
}
}
IBookmark dragDropBookmark; // bookmark being dragged (!=null if drag'n'drop is active)
double dragDropStartPoint;
double dragDropCurrentPoint;
bool dragStarted; // whether drag'n'drop operation has started (mouse was moved minimum distance)
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
int line = GetLineFromMousePosition(e);
if (!e.Handled && line > 0) {
IBookmark bm = GetBookmarkFromLine(line);
if (bm != null) {
bm.MouseDown(e);
if (!e.Handled) {
if (e.ChangedButton == MouseButton.Left && bm.CanDragDrop && CaptureMouse()) {
StartDragDrop(bm, e);
e.Handled = true;
}
}
}
}
// don't allow selecting text through the IconBarMargin
if (e.ChangedButton == MouseButton.Left)
e.Handled = true;
}
BookmarkBase GetBookmarkFromLine(int line)
@ -99,8 +131,8 @@ namespace ILSpy.Debugger.AvalonEdit @@ -99,8 +131,8 @@ namespace ILSpy.Debugger.AvalonEdit
BookmarkBase result = null;
foreach (BookmarkBase bm in BookmarkManager.Bookmarks) {
if (bm.LineNumber == line &&
IconBarMargin.CurrentType != null &&
bm.TypeName == IconBarMargin.CurrentType.FullName) {
CurrentType != null &&
bm.TypeName == CurrentType.FullName) {
if (result == null || bm.ZOrder > result.ZOrder)
result = bm;
}
@ -108,6 +140,46 @@ namespace ILSpy.Debugger.AvalonEdit @@ -108,6 +140,46 @@ namespace ILSpy.Debugger.AvalonEdit
return result;
}
protected override void OnLostMouseCapture(MouseEventArgs e)
{
CancelDragDrop();
base.OnLostMouseCapture(e);
}
void StartDragDrop(IBookmark bm, MouseEventArgs e)
{
dragDropBookmark = bm;
dragDropStartPoint = dragDropCurrentPoint = e.GetPosition(this).Y;
if (TextView != null) {
TextArea area = TextView.Services.GetService(typeof(TextArea)) as TextArea;
if (area != null)
area.PreviewKeyDown += TextArea_PreviewKeyDown;
}
}
void CancelDragDrop()
{
if (dragDropBookmark != null) {
dragDropBookmark = null;
dragStarted = false;
if (TextView != null) {
TextArea area = TextView.Services.GetService(typeof(TextArea)) as TextArea;
if (area != null)
area.PreviewKeyDown -= TextArea_PreviewKeyDown;
}
ReleaseMouseCapture();
InvalidateVisual();
}
}
void TextArea_PreviewKeyDown(object sender, KeyEventArgs e)
{
// any key press cancels drag'n'drop
CancelDragDrop();
if (e.Key == Key.Escape)
e.Handled = true;
}
int GetLineFromMousePosition(MouseEventArgs e)
{
TextView textView = this.TextView;
@ -119,10 +191,29 @@ namespace ILSpy.Debugger.AvalonEdit @@ -119,10 +191,29 @@ namespace ILSpy.Debugger.AvalonEdit
return vl.FirstDocumentLine.LineNumber;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (dragDropBookmark != null) {
dragDropCurrentPoint = e.GetPosition(this).Y;
if (Math.Abs(dragDropCurrentPoint - dragDropStartPoint) > SystemParameters.MinimumVerticalDragDistance)
dragStarted = true;
InvalidateVisual();
}
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
int line = GetLineFromMousePosition(e);
if (!e.Handled && dragDropBookmark != null) {
if (dragStarted) {
if (line != 0)
dragDropBookmark.Drop(line);
e.Handled = true;
}
CancelDragDrop();
}
if (!e.Handled && line != 0) {
IBookmark bm = GetBookmarkFromLine(line);
if (bm != null) {
@ -144,4 +235,4 @@ namespace ILSpy.Debugger.AvalonEdit @@ -144,4 +235,4 @@ namespace ILSpy.Debugger.AvalonEdit
}
}
}
}
}

2
Decompiler/.gitignore vendored

@ -1,2 +0,0 @@ @@ -1,2 +0,0 @@
bin/
obj/

31
Decompiler/tests/QuickSort/AssemblyInfo.cs

@ -1,31 +0,0 @@ @@ -1,31 +0,0 @@
#region Using directives
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
#endregion
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("QuickSort")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("QuickSort")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// This sets the default COM visibility of types in the assembly to invisible.
// If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)]
// The assembly version has following format :
//
// Major.Minor.Build.Revision
//
// You can specify all the values or you can use the default the Revision and
// Build Numbers by using the '*' as shown below:
[assembly: AssemblyVersion("1.0.*")]

49
Decompiler/tests/QuickSort/Program.cs

@ -1,49 +0,0 @@ @@ -1,49 +0,0 @@
static class QuickSortProgram
{
public static void Main(string[] args)
{
int[] intArray = new int[args.Length];
for (int i = 0; i < intArray.Length; i++) {
intArray[i] = int.Parse(args[i]);
}
QuickSort(intArray, 0, intArray.Length - 1);
for (int i = 0; i < intArray.Length; i++) {
System.Console.Write(intArray[i].ToString() + " ");
}
}
/// For description of this algorithm see:
/// http://en.wikipedia.org/wiki/Quick_sort
public static void QuickSort(int[] array, int left, int right)
{
if (right > left) {
int pivotIndex = (left + right) / 2;
int pivotNew = Partition(array, left, right, pivotIndex);
QuickSort(array, left, pivotNew - 1);
QuickSort(array, pivotNew + 1, right);
}
}
static int Partition(int[] array, int left, int right,
int pivotIndex)
{
int pivotValue = array[pivotIndex];
Swap(array, pivotIndex, right);
int storeIndex = left;
for(int i = left; i < right; i++) {
if (array[i] <= pivotValue) {
Swap(array, storeIndex, i);
storeIndex = storeIndex + 1;
}
}
Swap(array, right, storeIndex);
return storeIndex;
}
static void Swap(int[] array, int index1, int index2)
{
int tmp = array[index1];
array[index1] = array[index2];
array[index2] = tmp;
}
}

36
Decompiler/tests/QuickSort/QuickSort.csproj

@ -1,36 +0,0 @@ @@ -1,36 +0,0 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<ProjectGuid>{9C2A1C63-38EA-42CA-BDDD-7D80D9592893}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Exe</OutputType>
<RootNamespace>QuickSort</RootNamespace>
<AssemblyName>QuickSort</AssemblyName>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<OutputPath>bin\Debug\</OutputPath>
<DebugSymbols>True</DebugSymbols>
<DebugType>Full</DebugType>
<Optimize>False</Optimize>
<CheckForOverflowUnderflow>True</CheckForOverflowUnderflow>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>bin\Release\</OutputPath>
<DebugSymbols>False</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs" />
<Compile Include="Program.cs" />
</ItemGroup>
</Project>

18
Decompiler/tests/QuickSort/QuickSort.sln

@ -1,18 +0,0 @@ @@ -1,18 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 9.00
# Visual Studio 2005
# SharpDevelop 3.0.0.2707
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QuickSort", "QuickSort.csproj", "{9C2A1C63-38EA-42CA-BDDD-7D80D9592893}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{9C2A1C63-38EA-42CA-BDDD-7D80D9592893}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9C2A1C63-38EA-42CA-BDDD-7D80D9592893}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9C2A1C63-38EA-42CA-BDDD-7D80D9592893}.Release|Any CPU.Build.0 = Release|Any CPU
{9C2A1C63-38EA-42CA-BDDD-7D80D9592893}.Release|Any CPU.ActiveCfg = Release|Any CPU
EndGlobalSection
EndGlobal

BIN
Decompiler/tests/QuickSort/bin/Debug/QuickSort.exe

Binary file not shown.

BIN
Decompiler/tests/QuickSort/bin/Debug/QuickSort.pdb

Binary file not shown.

BIN
Decompiler/tests/Stack/StackTests.exe

Binary file not shown.

135
Decompiler/tests/Stack/StackTests.il

@ -1,135 +0,0 @@ @@ -1,135 +0,0 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly StackTests
{
.hash algorithm 0x00008004
.ver 1:0:4059:39717
}
.module StackTests.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000003 // ILONLY 32BITREQUIRED
.class private auto ansi beforefieldinit StackTests.Program extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
ldc.i4.0
call string StackTests.Program::Test1(bool cond)
call void [mscorlib]System.Console::WriteLine(string) // false
ldc.i4.1
call string StackTests.Program::Test1(bool cond)
call void [mscorlib]System.Console::WriteLine(string) // true
ldc.i4.0
ldc.i4.0
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 11
ldc.i4.0
ldc.i4.1
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 21
ldc.i4.1
ldc.i4.1
ldc.i4.1
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 32
ldc.i4.2
ldc.i4.1
ldc.i4.0
call int32 StackTests.Program::Test2(int32 switch1, int32 br1, int32 br2)
call void [mscorlib]System.Console::WriteLine(int32) // 23
ret
}
.method public hidebysig static string Test1(bool cond) cil managed
{
ldarg.0
brtrue TRUE
FALSE:
ldstr "false"
br EXIT
TRUE:
ldstr "true"
EXIT:
ret
}
.method public hidebysig static int32 Test2(int32 switch1, int32 br1, int32 br2) cil managed
{
ldarg.0
switch (ENTRY1, ENTRY2, ENTRY3)
ldc.i4.0
ret
ENTRY1:
ldc.i4.1
br BRANCH1
ENTRY2:
ldc.i4.2
br BRANCH1
ENTRY3:
ldc.i4.3
br BRANCH2
BRANCH1:
ldarg.1
brtrue BRANCH2
EXIT1:
ldc.i4 10
add
ret
BRANCH2:
ldarg.2
brtrue.s EXIT3
EXIT2:
ldc.i4 20
add
ret
EXIT3:
ldc.i4 30
add
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class StackTests.Program
// =============================================================
// *********** DISASSEMBLY COMPLETE ***********************
// WARNING: Created Win32 resource file D:\git\ILSpy\Decompiler\tests\Stack\StackTests.res

52
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -17,28 +17,12 @@ namespace Decompiler @@ -17,28 +17,12 @@ namespace Decompiler
public void GenerateCode(ITextOutput output)
{
for (int i = 0; i < 4; i++) {
if (Options.ReduceAstJumps) {
//astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null);
//astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null);
}
if (Options.ReduceAstLoops) {
//astCompileUnit.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null);
}
if (Options.ReduceAstOther) {
astCompileUnit.AcceptVisitor(new Transforms.Ast.Idioms(), null);
astCompileUnit.AcceptVisitor(new Transforms.Ast.RemoveEmptyElseBody(), null);
astCompileUnit.AcceptVisitor(new Transforms.Ast.PushNegation(), null);
}
}
if (Options.ReduceAstOther) {
astCompileUnit.AcceptVisitor(new Transforms.Ast.SimplifyTypeReferences(), null);
astCompileUnit.AcceptVisitor(new Transforms.Ast.Idioms(), null);
}
if (Options.ReduceAstLoops) {
//astCompileUnit.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null);
}
GenerateCode(output, null);
}
public void GenerateCode(ITextOutput output, Predicate<IAstVisitor<object, object>> transformAbortCondition)
{
Transforms.TransformationPipeline.RunTransformationsUntil(astCompileUnit, transformAbortCondition);
astCompileUnit.AcceptVisitor(new InsertParenthesesVisitor { InsertParenthesesForReadability = true }, null);
var outputFormatter = new TextOutputFormatter(output);
@ -263,14 +247,18 @@ namespace Decompiler @@ -263,14 +247,18 @@ namespace Decompiler
}
name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
if (ns.Length == 0)
return new SimpleType(name);
string[] parts = ns.Split('.');
AstType nsType = new SimpleType(parts[0]);
for (int i = 1; i < parts.Length; i++) {
nsType = new MemberType { Target = nsType, MemberName = parts[i] };
}
return new MemberType { Target = nsType, MemberName = name }.WithAnnotation(type);
// TODO: Until we can simplify type with 'using', use just the name without namesapce
return new SimpleType(name).WithAnnotation(type);
// if (ns.Length == 0)
// return new SimpleType(name).WithAnnotation(type);
// string[] parts = ns.Split('.');
// AstType nsType = new SimpleType(parts[0]);
// for (int i = 1; i < parts.Length; i++) {
// nsType = new MemberType { Target = nsType, MemberName = parts[i] };
// }
// return new MemberType { Target = nsType, MemberName = name }.WithAnnotation(type);
}
}
}
@ -487,7 +475,7 @@ namespace Decompiler @@ -487,7 +475,7 @@ namespace Decompiler
return astField;
}
IEnumerable<ParameterDeclaration> MakeParameters(IEnumerable<ParameterDefinition> paramCol)
public static IEnumerable<ParameterDeclaration> MakeParameters(IEnumerable<ParameterDefinition> paramCol)
{
foreach(ParameterDefinition paramDef in paramCol) {
ParameterDeclaration astParam = new ParameterDeclaration();
@ -502,4 +490,4 @@ namespace Decompiler @@ -502,4 +490,4 @@ namespace Decompiler
}
}
}
}
}

303
ICSharpCode.Decompiler/Ast/AstMetodBodyBuilder.cs → ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -15,8 +15,7 @@ namespace Decompiler @@ -15,8 +15,7 @@ namespace Decompiler
public class AstMethodBodyBuilder
{
MethodDefinition methodDef;
static Dictionary<string, Cecil.TypeReference> localVarTypes = new Dictionary<string, Cecil.TypeReference>();
static Dictionary<string, bool> localVarDefined = new Dictionary<string, bool>();
HashSet<ILVariable> definedLocalVars = new HashSet<ILVariable>();
public static BlockStatement CreateMethodBody(MethodDefinition methodDef)
{
@ -47,48 +46,45 @@ namespace Decompiler @@ -47,48 +46,45 @@ namespace Decompiler
if (methodDef.Body == null) return null;
ILBlock ilMethod = new ILBlock();
ilMethod.Body = new ILAstBuilder().Build(methodDef, true);
ILAstBuilder astBuilder = new ILAstBuilder();
ilMethod.Body = astBuilder.Build(methodDef, true);
ILAstOptimizer bodyGraph = new ILAstOptimizer();
bodyGraph.Optimize(ilMethod);
List<string> intNames = new List<string>(new string[] {"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t"});
Dictionary<string, int> typeNames = new Dictionary<string, int>();
foreach(VariableDefinition varDef in methodDef.Body.Variables) {
if (string.IsNullOrEmpty(varDef.Name)) {
if (varDef.VariableType.FullName == Constants.Int32 && intNames.Count > 0) {
varDef.Name = intNames[0];
intNames.RemoveAt(0);
} else {
string name;
if (varDef.VariableType.IsArray) {
name = "array";
} else if (!typeNameToVariableNameDict.TryGetValue(varDef.VariableType.FullName, out name)) {
name = varDef.VariableType.Name;
// remove the 'I' for interfaces
if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2]))
name = name.Substring(1);
// remove the backtick (generics)
int pos = name.IndexOf('`');
if (pos >= 0)
name = name.Substring(0, pos);
if (name.Length == 0)
name = "obj";
else
name = char.ToLower(name[0]) + name.Substring(1);
}
if (!typeNames.ContainsKey(name)) {
typeNames.Add(name, 0);
}
int count = typeNames[name];
if (count > 0) {
name += count.ToString();
}
varDef.Name = name;
foreach(ILVariable varDef in astBuilder.Variables) {
if (varDef.Type.FullName == Constants.Int32 && intNames.Count > 0) {
varDef.Name = intNames[0];
intNames.RemoveAt(0);
} else {
string name;
if (varDef.Type.IsArray) {
name = "array";
} else if (!typeNameToVariableNameDict.TryGetValue(varDef.Type.FullName, out name)) {
name = varDef.Type.Name;
// remove the 'I' for interfaces
if (name.Length >= 3 && name[0] == 'I' && char.IsUpper(name[1]) && char.IsLower(name[2]))
name = name.Substring(1);
// remove the backtick (generics)
int pos = name.IndexOf('`');
if (pos >= 0)
name = name.Substring(0, pos);
if (name.Length == 0)
name = "obj";
else
name = char.ToLower(name[0]) + name.Substring(1);
}
if (!typeNames.ContainsKey(name)) {
typeNames.Add(name, 0);
}
int count = ++(typeNames[name]);
if (count > 1) {
name += count.ToString();
}
varDef.Name = name;
}
localVarTypes[varDef.Name] = varDef.VariableType;
localVarDefined[varDef.Name] = false;
// Ast.VariableDeclaration astVar = new Ast.VariableDeclaration(varDef.Name);
// Ast.LocalVariableDeclaration astLocalVar = new Ast.LocalVariableDeclaration(astVar);
@ -117,8 +113,10 @@ namespace Decompiler @@ -117,8 +113,10 @@ namespace Decompiler
if (node is ILLabel) {
yield return new Ast.LabelStatement { Label = ((ILLabel)node).Name };
} else if (node is ILExpression) {
object codeExpr = TransformExpression((ILExpression)node);
List<ILRange> ilRanges = ((ILExpression)node).GetILRanges();
AstNode codeExpr = TransformExpression((ILExpression)node);
if (codeExpr != null) {
codeExpr = codeExpr.WithAnnotation(ilRanges);
if (codeExpr is Ast.Expression) {
yield return new Ast.ExpressionStatement { Expression = (Ast.Expression)codeExpr };
} else if (codeExpr is Ast.Statement) {
@ -151,12 +149,30 @@ namespace Decompiler @@ -151,12 +149,30 @@ namespace Decompiler
*/
} else if (node is ILCondition) {
ILCondition conditionalNode = (ILCondition)node;
// Swap bodies
yield return new Ast.IfElseStatement {
Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(conditionalNode.Condition)),
TrueStatement = TransformBlock(conditionalNode.FalseBlock),
FalseStatement = TransformBlock(conditionalNode.TrueBlock)
};
if (conditionalNode.FalseBlock.Body.Any()) {
// Swap bodies
yield return new Ast.IfElseStatement {
Condition = new UnaryOperatorExpression(UnaryOperatorType.Not, MakeBranchCondition(conditionalNode.Condition)),
TrueStatement = TransformBlock(conditionalNode.FalseBlock),
FalseStatement = TransformBlock(conditionalNode.TrueBlock)
};
} else {
yield return new Ast.IfElseStatement {
Condition = MakeBranchCondition(conditionalNode.Condition),
TrueStatement = TransformBlock(conditionalNode.TrueBlock),
FalseStatement = TransformBlock(conditionalNode.FalseBlock)
};
}
} else if (node is ILSwitch) {
ILSwitch ilSwitch = (ILSwitch)node;
SwitchStatement switchStmt = new SwitchStatement() { Expression = (Expression)TransformExpression(ilSwitch.Condition.Arguments[0]) };
for (int i = 0; i < ilSwitch.CaseBlocks.Count; i++) {
switchStmt.AddChild(new SwitchSection() {
CaseLabels = new[] { new CaseLabel() { Expression = new PrimitiveExpression(i) } },
Statements = new[] { TransformBlock(ilSwitch.CaseBlocks[i]) }
}, SwitchStatement.SwitchSectionRole);
}
yield return switchStmt;
} else if (node is ILTryCatchBlock) {
ILTryCatchBlock tryCatchNode = ((ILTryCatchBlock)node);
List<Ast.CatchClause> catchClauses = new List<CatchClause>();
@ -189,7 +205,7 @@ namespace Decompiler @@ -189,7 +205,7 @@ namespace Decompiler
return args;
}
object TransformExpression(ILExpression expr)
AstNode TransformExpression(ILExpression expr)
{
List<Ast.Expression> args = TransformExpressionArguments(expr);
return TransformByteCode(methodDef, expr, args);
@ -252,7 +268,7 @@ namespace Decompiler @@ -252,7 +268,7 @@ namespace Decompiler
*/
}
static object TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args)
AstNode TransformByteCode(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args)
{
try {
AstNode ret = TransformByteCode_Internal(methodDef, byteCode, args);
@ -267,7 +283,7 @@ namespace Decompiler @@ -267,7 +283,7 @@ namespace Decompiler
}
}
static string FormatByteCodeOperand(object operand)
string FormatByteCodeOperand(object operand)
{
if (operand == null) {
return string.Empty;
@ -292,7 +308,7 @@ namespace Decompiler @@ -292,7 +308,7 @@ namespace Decompiler
}
}
static AstNode TransformByteCode_Internal(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args)
AstNode TransformByteCode_Internal(MethodDefinition methodDef, ILExpression byteCode, List<Ast.Expression> args)
{
// throw new NotImplementedException();
@ -326,6 +342,7 @@ namespace Decompiler @@ -326,6 +342,7 @@ namespace Decompiler
case Code.Sub_Ovf: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case Code.Sub_Ovf_Un: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.Subtract, arg2);
case Code.And: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseAnd, arg2);
case Code.Or: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.BitwiseOr, arg2);
case Code.Xor: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ExclusiveOr, arg2);
case Code.Shl: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftLeft, arg2);
case Code.Shr: return new Ast.BinaryOperatorExpression(arg1, BinaryOperatorType.ShiftRight, arg2);
@ -360,7 +377,7 @@ namespace Decompiler @@ -360,7 +377,7 @@ namespace Decompiler
case Code.Ldelem_Any:
throw new NotImplementedException();
case Code.Ldelema:
return arg1.Indexer(arg2);
return MakeRef(arg1.Indexer(arg2));
case Code.Stelem_I:
case Code.Stelem_I1:
@ -459,49 +476,28 @@ namespace Decompiler @@ -459,49 +476,28 @@ namespace Decompiler
case Code.Box: throw new NotImplementedException();
case Code.Break: throw new NotImplementedException();
case Code.Call:
return TransformCall(false, operand, methodDef, args);
case Code.Callvirt:
// TODO: Diferentiate vitual and non-vitual dispach
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
Ast.Expression target;
List<Ast.Expression> methodArgs = new List<Ast.Expression>(args);
if (cecilMethod.HasThis) {
target = methodArgs[0];
methodArgs.RemoveAt(0);
} else {
target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType)};
return TransformCall(true, operand, methodDef, args);
case Code.Ldftn:
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
expr.TypeArguments = ConvertTypeArguments(cecilMethod);
expr.AddAnnotation(cecilMethod);
return new IdentifierExpression("ldftn").Invoke(expr)
.WithAnnotation(new Transforms.DelegateConstruction.Annotation(false, methodDef.DeclaringType));
}
// TODO: Constructors are ignored
if (cecilMethod.Name == ".ctor") {
return new CommentStatement("Constructor");
case Code.Ldvirtftn:
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
var expr = new Ast.IdentifierExpression(cecilMethod.Name);
expr.TypeArguments = ConvertTypeArguments(cecilMethod);
expr.AddAnnotation(cecilMethod);
return new IdentifierExpression("ldvirtftn").Invoke(expr)
.WithAnnotation(new Transforms.DelegateConstruction.Annotation(true, methodDef.DeclaringType));
}
// TODO: Hack, detect properties properly
if (cecilMethod.Name.StartsWith("get_")) {
return target.Member(cecilMethod.Name.Remove(0, 4)).WithAnnotation(cecilMethod);
} else if (cecilMethod.Name.StartsWith("set_")) {
return new Ast.AssignmentExpression(
target.Member(cecilMethod.Name.Remove(0, 4)).WithAnnotation(cecilMethod),
methodArgs[0]
);
}
// Multi-dimensional array acces // TODO: do properly
/*
if (cecilMethod.Name == "Get") {
return new Ast.IndexerExpression(target, methodArgs);
} else if (cecilMethod.Name == "Set") {
Expression val = methodArgs[methodArgs.Count - 1];
methodArgs.RemoveAt(methodArgs.Count - 1);
return new Ast.AssignmentExpression(
new Ast.IndexerExpression(target, methodArgs),
AssignmentOperatorType.Assign,
Convert(val, ((Cecil.ArrayType)((target.UserData as Dictionary<string, object>)["Type"])).ElementType)
);
}*/
// Default invocation
return target.Invoke(cecilMethod.Name, methodArgs).WithAnnotation(cecilMethod);
case Code.Calli: throw new NotImplementedException();
case Code.Castclass: return arg1.CastTo(operandAsTypeRef);
case Code.Ckfinite: throw new NotImplementedException();
@ -521,7 +517,12 @@ namespace Decompiler @@ -521,7 +517,12 @@ namespace Decompiler
} else {
return new Ast.IdentifierExpression(((ParameterDefinition)operand).Name);
}
case Code.Ldarga: throw new NotImplementedException();
case Code.Ldarga:
if (methodDef.HasThis && ((ParameterDefinition)operand).Index < 0) {
return MakeRef(new Ast.ThisReferenceExpression());
} else {
return MakeRef(new Ast.IdentifierExpression(((ParameterDefinition)operand).Name));
}
case Code.Ldc_I4:
case Code.Ldc_I8:
case Code.Ldc_R4:
@ -540,16 +541,24 @@ namespace Decompiler @@ -540,16 +541,24 @@ namespace Decompiler
.Member(((FieldReference)operand).Name).WithAnnotation(operand),
arg1);
case Code.Ldflda:
case Code.Ldsflda: throw new NotImplementedException();
case Code.Ldftn: throw new NotImplementedException();
return MakeRef(arg1.Member(((FieldReference) operand).Name).WithAnnotation(operand));
case Code.Ldsflda:
return MakeRef(
AstBuilder.ConvertType(((FieldReference)operand).DeclaringType)
.Member(((FieldReference)operand).Name).WithAnnotation(operand));
case Code.Ldloc:
if (operand is ILVariable) {
return new Ast.IdentifierExpression(((ILVariable)operand).Name);
} else {
return new Ast.IdentifierExpression(((VariableDefinition)operand).Name);
}
case Code.Ldloca: throw new NotImplementedException();
case Code.Ldnull: return new Ast.PrimitiveExpression(null);
case Code.Ldloca:
if (operand is ILVariable) {
return MakeRef(new Ast.IdentifierExpression(((ILVariable)operand).Name));
} else {
return MakeRef(new Ast.IdentifierExpression(((VariableDefinition)operand).Name));
}
case Code.Ldnull: return new Ast.NullReferenceExpression();
case Code.Ldobj: throw new NotImplementedException();
case Code.Ldstr: return new Ast.PrimitiveExpression(operand);
case Code.Ldtoken:
@ -558,7 +567,6 @@ namespace Decompiler @@ -558,7 +567,6 @@ namespace Decompiler
} else {
throw new NotImplementedException();
}
case Code.Ldvirtftn: throw new NotImplementedException();
case Code.Leave: return null;
case Code.Localloc: throw new NotImplementedException();
case Code.Mkrefany: throw new NotImplementedException();
@ -577,7 +585,6 @@ namespace Decompiler @@ -577,7 +585,6 @@ namespace Decompiler
};
case Code.No: throw new NotImplementedException();
case Code.Nop: return null;
case Code.Or: throw new NotImplementedException();
case Code.Pop: return arg1;
case Code.Readonly: throw new NotImplementedException();
case Code.Refanytype: throw new NotImplementedException();
@ -591,32 +598,18 @@ namespace Decompiler @@ -591,32 +598,18 @@ namespace Decompiler
}
}
case Code.Rethrow: return new Ast.ThrowStatement();
case Code.Sizeof: throw new NotImplementedException();
case Code.Sizeof: return new Ast.SizeOfExpression { Type = AstBuilder.ConvertType(operand as TypeReference) };
case Code.Starg: throw new NotImplementedException();
case Code.Stloc: {
if (operand is ILVariable) {
var astLocalVar = new Ast.VariableDeclarationStatement();
astLocalVar.Type = new Ast.PrimitiveType("var");
astLocalVar.Variables = new [] {
new Ast.VariableInitializer(((ILVariable)operand).Name, arg1)
ILVariable locVar = (ILVariable)operand;
if (!definedLocalVars.Contains(locVar)) {
definedLocalVars.Add(locVar);
return new Ast.VariableDeclarationStatement() {
Type = locVar.Type != null ? AstBuilder.ConvertType(locVar.Type) : new Ast.PrimitiveType("var"),
Variables = new[] { new Ast.VariableInitializer(locVar.Name, arg1) }
};
return astLocalVar;
}
VariableDefinition locVar = (VariableDefinition)operand;
string name = locVar.Name;
arg1 = Convert(arg1, locVar.VariableType);
if (localVarDefined.ContainsKey(name)) {
if (localVarDefined[name]) {
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(name), arg1);
} else {
var astLocalVar = new Ast.VariableDeclarationStatement();
astLocalVar.Type = AstBuilder.ConvertType(localVarTypes[name]);
astLocalVar.Variables = new[] { new Ast.VariableInitializer(name, arg1) };
localVarDefined[name] = true;
return astLocalVar;
}
} else {
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(name), arg1);
return new Ast.AssignmentExpression(new Ast.IdentifierExpression(locVar.Name), arg1);
}
}
case Code.Stobj: throw new NotImplementedException();
@ -631,6 +624,84 @@ namespace Decompiler @@ -631,6 +624,84 @@ namespace Decompiler
}
}
static AstNode TransformCall(bool isVirtual, object operand, MethodDefinition methodDef, List<Ast.Expression> args)
{
Cecil.MethodReference cecilMethod = ((MethodReference)operand);
Ast.Expression target;
List<Ast.Expression> methodArgs = new List<Ast.Expression>(args);
if (cecilMethod.HasThis) {
target = methodArgs[0];
methodArgs.RemoveAt(0);
// Unpack any DirectionExpression that is used as target for the call
// (calling methods on value types implicitly passes the first argument by reference)
if (target is DirectionExpression) {
target = ((DirectionExpression)target).Expression;
target.Remove(); // detach from DirectionExpression
}
} else {
target = new TypeReferenceExpression { Type = AstBuilder.ConvertType(cecilMethod.DeclaringType) };
}
if (target is ThisReferenceExpression && !isVirtual) {
// a non-virtual call on "this" might be a "base"-call.
if (cecilMethod.DeclaringType != methodDef.DeclaringType) {
// If we're not calling a method in the current class; we must be calling one in the base class.
target = new BaseReferenceExpression();
}
}
// Resolve the method to figure out whether it is an accessor:
Cecil.MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter && methodArgs.Count == 0) {
foreach (var prop in cecilMethodDef.DeclaringType.Properties) {
if (prop.GetMethod == cecilMethodDef)
return target.Member(prop.Name).WithAnnotation(prop);
}
} else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) {
foreach (var prop in cecilMethodDef.DeclaringType.Properties) {
if (prop.SetMethod == cecilMethodDef)
return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop), methodArgs[0]);
}
} else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) {
foreach (var ev in cecilMethodDef.DeclaringType.Events) {
if (ev.AddMethod == cecilMethodDef) {
return new Ast.AssignmentExpression {
Left = target.Member(ev.Name).WithAnnotation(ev),
Operator = AssignmentOperatorType.Add,
Right = methodArgs[0]
};
}
}
} else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) {
foreach (var ev in cecilMethodDef.DeclaringType.Events) {
if (ev.RemoveMethod == cecilMethodDef) {
return new Ast.AssignmentExpression {
Left = target.Member(ev.Name).WithAnnotation(ev),
Operator = AssignmentOperatorType.Subtract,
Right = methodArgs[0]
};
}
}
}
}
// Default invocation
return target.Invoke(cecilMethod.Name, ConvertTypeArguments(cecilMethod), methodArgs).WithAnnotation(cecilMethod);
}
static IEnumerable<AstType> ConvertTypeArguments(MethodReference cecilMethod)
{
GenericInstanceMethod g = cecilMethod as GenericInstanceMethod;
if (g == null)
return null;
return g.GenericArguments.Select(t => AstBuilder.ConvertType(t));
}
static Ast.DirectionExpression MakeRef(Ast.Expression expr)
{
return new DirectionExpression { Expression = expr, FieldDirection = FieldDirection.Ref };
}
static Ast.Expression Convert(Ast.Expression expr, Cecil.TypeReference reqType)
{
if (reqType == null) {

3
ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs

@ -10,7 +10,8 @@ namespace Decompiler @@ -10,7 +10,8 @@ namespace Decompiler
{
public static T WithAnnotation<T>(this T node, object annotation) where T : AstNode
{
node.AddAnnotation(annotation);
if (annotation != null)
node.AddAnnotation(annotation);
return node;
}
}

42
ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace Decompiler.Transforms
{
public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor<object, object>
{
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
ExpressionStatement stmt = constructorDeclaration.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (stmt == null)
return null;
InvocationExpression invocation = stmt.Expression as InvocationExpression;
if (invocation == null)
return null;
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
if (mre != null && mre.MemberName == ".ctor") {
ConstructorInitializer ci = new ConstructorInitializer();
if (mre.Target is ThisReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.This;
else if (mre.Target is BaseReferenceExpression)
ci.ConstructorInitializerType = ConstructorInitializerType.Base;
else
return null;
// Move arguments from invocation to initializer:
var arguments = invocation.Arguments.ToArray();
invocation.Arguments = null;
ci.Arguments = arguments;
// Add the initializer:
constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation<MethodReference>());
// Remove the statement:
stmt.Remove();
}
return null;
}
}
}

130
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -0,0 +1,130 @@ @@ -0,0 +1,130 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using System.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace Decompiler.Transforms
{
/// <summary>
/// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)".
/// For anonymous methods, creates an AnonymousMethodExpression.
/// </summary>
public class DelegateConstruction : DepthFirstAstVisitor<object, object>
{
internal sealed class Annotation
{
/// <summary>
/// ldftn or ldvirtftn?
/// </summary>
public readonly bool IsVirtual;
/// <summary>
/// The method being decompiled.
/// </summary>
public readonly TypeDefinition ContainingType;
public Annotation(bool isVirtual, TypeDefinition containingType)
{
this.IsVirtual = isVirtual;
this.ContainingType = containingType;
}
}
public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data)
{
if (objectCreateExpression.Arguments.Count() == 2) {
Expression obj = objectCreateExpression.Arguments.First();
Expression func = objectCreateExpression.Arguments.Last();
Annotation annotation = func.Annotation<Annotation>();
if (annotation != null) {
IdentifierExpression methodIdent = (IdentifierExpression)((InvocationExpression)func).Arguments.Single();
MethodReference method = methodIdent.Annotation<MethodReference>();
if (method != null) {
if (HandleAnonymousMethod(objectCreateExpression, obj, method, annotation.ContainingType))
return null;
// Perform the transformation to "new Action(obj.func)".
obj.Remove();
methodIdent.Remove();
if (!annotation.IsVirtual && obj is ThisReferenceExpression) {
// maybe it's getting the pointer of a base method?
if (method.DeclaringType != annotation.ContainingType) {
obj = new BaseReferenceExpression();
}
}
if (!annotation.IsVirtual && obj is NullReferenceExpression && !method.HasThis) {
// We're loading a static method.
// However it is possible to load extension methods with an instance, so we compare the number of arguments:
bool isExtensionMethod = false;
TypeReference delegateType = objectCreateExpression.Type.Annotation<TypeReference>();
if (delegateType != null) {
TypeDefinition delegateTypeDef = delegateType.Resolve();
if (delegateTypeDef != null) {
MethodDefinition invokeMethod = delegateTypeDef.Methods.FirstOrDefault(m => m.Name == "Invoke");
if (invokeMethod != null) {
isExtensionMethod = (invokeMethod.Parameters.Count + 1 == method.Parameters.Count);
}
}
}
if (!isExtensionMethod) {
obj = new TypeReferenceExpression { Type = AstBuilder.ConvertType(method.DeclaringType) };
}
}
// now transform the identifier into a member reference
MemberReferenceExpression mre = new MemberReferenceExpression {
Target = obj,
MemberName = methodIdent.Identifier,
TypeArguments = methodIdent.TypeArguments
};
mre.AddAnnotation(method);
objectCreateExpression.Arguments = new [] { mre };
return null;
}
}
}
return base.VisitObjectCreateExpression(objectCreateExpression, data);
}
bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef, TypeDefinition containingType)
{
// Anonymous methods are defined in the same assembly, so there's no need to Resolve().
MethodDefinition method = methodRef as MethodDefinition;
if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal))
return false;
if (!(method.IsCompilerGenerated() || method.DeclaringType.IsCompilerGenerated()))
return false;
TypeDefinition methodContainingType = method.DeclaringType;
// check that methodContainingType is within containingType
while (methodContainingType != containingType) {
methodContainingType = methodContainingType.DeclaringType;
if (methodContainingType == null)
return false;
}
// Decompile the anonymous method:
BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method);
TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction);
body.AcceptVisitor(this, null);
AnonymousMethodExpression ame = new AnonymousMethodExpression();
if (method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) {
ame.HasParameterList = false;
} else {
ame.HasParameterList = true;
ame.Parameters = AstBuilder.MakeParameters(method.Parameters);
}
ame.Body = body;
// Replace all occurrences of 'this' in the method body with the delegate's target:
foreach (AstNode node in body.Descendants) {
if (node is ThisReferenceExpression)
node.ReplaceWith(target.Clone());
}
objectCreateExpression.ReplaceWith(ame);
return true;
}
}
}

56
ICSharpCode.Decompiler/Ast/Transforms/RemoveDeadLabels.cs

@ -0,0 +1,56 @@ @@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
public class RemoveDeadLabels : DepthFirstAstVisitor<object, object>
{
List<string> usedLabels = new List<string>();
bool collectingUsedLabels;
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
collectingUsedLabels = true;
base.VisitConstructorDeclaration(constructorDeclaration, data);
collectingUsedLabels = false;
base.VisitConstructorDeclaration(constructorDeclaration, data);
return null;
}
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
collectingUsedLabels = true;
base.VisitMethodDeclaration(methodDeclaration, data);
collectingUsedLabels = false;
base.VisitMethodDeclaration(methodDeclaration, data);
return null;
}
public override object VisitAccessor(Accessor accessor, object data)
{
collectingUsedLabels = true;
base.VisitAccessor(accessor, data);
collectingUsedLabels = false;
return base.VisitAccessor(accessor, data);
}
public override object VisitGotoStatement(GotoStatement gotoStatement, object data)
{
if (collectingUsedLabels) {
usedLabels.Add(gotoStatement.Label);
}
return null;
}
public override object VisitLabelStatement(LabelStatement labelStatement, object data)
{
if (!collectingUsedLabels) {
if (!usedLabels.Contains(labelStatement.Label)) {
labelStatement.Remove();
}
}
return null;
}
}
}

71
ICSharpCode.Decompiler/Ast/Transforms/RemoveGotos.cs

@ -1,17 +1,15 @@ @@ -1,17 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
/*
public class RemoveGotos: DepthFirstAstVisitor<object, object>
{
Stack<Statement> enteredLoops = new Stack<Statement>();
Stack<ForStatement> enteredLoops = new Stack<ForStatement>();
Statement CurrentLoop {
ForStatement CurrentLoop {
get {
if (enteredLoops.Count > 0) {
return enteredLoops.Peek();
@ -29,32 +27,25 @@ namespace Decompiler.Transforms.Ast @@ -29,32 +27,25 @@ namespace Decompiler.Transforms.Ast
return null;
}
public override object VisitWhileStatement(WhileStatement whileStatement, object data)
{
enteredLoops.Push(whileStatement);
base.VisitWhileStatement(whileStatement, data);
enteredLoops.Pop();
return null;
}
// public override object VisitWhileStatement(WhileStatement whileStatement, object data)
// {
// enteredLoops.Push(whileStatement);
// base.VisitWhileStatement(whileStatement, data);
// enteredLoops.Pop();
// return null;
// }
public override object VisitBlockStatement(BlockStatement blockStatement, object data)
{
base.VisitBlockStatement(blockStatement, data);
// Remove redundant jump at the end of block
INode lastStmt = blockStatement.Children.Last();
// End of while loop
if (lastStmt is ContinueStatement &&
blockStatement.Parent is DoLoopStatement)
{
blockStatement.Children.Remove(lastStmt);
return null;
}
AstNode lastStmt = blockStatement.Children.LastOrDefault();
// End of for loop
if (lastStmt is ContinueStatement &&
blockStatement.Parent is ForStatement)
{
blockStatement.Children.Remove(lastStmt);
lastStmt.Remove();
return null;
}
// End of method
@ -62,7 +53,7 @@ namespace Decompiler.Transforms.Ast @@ -62,7 +53,7 @@ namespace Decompiler.Transforms.Ast
(blockStatement.Parent is MethodDeclaration || blockStatement.Parent is ConstructorDeclaration) &&
((ReturnStatement)lastStmt).Expression.IsNull)
{
blockStatement.Children.Remove(lastStmt);
lastStmt.Remove();
return null;
}
@ -75,7 +66,7 @@ namespace Decompiler.Transforms.Ast @@ -75,7 +66,7 @@ namespace Decompiler.Transforms.Ast
{
if (statement == null) throw new ArgumentNullException();
Statement next = (Statement)statement.Next();
Statement next = (Statement)statement.NextSibling;
if (next != null) {
return EnterBlockStatement(next);
@ -105,12 +96,12 @@ namespace Decompiler.Transforms.Ast @@ -105,12 +96,12 @@ namespace Decompiler.Transforms.Ast
// Iterator; Condition; Body
if (statement is ForStatement) {
ForStatement forLoop = statement as ForStatement;
if (forLoop.Iterator.Count > 0) {
return forLoop.Iterator[0];
if (forLoop.Iterators.Any()) {
return forLoop.Iterators.First();
} else if (!forLoop.Condition.IsNull) {
return forLoop.Condition;
} else {
return EnterBlockStatement((Statement)forLoop.EmbeddedStatement.Children.First());
return EnterBlockStatement((Statement)forLoop.EmbeddedStatement.FirstChild);
}
}
@ -125,14 +116,12 @@ namespace Decompiler.Transforms.Ast @@ -125,14 +116,12 @@ namespace Decompiler.Transforms.Ast
// For loop starts as follows: Initializers; Condition; Body
if (statement is ForStatement) {
ForStatement forLoop = statement as ForStatement;
if (forLoop.Initializers.Count > 0) {
return forLoop.Initializers[0];
if (forLoop.Initializers.Any()) {
return forLoop.Initializers.First();
} else if (!forLoop.Condition.IsNull) {
return forLoop.Condition;
} else if (forLoop.EmbeddedStatement is BlockStatement &&
forLoop.EmbeddedStatement.Children.Count > 0) {
statement = (Statement)forLoop.EmbeddedStatement.Children.First();
return EnterBlockStatement(statement); // Simplify again
} else if (forLoop.EmbeddedStatement.Children.FirstOrDefault() is Statement) {
return EnterBlockStatement((Statement)forLoop.EmbeddedStatement.FirstChild); // Simplify again
}
}
@ -142,11 +131,11 @@ namespace Decompiler.Transforms.Ast @@ -142,11 +131,11 @@ namespace Decompiler.Transforms.Ast
public override object VisitGotoStatement(GotoStatement gotoStatement, object data)
{
// Remove redundant goto which goes to a label that imideately follows
INode fallthoughTarget = GetNextStatement(gotoStatement);
AstNode fallthoughTarget = GetNextStatement(gotoStatement);
while(true) {
if (fallthoughTarget is LabelStatement) {
if ((fallthoughTarget as LabelStatement).Label == gotoStatement.Label) {
RemoveCurrentNode();
gotoStatement.Remove();
return null;
} else {
fallthoughTarget = GetNextStatement((LabelStatement)fallthoughTarget);
@ -159,10 +148,10 @@ namespace Decompiler.Transforms.Ast @@ -159,10 +148,10 @@ namespace Decompiler.Transforms.Ast
// Replace goto with 'break'
// Break statement moves right outside the looop
if (CurrentLoop != null) {
INode breakTarget = GetNextStatement(CurrentLoop);
AstNode breakTarget = GetNextStatement(CurrentLoop);
if ((breakTarget is LabelStatement) &&
(breakTarget as LabelStatement).Label == gotoStatement.Label) {
ReplaceCurrentNode(new BreakStatement());
gotoStatement.ReplaceWith(new BreakStatement());
return null;
}
}
@ -171,24 +160,24 @@ namespace Decompiler.Transforms.Ast @@ -171,24 +160,24 @@ namespace Decompiler.Transforms.Ast
// Continue statement which moves at the very end of loop
if (CurrentLoop != null &&
(CurrentLoop.EmbeddedStatement is BlockStatement) &&
((CurrentLoop.EmbeddedStatement as BlockStatement).Children.Last() as LabelStatement) != null &&
((CurrentLoop.EmbeddedStatement as BlockStatement).Children.Last() as LabelStatement).Label == gotoStatement.Label) {
ReplaceCurrentNode(new ContinueStatement());
((CurrentLoop.EmbeddedStatement as BlockStatement).LastChild as LabelStatement) != null &&
((CurrentLoop.EmbeddedStatement as BlockStatement).LastChild as LabelStatement).Label == gotoStatement.Label) {
gotoStatement.ReplaceWith(new ContinueStatement());
return null;
}
// Replace goto with 'continue'
// Continue statement which moves at the very start of for loop
if (CurrentLoop != null) {
INode continueTarget = ExitBlockStatement(CurrentLoop); // The start of the loop
AstNode continueTarget = ExitBlockStatement(CurrentLoop); // The start of the loop
if ((continueTarget is LabelStatement) &&
(continueTarget as LabelStatement).Label == gotoStatement.Label) {
ReplaceCurrentNode(new ContinueStatement());
gotoStatement.ReplaceWith(new ContinueStatement());
return null;
}
}
return null;
}
}*/
}
}

36
ICSharpCode.Decompiler/Ast/Transforms/Idioms.cs → ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -5,9 +5,13 @@ using Mono.Cecil; @@ -5,9 +5,13 @@ using Mono.Cecil;
using Ast = ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
namespace Decompiler.Transforms
{
public class Idioms: DepthFirstAstVisitor<object, object>
/// <summary>
/// Replaces method calls with the appropriate operator expressions.
/// Also simplifies "x = x op y" into "x op= y" where possible.
/// </summary>
public class ReplaceMethodCallsWithOperators : DepthFirstAstVisitor<object, object>
{
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
{
@ -58,6 +62,19 @@ namespace Decompiler.Transforms.Ast @@ -58,6 +62,19 @@ namespace Decompiler.Transforms.Ast
);
return null;
}
if (methodRef.Name == "op_Explicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
arguments[0].CastTo(AstBuilder.ConvertType(methodRef.ReturnType, methodRef.MethodReturnType))
.WithAnnotation(methodRef)
);
return null;
}
if (methodRef.Name == "op_Implicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(arguments[0]);
return null;
}
return null;
}
@ -162,7 +179,7 @@ namespace Decompiler.Transforms.Ast @@ -162,7 +179,7 @@ namespace Decompiler.Transforms.Ast
break;
}
if (assignment.Operator != AssignmentOperatorType.Assign) {
// If we found a shorter operators, get rid of the BinaryOperatorExpression:
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
assignment.Right = binary.Right;
}
}
@ -174,18 +191,5 @@ namespace Decompiler.Transforms.Ast @@ -174,18 +191,5 @@ namespace Decompiler.Transforms.Ast
{
return left is IdentifierExpression; // TODO
}
/*
public override object VisitCastExpression(CastExpression castExpression, object data)
{
MethodReference typeRef = invocationExpression.Annotation<TypeReference>();
if (typeRef.FullName == Constants.Int32 &&
castExpression.Expression is MemberReferenceExpression &&
(castExpression.Expression as MemberReferenceExpression).MemberName == "Length") {
ReplaceCurrentNode(castExpression.Expression);
return null;
}
return base.VisitCastExpression(castExpression, data);
}*/
}
}

49
ICSharpCode.Decompiler/Ast/Transforms/RestoreLoop.cs

@ -1,10 +1,9 @@ @@ -1,10 +1,9 @@
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
/*
public class RestoreLoop: DepthFirstAstVisitor<object, object>
{
public override object VisitForStatement(ForStatement forStatement, object data)
@ -12,25 +11,25 @@ namespace Decompiler.Transforms.Ast @@ -12,25 +11,25 @@ namespace Decompiler.Transforms.Ast
base.VisitForStatement(forStatement, data);
// Restore loop initializer
if (forStatement.Initializers.Count == 0) {
LocalVariableDeclaration varDeclr = forStatement.Previous() as LocalVariableDeclaration;
if (!forStatement.Initializers.Any()) {
VariableDeclarationStatement varDeclr = forStatement.PrevSibling as VariableDeclarationStatement;
if (varDeclr != null) {
varDeclr.ReplaceWith(Statement.Null);
forStatement.Initializers.Add(varDeclr);
forStatement.Initializers = new Statement[] { varDeclr };
}
}
// Restore loop condition
if (forStatement.Condition.IsNull &&
forStatement.EmbeddedStatement.Children.Count >= 3)
forStatement.EmbeddedStatement.Children.Count() >= 3)
{
IfElseStatement condition = forStatement.EmbeddedStatement.Children[0] as IfElseStatement;
BreakStatement breakStmt = forStatement.EmbeddedStatement.Children[1] as BreakStatement;
LabelStatement label = forStatement.EmbeddedStatement.Children[2] as LabelStatement;
IfElseStatement condition = forStatement.EmbeddedStatement.Children.First() as IfElseStatement;
BreakStatement breakStmt = forStatement.EmbeddedStatement.Children.Skip(1).First() as BreakStatement;
LabelStatement label = forStatement.EmbeddedStatement.Children.Skip(2).First() as LabelStatement;
if (condition != null && breakStmt != null && label != null &&
condition.TrueStatement.Count == 1)
condition.TrueStatement.Children.Count() == 1)
{
GotoStatement gotoStmt = condition.TrueStatement[0] as GotoStatement;
GotoStatement gotoStmt = condition.TrueStatement.FirstChild as GotoStatement;
if (gotoStmt != null && gotoStmt.Label == label.Label) {
condition.Remove();
breakStmt.Remove();
@ -41,34 +40,34 @@ namespace Decompiler.Transforms.Ast @@ -41,34 +40,34 @@ namespace Decompiler.Transforms.Ast
// Restore loop condition (version 2)
if (forStatement.Condition.IsNull) {
IfElseStatement condition = forStatement.EmbeddedStatement.Children.First() as IfElseStatement;
IfElseStatement condition = forStatement.EmbeddedStatement.FirstChild as IfElseStatement;
if (condition != null &&
condition.TrueStatement.Count == 1 &&
condition.TrueStatement[0] is BlockStatement &&
condition.TrueStatement[0].Children.Count == 1 &&
condition.TrueStatement[0].Children.First() is BreakStatement &&
condition.FalseStatement.Count == 1 &&
condition.FalseStatement[0] is BlockStatement &&
condition.FalseStatement[0].Children.Count == 0)
condition.TrueStatement.Children.Any() &&
condition.TrueStatement.FirstChild is BlockStatement &&
condition.TrueStatement.Children.Count() == 1 &&
condition.TrueStatement.FirstChild.FirstChild is BreakStatement &&
condition.FalseStatement.Children.Any() &&
condition.FalseStatement.FirstChild is BlockStatement &&
condition.FalseStatement.Children.Count() == 0)
{
condition.Remove();
forStatement.Condition = new UnaryOperatorExpression(condition.Condition, UnaryOperatorType.Not);
forStatement.Condition = new UnaryOperatorExpression() { Expression = condition.Condition, Operator = UnaryOperatorType.Not };
}
}
// Restore loop iterator
if (forStatement.EmbeddedStatement.Children.Count > 0 &&
forStatement.Iterator.Count == 0)
if (forStatement.EmbeddedStatement.Children.Any() &&
!forStatement.Iterators.Any())
{
ExpressionStatement lastStmt = forStatement.EmbeddedStatement.Children.Last() as ExpressionStatement;
ExpressionStatement lastStmt = forStatement.EmbeddedStatement.LastChild as ExpressionStatement;
if (lastStmt != null &&
(lastStmt.Expression is AssignmentExpression || lastStmt.Expression is UnaryOperatorExpression)) {
lastStmt.Remove();
forStatement.Iterator.Add(lastStmt);
forStatement.Iterators = new Statement[] { lastStmt };
}
}
return null;
}
}*/
}
}

28
ICSharpCode.Decompiler/Ast/Transforms/SimplifyTypeReferences.cs

@ -1,28 +0,0 @@ @@ -1,28 +0,0 @@
using System;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms.Ast
{
public class SimplifyTypeReferences: DepthFirstAstVisitor<object, object>
{
string currentNamepace = string.Empty;
string currentClass = null;
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
currentNamepace = namespaceDeclaration.Name;
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamepace = string.Empty;
return null;
}
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
currentClass = currentNamepace + "." + typeDeclaration.Name;
base.VisitTypeDeclaration(typeDeclaration, data);
currentClass = null;
return null;
}
}
}

50
ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs

@ -0,0 +1,50 @@ @@ -0,0 +1,50 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System;
using ICSharpCode.NRefactory.CSharp;
namespace Decompiler.Transforms
{
public static class TransformationPipeline
{
public static IAstVisitor<object, object>[] CreatePipeline()
{
return new IAstVisitor<object, object>[] {
new DelegateConstruction(),
new ConvertConstructorCallIntoInitializer(),
new ReplaceMethodCallsWithOperators()
};
}
public static void RunTransformations(AstNode node)
{
RunTransformationsUntil(node, null);
}
public static void RunTransformationsUntil(AstNode node, Predicate<IAstVisitor<object, object>> abortCondition)
{
if (node == null)
return;
for (int i = 0; i < 4; i++) {
if (Options.ReduceAstJumps) {
node.AcceptVisitor(new Transforms.Ast.RemoveGotos(), null);
node.AcceptVisitor(new Transforms.Ast.RemoveDeadLabels(), null);
}
if (Options.ReduceAstLoops) {
node.AcceptVisitor(new Transforms.Ast.RestoreLoop(), null);
}
if (Options.ReduceAstOther) {
node.AcceptVisitor(new Transforms.Ast.RemoveEmptyElseBody(), null);
node.AcceptVisitor(new Transforms.Ast.PushNegation(), null);
}
}
foreach (var visitor in CreatePipeline()) {
if (abortCondition != null && abortCondition(visitor))
return;
node.AcceptVisitor(visitor, null);
}
}
}
}

11
ICSharpCode.Decompiler/CecilExtensions.cs

@ -158,5 +158,16 @@ namespace ICSharpCode.Decompiler @@ -158,5 +158,16 @@ namespace ICSharpCode.Decompiler
}
return accessorMethods;
}
public static bool IsCompilerGenerated(this ICustomAttributeProvider provider)
{
if (provider.HasCustomAttributes) {
foreach (CustomAttribute a in provider.CustomAttributes) {
if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
return true;
}
}
return false;
}
}
}

9
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -50,16 +50,19 @@ @@ -50,16 +50,19 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Ast\AstBuilder.cs" />
<Compile Include="Ast\AstMetodBodyBuilder.cs" />
<Compile Include="Ast\AstMethodBodyBuilder.cs" />
<Compile Include="Ast\CommentStatement.cs" />
<Compile Include="Ast\NRefactoryExtensions.cs" />
<Compile Include="Ast\TextOutputFormatter.cs" />
<Compile Include="Ast\Transforms\Idioms.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="Ast\Transforms\PushNegation.cs" />
<Compile Include="Ast\Transforms\RemoveDeadLabels.cs" />
<Compile Include="Ast\Transforms\RemoveEmptyElseBody.cs" />
<Compile Include="Ast\Transforms\RemoveGotos.cs" />
<Compile Include="Ast\Transforms\RestoreLoop.cs" />
<Compile Include="Ast\Transforms\SimplifyTypeReferences.cs" />
<Compile Include="Ast\Transforms\TransformationPipeline.cs" />
<Compile Include="CecilExtensions.cs" />
<Compile Include="Disassembler\CodeMappings.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />

22
ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -32,6 +32,7 @@ namespace Decompiler @@ -32,6 +32,7 @@ namespace Decompiler
{
public ILLabel Label; // Non-null only if needed
public int Offset;
public int EndOffset;
public OpCode OpCode;
public object Operand;
public int? PopCount; // Null means pop all
@ -94,6 +95,8 @@ namespace Decompiler @@ -94,6 +95,8 @@ namespace Decompiler
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>();
Dictionary<ILVariable, bool> allowInline = new Dictionary<ILVariable, bool>();
public List<ILVariable> Variables;
public List<ILNode> Build(MethodDefinition methodDef, bool optimize)
{
this.methodDef = methodDef;
@ -118,6 +121,7 @@ namespace Decompiler @@ -118,6 +121,7 @@ namespace Decompiler
MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body);
ByteCode byteCode = new ByteCode() {
Offset = inst.Offset,
EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
OpCode = opCode,
Operand = operand,
PopCount = inst.GetPopCount(),
@ -247,27 +251,27 @@ namespace Decompiler @@ -247,27 +251,27 @@ namespace Decompiler
}
// Convert local varibles
List<ILVariable> vars = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name }).ToList();
int[] numReads = new int[vars.Count];
int[] numWrites = new int[vars.Count];
Variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name, Type = v.VariableType }).ToList();
int[] numReads = new int[Variables.Count];
int[] numWrites = new int[Variables.Count];
foreach(ByteCode byteCode in body) {
if (byteCode.OpCode == OpCodes.Ldloc) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = vars[index];
byteCode.Operand = Variables[index];
numReads[index]++;
}
if (byteCode.OpCode == OpCodes.Stloc) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = vars[index];
byteCode.Operand = Variables[index];
numWrites[index]++;
}
}
// Find which variables we can inline
if (this.optimize) {
for (int i = 0; i < vars.Count; i++) {
for (int i = 0; i < Variables.Count; i++) {
if (numReads[i] == 1 && numWrites[i] == 1) {
allowInline[vars[i]] = true;
allowInline[Variables[i]] = true;
}
}
}
@ -360,6 +364,7 @@ namespace Decompiler @@ -360,6 +364,7 @@ namespace Decompiler
MethodBodyRocks.ExpandMacro(ref opCode, ref operand, methodDef.Body);
ILExpression expr = new ILExpression(opCode, operand);
expr.ILRanges.Add(new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset });
// Label for this instruction
if (byteCode.Label != null) {
@ -417,6 +422,9 @@ namespace Decompiler @@ -417,6 +422,9 @@ namespace Decompiler
bool canInline;
allowInline.TryGetValue((ILVariable)arg.Operand, out canInline);
if (arg.Operand == currExpr.Operand && canInline) {
// Assigne the ranges for optimized away instrustions somewhere
currExpr.Arguments[0].ILRanges.AddRange(currExpr.ILRanges);
currExpr.Arguments[0].ILRanges.AddRange(nextExpr.Arguments[j].ILRanges);
ast.RemoveAt(i);
nextExpr.Arguments[j] = currExpr.Arguments[0]; // Inline the stloc body
i -= 2; // Try the same index again

137
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -14,25 +14,27 @@ namespace Decompiler.ControlFlow @@ -14,25 +14,27 @@ namespace Decompiler.ControlFlow
public void Optimize(ILBlock method)
{
var blocks = method.GetSelfAndChildrenRecursive<ILBlock>().ToList();
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().ToList()) {
SplitToMovableBlocks(block);
}
foreach(ILBlock block in blocks) {
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) {
ControlFlowGraph graph;
SplitToMovableBlocks(block);
graph = BuildGraph(block.Body, block.EntryPoint);
graph.ComputeDominance();
graph.ComputeDominanceFrontier();
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, true);
}
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>().Where(b => !(b is ILMoveableBlock)).ToList()) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, block.EntryPoint);
graph.ComputeDominance();
graph.ComputeDominanceFrontier();
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
}
OrderNodes(method);
// OrderNodes(method);
FlattenNestedMovableBlocks(method);
SimpleGotoRemoval(method);
RemoveDeadLabels(method);
@ -53,6 +55,7 @@ namespace Decompiler.ControlFlow @@ -53,6 +55,7 @@ namespace Decompiler.ControlFlow
void SplitToMovableBlocks(ILBlock block)
{
// Remve no-ops
// TODO: Assign the no-op range to someting
block.Body = block.Body.Where(n => !(n is ILExpression && ((ILExpression)n).OpCode == OpCodes.Nop)).ToList();
List<ILNode> moveableBlocks = new List<ILNode>();
@ -153,7 +156,7 @@ namespace Decompiler.ControlFlow @@ -153,7 +156,7 @@ namespace Decompiler.ControlFlow
return new ControlFlowGraph(cfNodes.ToArray());
}
static List<ILNode> FindLoops(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint)
List<ILNode> FindLoops(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, bool excludeEntryPoint)
{
List<ILNode> result = new List<ILNode>();
@ -164,14 +167,16 @@ namespace Decompiler.ControlFlow @@ -164,14 +167,16 @@ namespace Decompiler.ControlFlow
if (nodes.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& node != entryPoint)
&& (node != entryPoint || !excludeEntryPoint))
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(nodes, loopContents, node, node);
// Move the content into loop block
nodes.ExceptWith(loopContents);
result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node)) });
ILLabel entryLabel = new ILLabel() { Name = "Loop_" + (nextBlockIndex++) };
((ILBlock)node.UserData).Body.Insert(0, entryLabel);
result.Add(new ILLoop() { ContentBlock = new ILBlock(FindLoops(loopContents, node, true)) { EntryPoint = entryLabel } });
}
// Using the dominator tree should ensure we find the the widest loop first
@ -206,58 +211,118 @@ namespace Decompiler.ControlFlow @@ -206,58 +211,118 @@ namespace Decompiler.ControlFlow
while(agenda.Count > 0) {
ControlFlowNode node = agenda.Dequeue();
ILMoveableBlock block = node.UserData as ILMoveableBlock;
// Find a block that represents a simple condition
if (nodes.Contains(node) && block != null && block.Body.Count == 3) {
if (nodes.Contains(node)) {
ILLabel label = block.Body[0] as ILLabel;
ILExpression condBranch = block.Body[1] as ILExpression;
ILExpression statBranch = block.Body[2] as ILExpression;
ILMoveableBlock block = node.UserData as ILMoveableBlock;
if (label != null &&
condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 &&
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
{
ControlFlowNode condTarget;
ControlFlowNode statTarget;
if (labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget) &&
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget))
if (block != null && block.Body.Count == 3) {
ILLabel label = block.Body[0] as ILLabel;
ILExpression condBranch = block.Body[1] as ILExpression;
ILExpression statBranch = block.Body[2] as ILExpression;
// Switch
if (label != null &&
condBranch != null && condBranch.Operand is ILLabel[] && condBranch.Arguments.Count > 0 &&
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
{
ILSwitch ilSwitch = new ILSwitch() { Condition = condBranch };
// Replace the two branches with a conditional structure - this preserves the node label
block.Body.Remove(condBranch);
block.Body.Remove(statBranch);
block.Body.Add(ilSwitch);
ControlFlowNode statTarget = null;
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (statTarget != null)
frontiers.UnionWith(statTarget.DominanceFrontier);
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
}
foreach(ILLabel condLabel in (ILLabel[])condBranch.Operand) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
ILBlock caseBlock = new ILBlock() { EntryPoint = condLabel };
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
nodes.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget));
}
ilSwitch.CaseBlocks.Add(caseBlock);
}
// The labels will not be used - kill them
condBranch.Operand = null;
result.Add(block);
nodes.Remove(node);
}
// Two-way branch
if (label != null &&
condBranch != null && condBranch.Operand is ILLabel && condBranch.Arguments.Count > 0 &&
statBranch != null && statBranch.Operand is ILLabel && statBranch.Arguments.Count == 0)
{
ControlFlowNode statTarget = null;
labelToCfNode.TryGetValue((ILLabel)statBranch.Operand, out statTarget);
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue((ILLabel)condBranch.Operand, out condTarget);
ILCondition condition = new ILCondition() {
Condition = condBranch,
TrueBlock = new ILBlock() { EntryPoint = (ILLabel)condBranch.Operand },
FalseBlock = new ILBlock() { EntryPoint = (ILLabel)statBranch.Operand }
};
// The label will not be used - kill it
condBranch.Operand = null;
// Replace the two branches with a conditional structure
// Replace the two branches with a conditional structure - this preserves the node label
block.Body.Remove(condBranch);
block.Body.Remove(statBranch);
block.Body.Add(condition);
result.Add(block);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
frontiers.UnionWith(condTarget.DominanceFrontier);
frontiers.UnionWith(statTarget.DominanceFrontier);
if (statTarget != null)
frontiers.UnionWith(statTarget.DominanceFrontier);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier);
if (!frontiers.Contains(condTarget)) {
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, condTarget);
nodes.ExceptWith(content);
condition.TrueBlock.Body.AddRange(FindConditions(content, condTarget));
}
if (!frontiers.Contains(statTarget)) {
if (statTarget != null && !frontiers.Contains(statTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(nodes, statTarget);
nodes.ExceptWith(content);
condition.FalseBlock.Body.AddRange(FindConditions(content, statTarget));
}
// The label will not be used - kill it
condBranch.Operand = null;
result.Add(block);
nodes.Remove(node);
}
}
// Add the node now so that we have good ordering
if (nodes.Contains(node)) {
result.Add((ILNode)node.UserData);
nodes.Remove(node);
}
}
// Using the dominator tree should ensure we find the the widest loop first
@ -290,7 +355,8 @@ namespace Decompiler.ControlFlow @@ -290,7 +355,8 @@ namespace Decompiler.ControlFlow
}
}
}
result.Add(head);
if (nodes.Contains(head))
result.Add(head);
return result;
}
@ -404,6 +470,7 @@ namespace Decompiler.ControlFlow @@ -404,6 +470,7 @@ namespace Decompiler.ControlFlow
void SimpleGotoRemoval(ILBlock ast)
{
// TODO: Assign IL ranges from br to something else
var blocks = ast.GetSelfAndChildrenRecursive<ILBlock>().ToList();
foreach(ILBlock block in blocks) {
for (int i = 0; i < block.Body.Count; i++) {

63
ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -1,7 +1,7 @@ @@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Decompiler.ControlFlow;
using Mono.Cecil;
using Mono.Cecil.Cil;
@ -44,10 +44,16 @@ namespace Decompiler @@ -44,10 +44,16 @@ namespace Decompiler
{
yield break;
}
public override string ToString()
{
return this.GetType().Name;
}
}
public class ILBlock: ILNode
{
// TODO: This should really be a goto, not a label
public ILLabel EntryPoint;
public List<ILNode> Body;
@ -101,17 +107,13 @@ namespace Decompiler @@ -101,17 +107,13 @@ namespace Decompiler
if (this.FinallyBlock != null)
yield return this.FinallyBlock;
}
public override string ToString()
{
return "Try-Catch{}";
}
}
public class ILVariable
{
public string Name;
public bool IsGenerated;
public TypeReference Type;
public override string ToString()
{
@ -119,17 +121,31 @@ namespace Decompiler @@ -119,17 +121,31 @@ namespace Decompiler
}
}
public class ILRange
{
public int From;
public int To; // Exlusive
public override string ToString()
{
return string.Format("{0}-{1}", From, To);
}
}
public class ILExpression: ILNode
{
public OpCode OpCode { get; set; }
public object Operand { get; set; }
public List<ILExpression> Arguments { get; set; }
// Mapping to the original instructions (useful for debugging)
public List<ILRange> ILRanges { get; set; }
public ILExpression(OpCode opCode, object operand, params ILExpression[] args)
{
this.OpCode = opCode;
this.Operand = operand;
this.Arguments = new List<ILExpression>(args);
this.ILRanges = new List<ILRange>(1);
}
public IEnumerable<ILLabel> GetBranchTargets()
@ -143,6 +159,27 @@ namespace Decompiler @@ -143,6 +159,27 @@ namespace Decompiler
}
}
public List<ILRange> GetILRanges()
{
List<ILRange> ranges = new List<ILRange>();
foreach(ILExpression expr in this.GetSelfAndChildrenRecursive<ILExpression>()) {
ranges.AddRange(expr.ILRanges);
}
ranges = ranges.OrderBy(r => r.From).ToList();
for (int i = 0; i < ranges.Count - 1;) {
ILRange curr = ranges[i];
ILRange next = ranges[i + 1];
// Merge consequtive ranges if they intersect
if (curr.From <= next.From && next.From <= curr.To) {
curr.To = Math.Max(curr.To, next.To);
ranges.RemoveAt(i + 1);
} else {
i++;
}
}
return ranges;
}
public override IEnumerable<ILNode> GetChildren()
{
return Arguments;
@ -191,4 +228,18 @@ namespace Decompiler @@ -191,4 +228,18 @@ namespace Decompiler
yield return FalseBlock;
}
}
public class ILSwitch: ILNode
{
public ILExpression Condition;
public List<ILBlock> CaseBlocks = new List<ILBlock>();
public override IEnumerable<ILNode> GetChildren()
{
yield return Condition;
foreach (ILBlock caseBlock in this.CaseBlocks) {
yield return caseBlock;
}
}
}
}

39
ILSpy/CSharpLanguage.cs

@ -17,9 +17,11 @@ @@ -17,9 +17,11 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Decompiler;
using Decompiler.Transforms;
using ICSharpCode.Decompiler;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
@ -31,8 +33,33 @@ namespace ICSharpCode.ILSpy @@ -31,8 +33,33 @@ namespace ICSharpCode.ILSpy
/// </summary>
public class CSharpLanguage : Language
{
string name = "C#";
Predicate<IAstVisitor<object, object>> transformAbortCondition;
public CSharpLanguage()
{
}
#if DEBUG
internal static IEnumerable<CSharpLanguage> GetDebugLanguages()
{
string lastTransformName = "no transforms";
foreach (Type _transformType in TransformationPipeline.CreatePipeline().Select(v => v.GetType()).Distinct()) {
Type transformType = _transformType; // copy for lambda
yield return new CSharpLanguage {
transformAbortCondition = v => transformType.IsInstanceOfType(v),
name = "C# - " + lastTransformName
};
lastTransformName = "after " + transformType.Name;
}
yield return new CSharpLanguage {
name = "C# - " + lastTransformName
};
}
#endif
public override string Name {
get { return "C#"; }
get { return name; }
}
public override string FileExtension {
@ -43,35 +70,35 @@ namespace ICSharpCode.ILSpy @@ -43,35 +70,35 @@ namespace ICSharpCode.ILSpy
{
AstBuilder codeDomBuilder = new AstBuilder();
codeDomBuilder.AddMethod(method);
codeDomBuilder.GenerateCode(output);
codeDomBuilder.GenerateCode(output, transformAbortCondition);
}
public override void DecompileProperty(PropertyDefinition property, ITextOutput output, DecompilationOptions options)
{
AstBuilder codeDomBuilder = new AstBuilder();
codeDomBuilder.AddProperty(property);
codeDomBuilder.GenerateCode(output);
codeDomBuilder.GenerateCode(output, transformAbortCondition);
}
public override void DecompileField(FieldDefinition field, ITextOutput output, DecompilationOptions options)
{
AstBuilder codeDomBuilder = new AstBuilder();
codeDomBuilder.AddField(field);
codeDomBuilder.GenerateCode(output);
codeDomBuilder.GenerateCode(output, transformAbortCondition);
}
public override void DecompileEvent(EventDefinition ev, ITextOutput output, DecompilationOptions options)
{
AstBuilder codeDomBuilder = new AstBuilder();
codeDomBuilder.AddEvent(ev);
codeDomBuilder.GenerateCode(output);
codeDomBuilder.GenerateCode(output, transformAbortCondition);
}
public override void DecompileType(TypeDefinition type, ITextOutput output, DecompilationOptions options)
{
AstBuilder codeDomBuilder = new AstBuilder();
codeDomBuilder.AddType(type);
codeDomBuilder.GenerateCode(output);
codeDomBuilder.GenerateCode(output, transformAbortCondition);
}
public override string TypeToString(TypeReference type, bool includeNamespace, ICustomAttributeProvider typeAttributes)

3
ILSpy/ILSpy.csproj

@ -237,8 +237,5 @@ @@ -237,8 +237,5 @@
<Folder Include="TreeNodes" />
<Folder Include="TextView" />
</ItemGroup>
<ItemGroup>
<Content Include="ProfilingSessions\Session20110214_003955.sdps" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

8
ILSpy/Language.cs

@ -120,11 +120,15 @@ namespace ICSharpCode.ILSpy @@ -120,11 +120,15 @@ namespace ICSharpCode.ILSpy
/// <summary>
/// A list of all languages.
/// </summary>
public static readonly ReadOnlyCollection<Language> AllLanguages = Array.AsReadOnly(
public static readonly ReadOnlyCollection<Language> AllLanguages = new List<Language>(
new Language[] {
new CSharpLanguage(),
new ILLanguage(true)
});
}
#if DEBUG
.Concat(CSharpLanguage.GetDebugLanguages())
#endif
).AsReadOnly();
/// <summary>
/// Gets a language using its name.

1
ILSpy/MainWindow.xaml

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
MinHeight="200"
UseLayoutRounding="True"
TextOptions.TextFormattingMode="Display"
Icon="pack://application:,,,/ILSpy;component/images/Find.png"
FocusManager.FocusedElement="{Binding ElementName=treeView}"
>
<Window.Resources>

2
NRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstComparer.cs

@ -31,6 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp @@ -31,6 +31,8 @@ namespace ICSharpCode.NRefactory.CSharp
bool? result = true;
while (result != false && (child1 != null || child2 != null)) {
result &= AreEqual(child1, child2);
child1 = child1.NextSibling;
child2 = child2.NextSibling;
}
if (nodeTypesWithoutExtraInfo.Contains(nodeType))
return result;

Loading…
Cancel
Save