Browse Source

Merge branch 'decompiler' of https://github.com/icsharpcode/SharpDevelop

pull/16/head
Eusebiu Marcu 14 years ago
parent
commit
359ededfb7
  1. 538
      SharpDevelop.sln
  2. 9
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
  3. 12
      src/AddIns/Debugger/Debugger.AddIn/Pads/ConsolePad.cs
  4. 3
      src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs
  5. 16
      src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs
  6. 253
      src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
  7. 6
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs
  8. 28
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
  9. 6
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml.cs
  10. 15
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs
  11. 90
      src/AddIns/Debugger/Debugger.Core/Breakpoint.cs
  12. 18
      src/AddIns/Debugger/Debugger.Core/BreakpointCollection.cs
  13. 8
      src/AddIns/Debugger/Debugger.Core/Interop/CorDebug.cs
  14. 11
      src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
  15. 56
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
  16. 101
      src/AddIns/Debugger/Debugger.Core/Module.cs
  17. 344
      src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs
  18. 48
      src/AddIns/Debugger/Debugger.Core/Process.cs
  19. 61
      src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs
  20. 40
      src/AddIns/Debugger/Debugger.Core/StackFrame.cs
  21. 6
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj
  22. 11
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  23. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarManager.cs
  24. 19
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs
  25. 8
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
  26. 6
      src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin
  27. 62
      src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj
  28. 20
      src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs
  29. 0
      src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/SetILSpyPathDialog.Designer.cs
  30. 0
      src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/SetILSpyPathDialog.cs
  31. 0
      src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/SetILSpyPathDialog.resx
  32. 0
      src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/TextEditorContextMenuCommand.cs
  33. 77
      src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs
  34. 2
      src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs
  35. 272
      src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs
  36. 291
      src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs
  37. 1673
      src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
  38. 1162
      src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  39. 173
      src/Libraries/ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs
  40. 60
      src/Libraries/ICSharpCode.Decompiler/Ast/CommentStatement.cs
  41. 70
      src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
  42. 86
      src/Libraries/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
  43. 343
      src/Libraries/ICSharpCode.Decompiler/Ast/NameVariables.cs
  44. 193
      src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
  45. 356
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
  46. 157
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs
  47. 111
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs
  48. 176
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
  49. 58
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs
  50. 329
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
  51. 471
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  52. 62
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs
  53. 295
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs
  54. 106
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs
  55. 360
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  56. 1014
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  57. 150
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs
  58. 288
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  59. 64
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  60. 516
      src/Libraries/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
  61. 301
      src/Libraries/ICSharpCode.Decompiler/CecilExtensions.cs
  62. 346
      src/Libraries/ICSharpCode.Decompiler/CodeMappings.cs
  63. 42
      src/Libraries/ICSharpCode.Decompiler/DecompilerException.cs
  64. 243
      src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs
  65. 439
      src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  66. 228
      src/Libraries/ICSharpCode.Decompiler/Disassembler/ILStructure.cs
  67. 237
      src/Libraries/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  68. 1158
      src/Libraries/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  69. 78
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs
  70. 191
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs
  71. 439
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs
  72. 298
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
  73. 241
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs
  74. 312
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs
  75. 174
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs
  76. 60
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs
  77. 162
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs
  78. 257
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs
  79. 191
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs
  80. 138
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs
  81. 91
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs
  82. 254
      src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs
  83. 138
      src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  84. 128
      src/Libraries/ICSharpCode.Decompiler/ILAst/DefaultDictionary.cs
  85. 319
      src/Libraries/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  86. 823
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  87. 830
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  88. 593
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  89. 459
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs
  90. 515
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs
  91. 523
      src/Libraries/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  92. 449
      src/Libraries/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
  93. 159
      src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  94. 907
      src/Libraries/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  95. 376
      src/Libraries/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
  96. 1215
      src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  97. 956
      src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  98. 59
      src/Libraries/ICSharpCode.Decompiler/ITextOutput.cs
  99. 120
      src/Libraries/ICSharpCode.Decompiler/PlainTextOutput.cs
  100. 27
      src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs
  101. Some files were not shown because too many files have changed in this diff Show More

538
SharpDevelop.sln

@ -1,69 +1,69 @@ @@ -1,69 +1,69 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
# SharpDevelop 4.1.0.7374-alpha
# SharpDevelop 4.1.0.7646-alpha
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Main", "Main", "{256F5C28-532C-44C0-8AB8-D8EC5E492E01}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StartUp", "src\Main\StartUp\Project\StartUp.csproj", "{1152B71B-3C05-4598-B20D-823B5D40559E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.BuildWorker35", "src\Main\ICSharpCode.SharpDevelop.BuildWorker35\ICSharpCode.SharpDevelop.BuildWorker35.csproj", "{B5F54272-49F0-40DB-845A-8D837875D3BA}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Widgets", "src\Main\ICSharpCode.SharpDevelop.Widgets\Project\ICSharpCode.SharpDevelop.Widgets.csproj", "{8035765F-D51F-4A0C-A746-2FD100E19419}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.BuildWorker", "src\Main\ICSharpCode.SharpDevelop.BuildWorker\ICSharpCode.SharpDevelop.BuildWorker.csproj", "{C3CBC8E3-81D8-4C5B-9941-DCCD12D50B1F}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop", "src\Main\Base\Project\ICSharpCode.SharpDevelop.csproj", "{2748AD25-9C63-4E12-877B-4DCE96FBED54}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom.Tests", "src\Main\ICSharpCode.SharpDevelop.Dom\Tests\ICSharpCode.SharpDevelop.Dom.Tests\ICSharpCode.SharpDevelop.Dom.Tests.csproj", "{7DB80259-24D4-46C3-A024-53FF1987733D}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Sda", "src\Main\ICSharpCode.SharpDevelop.Sda\ICSharpCode.SharpDevelop.Sda.csproj", "{80318B5F-A25D-45AB-8A95-EF31D2370A4C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Tests", "src\Main\Base\Test\ICSharpCode.SharpDevelop.Tests.csproj", "{4980B743-B32F-4aba-AABD-45E2CAD3568D}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "src\Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.Tests", "src\Main\Core\Test\ICSharpCode.Core.Tests.csproj", "{AD6FAA08-D6F5-4DBA-AF85-F4DA9F40C3B5}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.WinForms", "src\Main\ICSharpCode.Core.WinForms\ICSharpCode.Core.WinForms.csproj", "{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core", "src\Main\Core\Project\ICSharpCode.Core.csproj", "{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.Presentation", "src\Main\ICSharpCode.Core.Presentation\ICSharpCode.Core.Presentation.csproj", "{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.Presentation", "src\Main\ICSharpCode.Core.Presentation\ICSharpCode.Core.Presentation.csproj", "{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.WinForms", "src\Main\ICSharpCode.Core.WinForms\ICSharpCode.Core.WinForms.csproj", "{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core", "src\Main\Core\Project\ICSharpCode.Core.csproj", "{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Core.Tests", "src\Main\Core\Test\ICSharpCode.Core.Tests.csproj", "{AD6FAA08-D6F5-4DBA-AF85-F4DA9F40C3B5}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom", "src\Main\ICSharpCode.SharpDevelop.Dom\Project\ICSharpCode.SharpDevelop.Dom.csproj", "{924EE450-603D-49C1-A8E5-4AFAA31CE6F3}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Tests", "src\Main\Base\Test\ICSharpCode.SharpDevelop.Tests.csproj", "{4980B743-B32F-4aba-AABD-45E2CAD3568D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Sda", "src\Main\ICSharpCode.SharpDevelop.Sda\ICSharpCode.SharpDevelop.Sda.csproj", "{80318B5F-A25D-45AB-8A95-EF31D2370A4C}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Dom.Tests", "src\Main\ICSharpCode.SharpDevelop.Dom\Tests\ICSharpCode.SharpDevelop.Dom.Tests\ICSharpCode.SharpDevelop.Dom.Tests.csproj", "{7DB80259-24D4-46C3-A024-53FF1987733D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop", "src\Main\Base\Project\ICSharpCode.SharpDevelop.csproj", "{2748AD25-9C63-4E12-877B-4DCE96FBED54}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.BuildWorker", "src\Main\ICSharpCode.SharpDevelop.BuildWorker\ICSharpCode.SharpDevelop.BuildWorker.csproj", "{C3CBC8E3-81D8-4C5B-9941-DCCD12D50B1F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.Widgets", "src\Main\ICSharpCode.SharpDevelop.Widgets\Project\ICSharpCode.SharpDevelop.Widgets.csproj", "{8035765F-D51F-4A0C-A746-2FD100E19419}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.SharpDevelop.BuildWorker35", "src\Main\ICSharpCode.SharpDevelop.BuildWorker35\ICSharpCode.SharpDevelop.BuildWorker35.csproj", "{B5F54272-49F0-40DB-845A-8D837875D3BA}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StartUp", "src\Main\StartUp\Project\StartUp.csproj", "{1152B71B-3C05-4598-B20D-823B5D40559E}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
@ -72,195 +72,188 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{ @@ -72,195 +72,188 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TreeView", "src\Libraries\SharpTreeView\ICSharpCode.TreeView\ICSharpCode.TreeView.csproj", "{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "src\Libraries\Mono.Cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aga.Controls", "src\Libraries\TreeViewAdv\Aga.Controls\Aga.Controls.csproj", "{E73BB233-D88B-44A7-A98F-D71EE158381D}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NRefactory", "NRefactory", "{E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Build.Tasks", "src\Libraries\ICSharpCode.Build.Tasks\Project\ICSharpCode.Build.Tasks.csproj", "{4139CCF6-FB49-4A9D-B2CF-331E9EA3198D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactoryASTGenerator", "src\Libraries\NRefactory\NRefactoryASTGenerator\NRefactoryASTGenerator.csproj", "{B22522AA-B5BF-4A58-AC6D-D4B45805521F}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactory", "src\Libraries\NRefactory\Project\NRefactory.csproj", "{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvalonDock", "src\Libraries\AvalonDock\AvalonDock\AvalonDock.csproj", "{2FF700C2-A38A-48BD-A637-8CAFD4FE6237}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NRefactory", "NRefactory", "{E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}"
ProjectSection(SolutionItems) = postProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.AvalonEdit", "src\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj", "{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactory", "src\Libraries\NRefactory\Project\NRefactory.csproj", "{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Build.Tasks", "src\Libraries\ICSharpCode.Build.Tasks\Project\ICSharpCode.Build.Tasks.csproj", "{4139CCF6-FB49-4A9D-B2CF-331E9EA3198D}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactoryASTGenerator", "src\Libraries\NRefactory\NRefactoryASTGenerator\NRefactoryASTGenerator.csproj", "{B22522AA-B5BF-4A58-AC6D-D4B45805521F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aga.Controls", "src\Libraries\TreeViewAdv\Aga.Controls\Aga.Controls.csproj", "{E73BB233-D88B-44A7-A98F-D71EE158381D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.TreeView", "src\Libraries\SharpTreeView\ICSharpCode.TreeView\ICSharpCode.TreeView.csproj", "{DDE2A481-8271-4EAC-A330-8FA6A38D13D1}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mono.Cecil", "src\Libraries\Mono.Cecil\Mono.Cecil.csproj", "{D68133BD-1E63-496E-9EDE-4FBDBF77B486}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AddIns", "AddIns", "{39327899-ED91-4F7F-988C-4FE4E17C014D}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analysis", "Analysis", "{F355E45F-F54F-4B42-8916-9A633A392789}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Version Control", "Version Control", "{F208FF4F-E5D8-41D5-A7C7-B463976F156E}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeQualityAnalysis", "src\AddIns\Analysis\CodeQuality\CodeQualityAnalysis.csproj", "{76DD1CC0-0D86-44A1-9BD6-D91F79807BC3}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitAddIn", "src\AddIns\VersionControl\GitAddIn\GitAddIn.csproj", "{83F15BA7-8478-4664-81BB-A82F146D88B3}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceAnalysis", "src\AddIns\Analysis\SourceAnalysis\SourceAnalysis.csproj", "{CE498514-D12D-4B6E-AE0E-FEC29BD43748}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubversionAddIn", "src\AddIns\VersionControl\SubversionAddIn\SubversionAddIn.csproj", "{17F4D7E0-6933-4C2E-8714-FD7E98D625D5}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCoverage", "src\AddIns\Analysis\CodeCoverage\Project\CodeCoverage.csproj", "{08CE9972-283B-44F4-82FA-966F7DFA6B7A}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Reports", "Reports", "{8789D7FF-B36C-4187-B57D-55ED64623272}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysis", "src\AddIns\Analysis\CodeAnalysis\CodeAnalysis.csproj", "{3EAA45A9-735C-4AC7-A799-947B93EA449D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Reports.Addin", "src\AddIns\Misc\Reports\ICSharpCode.Reports.Addin\ICSharpCode.Reports.Addin.csproj", "{35D002D7-C78B-44FB-92AA-104BEB431678}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTesting", "src\AddIns\Analysis\UnitTesting\UnitTesting.csproj", "{1F261725-6318-4434-A1B1-6C70CE4CD324}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Reports.Core", "src\AddIns\Misc\Reports\ICSharpCode.Reports.Core\ICSharpCode.Reports.Core.csproj", "{4B2239FF-8FD6-431D-9D22-1B8049BA6917}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Display Bindings", "Display Bindings", "{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Irony", "src\AddIns\Misc\Reports\Irony\Irony.csproj", "{D81F5C91-D7DB-46E5-BC99-49488FB6814C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WpfDesign", "WpfDesign", "{6022AC51-B658-4C54-97EF-79187AC65B47}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Profiler", "Profiler", "{C4035C32-026F-4158-AF15-113EA1EF1960}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign.XamlDom", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign.XamlDom\Project\WpfDesign.XamlDom.csproj", "{88DA149F-21B2-48AB-82C4-28FB6BDFD783}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.AddIn", "src\AddIns\Analysis\Profiler\Frontend\AddIn\Profiler.AddIn.csproj", "{D294A12D-4B38-4F25-9AA6-3D4A6CE26E7B}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign.Designer", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign.Designer\Project\WpfDesign.Designer.csproj", "{78CC29AC-CC79-4355-B1F2-97936DF198AC}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.Controls", "src\AddIns\Analysis\Profiler\Frontend\Controls\Profiler.Controls.csproj", "{BDA49550-5ED1-4C6B-B648-657B2CACD8E0}"
ProjectSection(ProjectDependencies) = postProject
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign.AddIn", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign.AddIn\WpfDesign.AddIn.csproj", "{9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.Controller", "src\AddIns\Analysis\Profiler\Controller\Profiler.Controller.csproj", "{72FFB35A-C9E2-4A31-B4FA-E3E3E28DED5F}"
ProjectSection(ProjectDependencies) = postProject
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign\Project\WpfDesign.csproj", "{66A378A1-E9F4-4AD5-8946-D0EC06C2902F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.X64Converter", "src\AddIns\Analysis\Profiler\X64Converter\Profiler.X64Converter.csproj", "{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IconEditor", "IconEditor", "{0773ED53-08E2-4495-A3BE-CA0B5D413C15}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Language Bindings", "Language Bindings", "{E0646C25-36F2-4524-969F-FA621353AB94}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IconEditorAddIn", "src\AddIns\DisplayBindings\IconEditor\IconEditorAddIn\IconEditorAddIn.csproj", "{DFB936AD-90EE-4B4F-941E-4F4A636F0D92}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IconEditor", "src\AddIns\DisplayBindings\IconEditor\IconEditor\IconEditor.csproj", "{DC1CCE11-CB91-40FA-9C47-4D9EB5D67BFD}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ruby", "Ruby", "{C7288E72-FFBE-48CD-84B4-6CBF95A7195A}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexEditor", "src\AddIns\DisplayBindings\HexEditor\Project\HexEditor.csproj", "{E618A9CD-A39F-4925-A538-E8A3FEF24E54}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RubyBinding", "src\AddIns\BackendBindings\Ruby\RubyBinding\Project\RubyBinding.csproj", "{C896FFFF-5B6C-4B0E-B6DF-049865F501B4}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEditor", "src\AddIns\DisplayBindings\ResourceEditor\Project\ResourceEditor.csproj", "{CBC6C247-747B-4908-B09A-4D2E0F640B6B}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppBinding", "src\AddIns\BackendBindings\CppBinding\CppBinding\CppBinding.csproj", "{70966F84-74C9-4067-A379-0C674A929233}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvalonEdit.AddIn", "src\AddIns\DisplayBindings\AvalonEdit.AddIn\AvalonEdit.AddIn.csproj", "{0162E499-42D0-409B-AA25-EED21F75336B}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Boo", "Boo", "{97B3B514-AB0E-4FE1-89DE-8A945F5112AE}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor", "src\AddIns\DisplayBindings\XmlEditor\Project\XmlEditor.csproj", "{DCA2703D-250A-463E-A68A-07ED105AE6BD}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooBinding", "src\AddIns\BackendBindings\Boo\BooBinding\Project\BooBinding.csproj", "{4AC2D5F1-F671-480C-A075-6BF62B3721B2}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FormsDesigner", "src\AddIns\DisplayBindings\FormsDesigner\Project\FormsDesigner.csproj", "{7D7E92DF-ACEB-4B69-92C8-8AC7A703CD57}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactoryToBooConverter", "src\AddIns\BackendBindings\Boo\NRefactoryToBooConverter\Project\NRefactoryToBooConverter.csproj", "{DBCF20A1-BA13-4582-BFA9-74DE4D987B73}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ClassDiagram", "ClassDiagram", "{BFA3BF26-33BD-4A65-B84D-C7F30D131668}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Python", "Python", "{CA76F702-5B4E-4918-B8D8-7FF8382434FF}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassCanvas", "src\AddIns\DisplayBindings\ClassDiagram\ClassCanvas\ClassCanvas.csproj", "{08F772A1-F0BE-433E-8B37-F6522953DB05}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.Build.Tasks", "src\AddIns\BackendBindings\Python\Python.Build.Tasks\Project\Python.Build.Tasks.csproj", "{D332F2D1-2CF1-43B7-903C-844BD5211A7E}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassDiagramAddin", "src\AddIns\DisplayBindings\ClassDiagram\ClassDiagramAddin\ClassDiagramAddin.csproj", "{5A1354DF-4989-4BB4-BC6B-D627C2E9FA13}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PythonBinding", "src\AddIns\BackendBindings\Python\PythonBinding\Project\PythonBinding.csproj", "{8D732610-8FC6-43BA-94C9-7126FD7FE361}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diagrams", "src\AddIns\DisplayBindings\ClassDiagram\DiagramRouter\Diagrams.csproj", "{0991423A-DBF6-4C89-B365-A1DF1EB32E42}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixBinding", "src\AddIns\BackendBindings\WixBinding\Project\WixBinding.csproj", "{E1B288A2-08EE-4318-8BBB-8AB72C69E33E}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{C7F29FC2-1B03-4CDD-9E30-400F4765FF04}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Addin", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Addin\ICSharpCode.Data.Addin.csproj", "{A9F12710-24E4-46D4-832C-6ECB395B9EAD}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpBinding", "src\AddIns\BackendBindings\CSharpBinding\Project\CSharpBinding.csproj", "{1F1AC7CD-D154-45BB-8EAF-804CA8055F5A}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Core", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Core\ICSharpCode.Data.Core.csproj", "{B7823AE9-4B43-4859-8796-2EBDC116FBB8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VBNetBinding", "src\AddIns\BackendBindings\VBNetBinding\Project\VBNetBinding.csproj", "{BF38FB72-B380-4196-AF8C-95749D726C61}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Core.UI", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Core.UI\ICSharpCode.Data.Core.UI.csproj", "{BAD94D6E-4159-4CB6-B991-486F412D9BB6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlBinding", "src\AddIns\BackendBindings\XamlBinding\XamlBinding\XamlBinding.csproj", "{7C96B65D-28A5-4F28-A35B-8D83CE831EE8}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.EDMDesigner.Core", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.EDMDesigner.Core\ICSharpCode.Data.EDMDesigner.Core.csproj", "{5C70D6AB-0A33-43F9-B8B5-54558C35BBB1}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharpBinding", "src\AddIns\BackendBindings\FSharpBinding\FSharpBinding.csproj", "{E954F3CB-A446-492F-A664-2B376EBC86E8}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.EDMDesigner.Core.UI", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.EDMDesigner.Core.UI\ICSharpCode.Data.EDMDesigner.Core.UI.csproj", "{EEF5E054-4192-4A57-8FBF-E860D808A51D}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Scripting", "src\AddIns\BackendBindings\Scripting\Project\ICSharpCode.Scripting.csproj", "{7048AE18-EB93-4A84-82D0-DD60EB58ADBD}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.SQLServer", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.SQLServer\ICSharpCode.Data.SQLServer.csproj", "{AFE34868-AFA1-4E1C-9450-47AB4BE329D5}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debugger", "Debugger", "{CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "src\AddIns\Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SettingsEditor", "src\AddIns\DisplayBindings\SettingsEditor\Project\SettingsEditor.csproj", "{85226AFB-CE71-4851-9A75-7EEC663A8E8A}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.AddIn", "src\AddIns\Debugger\Debugger.AddIn\Debugger.AddIn.csproj", "{EC06F96A-AEEC-49D6-B03D-AB87C6EB674C}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
@ -269,36 +262,47 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{F3662720-9 @@ -269,36 +262,47 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{F3662720-9
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpyAddIn", "src\AddIns\Misc\ILSpyAddIn\ILSpyAddIn.csproj", "{8AA421C8-D7AF-4957-9F43-5135328ACB24}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextTemplating", "src\AddIns\Misc\TextTemplating\Project\TextTemplating.csproj", "{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PackageManagement", "PackageManagement", "{50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement.Cmdlets", "src\AddIns\Misc\PackageManagement\Cmdlets\Project\PackageManagement.Cmdlets.csproj", "{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement", "src\AddIns\Misc\PackageManagement\Project\PackageManagement.csproj", "{AE4AB0FA-6087-4480-AF37-0FA1452B3DA1}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UsageDataCollector", "UsageDataCollector", "{DEFC8584-BEC3-4921-BD0F-40482E450B7B}"
ProjectSection(SolutionItems) = postProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement.PowerShell", "src\AddIns\Misc\PackageManagement\PowerShell\Project\PackageManagement.PowerShell.csproj", "{A406803B-C584-43A3-BCEE-A0BB3132CB5F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelpViewer", "src\AddIns\Misc\HelpViewer\HelpViewer.csproj", "{80F76D10-0B44-4D55-B4BD-DAEB5464090C}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageDataCollector.AddIn", "src\AddIns\Misc\UsageDataCollector\UsageDataCollector.AddIn\UsageDataCollector.AddIn.csproj", "{0008FCE9-9EB4-4E2E-979B-553278E5BBA6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpRefactoring", "src\AddIns\Misc\SharpRefactoring\Project\SharpRefactoring.csproj", "{3CA90546-3B4C-4663-9445-C4E9371750A7}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageDataCollector", "src\AddIns\Misc\UsageDataCollector\UsageDataCollector\UsageDataCollector.csproj", "{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SearchAndReplace", "src\AddIns\Misc\SearchAndReplace\Project\SearchAndReplace.csproj", "{9196DD8A-B4D4-4780-8742-C5762E547FC2}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceToolkit", "src\AddIns\Misc\ResourceToolkit\Project\ResourceToolkit.csproj", "{461606BD-E824-4D0A-8CBA-01810B1F5E02}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StartPage", "src\AddIns\Misc\StartPage\Project\StartPage.csproj", "{7D5C266F-D6FF-4D14-B315-0C0FC6C4EF51}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegExpTk", "src\AddIns\Misc\RegExpTk\Project\RegExpTk.csproj", "{64A3E5E6-90BF-47F6-94DF-68C94B62C817}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddInManager", "src\AddIns\Misc\AddInManager\Project\AddInManager.csproj", "{F93E52FD-DA66-4CE5-A0CB-BCD902811122}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PInvokeAddIn", "src\AddIns\Misc\PInvokeAddIn\Project\PInvokeAddIn.csproj", "{5EEB99CF-EA2B-4733-80A6-CE9192D68170}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddinScout", "src\AddIns\Misc\AddinScout\Project\AddinScout.csproj", "{4B8F0F98-8BE1-402B-AA8B-C8D548577B38}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
@ -308,194 +312,200 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiletypeRegisterer", "src\A @@ -308,194 +312,200 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FiletypeRegisterer", "src\A
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddinScout", "src\AddIns\Misc\AddinScout\Project\AddinScout.csproj", "{4B8F0F98-8BE1-402B-AA8B-C8D548577B38}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PInvokeAddIn", "src\AddIns\Misc\PInvokeAddIn\Project\PInvokeAddIn.csproj", "{5EEB99CF-EA2B-4733-80A6-CE9192D68170}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddInManager", "src\AddIns\Misc\AddInManager\Project\AddInManager.csproj", "{F93E52FD-DA66-4CE5-A0CB-BCD902811122}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RegExpTk", "src\AddIns\Misc\RegExpTk\Project\RegExpTk.csproj", "{64A3E5E6-90BF-47F6-94DF-68C94B62C817}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StartPage", "src\AddIns\Misc\StartPage\Project\StartPage.csproj", "{7D5C266F-D6FF-4D14-B315-0C0FC6C4EF51}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceToolkit", "src\AddIns\Misc\ResourceToolkit\Project\ResourceToolkit.csproj", "{461606BD-E824-4D0A-8CBA-01810B1F5E02}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SearchAndReplace", "src\AddIns\Misc\SearchAndReplace\Project\SearchAndReplace.csproj", "{9196DD8A-B4D4-4780-8742-C5762E547FC2}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UsageDataCollector", "UsageDataCollector", "{DEFC8584-BEC3-4921-BD0F-40482E450B7B}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageDataCollector", "src\AddIns\Misc\UsageDataCollector\UsageDataCollector\UsageDataCollector.csproj", "{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpRefactoring", "src\AddIns\Misc\SharpRefactoring\Project\SharpRefactoring.csproj", "{3CA90546-3B4C-4663-9445-C4E9371750A7}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UsageDataCollector.AddIn", "src\AddIns\Misc\UsageDataCollector\UsageDataCollector.AddIn\UsageDataCollector.AddIn.csproj", "{0008FCE9-9EB4-4E2E-979B-553278E5BBA6}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HelpViewer", "src\AddIns\Misc\HelpViewer\HelpViewer.csproj", "{80F76D10-0B44-4D55-B4BD-DAEB5464090C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpyAddIn", "src\AddIns\Misc\ILSpyAddIn\ILSpyAddIn.csproj", "{8AA421C8-D7AF-4957-9F43-5135328ACB24}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PackageManagement", "PackageManagement", "{50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Display Bindings", "Display Bindings", "{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement.PowerShell", "src\AddIns\Misc\PackageManagement\PowerShell\Project\PackageManagement.PowerShell.csproj", "{A406803B-C584-43A3-BCEE-A0BB3132CB5F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement", "src\AddIns\Misc\PackageManagement\Project\PackageManagement.csproj", "{AE4AB0FA-6087-4480-AF37-0FA1452B3DA1}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SettingsEditor", "src\AddIns\DisplayBindings\SettingsEditor\Project\SettingsEditor.csproj", "{85226AFB-CE71-4851-9A75-7EEC663A8E8A}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PackageManagement.Cmdlets", "src\AddIns\Misc\PackageManagement\Cmdlets\Project\PackageManagement.Cmdlets.csproj", "{E0A5E80A-003B-4335-A9DC-A76E2E46D38D}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TextTemplating", "src\AddIns\Misc\TextTemplating\Project\TextTemplating.csproj", "{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Debugger", "Debugger", "{CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{C7F29FC2-1B03-4CDD-9E30-400F4765FF04}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.AddIn", "src\AddIns\Debugger\Debugger.AddIn\Debugger.AddIn.csproj", "{EC06F96A-AEEC-49D6-B03D-AB87C6EB674C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.SQLServer", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.SQLServer\ICSharpCode.Data.SQLServer.csproj", "{AFE34868-AFA1-4E1C-9450-47AB4BE329D5}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger.Core", "src\AddIns\Debugger\Debugger.Core\Debugger.Core.csproj", "{1D18D788-F7EE-4585-A23B-34DC8EC63CB8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.EDMDesigner.Core.UI", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.EDMDesigner.Core.UI\ICSharpCode.Data.EDMDesigner.Core.UI.csproj", "{EEF5E054-4192-4A57-8FBF-E860D808A51D}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Language Bindings", "Language Bindings", "{E0646C25-36F2-4524-969F-FA621353AB94}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Scripting", "src\AddIns\BackendBindings\Scripting\Project\ICSharpCode.Scripting.csproj", "{7048AE18-EB93-4A84-82D0-DD60EB58ADBD}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.EDMDesigner.Core", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.EDMDesigner.Core\ICSharpCode.Data.EDMDesigner.Core.csproj", "{5C70D6AB-0A33-43F9-B8B5-54558C35BBB1}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FSharpBinding", "src\AddIns\BackendBindings\FSharpBinding\FSharpBinding.csproj", "{E954F3CB-A446-492F-A664-2B376EBC86E8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Core.UI", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Core.UI\ICSharpCode.Data.Core.UI.csproj", "{BAD94D6E-4159-4CB6-B991-486F412D9BB6}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XamlBinding", "src\AddIns\BackendBindings\XamlBinding\XamlBinding\XamlBinding.csproj", "{7C96B65D-28A5-4F28-A35B-8D83CE831EE8}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Core", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Core\ICSharpCode.Data.Core.csproj", "{B7823AE9-4B43-4859-8796-2EBDC116FBB8}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VBNetBinding", "src\AddIns\BackendBindings\VBNetBinding\Project\VBNetBinding.csproj", "{BF38FB72-B380-4196-AF8C-95749D726C61}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Data.Addin", "src\AddIns\DisplayBindings\Data\ICSharpCode.Data.Addin\ICSharpCode.Data.Addin.csproj", "{A9F12710-24E4-46D4-832C-6ECB395B9EAD}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharpBinding", "src\AddIns\BackendBindings\CSharpBinding\Project\CSharpBinding.csproj", "{1F1AC7CD-D154-45BB-8EAF-804CA8055F5A}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ClassDiagram", "ClassDiagram", "{BFA3BF26-33BD-4A65-B84D-C7F30D131668}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WixBinding", "src\AddIns\BackendBindings\WixBinding\Project\WixBinding.csproj", "{E1B288A2-08EE-4318-8BBB-8AB72C69E33E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Diagrams", "src\AddIns\DisplayBindings\ClassDiagram\DiagramRouter\Diagrams.csproj", "{0991423A-DBF6-4C89-B365-A1DF1EB32E42}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Python", "Python", "{CA76F702-5B4E-4918-B8D8-7FF8382434FF}"
ProjectSection(SolutionItems) = postProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassDiagramAddin", "src\AddIns\DisplayBindings\ClassDiagram\ClassDiagramAddin\ClassDiagramAddin.csproj", "{5A1354DF-4989-4BB4-BC6B-D627C2E9FA13}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PythonBinding", "src\AddIns\BackendBindings\Python\PythonBinding\Project\PythonBinding.csproj", "{8D732610-8FC6-43BA-94C9-7126FD7FE361}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClassCanvas", "src\AddIns\DisplayBindings\ClassDiagram\ClassCanvas\ClassCanvas.csproj", "{08F772A1-F0BE-433E-8B37-F6522953DB05}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Python.Build.Tasks", "src\AddIns\BackendBindings\Python\Python.Build.Tasks\Project\Python.Build.Tasks.csproj", "{D332F2D1-2CF1-43B7-903C-844BD5211A7E}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FormsDesigner", "src\AddIns\DisplayBindings\FormsDesigner\Project\FormsDesigner.csproj", "{7D7E92DF-ACEB-4B69-92C8-8AC7A703CD57}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Boo", "Boo", "{97B3B514-AB0E-4FE1-89DE-8A945F5112AE}"
ProjectSection(SolutionItems) = postProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XmlEditor", "src\AddIns\DisplayBindings\XmlEditor\Project\XmlEditor.csproj", "{DCA2703D-250A-463E-A68A-07ED105AE6BD}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NRefactoryToBooConverter", "src\AddIns\BackendBindings\Boo\NRefactoryToBooConverter\Project\NRefactoryToBooConverter.csproj", "{DBCF20A1-BA13-4582-BFA9-74DE4D987B73}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvalonEdit.AddIn", "src\AddIns\DisplayBindings\AvalonEdit.AddIn\AvalonEdit.AddIn.csproj", "{0162E499-42D0-409B-AA25-EED21F75336B}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BooBinding", "src\AddIns\BackendBindings\Boo\BooBinding\Project\BooBinding.csproj", "{4AC2D5F1-F671-480C-A075-6BF62B3721B2}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ResourceEditor", "src\AddIns\DisplayBindings\ResourceEditor\Project\ResourceEditor.csproj", "{CBC6C247-747B-4908-B09A-4D2E0F640B6B}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CppBinding", "src\AddIns\BackendBindings\CppBinding\CppBinding\CppBinding.csproj", "{70966F84-74C9-4067-A379-0C674A929233}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexEditor", "src\AddIns\DisplayBindings\HexEditor\Project\HexEditor.csproj", "{E618A9CD-A39F-4925-A538-E8A3FEF24E54}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ruby", "Ruby", "{C7288E72-FFBE-48CD-84B4-6CBF95A7195A}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IconEditor", "IconEditor", "{0773ED53-08E2-4495-A3BE-CA0B5D413C15}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RubyBinding", "src\AddIns\BackendBindings\Ruby\RubyBinding\Project\RubyBinding.csproj", "{C896FFFF-5B6C-4B0E-B6DF-049865F501B4}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IconEditor", "src\AddIns\DisplayBindings\IconEditor\IconEditor\IconEditor.csproj", "{DC1CCE11-CB91-40FA-9C47-4D9EB5D67BFD}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Profiler", "Profiler", "{C4035C32-026F-4158-AF15-113EA1EF1960}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IconEditorAddIn", "src\AddIns\DisplayBindings\IconEditor\IconEditorAddIn\IconEditorAddIn.csproj", "{DFB936AD-90EE-4B4F-941E-4F4A636F0D92}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WpfDesign", "WpfDesign", "{6022AC51-B658-4C54-97EF-79187AC65B47}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.X64Converter", "src\AddIns\Analysis\Profiler\X64Converter\Profiler.X64Converter.csproj", "{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign\Project\WpfDesign.csproj", "{66A378A1-E9F4-4AD5-8946-D0EC06C2902F}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.Controller", "src\AddIns\Analysis\Profiler\Controller\Profiler.Controller.csproj", "{72FFB35A-C9E2-4A31-B4FA-E3E3E28DED5F}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign.AddIn", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign.AddIn\WpfDesign.AddIn.csproj", "{9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865}"
ProjectSection(ProjectDependencies) = postProject
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.Controls", "src\AddIns\Analysis\Profiler\Frontend\Controls\Profiler.Controls.csproj", "{BDA49550-5ED1-4C6B-B648-657B2CACD8E0}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign.Designer", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign.Designer\Project\WpfDesign.Designer.csproj", "{78CC29AC-CC79-4355-B1F2-97936DF198AC}"
ProjectSection(ProjectDependencies) = postProject
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Profiler.AddIn", "src\AddIns\Analysis\Profiler\Frontend\AddIn\Profiler.AddIn.csproj", "{D294A12D-4B38-4F25-9AA6-3D4A6CE26E7B}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WpfDesign.XamlDom", "src\AddIns\DisplayBindings\WpfDesign\WpfDesign.XamlDom\Project\WpfDesign.XamlDom.csproj", "{88DA149F-21B2-48AB-82C4-28FB6BDFD783}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Reports", "Reports", "{8789D7FF-B36C-4187-B57D-55ED64623272}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Decompiler", "Decompiler", "{814DFF39-5324-40BE-90EA-F62F758660B3}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Irony", "src\AddIns\Misc\Reports\Irony\Irony.csproj", "{D81F5C91-D7DB-46E5-BC99-49488FB6814C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Decompiler", "src\Libraries\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj", "{984CC812-9470-4A13-AFF9-CC44068D666C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Reports.Core", "src\AddIns\Misc\Reports\ICSharpCode.Reports.Core\ICSharpCode.Reports.Core.csproj", "{4B2239FF-8FD6-431D-9D22-1B8049BA6917}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.NRefactory", "src\Libraries\NewNRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj", "{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ILSpyAddIn", "src\AddIns\DisplayBindings\ILSpyAddIn\ILSpyAddIn.csproj", "88305E42-C90D-410F-B831-15161E23BAAA"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Analysis", "Analysis", "{F355E45F-F54F-4B42-8916-9A633A392789}"
ProjectSection(SolutionItems) = postProject
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTesting", "src\AddIns\Analysis\UnitTesting\UnitTesting.csproj", "{1F261725-6318-4434-A1B1-6C70CE4CD324}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ICSharpCode.Reports.Addin", "src\AddIns\Misc\Reports\ICSharpCode.Reports.Addin\ICSharpCode.Reports.Addin.csproj", "{35D002D7-C78B-44FB-92AA-104BEB431678}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeAnalysis", "src\AddIns\Analysis\CodeAnalysis\CodeAnalysis.csproj", "{3EAA45A9-735C-4AC7-A799-947B93EA449D}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Version Control", "Version Control", "{F208FF4F-E5D8-41D5-A7C7-B463976F156E}"
ProjectSection(SolutionItems) = postProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeCoverage", "src\AddIns\Analysis\CodeCoverage\Project\CodeCoverage.csproj", "{08CE9972-283B-44F4-82FA-966F7DFA6B7A}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SubversionAddIn", "src\AddIns\VersionControl\SubversionAddIn\SubversionAddIn.csproj", "{17F4D7E0-6933-4C2E-8714-FD7E98D625D5}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceAnalysis", "src\AddIns\Analysis\SourceAnalysis\SourceAnalysis.csproj", "{CE498514-D12D-4B6E-AE0E-FEC29BD43748}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitAddIn", "src\AddIns\VersionControl\GitAddIn\GitAddIn.csproj", "{83F15BA7-8478-4664-81BB-A82F146D88B3}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeQualityAnalysis", "src\AddIns\Analysis\CodeQuality\CodeQualityAnalysis.csproj", "{76DD1CC0-0D86-44A1-9BD6-D91F79807BC3}"
ProjectSection(ProjectDependencies) = postProject
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {35CEF10F-2D4C-45F2-9DD1-161E0FEC583C}
EndProjectSection
@ -1082,115 +1092,167 @@ Global @@ -1082,115 +1092,167 @@ Global
{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF}.Release|x86.ActiveCfg = Release|x86
{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF}.Release|Debug.Build.0 = Release|x86
{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF}.Release|Debug.ActiveCfg = Release|x86
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Win32.Build.0 = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Win32.ActiveCfg = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|x86.Build.0 = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|x86.ActiveCfg = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Debug.Build.0 = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Debug|Debug.ActiveCfg = Debug|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.Build.0 = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Win32.Build.0 = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Win32.ActiveCfg = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.Build.0 = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|x86.ActiveCfg = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Debug.Build.0 = Release|Any CPU
{984CC812-9470-4A13-AFF9-CC44068D666C}.Release|Debug.ActiveCfg = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Win32.Build.0 = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Win32.ActiveCfg = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|x86.Build.0 = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|x86.ActiveCfg = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Debug.Build.0 = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Debug|Debug.ActiveCfg = Debug|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.Build.0 = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Win32.Build.0 = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Win32.ActiveCfg = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|x86.Build.0 = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|x86.ActiveCfg = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Debug.Build.0 = Release|Any CPU
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}.Release|Debug.ActiveCfg = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|Any CPU.Build.0 = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|Any CPU.ActiveCfg = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|Win32.Build.0 = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|Win32.ActiveCfg = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|x86.Build.0 = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|x86.ActiveCfg = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|Debug.Build.0 = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Debug|Debug.ActiveCfg = Debug|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|Any CPU.Build.0 = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|Any CPU.ActiveCfg = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|Win32.Build.0 = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|Win32.ActiveCfg = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|x86.Build.0 = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|x86.ActiveCfg = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|Debug.Build.0 = Release|Any CPU
88305E42-C90D-410F-B831-15161E23BAAA.Release|Debug.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{B5F54272-49F0-40DB-845A-8D837875D3BA} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{C3CBC8E3-81D8-4C5B-9941-DCCD12D50B1F} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{7DB80259-24D4-46C3-A024-53FF1987733D} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{4980B743-B32F-4aba-AABD-45E2CAD3568D} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{AD6FAA08-D6F5-4DBA-AF85-F4DA9F40C3B5} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{80318B5F-A25D-45AB-8A95-EF31D2370A4C} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{2748AD25-9C63-4E12-877B-4DCE96FBED54} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{8035765F-D51F-4A0C-A746-2FD100E19419} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{1152B71B-3C05-4598-B20D-823B5D40559E} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{D68133BD-1E63-496E-9EDE-4FBDBF77B486} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{2FF700C2-A38A-48BD-A637-8CAFD4FE6237} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{4139CCF6-FB49-4A9D-B2CF-331E9EA3198D} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{E73BB233-D88B-44A7-A98F-D71EE158381D} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{8035765F-D51F-4A0C-A746-2FD100E19419} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{2748AD25-9C63-4E12-877B-4DCE96FBED54} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{80318B5F-A25D-45AB-8A95-EF31D2370A4C} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{924EE450-603D-49C1-A8E5-4AFAA31CE6F3} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{857CA1A3-FC88-4BE0-AB6A-D1EE772AB288} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{7E4A7172-7FF5-48D0-B719-7CD959DD1AC9} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{35CEF10F-2D4C-45F2-9DD1-161E0FEC583C} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{AD6FAA08-D6F5-4DBA-AF85-F4DA9F40C3B5} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{4980B743-B32F-4aba-AABD-45E2CAD3568D} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{7DB80259-24D4-46C3-A024-53FF1987733D} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{C3CBC8E3-81D8-4C5B-9941-DCCD12D50B1F} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{B5F54272-49F0-40DB-845A-8D837875D3BA} = {256F5C28-532C-44C0-8AB8-D8EC5E492E01}
{DDE2A481-8271-4EAC-A330-8FA6A38D13D1} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{B22522AA-B5BF-4A58-AC6D-D4B45805521F} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{E73BB233-D88B-44A7-A98F-D71EE158381D} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{4139CCF6-FB49-4A9D-B2CF-331E9EA3198D} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{2FF700C2-A38A-48BD-A637-8CAFD4FE6237} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{D68133BD-1E63-496E-9EDE-4FBDBF77B486} = {2A232EF1-EB95-41C6-B63A-C106E0C95D3C}
{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{F208FF4F-E5D8-41D5-A7C7-B463976F156E} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{8789D7FF-B36C-4187-B57D-55ED64623272} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{C4035C32-026F-4158-AF15-113EA1EF1960} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{E0646C25-36F2-4524-969F-FA621353AB94} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{F3662720-9EA2-4591-BBC6-97361DCE50A9} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{B22522AA-B5BF-4A58-AC6D-D4B45805521F} = {E5A0F4D8-37FD-4A30-BEB0-4409DC4E0865}
{F355E45F-F54F-4B42-8916-9A633A392789} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{1F261725-6318-4434-A1B1-6C70CE4CD324} = {F355E45F-F54F-4B42-8916-9A633A392789}
{3EAA45A9-735C-4AC7-A799-947B93EA449D} = {F355E45F-F54F-4B42-8916-9A633A392789}
{08CE9972-283B-44F4-82FA-966F7DFA6B7A} = {F355E45F-F54F-4B42-8916-9A633A392789}
{CE498514-D12D-4B6E-AE0E-FEC29BD43748} = {F355E45F-F54F-4B42-8916-9A633A392789}
{76DD1CC0-0D86-44A1-9BD6-D91F79807BC3} = {F355E45F-F54F-4B42-8916-9A633A392789}
{85226AFB-CE71-4851-9A75-7EEC663A8E8A} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{C7F29FC2-1B03-4CDD-9E30-400F4765FF04} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{BFA3BF26-33BD-4A65-B84D-C7F30D131668} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{7D7E92DF-ACEB-4B69-92C8-8AC7A703CD57} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{DCA2703D-250A-463E-A68A-07ED105AE6BD} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{0162E499-42D0-409B-AA25-EED21F75336B} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{CBC6C247-747B-4908-B09A-4D2E0F640B6B} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{E618A9CD-A39F-4925-A538-E8A3FEF24E54} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{0773ED53-08E2-4495-A3BE-CA0B5D413C15} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{6022AC51-B658-4C54-97EF-79187AC65B47} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{66A378A1-E9F4-4AD5-8946-D0EC06C2902F} = {6022AC51-B658-4C54-97EF-79187AC65B47}
{9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865} = {6022AC51-B658-4C54-97EF-79187AC65B47}
{78CC29AC-CC79-4355-B1F2-97936DF198AC} = {6022AC51-B658-4C54-97EF-79187AC65B47}
{88DA149F-21B2-48AB-82C4-28FB6BDFD783} = {6022AC51-B658-4C54-97EF-79187AC65B47}
{DC1CCE11-CB91-40FA-9C47-4D9EB5D67BFD} = {0773ED53-08E2-4495-A3BE-CA0B5D413C15}
{DFB936AD-90EE-4B4F-941E-4F4A636F0D92} = {0773ED53-08E2-4495-A3BE-CA0B5D413C15}
{0991423A-DBF6-4C89-B365-A1DF1EB32E42} = {BFA3BF26-33BD-4A65-B84D-C7F30D131668}
{5A1354DF-4989-4BB4-BC6B-D627C2E9FA13} = {BFA3BF26-33BD-4A65-B84D-C7F30D131668}
{08F772A1-F0BE-433E-8B37-F6522953DB05} = {BFA3BF26-33BD-4A65-B84D-C7F30D131668}
{AFE34868-AFA1-4E1C-9450-47AB4BE329D5} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{EEF5E054-4192-4A57-8FBF-E860D808A51D} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{5C70D6AB-0A33-43F9-B8B5-54558C35BBB1} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{BAD94D6E-4159-4CB6-B991-486F412D9BB6} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{B7823AE9-4B43-4859-8796-2EBDC116FBB8} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{A9F12710-24E4-46D4-832C-6ECB395B9EAD} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{50B51AAA-80E3-4C4A-8B2D-CAF440A82B78} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{80F76D10-0B44-4D55-B4BD-DAEB5464090C} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{3CA90546-3B4C-4663-9445-C4E9371750A7} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{9196DD8A-B4D4-4780-8742-C5762E547FC2} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{7D5C266F-D6FF-4D14-B315-0C0FC6C4EF51} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{F93E52FD-DA66-4CE5-A0CB-BCD902811122} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{4B8F0F98-8BE1-402B-AA8B-C8D548577B38} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{D022A6CE-7438-41E8-AC64-F2DE18EC54C6} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{5EEB99CF-EA2B-4733-80A6-CE9192D68170} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{64A3E5E6-90BF-47F6-94DF-68C94B62C817} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{461606BD-E824-4D0A-8CBA-01810B1F5E02} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{DEFC8584-BEC3-4921-BD0F-40482E450B7B} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{8AA421C8-D7AF-4957-9F43-5135328ACB24} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{0008FCE9-9EB4-4E2E-979B-553278E5BBA6} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D} = {50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}
{AE4AB0FA-6087-4480-AF37-0FA1452B3DA1} = {50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}
{A406803B-C584-43A3-BCEE-A0BB3132CB5F} = {50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}
{1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF}
{EC06F96A-AEEC-49D6-B03D-AB87C6EB674C} = {CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF}
{C7288E72-FFBE-48CD-84B4-6CBF95A7195A} = {E0646C25-36F2-4524-969F-FA621353AB94}
{70966F84-74C9-4067-A379-0C674A929233} = {E0646C25-36F2-4524-969F-FA621353AB94}
{97B3B514-AB0E-4FE1-89DE-8A945F5112AE} = {E0646C25-36F2-4524-969F-FA621353AB94}
{CA76F702-5B4E-4918-B8D8-7FF8382434FF} = {E0646C25-36F2-4524-969F-FA621353AB94}
{E1B288A2-08EE-4318-8BBB-8AB72C69E33E} = {E0646C25-36F2-4524-969F-FA621353AB94}
{1F1AC7CD-D154-45BB-8EAF-804CA8055F5A} = {E0646C25-36F2-4524-969F-FA621353AB94}
{BF38FB72-B380-4196-AF8C-95749D726C61} = {E0646C25-36F2-4524-969F-FA621353AB94}
{7C96B65D-28A5-4F28-A35B-8D83CE831EE8} = {E0646C25-36F2-4524-969F-FA621353AB94}
{E954F3CB-A446-492F-A664-2B376EBC86E8} = {E0646C25-36F2-4524-969F-FA621353AB94}
{11BF9245-88A3-4A0A-9A8A-EC9D98036B0F} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{F3662720-9EA2-4591-BBC6-97361DCE50A9} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{E0646C25-36F2-4524-969F-FA621353AB94} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{C4035C32-026F-4158-AF15-113EA1EF1960} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{8789D7FF-B36C-4187-B57D-55ED64623272} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{F208FF4F-E5D8-41D5-A7C7-B463976F156E} = {39327899-ED91-4F7F-988C-4FE4E17C014D}
{17F4D7E0-6933-4C2E-8714-FD7E98D625D5} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E}
{83F15BA7-8478-4664-81BB-A82F146D88B3} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E}
{D81F5C91-D7DB-46E5-BC99-49488FB6814C} = {8789D7FF-B36C-4187-B57D-55ED64623272}
{4B2239FF-8FD6-431D-9D22-1B8049BA6917} = {8789D7FF-B36C-4187-B57D-55ED64623272}
{35D002D7-C78B-44FB-92AA-104BEB431678} = {8789D7FF-B36C-4187-B57D-55ED64623272}
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{72FFB35A-C9E2-4A31-B4FA-E3E3E28DED5F} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{BDA49550-5ED1-4C6B-B648-657B2CACD8E0} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{D294A12D-4B38-4F25-9AA6-3D4A6CE26E7B} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{7048AE18-EB93-4A84-82D0-DD60EB58ADBD} = {E0646C25-36F2-4524-969F-FA621353AB94}
{D332F2D1-2CF1-43B7-903C-844BD5211A7E} = {CA76F702-5B4E-4918-B8D8-7FF8382434FF}
{8D732610-8FC6-43BA-94C9-7126FD7FE361} = {CA76F702-5B4E-4918-B8D8-7FF8382434FF}
{4AC2D5F1-F671-480C-A075-6BF62B3721B2} = {97B3B514-AB0E-4FE1-89DE-8A945F5112AE}
{DBCF20A1-BA13-4582-BFA9-74DE4D987B73} = {97B3B514-AB0E-4FE1-89DE-8A945F5112AE}
{E954F3CB-A446-492F-A664-2B376EBC86E8} = {E0646C25-36F2-4524-969F-FA621353AB94}
{7C96B65D-28A5-4F28-A35B-8D83CE831EE8} = {E0646C25-36F2-4524-969F-FA621353AB94}
{BF38FB72-B380-4196-AF8C-95749D726C61} = {E0646C25-36F2-4524-969F-FA621353AB94}
{1F1AC7CD-D154-45BB-8EAF-804CA8055F5A} = {E0646C25-36F2-4524-969F-FA621353AB94}
{E1B288A2-08EE-4318-8BBB-8AB72C69E33E} = {E0646C25-36F2-4524-969F-FA621353AB94}
{CA76F702-5B4E-4918-B8D8-7FF8382434FF} = {E0646C25-36F2-4524-969F-FA621353AB94}
{97B3B514-AB0E-4FE1-89DE-8A945F5112AE} = {E0646C25-36F2-4524-969F-FA621353AB94}
{70966F84-74C9-4067-A379-0C674A929233} = {E0646C25-36F2-4524-969F-FA621353AB94}
{C7288E72-FFBE-48CD-84B4-6CBF95A7195A} = {E0646C25-36F2-4524-969F-FA621353AB94}
{C896FFFF-5B6C-4B0E-B6DF-049865F501B4} = {C7288E72-FFBE-48CD-84B4-6CBF95A7195A}
{D294A12D-4B38-4F25-9AA6-3D4A6CE26E7B} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{BDA49550-5ED1-4C6B-B648-657B2CACD8E0} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{72FFB35A-C9E2-4A31-B4FA-E3E3E28DED5F} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{FE88FE17-D9FB-4FCC-9A35-6BFFB6B26CC6} = {C4035C32-026F-4158-AF15-113EA1EF1960}
{35D002D7-C78B-44FB-92AA-104BEB431678} = {8789D7FF-B36C-4187-B57D-55ED64623272}
{4B2239FF-8FD6-431D-9D22-1B8049BA6917} = {8789D7FF-B36C-4187-B57D-55ED64623272}
{D81F5C91-D7DB-46E5-BC99-49488FB6814C} = {8789D7FF-B36C-4187-B57D-55ED64623272}
{83F15BA7-8478-4664-81BB-A82F146D88B3} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E}
{17F4D7E0-6933-4C2E-8714-FD7E98D625D5} = {F208FF4F-E5D8-41D5-A7C7-B463976F156E}
{DBCF20A1-BA13-4582-BFA9-74DE4D987B73} = {97B3B514-AB0E-4FE1-89DE-8A945F5112AE}
{4AC2D5F1-F671-480C-A075-6BF62B3721B2} = {97B3B514-AB0E-4FE1-89DE-8A945F5112AE}
{8D732610-8FC6-43BA-94C9-7126FD7FE361} = {CA76F702-5B4E-4918-B8D8-7FF8382434FF}
{D332F2D1-2CF1-43B7-903C-844BD5211A7E} = {CA76F702-5B4E-4918-B8D8-7FF8382434FF}
{EC06F96A-AEEC-49D6-B03D-AB87C6EB674C} = {CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF}
{1D18D788-F7EE-4585-A23B-34DC8EC63CB8} = {CDE0C5A4-2096-48B5-BEA3-74DBA0F0E1EF}
{8AA421C8-D7AF-4957-9F43-5135328ACB24} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{DEFC8584-BEC3-4921-BD0F-40482E450B7B} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{461606BD-E824-4D0A-8CBA-01810B1F5E02} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{64A3E5E6-90BF-47F6-94DF-68C94B62C817} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{5EEB99CF-EA2B-4733-80A6-CE9192D68170} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{D022A6CE-7438-41E8-AC64-F2DE18EC54C6} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{4B8F0F98-8BE1-402B-AA8B-C8D548577B38} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{F93E52FD-DA66-4CE5-A0CB-BCD902811122} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{7D5C266F-D6FF-4D14-B315-0C0FC6C4EF51} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{9196DD8A-B4D4-4780-8742-C5762E547FC2} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{3CA90546-3B4C-4663-9445-C4E9371750A7} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{80F76D10-0B44-4D55-B4BD-DAEB5464090C} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{50B51AAA-80E3-4C4A-8B2D-CAF440A82B78} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{B5D8C3E6-42EC-4D4B-AD05-3644B32563EF} = {F3662720-9EA2-4591-BBC6-97361DCE50A9}
{A406803B-C584-43A3-BCEE-A0BB3132CB5F} = {50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}
{AE4AB0FA-6087-4480-AF37-0FA1452B3DA1} = {50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}
{E0A5E80A-003B-4335-A9DC-A76E2E46D38D} = {50B51AAA-80E3-4C4A-8B2D-CAF440A82B78}
{0008FCE9-9EB4-4E2E-979B-553278E5BBA6} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{6B1CFE35-DA17-4DEB-9C6E-227E5E251DA0} = {DEFC8584-BEC3-4921-BD0F-40482E450B7B}
{814DFF39-5324-40BE-90EA-F62F758660B3} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{6022AC51-B658-4C54-97EF-79187AC65B47} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{0773ED53-08E2-4495-A3BE-CA0B5D413C15} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{E618A9CD-A39F-4925-A538-E8A3FEF24E54} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{CBC6C247-747B-4908-B09A-4D2E0F640B6B} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{0162E499-42D0-409B-AA25-EED21F75336B} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{DCA2703D-250A-463E-A68A-07ED105AE6BD} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{7D7E92DF-ACEB-4B69-92C8-8AC7A703CD57} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{BFA3BF26-33BD-4A65-B84D-C7F30D131668} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{C7F29FC2-1B03-4CDD-9E30-400F4765FF04} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{85226AFB-CE71-4851-9A75-7EEC663A8E8A} = {11BF9245-88A3-4A0A-9A8A-EC9D98036B0F}
{A9F12710-24E4-46D4-832C-6ECB395B9EAD} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{B7823AE9-4B43-4859-8796-2EBDC116FBB8} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{BAD94D6E-4159-4CB6-B991-486F412D9BB6} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{5C70D6AB-0A33-43F9-B8B5-54558C35BBB1} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{EEF5E054-4192-4A57-8FBF-E860D808A51D} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{AFE34868-AFA1-4E1C-9450-47AB4BE329D5} = {C7F29FC2-1B03-4CDD-9E30-400F4765FF04}
{08F772A1-F0BE-433E-8B37-F6522953DB05} = {BFA3BF26-33BD-4A65-B84D-C7F30D131668}
{5A1354DF-4989-4BB4-BC6B-D627C2E9FA13} = {BFA3BF26-33BD-4A65-B84D-C7F30D131668}
{0991423A-DBF6-4C89-B365-A1DF1EB32E42} = {BFA3BF26-33BD-4A65-B84D-C7F30D131668}
{DFB936AD-90EE-4B4F-941E-4F4A636F0D92} = {0773ED53-08E2-4495-A3BE-CA0B5D413C15}
{DC1CCE11-CB91-40FA-9C47-4D9EB5D67BFD} = {0773ED53-08E2-4495-A3BE-CA0B5D413C15}
{88DA149F-21B2-48AB-82C4-28FB6BDFD783} = {6022AC51-B658-4C54-97EF-79187AC65B47}
{78CC29AC-CC79-4355-B1F2-97936DF198AC} = {6022AC51-B658-4C54-97EF-79187AC65B47}
{9A9D6FD4-6A2E-455D-ACC3-DDA775FE9865} = {6022AC51-B658-4C54-97EF-79187AC65B47}
{66A378A1-E9F4-4AD5-8946-D0EC06C2902F} = {6022AC51-B658-4C54-97EF-79187AC65B47}
88305E42-C90D-410F-B831-15161E23BAAA = {814DFF39-5324-40BE-90EA-F62F758660B3}
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371} = {814DFF39-5324-40BE-90EA-F62F758660B3}
{984CC812-9470-4A13-AFF9-CC44068D666C} = {814DFF39-5324-40BE-90EA-F62F758660B3}
{76DD1CC0-0D86-44A1-9BD6-D91F79807BC3} = {F355E45F-F54F-4B42-8916-9A633A392789}
{CE498514-D12D-4B6E-AE0E-FEC29BD43748} = {F355E45F-F54F-4B42-8916-9A633A392789}
{08CE9972-283B-44F4-82FA-966F7DFA6B7A} = {F355E45F-F54F-4B42-8916-9A633A392789}
{3EAA45A9-735C-4AC7-A799-947B93EA449D} = {F355E45F-F54F-4B42-8916-9A633A392789}
{1F261725-6318-4434-A1B1-6C70CE4CD324} = {F355E45F-F54F-4B42-8916-9A633A392789}
EndGlobalSection
EndGlobal

9
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj

@ -353,6 +353,15 @@ @@ -353,6 +353,15 @@
<Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">
<Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project>
<Name>ICSharpCode.SharpDevelop</Name>

12
src/AddIns/Debugger/Debugger.AddIn/Pads/ConsolePad.cs

@ -52,7 +52,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -52,7 +52,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
return "The process is running";
}
try {
Value val = ExpressionEvaluator.Evaluate(code, SelectedLanguage, process.SelectedStackFrame);
var context = !process.IsInExternalCode ? process.SelectedStackFrame : process.SelectedThread.MostRecentStackFrame;
Value val = ExpressionEvaluator.Evaluate(code, SelectedLanguage, context);
return ExpressionEvaluator.FormatValue(val);
} catch (GetValueException e) {
return e.Message;
@ -107,7 +108,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -107,7 +108,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
if (this.process == null || this.process.IsRunning)
return;
if (this.process.SelectedStackFrame == null || this.process.SelectedStackFrame.NextStatement == null)
var context = !process.IsInExternalCode ? process.SelectedStackFrame : process.SelectedThread.MostRecentStackFrame;
if (context == null)
return;
foreach (char ch in e.Text) {
@ -119,7 +121,11 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -119,7 +121,11 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
void ShowDotCompletion(string currentText)
{
var seg = process.SelectedStackFrame.NextStatement;
var context = !process.IsInExternalCode ? process.SelectedStackFrame : process.SelectedThread.MostRecentStackFrame;
if (context == null)
return;
var seg = context.NextStatement;
var expressionFinder = ParserService.GetExpressionFinder(seg.Filename);
var info = ParserService.GetParseInformation(seg.Filename);

3
src/AddIns/Debugger/Debugger.AddIn/Pads/LocalVarPad.cs

@ -65,7 +65,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -65,7 +65,8 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
using(new PrintTimes("Local Variables refresh")) {
try {
Utils.DoEvents(debuggedProcess);
foreach (var item in new StackFrameNode(debuggedProcess.SelectedStackFrame).ChildNodes) {
var frame = !debuggedProcess.IsInExternalCode ? debuggedProcess.SelectedStackFrame : debuggedProcess.SelectedThread.MostRecentStackFrame;
foreach (var item in new StackFrameNode(frame).ChildNodes) {
localVarList.WatchItems.Add(item);
}
}

16
src/AddIns/Debugger/Debugger.AddIn/Pads/WatchPad.cs

@ -170,25 +170,25 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads @@ -170,25 +170,25 @@ namespace ICSharpCode.SharpDevelop.Gui.Pads
Utils.DoEvents(debuggedProcess);
List<TreeNode> nodes = new List<TreeNode>();
foreach (var nod in watchList.WatchItems) {
foreach (var node in watchList.WatchItems) {
try {
LoggingService.Info("Evaluating: " + (string.IsNullOrEmpty(nod.Name) ? "is null or empty!" : nod.Name));
var nodExpression = debugger.GetExpression(nod.Name);
LoggingService.Info("Evaluating: " + (string.IsNullOrEmpty(node.Name) ? "is null or empty!" : node.Name));
var nodExpression = debugger.GetExpression(node.Name);
//Value val = ExpressionEvaluator.Evaluate(nod.Name, nod.Language, debuggedProcess.SelectedStackFrame);
ExpressionNode valNode = new ExpressionNode(null, nod.Name, nodExpression);
ExpressionNode valNode = new ExpressionNode(null, node.Name, nodExpression);
nodes.Add(valNode);
}
catch (GetValueException) {
string error = String.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Watch.InvalidExpression}"), nod.Name);
ErrorInfoNode infoNode = new ErrorInfoNode(nod.Name, error);
string error = String.Format(StringParser.Parse("${res:MainWindow.Windows.Debug.Watch.InvalidExpression}"), node.Name);
ErrorInfoNode infoNode = new ErrorInfoNode(node.Name, error);
nodes.Add(infoNode);
}
}
// rebuild list
watchList.WatchItems.Clear();
foreach (var nod in nodes)
watchList.WatchItems.Add(nod);
foreach (var node in nodes)
watchList.WatchItems.Add(node);
}
catch(AbortedBecauseDebuggeeResumedException) { }
catch(Exception ex) {

253
src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs

@ -2,6 +2,7 @@ @@ -2,6 +2,7 @@
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
@ -15,8 +16,11 @@ using Debugger; @@ -15,8 +16,11 @@ using Debugger;
using Debugger.AddIn.Tooltips;
using Debugger.AddIn.TreeModel;
using Debugger.Interop.CorPublish;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.Core.WinForms;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
@ -25,6 +29,7 @@ using ICSharpCode.SharpDevelop.Debugging; @@ -25,6 +29,7 @@ using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Gui.OptionPanels;
using ICSharpCode.SharpDevelop.Project;
using Mono.Cecil;
using Process = Debugger.Process;
namespace ICSharpCode.SharpDevelop.Services
@ -81,6 +86,15 @@ namespace ICSharpCode.SharpDevelop.Services @@ -81,6 +86,15 @@ namespace ICSharpCode.SharpDevelop.Services
set;
}
public bool IsInExternalCode {
get {
if (debuggedProcess == null)
return true;
return debuggedProcess.IsInExternalCode;
}
}
protected virtual void OnProcessSelected(ProcessEventArgs e)
{
if (ProcessSelected != null) {
@ -210,6 +224,8 @@ namespace ICSharpCode.SharpDevelop.Services @@ -210,6 +224,8 @@ namespace ICSharpCode.SharpDevelop.Services
DebugStarting(this, EventArgs.Empty);
try {
// set the JIT flag for evaluating optimized code
Process.DebugMode = DebugModeFlag.Debug;
Process process = debugger.Start(processStartInfo.FileName,
processStartInfo.WorkingDirectory,
processStartInfo.Arguments);
@ -271,6 +287,8 @@ namespace ICSharpCode.SharpDevelop.Services @@ -271,6 +287,8 @@ namespace ICSharpCode.SharpDevelop.Services
DebugStarting(this, EventArgs.Empty);
try {
// set the JIT flag for evaluating optimized code
Process.DebugMode = DebugModeFlag.Debug;
Process process = debugger.Attach(existingProcess);
attached = true;
SelectProcess(process);
@ -444,16 +462,59 @@ namespace ICSharpCode.SharpDevelop.Services @@ -444,16 +462,59 @@ namespace ICSharpCode.SharpDevelop.Services
// Stepping:
SourceCodeMapping GetCurrentCodeMapping(out bool isMatch)
{
var frame = debuggedProcess.SelectedThread.MostRecentStackFrame;
int typeToken = frame.MethodInfo.DeclaringType.MetadataToken;
int methodToken = frame.MethodInfo.MetadataToken;
DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation[typeToken];
isMatch = false;
if (debugInformation == null)
return null;
// get the mapped instruction from the current line marker or the next one
if (!debugInformation.CodeMappings.ContainsKey(methodToken))
return null;
var mappings = (List<MemberMapping>)debugInformation.CodeMappings[methodToken];
return mappings.GetInstructionByTokenAndOffset(methodToken, frame.IP, out isMatch);
}
Debugger.StackFrame GetStackFrame()
{
bool isMatch;
Debugger.StackFrame frame = debuggedProcess.SelectedThread.MostRecentStackFrame;
var map = GetCurrentCodeMapping(out isMatch);
if (map == null) {
frame = debuggedProcess.SelectedThread.MostRecentStackFrame;
frame.ILRanges = new [] { 0, 1 };
} else {
frame.SourceCodeLine = map.SourceCodeLine;
frame.ILRanges = map.ToArray(isMatch);
}
return frame;
}
public void StepInto()
{
if (!IsDebugging) {
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepInto}");
return;
}
if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
var frame = debuggedProcess.SelectedStackFrame ?? debuggedProcess.SelectedThread.MostRecentStackFrame;
if (frame == null || debuggedProcess.IsRunning) {
MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}");
} else {
debuggedProcess.SelectedStackFrame.AsyncStepInto();
if (IsInExternalCode) {
// get frame info from external code mappings
frame = GetStackFrame();
}
frame.AsyncStepInto();
}
}
@ -463,10 +524,17 @@ namespace ICSharpCode.SharpDevelop.Services @@ -463,10 +524,17 @@ namespace ICSharpCode.SharpDevelop.Services
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOver}");
return;
}
if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
var frame = debuggedProcess.SelectedStackFrame ?? debuggedProcess.SelectedThread.MostRecentStackFrame;
if (frame == null || debuggedProcess.IsRunning) {
MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepOver}");
} else {
debuggedProcess.SelectedStackFrame.AsyncStepOver();
if (IsInExternalCode) {
// get frame info from external code mappings
frame = GetStackFrame();
}
frame.AsyncStepOver();
}
}
@ -476,10 +544,17 @@ namespace ICSharpCode.SharpDevelop.Services @@ -476,10 +544,17 @@ namespace ICSharpCode.SharpDevelop.Services
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOut}");
return;
}
if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepOut}");
var frame = debuggedProcess.SelectedStackFrame ?? debuggedProcess.SelectedThread.MostRecentStackFrame;
if (frame == null || debuggedProcess.IsRunning) {
MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}");
} else {
debuggedProcess.SelectedStackFrame.AsyncStepOut();
if (IsInExternalCode) {
// get frame info from external code mappings
frame = GetStackFrame();
}
frame.AsyncStepOut();
}
}
@ -505,9 +580,19 @@ namespace ICSharpCode.SharpDevelop.Services @@ -505,9 +580,19 @@ namespace ICSharpCode.SharpDevelop.Services
if (!CanEvaluate) {
return null;
}
return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, debuggedProcess.SelectedStackFrame);
var stackFrame = !debuggedProcess.IsInExternalCode ? debuggedProcess.SelectedStackFrame : debuggedProcess.SelectedThread.MostRecentStackFrame;
try {
object data = GetLocalVariableIndex(stackFrame, variableName);
// evaluate expression
return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, stackFrame, data);
} catch {
throw;
}
}
/// <summary>
/// Gets Expression for given variable. Can throw GetValueException.
/// <exception cref="GetValueException">Thrown when getting expression fails. Exception message explains reason.</exception>
@ -550,7 +635,8 @@ namespace ICSharpCode.SharpDevelop.Services @@ -550,7 +635,8 @@ namespace ICSharpCode.SharpDevelop.Services
bool CanEvaluate
{
get {
return debuggedProcess != null && !debuggedProcess.IsRunning && debuggedProcess.SelectedStackFrame != null;
return debuggedProcess != null && !debuggedProcess.IsRunning &&
(debuggedProcess.SelectedStackFrame != null || debuggedProcess.SelectedThread.MostRecentStackFrame != null);
}
}
@ -566,7 +652,7 @@ namespace ICSharpCode.SharpDevelop.Services @@ -566,7 +652,7 @@ namespace ICSharpCode.SharpDevelop.Services
var image = ExpressionNode.GetImageForLocalVariable(out imageName);
ExpressionNode expressionNode = new ExpressionNode(image, variableName, tooltipExpression);
expressionNode.ImageName = imageName;
return new DebuggerTooltipControl(logicalPosition, expressionNode);
return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = !IsInExternalCode };
} catch (GetValueException) {
return null;
}
@ -662,7 +748,44 @@ namespace ICSharpCode.SharpDevelop.Services @@ -662,7 +748,44 @@ namespace ICSharpCode.SharpDevelop.Services
void AddBreakpoint(BreakpointBookmark bookmark)
{
Breakpoint breakpoint = debugger.Breakpoints.Add(bookmark.FileName, null, bookmark.LineNumber, 0, bookmark.IsEnabled);
Breakpoint breakpoint = null;
if (bookmark is DecompiledBreakpointBookmark) {
var dbb = (DecompiledBreakpointBookmark)bookmark;
var memberReference = dbb.MemberReference;
if (!DebuggerService.ExternalDebugInformation.ContainsKey(memberReference.MetadataToken.ToInt32()))
return;
// check if the codemappings exists for bookmark line
DecompileInformation data = (DecompileInformation)DebuggerService.ExternalDebugInformation[memberReference.MetadataToken.ToInt32()];
var storage = data.CodeMappings;
int token = 0;
foreach (var key in storage.Keys) {
var instruction = storage[key].GetInstructionByLineNumber(dbb.LineNumber, out token);
if (instruction == null) {
continue;
}
dbb.ILFrom = instruction.ILInstructionOffset.From;
dbb.ILTo = instruction.ILInstructionOffset.To;
breakpoint = new ILBreakpoint(
debugger,
dbb.MemberReference.FullName,
dbb.LineNumber,
memberReference.MetadataToken.ToInt32(),
instruction.MemberMapping.MetadataToken,
dbb.ILFrom,
dbb.IsEnabled);
debugger.Breakpoints.Add(breakpoint);
break;
}
} else {
breakpoint = debugger.Breakpoints.Add(bookmark.FileName, null, bookmark.LineNumber, 0, bookmark.IsEnabled);
}
MethodInvoker setBookmarkColor = delegate {
if (debugger.Processes.Count == 0) {
bookmark.IsHealthy = true;
@ -670,10 +793,13 @@ namespace ICSharpCode.SharpDevelop.Services @@ -670,10 +793,13 @@ namespace ICSharpCode.SharpDevelop.Services
} else if (!breakpoint.IsSet) {
bookmark.IsHealthy = false;
bookmark.Tooltip = "Breakpoint was not found in any loaded modules";
} else if (breakpoint.OriginalLocation.CheckSum == null) {
} else if (breakpoint.OriginalLocation == null || breakpoint.OriginalLocation.CheckSum == null) {
bookmark.IsHealthy = true;
bookmark.Tooltip = null;
} else {
if (!File.Exists(bookmark.FileName))
return;
byte[] fileMD5;
IEditable file = FileService.GetOpenFile(bookmark.FileName) as IEditable;
if (file != null) {
@ -805,12 +931,14 @@ namespace ICSharpCode.SharpDevelop.Services @@ -805,12 +931,14 @@ namespace ICSharpCode.SharpDevelop.Services
debuggedProcess.Paused -= debuggedProcess_DebuggingPaused;
debuggedProcess.ExceptionThrown -= debuggedProcess_ExceptionThrown;
debuggedProcess.Resumed -= debuggedProcess_DebuggingResumed;
debuggedProcess.ModulesAdded -= debuggedProcess_ModulesAdded;
}
debuggedProcess = process;
if (debuggedProcess != null) {
debuggedProcess.Paused += debuggedProcess_DebuggingPaused;
debuggedProcess.ExceptionThrown += debuggedProcess_ExceptionThrown;
debuggedProcess.Resumed += debuggedProcess_DebuggingResumed;
debuggedProcess.ModulesAdded += debuggedProcess_ModulesAdded;
debuggedProcess.BreakAtBeginning = BreakAtBeginning;
}
@ -821,6 +949,23 @@ namespace ICSharpCode.SharpDevelop.Services @@ -821,6 +949,23 @@ namespace ICSharpCode.SharpDevelop.Services
OnProcessSelected(new ProcessEventArgs(process));
}
void debuggedProcess_ModulesAdded(object sender, ModuleEventArgs e)
{
var currentModuleTypes = e.Module.GetNamesOfDefinedTypes();
foreach (var bookmark in DebuggerService.Breakpoints.OfType<DecompiledBreakpointBookmark>()) {
var breakpoint = debugger.Breakpoints.FirstOrDefault(
b => b is ILBreakpoint && b.Line == bookmark.LineNumber &&
((ILBreakpoint)b).MetadataToken == bookmark.MemberReference.MetadataToken.ToInt32());
if (breakpoint == null)
continue;
// set the breakpoint only if the module contains the type
if (!currentModuleTypes.Contains(breakpoint.TypeName))
continue;
breakpoint.SetBreakpoint(e.Module);
}
}
void debuggedProcess_DebuggingPaused(object sender, ProcessEventArgs e)
{
OnIsProcessRunningChanged(EventArgs.Empty);
@ -879,15 +1024,68 @@ namespace ICSharpCode.SharpDevelop.Services @@ -879,15 +1024,68 @@ namespace ICSharpCode.SharpDevelop.Services
public void JumpToCurrentLine()
{
if (debuggedProcess == null || debuggedProcess.SelectedThread == null)
return;
WorkbenchSingleton.MainWindow.Activate();
DebuggerService.RemoveCurrentLineMarker();
if (debuggedProcess != null) {
if (!IsInExternalCode) {
SourcecodeSegment nextStatement = debuggedProcess.NextStatement;
if (nextStatement != null) {
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
}
} else {
// use most recent stack frame because we don't have the symbols
var frame = debuggedProcess.SelectedThread.MostRecentStackFrame;
if (frame == null)
return;
int typeToken = frame.MethodInfo.DeclaringType.MetadataToken;
// get external data
object data = null;
DebuggerService.ExternalDebugInformation.TryGetValue(typeToken, out data);
DecompileInformation externalData = data as DecompileInformation;
int token = frame.MethodInfo.MetadataToken;
int ilOffset = frame.IP;
int line;
MemberReference memberReference;
if (externalData != null && externalData.CodeMappings.ContainsKey(token)) {
var mappings = externalData.CodeMappings[token];
if (mappings.GetInstructionByTokenAndOffset(token, ilOffset, out memberReference, out line)) {
DebuggerService.DebugStepInformation = null; // we do not need to step into/out
// update marker
var debugType = (DebugType)frame.MethodInfo.DeclaringType;
string fullTypeName = debugType.FullNameWithoutGenericArguments;
string shortTypeName = fullTypeName.Substring(fullTypeName.LastIndexOf('.') + 1);
string title = "[" + shortTypeName + "]";
foreach (var vc in WorkbenchSingleton.Workbench.ViewContentCollection.OfType<AbstractViewContentWithoutFile>()) {
if (string.Equals(vc.TitleName, title, StringComparison.OrdinalIgnoreCase)) {
CurrentLineBookmark.SetPosition(vc, line, 0, line, 0);
vc.WorkbenchWindow.SelectWindow();
return;
}
}
// the decompiled content was closed so we have to recreate it
StepIntoUnknownFrame(frame, token, ilOffset);
} else {
StepIntoUnknownFrame(frame, token, ilOffset);
}
} else {
StepIntoUnknownFrame(frame, token, ilOffset);
}
}
}
void StepIntoUnknownFrame(Debugger.StackFrame frame, int token, int ilOffset)
{
var debugType = (DebugType)frame.MethodInfo.DeclaringType;
string fullName = debugType.FullNameWithoutGenericArguments;
DebuggerService.DebugStepInformation = Tuple.Create(token, ilOffset);
NavigationService.NavigateTo(debugType.DebugModule.FullPath, debugType.FullNameWithoutGenericArguments, string.Empty);
}
StopAttachedProcessDialogResult ShowStopAttachedProcessDialog()
{
@ -906,5 +1104,32 @@ namespace ICSharpCode.SharpDevelop.Services @@ -906,5 +1104,32 @@ namespace ICSharpCode.SharpDevelop.Services
.Where(p => e.Item.Name.IndexOf(p.Name) >= 0)
.ForEach(p => e.Item.LoadSymbolsFromDisk(new []{ Path.GetDirectoryName(p.OutputAssemblyFullPath) }));
}
public static object GetLocalVariableIndex(Debugger.StackFrame stackFrame, string variableName)
{
// get the target name
int typeToken = stackFrame.MethodInfo.DeclaringType.MetadataToken;
int methodToken = stackFrame.MethodInfo.MetadataToken;
int index = variableName.IndexOf('.');
string targetName = variableName;
if (index != -1) {
targetName = variableName.Substring(0, index);
}
// get local variable index and store it in UserData property - used in evaluator
object data = null;
IEnumerable<ILVariable> list;
if (DebuggerService.ExternalDebugInformation == null || !DebuggerService.ExternalDebugInformation.ContainsKey(typeToken))
return null;
DecompileInformation externalData = (DecompileInformation)DebuggerService.ExternalDebugInformation[typeToken];
if (externalData.LocalVariables.TryGetValue(methodToken, out list)) {
var variable = list.FirstOrDefault(v => v.Name == targetName);
if (variable != null && variable.OriginalVariable != null) {
data = new[] { variable.OriginalVariable.Index };
}
}
return data;
}
}
}

6
src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs

@ -23,15 +23,15 @@ namespace Debugger.AddIn.Tooltips @@ -23,15 +23,15 @@ namespace Debugger.AddIn.Tooltips
public DebuggerPopup(DebuggerTooltipControl parentControl, Location logicalPosition, bool showPins = true)
{
this.innerControl = new DebuggerTooltipControl(parentControl, logicalPosition, showPins);
this.innerControl = new DebuggerTooltipControl(parentControl, logicalPosition) { ShowPins = showPins };
this.innerControl.containingPopup = this;
this.Child = this.innerControl;
this.IsLeaf = false;
//this.KeyDown += new KeyEventHandler(DebuggerPopup_KeyDown);
//this.contentControl.Focusable = true;
//Keyboard.Focus(this.contentControl);
//this.innerControl.Focusable = true;
//Keyboard.Focus(this.innerControl);
//this.AllowsTransparency = true;
//this.PopupAnimation = PopupAnimation.Slide;
}

28
src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs

@ -58,11 +58,10 @@ namespace Debugger.AddIn.Tooltips @@ -58,11 +58,10 @@ namespace Debugger.AddIn.Tooltips
this.itemsSource = nodes;
}
public DebuggerTooltipControl(DebuggerTooltipControl parentControl, Location logicalPosition, bool showPins = true)
public DebuggerTooltipControl(DebuggerTooltipControl parentControl, Location logicalPosition)
: this(logicalPosition)
{
this.parentControl = parentControl;
this.showPins = showPins;
}
private void OnLoaded(object sender, RoutedEventArgs e)
@ -82,6 +81,11 @@ namespace Debugger.AddIn.Tooltips @@ -82,6 +81,11 @@ namespace Debugger.AddIn.Tooltips
}
}
public bool ShowPins {
get { return showPins; }
set { showPins = value; }
}
public IEnumerable<ITreeNode> ItemsSource {
get { return this.itemsSource; }
}
@ -95,8 +99,16 @@ namespace Debugger.AddIn.Tooltips @@ -95,8 +99,16 @@ namespace Debugger.AddIn.Tooltips
this.itemsSource.ForEach(item => observable.Add(item));
// verify if at the line of the root there's a pin bookmark
ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
var editor = provider.TextEditor;
ITextEditor editor;
var viewContent = WorkbenchSingleton.Workbench.ActiveViewContent;
ITextEditorProvider provider = viewContent as ITextEditorProvider;
if (provider != null) {
editor = provider.TextEditor;
} else {
dynamic codeView = viewContent.Control;
editor = codeView.TextEditor as ITextEditor;
}
if (editor != null) {
var pin = BookmarkManager.Bookmarks.Find(
b => b is PinBookmark &&
@ -208,7 +220,7 @@ namespace Debugger.AddIn.Tooltips @@ -208,7 +220,7 @@ namespace Debugger.AddIn.Tooltips
// open child Popup
if (this.childPopup == null) {
this.childPopup = new DebuggerPopup(this, logicalPosition);
this.childPopup = new DebuggerPopup(this, logicalPosition, showPins);
this.childPopup.Placement = PlacementMode.Absolute;
}
if (this.containingPopup != null) {
@ -217,8 +229,10 @@ namespace Debugger.AddIn.Tooltips @@ -217,8 +229,10 @@ namespace Debugger.AddIn.Tooltips
this.childPopup.IsLeaf = true;
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet;
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet;
this.childPopup.ItemsSource = clickedNode.ChildNodes;
this.childPopup.Open();
if (clickedNode.ChildNodes != null) {
this.childPopup.ItemsSource = clickedNode.ChildNodes;
this.childPopup.Open();
}
} else {
CloseChildPopups();
}

6
src/AddIns/Debugger/Debugger.AddIn/Tooltips/PinDebuggerControl.xaml.cs

@ -207,8 +207,10 @@ namespace Debugger.AddIn.Tooltips @@ -207,8 +207,10 @@ namespace Debugger.AddIn.Tooltips
this.childPopup.IsLeaf = true;
this.childPopup.HorizontalOffset = buttonPos.X + ChildPopupOpenXOffet;
this.childPopup.VerticalOffset = buttonPos.Y + ChildPopupOpenYOffet;
this.childPopup.ItemsSource = clickedNode.ChildNodes;
this.childPopup.Open();
if (clickedNode.ChildNodes != null) {
this.childPopup.ItemsSource = clickedNode.ChildNodes;
this.childPopup.Open();
}
} else {
}

15
src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs

@ -5,13 +5,17 @@ using System; @@ -5,13 +5,17 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Debugger.AddIn.Visualizers;
using Debugger.MetaData;
using ICSharpCode.Core;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging;
@ -154,6 +158,17 @@ namespace Debugger.AddIn.TreeModel @@ -154,6 +158,17 @@ namespace Debugger.AddIn.TreeModel
Value val;
try {
var process = WindowsDebugger.DebuggedProcess;
var context = !process.IsInExternalCode ? process.SelectedStackFrame : process.SelectedThread.MostRecentStackFrame;
object data = WindowsDebugger.GetLocalVariableIndex(WindowsDebugger.DebuggedProcess.SelectedThread.MostRecentStackFrame, Name);
if (expression is MemberReferenceExpression) {
var memberExpression = (MemberReferenceExpression)expression;
memberExpression.TargetObject.UserData = data;
} else {
expression.UserData = data;
}
// evaluate expression
val = expression.Evaluate(WindowsDebugger.DebuggedProcess);
} catch (GetValueException e) {
error = e;

90
src/AddIns/Debugger/Debugger.Core/Breakpoint.cs

@ -21,7 +21,7 @@ namespace Debugger @@ -21,7 +21,7 @@ namespace Debugger
SourcecodeSegment originalLocation;
List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>();
protected List<ICorDebugFunctionBreakpoint> corBreakpoints = new List<ICorDebugFunctionBreakpoint>();
public event EventHandler<BreakpointEventArgs> Hit;
public event EventHandler<BreakpointEventArgs> Set;
@ -29,6 +29,7 @@ namespace Debugger @@ -29,6 +29,7 @@ namespace Debugger
[Debugger.Tests.Ignore]
public NDebugger Debugger {
get { return debugger; }
protected set { debugger = value; }
}
public string FileName {
@ -62,12 +63,16 @@ namespace Debugger @@ -62,12 +63,16 @@ namespace Debugger
get { return originalLocation; }
}
public bool IsSet {
get {
public bool IsSet {
get {
return corBreakpoints.Count > 0;
}
}
public string TypeName {
get; protected set;
}
protected virtual void OnHit(BreakpointEventArgs e)
{
if (Hit != null) {
@ -88,6 +93,8 @@ namespace Debugger @@ -88,6 +93,8 @@ namespace Debugger
}
}
public Breakpoint() { }
public Breakpoint(NDebugger debugger, ICorDebugFunctionBreakpoint corBreakpoint)
{
this.debugger = debugger;
@ -104,7 +111,7 @@ namespace Debugger @@ -104,7 +111,7 @@ namespace Debugger
this.enabled = enabled;
}
internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint)
internal bool IsOwnerOf(ICorDebugBreakpoint breakpoint)
{
foreach(ICorDebugFunctionBreakpoint corFunBreakpoint in corBreakpoints) {
if (((ICorDebugBreakpoint)corFunBreakpoint).Equals(breakpoint)) return true;
@ -116,18 +123,18 @@ namespace Debugger @@ -116,18 +123,18 @@ namespace Debugger
{
foreach(ICorDebugFunctionBreakpoint corBreakpoint in corBreakpoints) {
#if DEBUG
// Get repro
corBreakpoint.Activate(0);
// Get repro
corBreakpoint.Activate(0);
#else
try {
corBreakpoint.Activate(0);
} catch(COMException e) {
// Sometimes happens, but we had not repro yet.
// 0x80131301: Process was terminated.
if ((uint)e.ErrorCode == 0x80131301)
continue;
throw;
}
try {
corBreakpoint.Activate(0);
} catch(COMException e) {
// Sometimes happens, but we had not repro yet.
// 0x80131301: Process was terminated.
if ((uint)e.ErrorCode == 0x80131301)
continue;
throw;
}
#endif
}
corBreakpoints.Clear();
@ -138,9 +145,9 @@ namespace Debugger @@ -138,9 +145,9 @@ namespace Debugger
corBreakpoints.Clear();
}
internal bool SetBreakpoint(Module module)
public virtual bool SetBreakpoint(Module module)
{
if (this.fileName == null)
if (this.fileName == null)
return false;
SourcecodeSegment segment = SourcecodeSegment.Resolve(module, FileName, CheckSum, Line, Column);
@ -165,20 +172,57 @@ namespace Debugger @@ -165,20 +172,57 @@ namespace Debugger
}
}
public class ILBreakpoint : Breakpoint
{
public ILBreakpoint(NDebugger debugger, string typeName, int line, int metadataToken, int memberToken, int offset, bool enabled)
{
this.Debugger = debugger;
this.Line = line;
this.TypeName = typeName;
this.MetadataToken = metadataToken;
this.MemberMetadataToken = memberToken;
this.ILOffset = offset;
this.Enabled = enabled;
}
public int MetadataToken { get; private set; }
public int MemberMetadataToken { get; private set; }
public int ILOffset { get; private set; }
public override bool SetBreakpoint(Module module)
{
SourcecodeSegment segment = SourcecodeSegment.CreateForIL(module, this.Line, MemberMetadataToken, ILOffset);
if (segment == null)
return false;
try {
ICorDebugFunctionBreakpoint corBreakpoint = segment.CorFunction.GetILCode().CreateBreakpoint((uint)segment.ILStart);
corBreakpoint.Activate(Enabled ? 1 : 0);
corBreakpoints.Add(corBreakpoint);
OnSet(new BreakpointEventArgs(this));
return true;
} catch
#if DEBUG
(System.Exception ex)
#endif
{
return false;
}
}
}
[Serializable]
public class BreakpointEventArgs : DebuggerEventArgs
{
Breakpoint breakpoint;
public Breakpoint Breakpoint {
get {
return breakpoint;
}
get; private set;
}
public BreakpointEventArgs(Breakpoint breakpoint): base(breakpoint.Debugger)
{
this.breakpoint = breakpoint;
this.Breakpoint = breakpoint;
}
}
}

18
src/AddIns/Debugger/Debugger.Core/BreakpointCollection.cs

@ -54,6 +54,12 @@ namespace Debugger @@ -54,6 +54,12 @@ namespace Debugger
{
foreach(Process process in this.Debugger.Processes) {
foreach(Module module in process.Modules) {
if (breakpoint is ILBreakpoint) {
var currentModuleTypes = module.GetNamesOfDefinedTypes();
// set the breakpoint only if the module contains the type
if (!currentModuleTypes.Contains(breakpoint.TypeName))
continue;
}
breakpoint.SetBreakpoint(module);
}
}
@ -73,15 +79,23 @@ namespace Debugger @@ -73,15 +79,23 @@ namespace Debugger
base.OnRemoved(breakpoint);
}
internal void SetInModule(Module module)
internal void SetInModule(Module module)
{
// This is in case that the client modifies the collection as a response to set breakpoint
// NB: If client adds new breakpoint, it will be set directly as a result of his call, not here (because module is already loaded)
List<Breakpoint> collection = new List<Breakpoint>();
collection.AddRange(this);
var currentModuleTypes = module.GetNamesOfDefinedTypes();
foreach (Breakpoint b in collection) {
b.SetBreakpoint(module);
if (b is ILBreakpoint) {
// set the breakpoint only if the module contains the type
if (!currentModuleTypes.Contains(b.TypeName))
continue;
b.SetBreakpoint(module);
} else {
b.SetBreakpoint(module);
}
}
}
}

8
src/AddIns/Debugger/Debugger.Core/Interop/CorDebug.cs

@ -62,6 +62,14 @@ namespace Debugger.Interop.CorDebug @@ -62,6 +62,14 @@ namespace Debugger.Interop.CorDebug
CHAIN_SECURITY = 4,
CHAIN_THREAD_START = 0x40
}
[Flags]
public enum CorDebugJITCompilerFlags
{
CORDEBUG_JIT_DEFAULT = 0x1,
CORDEBUG_JIT_DISABLE_OPTIMIZATION = 0x3,
CORDEBUG_JIT_ENABLE_ENC = 0x7
}
[ComImport, TypeLibType((short) 2), Guid("6FEF44D0-39E7-4C77-BE8E-C9F8CF988630"), ClassInterface((short) 0)]
public class CorDebugClass : ICorDebug, CorDebug

11
src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs

@ -320,6 +320,17 @@ namespace Debugger @@ -320,6 +320,17 @@ namespace Debugger
EnterCallback(PausedReason.Other, "CreateProcess", pProcess);
// Process is added in NDebugger.Start
// disable NGen
if (!this.process.Options.EnableJustMyCode && !this.process.Options.StepOverNoSymbols) {
ICorDebugProcess2 pProcess2 = pProcess as ICorDebugProcess2;
if (pProcess2 != null && Process.DebugMode == DebugModeFlag.Debug) {
try {
pProcess2.SetDesiredNGENCompilerFlags((uint)CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION);
} catch (COMException) {
// we cannot set the NGEN flag => no evaluation for optimized code.
}
}
}
ExitCallback();
}

56
src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs

@ -68,7 +68,7 @@ namespace Debugger.MetaData @@ -68,7 +68,7 @@ namespace Debugger.MetaData
sb.Append(this.ReturnType.Name);
sb.Append(" ");
} else {
sb.Append("void ");
sb.Append("System.Void ");
}
sb.Append(this.DeclaringType.FullName);
@ -80,7 +80,7 @@ namespace Debugger.MetaData @@ -80,7 +80,7 @@ namespace Debugger.MetaData
if (!first)
sb.Append(", ");
first = false;
sb.Append(p.ParameterType.Name);
sb.Append(p.ParameterType.FullName);
sb.Append(" ");
sb.Append(p.Name);
}
@ -89,6 +89,37 @@ namespace Debugger.MetaData @@ -89,6 +89,37 @@ namespace Debugger.MetaData
}
}
/// <summary> Name including the declaring type, return type without parameters names</summary>
public string FullNameWithoutParameterNames {
get {
StringBuilder sb = new StringBuilder();
if (this.IsStatic) {
sb.Append("static ");
}
if (this.ReturnType != null) {
sb.Append(this.ReturnType.Name);
sb.Append(" ");
} else {
sb.Append("System.Void ");
}
sb.Append(this.DeclaringType.FullName);
sb.Append(".");
sb.Append(this.Name);
sb.Append("(");
bool first = true;
foreach(DebugParameterInfo p in GetParameters()) {
if (!first)
sb.Append(", ");
first = false;
sb.Append(p.ParameterType.FullName);
}
sb.Append(")");
return sb.ToString();
}
}
/// <inheritdoc/>
public override string Name {
get { return methodProps.Name; }
@ -343,12 +374,12 @@ namespace Debugger.MetaData @@ -343,12 +374,12 @@ namespace Debugger.MetaData
uint token = 0;
bool success =
(Read(code, 0x00) || true) && // nop || nothing
(Read(code, 0x00) || true) && // nop || nothing
(Read(code, 0x02, 0x7B) || Read(code, 0x7E)) && // ldarg.0; ldfld || ldsfld
ReadToken(code, ref token) && // <field token>
(Read(code, 0x0A, 0x2B, 0x00, 0x06) || true) && // stloc.0; br.s; offset+00; ldloc.0 || nothing
Read(code, 0x2A); // ret
if (!success) return;
if (this.Process.Options.Verbose) {
@ -427,7 +458,7 @@ namespace Debugger.MetaData @@ -427,7 +458,7 @@ namespace Debugger.MetaData
// Look on the method
DebugType.IsDefined(
this,
false,
false,
typeof(System.Diagnostics.DebuggerStepThroughAttribute),
typeof(System.Diagnostics.DebuggerNonUserCodeAttribute),
typeof(System.Diagnostics.DebuggerHiddenAttribute))
@ -435,7 +466,7 @@ namespace Debugger.MetaData @@ -435,7 +466,7 @@ namespace Debugger.MetaData
// Look on the type
DebugType.IsDefined(
declaringType,
false,
false,
typeof(System.Diagnostics.DebuggerStepThroughAttribute),
typeof(System.Diagnostics.DebuggerNonUserCodeAttribute),
typeof(System.Diagnostics.DebuggerHiddenAttribute));
@ -464,6 +495,19 @@ namespace Debugger.MetaData @@ -464,6 +495,19 @@ namespace Debugger.MetaData
}
}
public static Value GetLocalVariableValue(StackFrame context, int varIndex)
{
ICorDebugValue corVal;
try {
corVal = context.CorILFrame.GetLocalVariable((uint)varIndex);
} catch (COMException e) {
if ((uint)e.ErrorCode == 0x80131304) throw new GetValueException("Unavailable in optimized code");
// show the message in case of bad index
throw new GetValueException(e.Message);
}
return new Value(context.AppDomain, corVal);
}
public DebugLocalVariableInfo GetLocalVariable(int offset, string name)
{
foreach(DebugLocalVariableInfo loc in GetLocalVariables(offset)) {

101
src/AddIns/Debugger/Debugger.Core/Module.cs

@ -93,22 +93,22 @@ namespace Debugger @@ -93,22 +93,22 @@ namespace Debugger
}
[Debugger.Tests.Ignore]
public ulong BaseAdress {
public ulong BaseAdress {
get {
return this.CorModule.GetBaseAddress();
}
}
}
public bool IsDynamic {
public bool IsDynamic {
get {
return this.CorModule.IsDynamic() == 1;
}
}
}
public bool IsInMemory {
public bool IsInMemory {
get {
return this.CorModule.IsInMemory() == 1;
}
}
}
internal uint AppDomainID {
@ -124,19 +124,19 @@ namespace Debugger @@ -124,19 +124,19 @@ namespace Debugger
}
[Debugger.Tests.Ignore]
public string FullPath {
public string FullPath {
get {
return fullPath;
}
}
}
public bool HasSymbols {
public bool HasSymbols {
get {
return symReader != null;
}
}
}
public int OrderOfLoading {
public int OrderOfLoading {
get {
return orderOfLoading;
}
@ -145,6 +145,22 @@ namespace Debugger @@ -145,6 +145,22 @@ namespace Debugger
}
}
[Debugger.Tests.Ignore]
public CorDebugJITCompilerFlags JITCompilerFlags
{
get
{
uint retval = ((ICorDebugModule2)corModule).GetJITCompilerFlags();
return (CorDebugJITCompilerFlags)retval;
}
set
{
// ICorDebugModule2.SetJITCompilerFlags can return successful HRESULTS other than S_OK.
// Since we have asked the COMInterop layer to preservesig, we need to marshal any failing HRESULTS.
((ICorDebugModule2)corModule).SetJITCompilerFlags((uint)value);
}
}
/// <summary> Returns all non-generic types defined in the module </summary>
/// <remarks> Generic types can not be returned, because we do not know how to instanciate them </remarks>
public List<DebugType> GetDefinedTypes()
@ -183,6 +199,8 @@ namespace Debugger @@ -183,6 +199,8 @@ namespace Debugger
name = System.IO.Path.GetFileName(FullPath);
}
SetJITCompilerFlags();
LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths);
ResetJustMyCodeStatus();
}
@ -268,7 +286,7 @@ namespace Debugger @@ -268,7 +286,7 @@ namespace Debugger
}
}
void SetBreakpoints()
void SetBreakpoints()
{
if (this.HasSymbols) {
// This is in case that the client modifies the collection as a response to set breakpoint
@ -276,12 +294,46 @@ namespace Debugger @@ -276,12 +294,46 @@ namespace Debugger
List<Breakpoint> collection = new List<Breakpoint>();
collection.AddRange(this.Debugger.Breakpoints);
var currentModuleTypes = this.GetNamesOfDefinedTypes();
foreach (Breakpoint b in collection) {
if (b is ILBreakpoint) {
// set the breakpoint only if the module contains the type
if (!currentModuleTypes.Contains(b.TypeName))
continue;
}
b.SetBreakpoint(this);
}
}
}
void SetJITCompilerFlags()
{
if (Process.DebugMode != DebugModeFlag.Default) {
// translate DebugModeFlags to JITCompilerFlags
CorDebugJITCompilerFlags jcf = MapDebugModeToJITCompilerFlags(Process.DebugMode);
try
{
this.JITCompilerFlags = jcf;
// Flags may succeed but not set all bits, so requery.
CorDebugJITCompilerFlags jcfActual = this.JITCompilerFlags;
#if DEBUG
if (jcf != jcfActual)
Console.WriteLine("Couldn't set all flags. Actual flags:" + jcfActual.ToString());
else
Console.WriteLine("Actual flags:" + jcfActual.ToString());
#endif
}
catch (COMException ex)
{
// we'll ignore the error if we cannot set the jit flags
Console.WriteLine(string.Format("Failed to set flags with hr=0x{0:x}", ex.ErrorCode));
}
}
}
/// <summary> Sets all code as being 'my code'. The code will be gradually
/// set to not-user-code as encountered acording to stepping options </summary>
public void ResetJustMyCodeStatus()
@ -293,7 +345,7 @@ namespace Debugger @@ -293,7 +345,7 @@ namespace Debugger
return;
}
try {
this.CorModule2.SetJMCStatus(1, 0, ref unused);
this.CorModule2.SetJMCStatus(process.Options.EnableJustMyCode ? 1 : 0, 0, ref unused);
} catch (COMException e) {
// Cannot use JMC on this code (likely wrong JIT settings).
if ((uint)e.ErrorCode == 0x80131323) {
@ -319,6 +371,29 @@ namespace Debugger @@ -319,6 +371,29 @@ namespace Debugger
{
return string.Format("{0}", this.Name);
}
public static CorDebugJITCompilerFlags MapDebugModeToJITCompilerFlags(DebugModeFlag debugMode)
{
CorDebugJITCompilerFlags jcf;
switch (debugMode)
{
case DebugModeFlag.Optimized:
jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DEFAULT; // DEFAULT really means force optimized.
break;
case DebugModeFlag.Debug:
jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION;
break;
case DebugModeFlag.Enc:
jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_ENABLE_ENC;
break;
default:
// we don't have mapping from default to "default",
// therefore we'll use DISABLE_OPTIMIZATION.
jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION;
break;
}
return jcf;
}
}
[Serializable]

344
src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs

@ -71,29 +71,40 @@ namespace ICSharpCode.NRefactory.Visitors @@ -71,29 +71,40 @@ namespace ICSharpCode.NRefactory.Visitors
/// <summary> Evaluate given expression. If you have expression tree already, use overloads of this method.</summary>
/// <returns> Returned value or null for statements </returns>
public static Value Evaluate(string code, SupportedLanguage language, StackFrame context)
public static Value Evaluate(string code, SupportedLanguage language, StackFrame context, object data = null)
{
return Evaluate(Parse(code, language), context);
return Evaluate(Parse(code, language), context, data);
}
public static Value Evaluate(INode code, Process context)
{
if (context.SelectedStackFrame != null) {
return Evaluate(code, context.SelectedStackFrame);
} else if (context.SelectedThread.MostRecentStackFrame != null ) {
return Evaluate(code, context.SelectedThread.MostRecentStackFrame);
} else {
StackFrame stackFrame = null;
if (context.SelectedStackFrame == null && context.SelectedThread.MostRecentStackFrame == null)
// This can happen when needed 'dll' is missing. This causes an exception dialog to be shown even before the applicaiton starts
throw new GetValueException("Can not evaluate because the process has no managed stack frames");
if (context.SelectedStackFrame != null) {
if (context.SelectedThread.MostRecentStackFrame != null) {
if (context.SelectedStackFrame.HasSymbols && context.SelectedThread.MostRecentStackFrame.HasSymbols)
stackFrame = context.SelectedStackFrame;
else
stackFrame = context.SelectedThread.MostRecentStackFrame;
} else {
stackFrame = context.SelectedThread.MostRecentStackFrame;
}
} else {
stackFrame = context.SelectedThread.MostRecentStackFrame;
}
return Evaluate(code, stackFrame);
}
public static Value Evaluate(INode code, StackFrame context)
public static Value Evaluate(INode code, StackFrame context, object data = null)
{
if (context == null) throw new ArgumentNullException("context");
if (context.IsInvalid) throw new DebuggerException("The context is no longer valid");
TypedValue val = new ExpressionEvaluator(context).Evaluate(code, false);
TypedValue val = new ExpressionEvaluator(context).Evaluate(code, false, data);
if (val == null)
return null;
return val.Value;
@ -164,7 +175,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -164,7 +175,7 @@ namespace ICSharpCode.NRefactory.Visitors
return Evaluate(expression, true);
}
TypedValue Evaluate(INode expression, bool permRef)
TypedValue Evaluate(INode expression, bool permRef, object data = null)
{
// Try to get the value from cache
// (the cache is cleared when the process is resumed)
@ -177,7 +188,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -177,7 +188,7 @@ namespace ICSharpCode.NRefactory.Visitors
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
try {
val = (TypedValue)expression.AcceptVisitor(this, null);
val = (TypedValue)expression.AcceptVisitor(this, data);
if (val != null && permRef)
val = new TypedValue(val.Value.GetPermanentReference(), val.Type);
} catch (GetValueException e) {
@ -277,21 +288,21 @@ namespace ICSharpCode.NRefactory.Visitors @@ -277,21 +288,21 @@ namespace ICSharpCode.NRefactory.Visitors
{
BinaryOperatorType op;
switch (assignmentExpression.Op) {
case AssignmentOperatorType.Assign: op = BinaryOperatorType.None; break;
case AssignmentOperatorType.Add: op = BinaryOperatorType.Add; break;
case AssignmentOperatorType.ConcatString: op = BinaryOperatorType.Concat; break;
case AssignmentOperatorType.Subtract: op = BinaryOperatorType.Subtract; break;
case AssignmentOperatorType.Multiply: op = BinaryOperatorType.Multiply; break;
case AssignmentOperatorType.Divide: op = BinaryOperatorType.Divide; break;
case AssignmentOperatorType.DivideInteger: op = BinaryOperatorType.DivideInteger; break;
case AssignmentOperatorType.ShiftLeft: op = BinaryOperatorType.ShiftLeft; break;
case AssignmentOperatorType.ShiftRight: op = BinaryOperatorType.ShiftRight; break;
case AssignmentOperatorType.ExclusiveOr: op = BinaryOperatorType.ExclusiveOr; break;
case AssignmentOperatorType.Modulus: op = BinaryOperatorType.Modulus; break;
case AssignmentOperatorType.BitwiseAnd: op = BinaryOperatorType.BitwiseAnd; break;
case AssignmentOperatorType.BitwiseOr: op = BinaryOperatorType.BitwiseOr; break;
case AssignmentOperatorType.Power: op = BinaryOperatorType.Power; break;
default: throw new GetValueException("Unknown operator " + assignmentExpression.Op);
case AssignmentOperatorType.Assign: op = BinaryOperatorType.None; break;
case AssignmentOperatorType.Add: op = BinaryOperatorType.Add; break;
case AssignmentOperatorType.ConcatString: op = BinaryOperatorType.Concat; break;
case AssignmentOperatorType.Subtract: op = BinaryOperatorType.Subtract; break;
case AssignmentOperatorType.Multiply: op = BinaryOperatorType.Multiply; break;
case AssignmentOperatorType.Divide: op = BinaryOperatorType.Divide; break;
case AssignmentOperatorType.DivideInteger: op = BinaryOperatorType.DivideInteger; break;
case AssignmentOperatorType.ShiftLeft: op = BinaryOperatorType.ShiftLeft; break;
case AssignmentOperatorType.ShiftRight: op = BinaryOperatorType.ShiftRight; break;
case AssignmentOperatorType.ExclusiveOr: op = BinaryOperatorType.ExclusiveOr; break;
case AssignmentOperatorType.Modulus: op = BinaryOperatorType.Modulus; break;
case AssignmentOperatorType.BitwiseAnd: op = BinaryOperatorType.BitwiseAnd; break;
case AssignmentOperatorType.BitwiseOr: op = BinaryOperatorType.BitwiseOr; break;
case AssignmentOperatorType.Power: op = BinaryOperatorType.Power; break;
default: throw new GetValueException("Unknown operator " + assignmentExpression.Op);
}
TypedValue right;
@ -382,6 +393,14 @@ namespace ICSharpCode.NRefactory.Visitors @@ -382,6 +393,14 @@ namespace ICSharpCode.NRefactory.Visitors
if (loc != null)
return new TypedValue(loc.GetValue(context), (DebugType)loc.LocalType);
// try get local var from external information - data or UserData
int[] localIndex = (data ?? identifierExpression.UserData) as int[];
if (localIndex != null) {
Value localValue = DebugMethodInfo.GetLocalVariableValue(context, localIndex[0]);
return new TypedValue(localValue, localValue.Type);
}
// Instance class members
// Note that the method might be generated instance method that represents anonymous method
TypedValue thisValue = GetThisValue();
@ -554,7 +573,14 @@ namespace ICSharpCode.NRefactory.Visitors @@ -554,7 +573,14 @@ namespace ICSharpCode.NRefactory.Visitors
DebugLocalVariableInfo thisVar = context.MethodInfo.GetLocalVariableThis();
if (thisVar != null)
return new TypedValue(thisVar.GetValue(context), (DebugType)thisVar.LocalType);
return null;
// when symbols are not present
try {
return new TypedValue(context.GetThisValue(), (DebugType)context.MethodInfo.DeclaringType);
} catch (GetValueException) {
// static method
return null;
}
}
public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data)
@ -640,7 +666,7 @@ namespace ICSharpCode.NRefactory.Visitors @@ -640,7 +666,7 @@ namespace ICSharpCode.NRefactory.Visitors
}
}
}
throw new EvaluateException(unaryOperatorExpression, "Can not use the unary operator {0} on type {1}", op.ToString(), value.Type.FullName);
}
@ -654,57 +680,57 @@ namespace ICSharpCode.NRefactory.Visitors @@ -654,57 +680,57 @@ namespace ICSharpCode.NRefactory.Visitors
if (argType == typeof(bool)) {
bool a = (bool)val;
switch (op) {
case UnaryOperatorType.Not: return !a;
case UnaryOperatorType.Not: return !a;
}
}
if (argType == typeof(float)) {
float a = (float)val;
switch (op) {
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
}
}
if (argType == typeof(double)) {
double a = (double)val;
switch (op) {
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
}
}
if (argType == typeof(int)) {
int a = (int)val;
switch (op) {
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
}
}
if (argType == typeof(uint)) {
uint a = (uint)val;
switch (op) {
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
}
}
if (argType == typeof(long)) {
long a = (long)val;
switch (op) {
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
case UnaryOperatorType.Minus: return -a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
}
}
if (argType == typeof(ulong)) {
ulong a = (ulong)val;
switch (op) {
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
case UnaryOperatorType.Plus: return +a;
case UnaryOperatorType.BitNot: return ~a;
}
}
}
@ -789,8 +815,8 @@ namespace ICSharpCode.NRefactory.Visitors @@ -789,8 +815,8 @@ namespace ICSharpCode.NRefactory.Visitors
// void operator +(ulong x, long y);
// float operator +(float x, float y);
// double operator +(double x, double y);
//
// &, |, ^, &&, ||
//
// &, |, ^, &&, ||
// ==, !=
// bool operator &(bool x, bool y);
@ -834,9 +860,9 @@ namespace ICSharpCode.NRefactory.Visitors @@ -834,9 +860,9 @@ namespace ICSharpCode.NRefactory.Visitors
string a = (string)left;
string b = (string)right;
switch (op) {
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.Add: return a + b;
}
}
@ -844,13 +870,13 @@ namespace ICSharpCode.NRefactory.Visitors @@ -844,13 +870,13 @@ namespace ICSharpCode.NRefactory.Visitors
bool a = (bool)left;
bool b = (bool)right;
switch (op) {
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.LogicalAnd: return a && b;
case BinaryOperatorType.LogicalOr: return a || b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.LogicalAnd: return a && b;
case BinaryOperatorType.LogicalOr: return a || b;
}
}
@ -858,19 +884,19 @@ namespace ICSharpCode.NRefactory.Visitors @@ -858,19 +884,19 @@ namespace ICSharpCode.NRefactory.Visitors
float a = (float)left;
float b = (float)right;
switch (op) {
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
}
}
@ -878,134 +904,134 @@ namespace ICSharpCode.NRefactory.Visitors @@ -878,134 +904,134 @@ namespace ICSharpCode.NRefactory.Visitors
double a = (double)left;
double b = (double)right;
switch (op) {
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(int)) {
switch (op) {
case BinaryOperatorType.ShiftLeft: return (int)left << (int)right;
case BinaryOperatorType.ShiftRight: return (int)left >> (int)right;
case BinaryOperatorType.ShiftLeft: return (int)left << (int)right;
case BinaryOperatorType.ShiftRight: return (int)left >> (int)right;
}
int a = (int)left;
int b = (int)right;
switch (op) {
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(uint)) {
switch (op) {
case BinaryOperatorType.ShiftLeft: return (uint)left << (int)right;
case BinaryOperatorType.ShiftRight: return (uint)left >> (int)right;
case BinaryOperatorType.ShiftLeft: return (uint)left << (int)right;
case BinaryOperatorType.ShiftRight: return (uint)left >> (int)right;
}
uint a = (uint)left;
uint b = (uint)right;
switch (op) {
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(long)) {
switch (op) {
case BinaryOperatorType.ShiftLeft: return (long)left << (int)right;
case BinaryOperatorType.ShiftRight: return (long)left >> (int)right;
case BinaryOperatorType.ShiftLeft: return (long)left << (int)right;
case BinaryOperatorType.ShiftRight: return (long)left >> (int)right;
}
long a = (long)left;
long b = (long)right;
switch (op) {
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(ulong)) {
switch (op) {
case BinaryOperatorType.ShiftLeft: return (ulong)left << (int)right;
case BinaryOperatorType.ShiftRight: return (ulong)left >> (int)right;
case BinaryOperatorType.ShiftLeft: return (ulong)left << (int)right;
case BinaryOperatorType.ShiftRight: return (ulong)left >> (int)right;
}
ulong a = (ulong)left;
ulong b = (ulong)right;
switch (op) {
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
case BinaryOperatorType.BitwiseAnd: return a & b;
case BinaryOperatorType.BitwiseOr: return a | b;
case BinaryOperatorType.ExclusiveOr: return a ^ b;
case BinaryOperatorType.GreaterThan: return a > b;
case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
case BinaryOperatorType.Equality: return a == b;
case BinaryOperatorType.InEquality: return a != b;
case BinaryOperatorType.LessThan: return a < b;
case BinaryOperatorType.LessThanOrEqual: return a <= b;
case BinaryOperatorType.Add: return a + b;
case BinaryOperatorType.Subtract: return a - b;
case BinaryOperatorType.Multiply: return a * b;
case BinaryOperatorType.Divide: return a / b;
case BinaryOperatorType.Modulus: return a % b;
case BinaryOperatorType.Concat: return a + b;
}
}
return null;
}
}

48
src/AddIns/Debugger/Debugger.Core/Process.cs

@ -14,6 +14,29 @@ namespace Debugger @@ -14,6 +14,29 @@ namespace Debugger
{
internal enum DebuggeeStateAction { Keep, Clear }
/// <summary>
/// Debug Mode Flags.
/// </summary>
public enum DebugModeFlag
{
/// <summary>
/// Run in the same mode as without debugger.
/// </summary>
Default,
/// <summary>
/// Run in forced optimized mode.
/// </summary>
Optimized,
/// <summary>
/// Run in debug mode (easy inspection) but slower.
/// </summary>
Debug,
/// <summary>
/// Run in ENC mode (ENC possible) but even slower than debug
/// </summary>
Enc
}
public class Process: DebuggerObject
{
NDebugger debugger;
@ -28,6 +51,7 @@ namespace Debugger @@ -28,6 +51,7 @@ namespace Debugger
string workingDirectory;
public NDebugger Debugger {
get { return debugger; }
}
@ -108,6 +132,23 @@ namespace Debugger @@ -108,6 +132,23 @@ namespace Debugger
get { return workingDirectory; }
}
public static DebugModeFlag DebugMode { get; set; }
public bool IsInExternalCode {
get {
if (SelectedStackFrame == null && SelectedThread.MostRecentStackFrame == null)
return true;
if (SelectedStackFrame == null && SelectedThread.MostRecentStackFrame != null)
return true;
if (SelectedThread.MostRecentStackFrame == null)
return true;
return !(SelectedStackFrame.HasSymbols && SelectedThread.MostRecentStackFrame.HasSymbols);
}
}
internal Process(NDebugger debugger, ICorDebugProcess corProcess, string workingDirectory)
{
this.debugger = debugger;
@ -666,13 +707,18 @@ namespace Debugger @@ -666,13 +707,18 @@ namespace Debugger
breakpoint.Remove();
breakpoint = null;
};
} catch {
} catch {
// the app does not have an entry point - COM exception
}
BreakAtBeginning = false;
}
if (ModulesAdded != null)
ModulesAdded(this, new ModuleEventArgs(e.Item));
}
#endregion
public event EventHandler<ModuleEventArgs> ModulesAdded;
}
}

61
src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs

@ -16,6 +16,7 @@ namespace Debugger @@ -16,6 +16,7 @@ namespace Debugger
Module module;
string filename;
string typename;
byte[] checkSum;
int startLine;
int startColumn;
@ -35,6 +36,10 @@ namespace Debugger @@ -35,6 +36,10 @@ namespace Debugger
get { return filename; }
}
public string Typename {
get { return typename; }
}
public byte[] CheckSum {
get { return checkSum; }
}
@ -99,7 +104,7 @@ namespace Debugger @@ -99,7 +104,7 @@ namespace Debugger
yield return symUrl;
yield break;
}
if (Path.IsPathRooted(symUrl)) {
Dictionary<string, object> returned = new Dictionary<string, object>();
@ -207,7 +212,7 @@ namespace Debugger @@ -207,7 +212,7 @@ namespace Debugger
segment.corFunction = module.CorModule.GetFunctionFromToken(symMethod.GetToken());
segment.ilStart = (int)sqPoint.Offset;
segment.ilEnd = (int)sqPoint.Offset;
segment.stepRanges = null;
segment.stepRanges = null;
return segment;
}
}
@ -336,7 +341,57 @@ namespace Debugger @@ -336,7 +341,57 @@ namespace Debugger
public override string ToString()
{
return string.Format("{0}:{1},{2}-{3},{4}", Path.GetFileName(this.Filename), this.startLine, this.startColumn, this.endLine, this.endColumn);
return string.Format("{0}:{1},{2}-{3},{4}",
Path.GetFileName(this.Filename ?? string.Empty),
this.startLine, this.startColumn, this.endLine, this.endColumn);
}
#region Decompiled breakpoint
public static SourcecodeSegment CreateForIL(Module module, int line, int metadataToken, int iLOffset)
{
try {
SourcecodeSegment segment = new SourcecodeSegment();
segment.module = module;
segment.typename = null;
segment.checkSum = null;
segment.startLine = line;
segment.startColumn = 0;
segment.endLine = line;
segment.endColumn = 0;
segment.corFunction = module.CorModule.GetFunctionFromToken((uint)metadataToken);
segment.ilStart = iLOffset;
segment.ilEnd = iLOffset;
segment.stepRanges = null;
return segment;
} catch {
return null;
}
}
public static SourcecodeSegment ResolveForIL(Module module, ICorDebugFunction corFunction, int line, int offset, int[] ranges)
{
try {
SourcecodeSegment segment = new SourcecodeSegment();
segment.module = module;
segment.typename = null;
segment.checkSum = null;
segment.startLine = line;
segment.startColumn = 0;
segment.endLine = line;
segment.endColumn = 0;
segment.corFunction = corFunction;
segment.ilStart = offset;
segment.ilEnd = ranges[1];
segment.stepRanges = ranges;
return segment;
} catch {
return null;
}
}
#endregion
}
}

40
src/AddIns/Debugger/Debugger.Core/StackFrame.cs

@ -16,7 +16,7 @@ namespace Debugger @@ -16,7 +16,7 @@ namespace Debugger
/// Use to obtain arguments or local variables.
/// </summary>
public class StackFrame: DebuggerObject
{
{
Thread thread;
AppDomain appDomain;
Process process;
@ -59,11 +59,11 @@ namespace Debugger @@ -59,11 +59,11 @@ namespace Debugger
}
/// <summary> True if the stack frame has symbols defined.
/// <summary> True if the stack frame has symbols defined.
/// (That is has accesss to the .pdb file) </summary>
public bool HasSymbols {
get {
return GetSegmentForOffet(0) != null;
return GetSegmentForOffset(0) != null;
}
}
@ -139,8 +139,14 @@ namespace Debugger @@ -139,8 +139,14 @@ namespace Debugger
}
}
SourcecodeSegment GetSegmentForOffet(int offset)
public int[] ILRanges { get; set; }
public int SourceCodeLine { get; set; }
SourcecodeSegment GetSegmentForOffset(int offset)
{
if (SourceCodeLine != 0)
return SourcecodeSegment.ResolveForIL(this.MethodInfo.DebugModule, corFunction, SourceCodeLine, offset, ILRanges);
return SourcecodeSegment.Resolve(this.MethodInfo.DebugModule, corFunction, offset);
}
@ -187,17 +193,23 @@ namespace Debugger @@ -187,17 +193,23 @@ namespace Debugger
void AsyncStep(bool stepIn)
{
if (this.MethodInfo.DebugModule.HasSymbols == false) {
if (this.MethodInfo.DebugModule.HasSymbols == false && process.Options.StepOverNoSymbols) {
throw new DebuggerException("Unable to step. No symbols loaded.");
}
SourcecodeSegment nextSt = NextStatement;
if (nextSt == null) {
throw new DebuggerException("Unable to step. Next statement not aviable");
int[] stepRanges;
if (ILRanges == null) {
SourcecodeSegment nextSt = NextStatement;
if (nextSt == null) {
throw new DebuggerException("Unable to step. Next statement not aviable");
}
stepRanges = nextSt.StepRanges;
} else {
stepRanges = ILRanges;
}
if (stepIn) {
Stepper stepInStepper = Stepper.StepIn(this, nextSt.StepRanges, "normal");
Stepper stepInStepper = Stepper.StepIn(this, stepRanges, "normal");
this.Thread.CurrentStepIn = stepInStepper;
Stepper clearCurrentStepIn = Stepper.StepOut(this, "clear current step in");
clearCurrentStepIn.StepComplete += delegate {
@ -207,7 +219,7 @@ namespace Debugger @@ -207,7 +219,7 @@ namespace Debugger
};
clearCurrentStepIn.Ignore = true;
} else {
Stepper.StepOver(this, nextSt.StepRanges, "normal");
Stepper.StepOver(this, stepRanges, "normal");
}
AsyncContinue();
@ -229,7 +241,7 @@ namespace Debugger @@ -229,7 +241,7 @@ namespace Debugger
/// </summary>
public SourcecodeSegment NextStatement {
get {
return GetSegmentForOffet(IP);
return GetSegmentForOffset(IP);
}
}
@ -276,7 +288,7 @@ namespace Debugger @@ -276,7 +288,7 @@ namespace Debugger
return null;
}
/// <summary>
/// <summary>
/// Gets the instance of the class asociated with the current frame.
/// That is, 'this' in C#.
/// Note that for delegates and enumerators this returns the instance of the display class.
@ -397,8 +409,8 @@ namespace Debugger @@ -397,8 +409,8 @@ namespace Debugger
{
int hashCode = 0;
unchecked {
if (thread != null) hashCode += 1000000009 * thread.GetHashCode();
if (methodInfo != null) hashCode += 1000000093 * methodInfo.GetHashCode();
if (thread != null) hashCode += 1000000009 * thread.GetHashCode();
if (methodInfo != null) hashCode += 1000000093 * methodInfo.GetHashCode();
hashCode += 1000000097 * chainIndex.GetHashCode();
hashCode += 1000000103 * frameIndex.GetHashCode();
}

6
src/AddIns/DisplayBindings/AvalonEdit.AddIn/AvalonEdit.AddIn.csproj

@ -43,6 +43,7 @@ @@ -43,6 +43,7 @@
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup>
<Reference Include="Microsoft.CSharp" />
<Reference Include="PresentationCore">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
@ -191,6 +192,11 @@ @@ -191,6 +192,11 @@
<Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\NRefactory\Project\NRefactory.csproj">
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name>

11
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs

@ -31,11 +31,20 @@ using ICSharpCode.SharpDevelop.Editor.CodeCompletion; @@ -31,11 +31,20 @@ using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
namespace ICSharpCode.AvalonEdit.AddIn
{
public interface ICodeEditor
{
TextDocument Document { get; }
void Redraw(ISegment segment, DispatcherPriority priority);
event EventHandler DocumentChanged;
}
/// <summary>
/// Integrates AvalonEdit with SharpDevelop.
/// Also provides support for Split-View (showing two AvalonEdit instances using the same TextDocument)
/// </summary>
public class CodeEditor : Grid, IDisposable
public class CodeEditor : Grid, IDisposable, ICodeEditor
{
const string contextMenuPath = "/SharpDevelop/ViewContent/AvalonEdit/ContextMenu";

2
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarManager.cs

@ -40,7 +40,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -40,7 +40,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
public event EventHandler RedrawRequested;
internal void UpdateClassMemberBookmarks(ParseInformation parseInfo)
public void UpdateClassMemberBookmarks(ParseInformation parseInfo)
{
for (int i = bookmarks.Count - 1; i >= 0; i--) {
if (IsClassMemberBookmark(bookmarks[i]))

19
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs

@ -11,7 +11,10 @@ using ICSharpCode.AvalonEdit.Editing; @@ -11,7 +11,10 @@ using ICSharpCode.AvalonEdit.Editing;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.AvalonEdit.Utils;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Gui;
using Mono.Cecil;
namespace ICSharpCode.AvalonEdit.AddIn
{
@ -20,9 +23,9 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -20,9 +23,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
/// </summary>
public class IconBarMargin : AbstractMargin, IDisposable
{
readonly IconBarManager manager;
readonly IBookmarkMargin manager;
public IconBarMargin(IconBarManager manager)
public IconBarMargin(IBookmarkMargin manager)
{
if (manager == null)
throw new ArgumentNullException("manager");
@ -232,7 +235,17 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -232,7 +235,17 @@ namespace ICSharpCode.AvalonEdit.AddIn
// no bookmark on the line: create a new breakpoint
ITextEditor textEditor = TextView.Services.GetService(typeof(ITextEditor)) as ITextEditor;
if (textEditor != null) {
ICSharpCode.SharpDevelop.Debugging.DebuggerService.ToggleBreakpointAt(textEditor, line);
DebuggerService.ToggleBreakpointAt(textEditor, line);
return;
}
// create breakpoint for the decompiled content
dynamic viewContent = WorkbenchSingleton.Workbench.ActiveContent;
if (viewContent is AbstractViewContentWithoutFile) {
dynamic codeView = ((AbstractViewContentWithoutFile)viewContent).Control;
var editor = codeView.TextEditor as ITextEditor;
var memberReference = viewContent.MemberReference as MemberReference;
if (editor != null && !string.IsNullOrEmpty(editor.FileName))
DebuggerService.ToggleBreakpointAt(memberReference, editor, line);
}
}
}

8
src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs

@ -18,12 +18,12 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -18,12 +18,12 @@ namespace ICSharpCode.AvalonEdit.AddIn
/// <summary>
/// Handles the text markers for a code editor.
/// </summary>
sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService
public sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService
{
readonly CodeEditor codeEditor;
readonly ICodeEditor codeEditor;
TextSegmentCollection<TextMarker> markers;
public TextMarkerService(CodeEditor codeEditor)
public TextMarkerService(ICodeEditor codeEditor)
{
if (codeEditor == null)
throw new ArgumentNullException("codeEditor");
@ -200,7 +200,7 @@ namespace ICSharpCode.AvalonEdit.AddIn @@ -200,7 +200,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
#endregion
}
sealed class TextMarker : TextSegment, ITextMarker
public sealed class TextMarker : TextSegment, ITextMarker
{
readonly TextMarkerService service;

6
src/AddIns/Misc/ILSpyAddIn/ILSpyAddIn.addin → src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.addin

@ -12,7 +12,11 @@ @@ -12,7 +12,11 @@
<Import assembly = "ILSpyAddIn.dll"/>
</Runtime>
<!-- Text editor context menu -->
<Path name="/SharpDevelop/Services/NavigateToEntityService">
<Class id="ILSpy" class="ICSharpCode.ILSpyAddIn.NavigateToDecompiledEntityService"/>
</Path>
<!-- Text editor context menu: Launch ILSpy command -->
<Path name = "/SharpDevelop/ViewContent/DefaultTextEditor/ClassMemberContextMenu">
<MenuItem id="ILSpy" icon="ILSpy" type="Item" label="${res:ILSpyAddIn.OpenILSpyCommand}" class="ICSharpCode.ILSpyAddIn.TextEditorContextMenuCommand"/>

62
src/AddIns/Misc/ILSpyAddIn/ILSpyAddIn.csproj → src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj

@ -9,7 +9,7 @@ @@ -9,7 +9,7 @@
<AssemblyName>ILSpyAddIn</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<OutputPath>..\..\..\..\AddIns\Misc\ILSpyAddIn\</OutputPath>
<OutputPath>..\..\..\..\AddIns\DisplayBindings\Decompiler\</OutputPath>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<WarningLevel>4</WarningLevel>
@ -40,32 +40,69 @@ @@ -40,32 +40,69 @@
</PropertyGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
<ItemGroup>
<Reference Include="PresentationCore">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="PresentationFramework">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase">
<RequiredTargetFramework>3.0</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\..\Main\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="ILSpyController.cs" />
<Compile Include="NavigateToDecompiledEntityService.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SetILSpyPathDialog.cs" />
<Compile Include="SetILSpyPathDialog.Designer.cs">
<Compile Include="LaunchILSpy\ILSpyController.cs" />
<Compile Include="LaunchILSpy\SetILSpyPathDialog.cs" />
<Compile Include="LaunchILSpy\SetILSpyPathDialog.Designer.cs">
<DependentUpon>SetILSpyPathDialog.cs</DependentUpon>
</Compile>
<Compile Include="TextEditorContextMenuCommand.cs" />
<Compile Include="LaunchILSpy\TextEditorContextMenuCommand.cs" />
<Compile Include="ViewContent\CodeView.cs" />
<Compile Include="ViewContent\DecompiledViewContent.cs" />
<EmbeddedResource Include="LaunchILSpy\SetILSpyPathDialog.resx">
<DependentUpon>SetILSpyPathDialog.cs</DependentUpon>
</EmbeddedResource>
<None Include="ILSpyAddIn.addin">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
<EmbeddedResource Include="SetILSpyPathDialog.resx">
<DependentUpon>SetILSpyPathDialog.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\Libraries\AvalonEdit\ICSharpCode.AvalonEdit\ICSharpCode.AvalonEdit.csproj">
<Project>{6C55B776-26D4-4DB3-A6AB-87E783B2F3D1}</Project>
<Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\ICSharpCode.Decompiler\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\NewNRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
<Name>ICSharpCode.NRefactory</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Libraries\NRefactory\Project\NRefactory.csproj">
<Project>{3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}</Project>
<Name>NRefactory</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">
<Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project>
<Name>ICSharpCode.SharpDevelop</Name>
@ -81,5 +118,14 @@ @@ -81,5 +118,14 @@
<Name>ICSharpCode.SharpDevelop.Dom</Name>
<Private>False</Private>
</ProjectReference>
<ProjectReference Include="..\AvalonEdit.AddIn\AvalonEdit.AddIn.csproj">
<Project>{0162E499-42D0-409B-AA25-EED21F75336B}</Project>
<Name>AvalonEdit.AddIn</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="LaunchILSpy" />
<Folder Include="ViewContent" />
</ItemGroup>
</Project>

20
src/AddIns/Misc/ILSpyAddIn/ILSpyController.cs → src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/ILSpyController.cs

@ -32,12 +32,7 @@ namespace ICSharpCode.ILSpyAddIn @@ -32,12 +32,7 @@ namespace ICSharpCode.ILSpyAddIn
ReflectionProjectContent rpc = pc as ReflectionProjectContent;
string assemblyLocation = null;
if (rpc != null) {
// prefer GAC assemblies over reference assemblies:
assemblyLocation = FindAssemblyInNetGac(new DomAssemblyName(rpc.AssemblyFullName));
if (string.IsNullOrEmpty(assemblyLocation)) {
// use file only if assembly isn't in GAC:
assemblyLocation = rpc.AssemblyLocation;
}
assemblyLocation = GetAssemblyLocation(rpc);
} else {
IProject project = pc.Project as IProject;
if (project != null) {
@ -59,6 +54,19 @@ namespace ICSharpCode.ILSpyAddIn @@ -59,6 +54,19 @@ namespace ICSharpCode.ILSpyAddIn
Process.Start(ilspyPath, commandLine);
}
public static string GetAssemblyLocation(ReflectionProjectContent rpc)
{
if (rpc == null)
throw new ArgumentNullException("rpc");
// prefer GAC assemblies over reference assemblies:
string assemblyLocation = FindAssemblyInNetGac(new DomAssemblyName(rpc.AssemblyFullName));
if (string.IsNullOrEmpty(assemblyLocation)) {
// use file only if assembly isn't in GAC:
assemblyLocation = rpc.AssemblyLocation;
}
return assemblyLocation;
}
#region Find ILSpy
internal const string ILSpyExePathPropertyName = "ILSpyAddIn.ILSpyExePath";

0
src/AddIns/Misc/ILSpyAddIn/SetILSpyPathDialog.Designer.cs → src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/SetILSpyPathDialog.Designer.cs generated

0
src/AddIns/Misc/ILSpyAddIn/SetILSpyPathDialog.cs → src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/SetILSpyPathDialog.cs

0
src/AddIns/Misc/ILSpyAddIn/SetILSpyPathDialog.resx → src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/SetILSpyPathDialog.resx

0
src/AddIns/Misc/ILSpyAddIn/TextEditorContextMenuCommand.cs → src/AddIns/DisplayBindings/ILSpyAddIn/LaunchILSpy/TextEditorContextMenuCommand.cs

77
src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs

@ -0,0 +1,77 @@ @@ -0,0 +1,77 @@
// 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.IO;
using System.Linq;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.ILSpyAddIn
{
public class NavigateToDecompiledEntityService : INavigateToEntityService, INavigateToMemberService
{
public bool NavigateToEntity(IEntity entity)
{
if (entity == null)
throw new ArgumentNullException("entity");
// Get the underlying entity for generic instance members
while ((entity is IMember) && ((IMember)entity).GenericMember != null)
entity = ((IMember)entity).GenericMember;
IClass declaringType = (entity as IClass) ?? entity.DeclaringType;
if (declaringType == null)
return false;
// get the top-level type
while (declaringType.DeclaringType != null)
declaringType = declaringType.DeclaringType;
ReflectionProjectContent rpc = entity.ProjectContent as ReflectionProjectContent;
if (rpc != null) {
string assemblyLocation = ILSpyController.GetAssemblyLocation(rpc);
if (!string.IsNullOrEmpty(assemblyLocation) && File.Exists(assemblyLocation)) {
NavigateTo(assemblyLocation, declaringType.DotNetName, ((AbstractEntity)entity).DocumentationTag);
return true;
}
}
return false;
}
public static void NavigateTo(string assemblyFile, string typeName, string entityTag)
{
if (string.IsNullOrEmpty(assemblyFile))
throw new ArgumentException("assemblyFile is null or empty");
if (string.IsNullOrEmpty(typeName))
throw new ArgumentException("typeName is null or empty");
foreach (var vc in WorkbenchSingleton.Workbench.ViewContentCollection.OfType<DecompiledViewContent>()) {
if (string.Equals(vc.AssemblyFile, assemblyFile, StringComparison.OrdinalIgnoreCase) && typeName == vc.FullTypeName) {
vc.WorkbenchWindow.SelectWindow();
vc.JumpToEntity(entityTag);
return;
}
}
WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag));
}
public bool NavigateToMember(string assemblyFile, string typeName, string entityTag, int lineNumber)
{
//close the window if exists - this is a workaround
foreach (var vc in WorkbenchSingleton.Workbench.ViewContentCollection.OfType<DecompiledViewContent>()) {
if (string.Equals(vc.AssemblyFile, assemblyFile, StringComparison.OrdinalIgnoreCase) && typeName == vc.FullTypeName) {
vc.WorkbenchWindow.CloseWindow(true);
break;
}
}
var view = new DecompiledViewContent(assemblyFile, typeName, entityTag);
view.DecompilationFinished += delegate { view.JumpTo(lineNumber); };
WorkbenchSingleton.Workbench.ShowView(view);
return true;
}
}
}

2
src/AddIns/Misc/ILSpyAddIn/Properties/AssemblyInfo.cs → src/AddIns/DisplayBindings/ILSpyAddIn/Properties/AssemblyInfo.cs

@ -20,5 +20,3 @@ using System.Reflection; @@ -20,5 +20,3 @@ using System.Reflection;
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: System.CLSCompliant(true)]

272
src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs

@ -0,0 +1,272 @@ @@ -0,0 +1,272 @@
// 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.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.AddIn;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
namespace ICSharpCode.ILSpyAddIn.ViewContent
{
class DecompiledTextEditorAdapter : AvalonEditTextEditorAdapter
{
public DecompiledTextEditorAdapter(TextEditor textEditor) : base(textEditor)
{}
public string DecompiledFullTypeName { get; set; }
public override ICSharpCode.Core.FileName FileName {
get { return ICSharpCode.Core.FileName.Create(DecompiledFullTypeName); }
}
}
/// <summary>
/// Equivalent to AE.AddIn CodeEditor, but without editing capabilities.
/// </summary>
public class CodeView : Grid, IDisposable, ICodeEditor
{
public event EventHandler DocumentChanged;
readonly DecompiledTextEditorAdapter adapter;
readonly IconBarManager iconBarManager;
readonly IconBarMargin iconMargin;
readonly TextMarkerService textMarkerService;
public CodeView(string decompiledFullTypeName)
{
DecompiledFullTypeName = decompiledFullTypeName;
this.adapter = new DecompiledTextEditorAdapter(new SharpDevelopTextEditor { IsReadOnly = true }) {
DecompiledFullTypeName = decompiledFullTypeName
};
this.Children.Add(adapter.TextEditor);
adapter.TextEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
// add margin
this.iconMargin = new IconBarMargin(iconBarManager = new IconBarManager());
this.adapter.TextEditor.TextArea.LeftMargins.Insert(0, iconMargin);
this.adapter.TextEditor.TextArea.TextView.VisualLinesChanged += delegate { iconMargin.InvalidateVisual(); };
// add marker service
this.textMarkerService = new TextMarkerService(this);
this.adapter.TextEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService);
this.adapter.TextEditor.TextArea.TextView.LineTransformers.Add(textMarkerService);
this.adapter.TextEditor.TextArea.TextView.Services.AddService(typeof(ITextMarkerService), textMarkerService);
this.adapter.TextEditor.TextArea.TextView.Services.AddService(typeof(IBookmarkMargin), iconBarManager);
// add events
this.adapter.TextEditor.MouseHover += TextEditorMouseHover;
this.adapter.TextEditor.MouseHoverStopped += TextEditorMouseHoverStopped;
this.adapter.TextEditor.MouseLeave += TextEditorMouseLeave;
}
#region Popup
ToolTip toolTip;
Popup popup;
void TextEditorMouseHover(object sender, MouseEventArgs e)
{
ToolTipRequestEventArgs args = new ToolTipRequestEventArgs(this.adapter);
var pos = adapter.TextEditor.GetPositionFromPoint(e.GetPosition(this));
args.InDocument = pos.HasValue;
if (pos.HasValue) {
args.LogicalPosition = AvalonEditDocumentAdapter.ToLocation(pos.Value);
}
if (!args.Handled) {
// if request wasn't handled by a marker, pass it to the ToolTipRequestService
ToolTipRequestService.RequestToolTip(args);
}
if (args.ContentToShow != null) {
var contentToShowITooltip = args.ContentToShow as ITooltip;
if (contentToShowITooltip != null && contentToShowITooltip.ShowAsPopup) {
if (!(args.ContentToShow is UIElement)) {
throw new NotSupportedException("Content to show in Popup must be UIElement: " + args.ContentToShow);
}
if (popup == null) {
popup = CreatePopup();
}
if (TryCloseExistingPopup(false)) {
// when popup content decides to close, close the popup
contentToShowITooltip.Closed += (closedSender, closedArgs) => { popup.IsOpen = false; };
popup.Child = (UIElement)args.ContentToShow;
//ICSharpCode.SharpDevelop.Debugging.DebuggerService.CurrentDebugger.IsProcessRunningChanged
SetPopupPosition(popup, e);
popup.IsOpen = true;
}
e.Handled = true;
} else {
if (toolTip == null) {
toolTip = new ToolTip();
toolTip.Closed += delegate { toolTip = null; };
}
toolTip.PlacementTarget = this.adapter.TextEditor; // required for property inheritance
if(args.ContentToShow is string) {
toolTip.Content = new TextBlock
{
Text = args.ContentToShow as string,
TextWrapping = TextWrapping.Wrap
};
}
else
toolTip.Content = args.ContentToShow;
toolTip.IsOpen = true;
e.Handled = true;
}
} else {
// close popup if mouse hovered over empty area
if (popup != null) {
e.Handled = true;
}
TryCloseExistingPopup(false);
}
}
bool TryCloseExistingPopup(bool mouseClick)
{
bool canClose = true;
if (popup != null) {
var popupContentITooltip = popup.Child as ITooltip;
if (popupContentITooltip != null) {
canClose = popupContentITooltip.Close(mouseClick);
}
if (canClose) {
popup.IsOpen = false;
}
}
return canClose;
}
void SetPopupPosition(Popup popup, MouseEventArgs mouseArgs)
{
var popupPosition = GetPopupPosition(mouseArgs);
popup.HorizontalOffset = popupPosition.X;
popup.VerticalOffset = popupPosition.Y;
}
/// <summary> Returns Popup position based on mouse position, in device independent units </summary>
Point GetPopupPosition(MouseEventArgs mouseArgs)
{
Point mousePos = mouseArgs.GetPosition(this);
Point positionInPixels;
// align Popup with line bottom
TextViewPosition? logicalPos = adapter.TextEditor.GetPositionFromPoint(mousePos);
if (logicalPos.HasValue) {
var textView = adapter.TextEditor.TextArea.TextView;
positionInPixels =
textView.PointToScreen(
textView.GetVisualPosition(logicalPos.Value, VisualYPosition.LineBottom) - textView.ScrollOffset);
positionInPixels.X -= 4;
} else {
positionInPixels = PointToScreen(mousePos + new Vector(-4, 6));
}
// use device independent units, because Popup Left/Top are in independent units
return positionInPixels.TransformFromDevice(this);
}
Popup CreatePopup()
{
popup = new Popup();
popup.Closed += (s, e) => popup = null;
popup.AllowsTransparency = true;
popup.PlacementTarget = this; // required for property inheritance
popup.Placement = PlacementMode.Absolute;
popup.StaysOpen = true;
return popup;
}
void TextEditorMouseHoverStopped(object sender, MouseEventArgs e)
{
if (toolTip != null) {
toolTip.IsOpen = false;
e.Handled = true;
}
TextEditorMouseLeave(sender, e);
}
void TextEditorMouseLeave(object sender, MouseEventArgs e)
{
if (popup != null && !popup.IsMouseOver) {
// do not close popup if mouse moved from editor to popup
TryCloseExistingPopup(false);
}
}
#endregion
public TextDocument Document {
get { return adapter.TextEditor.Document; }
set {
adapter.TextEditor.Document = value;
if (DocumentChanged != null) {
DocumentChanged(value, EventArgs.Empty);
}
}
}
public ITextEditor TextEditor {
get {
return adapter;
}
}
public IconBarManager IconBarManager {
get { return iconBarManager; }
}
public AvalonEditTextEditorAdapter Adapter {
get {
return adapter;
}
}
public string DecompiledFullTypeName {
get; private set;
}
public void Dispose()
{
}
public void UnfoldAndScroll(int lineNumber)
{
if (lineNumber <= 0 || lineNumber > adapter.Document.TotalNumberOfLines)
return;
//var line = adapter.TextEditor.Document.GetLineByNumber(lineNumber);
// unfold
// var foldings = foldingManager.GetFoldingsContaining(line.Offset);
// if (foldings != null) {
// foreach (var folding in foldings) {
// if (folding.IsFolded) {
// folding.IsFolded = false;
// }
// }
// }
// scroll to
adapter.TextEditor.ScrollTo(lineNumber, 0);
}
public void Redraw(ISegment segment, System.Windows.Threading.DispatcherPriority priority)
{
this.adapter.TextEditor.TextArea.TextView.Redraw(segment, priority);
}
}
}

291
src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs

@ -0,0 +1,291 @@ @@ -0,0 +1,291 @@
// 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.IO;
using System.Linq;
using System.Threading;
using ICSharpCode.Core;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.ILSpyAddIn.ViewContent;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Gui;
using Mono.Cecil;
namespace ICSharpCode.ILSpyAddIn
{
/// <summary>
/// Hosts a decompiled type.
/// </summary>
public class DecompiledViewContent : AbstractViewContentWithoutFile
{
readonly string assemblyFile;
readonly string fullTypeName;
readonly string tempFileName;
/// <summary>
/// Entity to jump to once decompilation has finished.
/// </summary>
string jumpToEntityTagWhenDecompilationFinished;
bool decompilationFinished;
readonly CodeView codeView;
readonly CancellationTokenSource cancellation = new CancellationTokenSource();
#region Constructor
public DecompiledViewContent(string assemblyFile, string fullTypeName, string entityTag)
{
codeView = new CodeView(string.Format("{0},{1}", assemblyFile, fullTypeName));
this.assemblyFile = assemblyFile;
this.fullTypeName = fullTypeName;
this.jumpToEntityTagWhenDecompilationFinished = entityTag;
string shortTypeName = fullTypeName.Substring(fullTypeName.LastIndexOf('.') + 1);
this.TitleName = "[" + shortTypeName + "]";
tempFileName = string.Format("decompiled/{0}.cs", fullTypeName);
this.InfoTip = tempFileName;
Thread thread = new Thread(DecompilationThread);
thread.Name = "Decompiler (" + shortTypeName + ")";
thread.Start();
BookmarkManager.Removed += BookmarkManager_Removed;
BookmarkManager.Added += BookmarkManager_Added;
}
#endregion
#region Properties
public string AssemblyFile {
get { return assemblyFile; }
}
public string FullTypeName {
get { return fullTypeName; }
}
public override object Control {
get { return codeView; }
}
public override bool IsReadOnly {
get { return true; }
}
public MemberReference MemberReference {
get; private set;
}
#endregion
#region Dispose
public override void Dispose()
{
cancellation.Cancel();
codeView.Dispose();
BookmarkManager.Added -= BookmarkManager_Added;
BookmarkManager.Removed -= BookmarkManager_Removed;
base.Dispose();
}
#endregion
#region Load/Save
public override void Load()
{
// nothing to do...
}
public override void Save()
{
if (!decompilationFinished)
return;
// TODO: show Save As dialog to allow the user to save the decompiled file
}
#endregion
#region JumpToEntity
public void JumpToEntity(string entityTag)
{
if (!decompilationFinished) {
this.jumpToEntityTagWhenDecompilationFinished = entityTag;
return;
}
// TODO: implement this
}
#endregion
#region Decompilation
void DecompilationThread()
{
try {
StringWriter writer = new StringWriter();
RunDecompiler(assemblyFile, fullTypeName, new PlainTextOutput(writer), cancellation.Token);
if (!cancellation.IsCancellationRequested) {
WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer);
}
} catch (OperationCanceledException) {
// ignore cancellation
} catch (Exception ex) {
if (cancellation.IsCancellationRequested) {
MessageService.ShowException(ex);
return;
}
AnalyticsMonitorService.TrackException(ex);
StringWriter writer = new StringWriter();
writer.WriteLine("Exception while decompiling " + fullTypeName);
writer.WriteLine();
writer.WriteLine(ex.ToString());
WorkbenchSingleton.SafeThreadAsyncCall(OnDecompilationFinished, writer);
}
}
void RunDecompiler(string assemblyFile, string fullTypeName, ITextOutput textOutput, CancellationToken cancellationToken)
{
ReaderParameters readerParameters = new ReaderParameters();
// Use new assembly resolver instance so that the AssemblyDefinitions can be garbage-collected
// once the code is decompiled.
readerParameters.AssemblyResolver = new DefaultAssemblyResolver();
ModuleDefinition module = ModuleDefinition.ReadModule(assemblyFile, readerParameters);
TypeDefinition typeDefinition = module.GetType(fullTypeName);
if (typeDefinition == null)
throw new InvalidOperationException("Could not find type");
DecompilerContext context = new DecompilerContext(module);
context.CancellationToken = cancellationToken;
AstBuilder astBuilder = new AstBuilder(context);
astBuilder.AddType(typeDefinition);
astBuilder.GenerateCode(textOutput);
// save decompilation data
var nodes = TreeTraversal
.PreOrder((AstNode)astBuilder.CompilationUnit, n => n.Children)
.Where(n => n is AttributedNode && n.Annotation<Tuple<int, int>>() != null);
MemberReference = typeDefinition;
int token = MemberReference.MetadataToken.ToInt32();
if (!DebuggerService.ExternalDebugInformation.ContainsKey(token)) {
DebuggerService.ExternalDebugInformation.Add(token, new DecompileInformation {
CodeMappings = astBuilder.CodeMappings,
LocalVariables = astBuilder.LocalVariables,
DecompiledMemberReferences = astBuilder.DecompiledMemberReferences,
AstNodes = nodes
});
} else {
DebuggerService.ExternalDebugInformation[token] = new DecompileInformation {
CodeMappings = astBuilder.CodeMappings,
LocalVariables = astBuilder.LocalVariables,
DecompiledMemberReferences = astBuilder.DecompiledMemberReferences,
AstNodes = nodes
};
}
}
void OnDecompilationFinished(StringWriter output)
{
if (cancellation.IsCancellationRequested)
return;
codeView.Document.Text = output.ToString();
codeView.Document.UndoStack.ClearAll();
this.decompilationFinished = true;
JumpToEntity(this.jumpToEntityTagWhenDecompilationFinished);
// update UI
UpdateIconMargin(output.ToString());
UpdateDebuggingUI();
// fire event
OnDecompilationFinished(EventArgs.Empty);
}
#endregion
#region Update UI
void UpdateIconMargin(string text)
{
codeView.IconBarManager.UpdateClassMemberBookmarks(ParserService.ParseFile(tempFileName, new StringTextBuffer(text)));
// load bookmarks
foreach (SDBookmark bookmark in BookmarkManager.GetBookmarks(codeView.Adapter.FileName)) {
bookmark.Document = codeView.Adapter.Document;
codeView.IconBarManager.Bookmarks.Add(bookmark);
}
}
void UpdateDebuggingUI()
{
if (!DebuggerService.IsDebuggerStarted)
return;
if (DebuggerService.DebugStepInformation != null) {
// get debugging information
DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation[MemberReference.MetadataToken.ToInt32()];
int token = DebuggerService.DebugStepInformation.Item1;
int ilOffset = DebuggerService.DebugStepInformation.Item2;
int line;
MemberReference member;
if (debugInformation.CodeMappings == null || !debugInformation.CodeMappings.ContainsKey(token))
return;
debugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out member, out line);
// HACK : if the codemappings are not built
if (line == 0) {
DebuggerService.CurrentDebugger.StepOver();
return;
}
// update bookmark & marker
codeView.UnfoldAndScroll(line);
CurrentLineBookmark.SetPosition(this, line, 0, line, 0);
}
}
public void JumpTo(int lineNumber)
{
if (lineNumber <= 0)
return;
if (codeView == null)
return;
codeView.UnfoldAndScroll(lineNumber);
}
#endregion
#region Bookmarks
void BookmarkManager_Removed(object sender, BookmarkEventArgs e)
{
var mark = e.Bookmark;
if (mark != null && codeView.IconBarManager.Bookmarks.Contains(mark)) {
codeView.IconBarManager.Bookmarks.Remove(mark);
mark.Document = null;
}
}
void BookmarkManager_Added(object sender, BookmarkEventArgs e)
{
var mark = e.Bookmark;
if (mark != null && mark is BreakpointBookmark && mark.FileName == codeView.DecompiledFullTypeName) {
codeView.IconBarManager.Bookmarks.Add(mark);
mark.Document = codeView.Adapter.Document;
}
}
#endregion
#region Events
public event EventHandler DecompilationFinished;
protected virtual void OnDecompilationFinished(EventArgs e)
{
if (DecompilationFinished != null) {
DecompilationFinished(this, e);
}
}
#endregion
}
}

1673
src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs

File diff suppressed because it is too large Load Diff

1162
src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

File diff suppressed because it is too large Load Diff

173
src/Libraries/ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs

@ -0,0 +1,173 @@ @@ -0,0 +1,173 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
/// <summary>
/// ITypeResolveContext implementation that lazily loads types from Cecil.
/// </summary>
public class CecilTypeResolveContext : AbstractAnnotatable, ISynchronizedTypeResolveContext, IProjectContent
{
readonly ModuleDefinition module;
readonly string[] namespaces;
readonly CecilLoader loader;
Dictionary<TypeDefinition, WeakReference> dict = new Dictionary<TypeDefinition, WeakReference>();
int countUntilNextCleanup = 4;
public CecilTypeResolveContext(ModuleDefinition module)
{
this.loader = new CecilLoader();
this.loader.IncludeInternalMembers = true;
this.module = module;
this.namespaces = module.Types.Select(t => t.Namespace).Distinct().ToArray();
List<IAttribute> assemblyAttributes = new List<IAttribute>();
foreach (var attr in module.Assembly.CustomAttributes) {
assemblyAttributes.Add(loader.ReadAttribute(attr));
}
this.AssemblyAttributes = assemblyAttributes.AsReadOnly();
}
ITypeDefinition GetClass(TypeDefinition cecilType)
{
lock (dict) {
WeakReference wr;
ITypeDefinition type;
if (dict.TryGetValue(cecilType, out wr)) {
type = (ITypeDefinition)wr.Target;
} else {
wr = null;
type = null;
}
if (type == null) {
type = loader.LoadType(cecilType, this);
}
if (wr == null) {
if (--countUntilNextCleanup <= 0)
CleanupDict();
wr = new WeakReference(type);
dict.Add(cecilType, wr);
} else {
wr.Target = type;
}
return type;
}
}
void CleanupDict()
{
List<TypeDefinition> deletedKeys = new List<TypeDefinition>();
foreach (var pair in dict) {
if (!pair.Value.IsAlive) {
deletedKeys.Add(pair.Key);
}
}
foreach (var key in deletedKeys) {
dict.Remove(key);
}
countUntilNextCleanup = dict.Count + 4;
}
public IList<IAttribute> AssemblyAttributes { get; private set; }
public ITypeDefinition GetTypeDefinition(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
{
if (typeParameterCount > 0)
name = name + "`" + typeParameterCount.ToString();
if (nameComparer == StringComparer.Ordinal) {
TypeDefinition cecilType = module.GetType(nameSpace, name);
if (cecilType != null)
return GetClass(cecilType);
else
return null;
}
foreach (TypeDefinition cecilType in module.Types) {
if (nameComparer.Equals(name, cecilType.Name)
&& nameComparer.Equals(nameSpace, cecilType.Namespace)
&& cecilType.GenericParameters.Count == typeParameterCount)
{
return GetClass(cecilType);
}
}
return null;
}
public IEnumerable<ITypeDefinition> GetTypes()
{
foreach (TypeDefinition cecilType in module.Types) {
yield return GetClass(cecilType);
}
}
public IEnumerable<ITypeDefinition> GetTypes(string nameSpace, StringComparer nameComparer)
{
foreach (TypeDefinition cecilType in module.Types) {
if (nameComparer.Equals(nameSpace, cecilType.Namespace))
yield return GetClass(cecilType);
}
}
public IEnumerable<string> GetNamespaces()
{
return namespaces;
}
public string GetNamespace(string nameSpace, StringComparer nameComparer)
{
foreach (string ns in namespaces) {
if (nameComparer.Equals(ns, nameSpace))
return ns;
}
return null;
}
ICSharpCode.NRefactory.Utils.CacheManager ITypeResolveContext.CacheManager {
get {
// We don't support caching
return null;
}
}
ISynchronizedTypeResolveContext ITypeResolveContext.Synchronize()
{
// This class is logically immutable
return this;
}
void IDisposable.Dispose()
{
// exit from Synchronize() block
}
IEnumerable<IParsedFile> IProjectContent.Files {
get { return new IParsedFile[0]; }
}
IParsedFile IProjectContent.GetFile(string fileName)
{
return null;
}
}
}

60
src/Libraries/ICSharpCode.Decompiler/Ast/CommentStatement.cs

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast
{
/// <summary>
/// Allows storing comments inside IEnumerable{Statement}. Used in the AstMethodBuilder.
/// CommentStatement nodes are replaced with regular comments later on.
/// </summary>
class CommentStatement : Statement
{
string comment;
public CommentStatement(string comment)
{
if (comment == null)
throw new ArgumentNullException("comment");
this.comment = comment;
}
public override S AcceptVisitor<T, S>(IAstVisitor<T, S> visitor, T data)
{
return default(S);
}
public static void ReplaceAll(AstNode tree)
{
foreach (var cs in tree.Descendants.OfType<CommentStatement>()) {
cs.Parent.InsertChildBefore(cs, new Comment(cs.comment), Roles.Comment);
cs.Remove();
}
}
protected override bool DoMatch(AstNode other, Match match)
{
CommentStatement o = other as CommentStatement;
return o != null && MatchString(comment, o.comment);
}
}
}

70
src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Threading;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.TypeSystem.Implementation;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
public class DecompilerContext
{
public ModuleDefinition CurrentModule;
public CancellationToken CancellationToken;
public TypeDefinition CurrentType;
public MethodDefinition CurrentMethod;
public DecompilerSettings Settings = new DecompilerSettings();
// public ITypeResolveContext TypeResolveContext;
// public IProjectContent ProjectContent;
public DecompilerContext(ModuleDefinition currentModule)
{
if (currentModule == null)
throw new ArgumentNullException("currentModule");
this.CurrentModule = currentModule;
// this.ProjectContent = new CecilTypeResolveContext(currentModule);
// List<ITypeResolveContext> resolveContexts = new List<ITypeResolveContext>();
// resolveContexts.Add(this.ProjectContent);
// foreach (AssemblyNameReference r in currentModule.AssemblyReferences) {
// AssemblyDefinition d = currentModule.AssemblyResolver.Resolve(r);
// if (d != null) {
// resolveContexts.Add(new CecilTypeResolveContext(d.MainModule));
// }
// }
// this.TypeResolveContext = new CompositeTypeResolveContext(resolveContexts);
}
/// <summary>
/// Used to pass variable names from a method to its anonymous methods.
/// </summary>
internal List<string> ReservedVariableNames = new List<string>();
public DecompilerContext Clone()
{
DecompilerContext ctx = (DecompilerContext)MemberwiseClone();
ctx.ReservedVariableNames = new List<string>(ctx.ReservedVariableNames);
return ctx;
}
}
}

86
src/Libraries/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs

@ -0,0 +1,86 @@ @@ -0,0 +1,86 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast
{
static class NRefactoryExtensions
{
public static T WithAnnotation<T>(this T node, object annotation) where T : AstNode
{
if (annotation != null)
node.AddAnnotation(annotation);
return node;
}
public static T CopyAnnotationsFrom<T>(this T node, AstNode other) where T : AstNode
{
foreach (object annotation in other.Annotations) {
node.AddAnnotation(annotation);
}
return node;
}
public static T Detach<T>(this T node) where T : AstNode
{
node.Remove();
return node;
}
public static Expression WithName(this Expression node, string patternGroupName)
{
return new NamedNode(patternGroupName, node);
}
public static Statement WithName(this Statement node, string patternGroupName)
{
return new NamedNode(patternGroupName, node);
}
public static void AddNamedArgument(this NRefactory.CSharp.Attribute attribute, string name, Expression argument)
{
attribute.Arguments.Add(new AssignmentExpression(new IdentifierExpression(name), argument));
}
public static AstType ToType(this Pattern pattern)
{
return pattern;
}
public static Expression ToExpression(this Pattern pattern)
{
return pattern;
}
public static Statement ToStatement(this Pattern pattern)
{
return pattern;
}
public static Statement GetNextStatement(this Statement statement)
{
AstNode next = statement.NextSibling;
while (next != null && !(next is Statement))
next = next.NextSibling;
return (Statement)next;
}
}
}

343
src/Libraries/ICSharpCode.Decompiler/Ast/NameVariables.cs

@ -0,0 +1,343 @@ @@ -0,0 +1,343 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.ILAst;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
public class NameVariables
{
static readonly Dictionary<string, string> typeNameToVariableNameDict = new Dictionary<string, string> {
{ "System.Boolean", "flag" },
{ "System.Byte", "b" },
{ "System.SByte", "b" },
{ "System.Int16", "num" },
{ "System.Int32", "num" },
{ "System.Int64", "num" },
{ "System.UInt16", "num" },
{ "System.UInt32", "num" },
{ "System.UInt64", "num" },
{ "System.Single", "num" },
{ "System.Double", "num" },
{ "System.Decimal", "num" },
{ "System.String", "text" },
{ "System.Object", "obj" },
{ "System.Char", "c" }
};
public static void AssignNamesToVariables(DecompilerContext context, IEnumerable<ILVariable> parameters, IEnumerable<ILVariable> variables, ILBlock methodBody)
{
NameVariables nv = new NameVariables();
nv.context = context;
nv.fieldNamesInCurrentType = context.CurrentType.Fields.Select(f => f.Name).ToList();
// First mark existing variable names as reserved.
foreach (string name in context.ReservedVariableNames)
nv.AddExistingName(name);
foreach (var p in parameters)
nv.AddExistingName(p.Name);
foreach (var v in variables) {
if (v.IsGenerated) {
// don't introduce names for variables generated by ILSpy - keep "expr"/"arg"
nv.AddExistingName(v.Name);
} else if (v.OriginalVariable != null && context.Settings.UseDebugSymbols) {
string varName = v.OriginalVariable.Name;
if (string.IsNullOrEmpty(varName) || varName.StartsWith("V_", StringComparison.Ordinal) || !IsValidName(varName))
{
// don't use the name from the debug symbols if it looks like a generated name
v.Name = null;
} else {
// use the name from the debug symbols
// (but ensure we don't use the same name for two variables)
v.Name = nv.GetAlternativeName(varName);
}
} else {
v.Name = null;
}
}
// Now generate names:
foreach (ILVariable p in parameters) {
if (string.IsNullOrEmpty(p.Name))
p.Name = nv.GenerateNameForVariable(p, methodBody);
}
foreach (ILVariable varDef in variables) {
if (string.IsNullOrEmpty(varDef.Name))
varDef.Name = nv.GenerateNameForVariable(varDef, methodBody);
}
}
static bool IsValidName(string varName)
{
if (string.IsNullOrEmpty(varName))
return false;
if (!(char.IsLetter(varName[0]) || varName[0] == '_'))
return false;
for (int i = 1; i < varName.Length; i++) {
if (!(char.IsLetterOrDigit(varName[i]) || varName[i] == '_'))
return false;
}
return true;
}
DecompilerContext context;
List<string> fieldNamesInCurrentType;
Dictionary<string, int> typeNames = new Dictionary<string, int>();
public void AddExistingName(string name)
{
if (string.IsNullOrEmpty(name))
return;
int number;
string nameWithoutDigits = SplitName(name, out number);
int existingNumber;
if (typeNames.TryGetValue(nameWithoutDigits, out existingNumber)) {
typeNames[nameWithoutDigits] = Math.Max(number, existingNumber);
} else {
typeNames.Add(nameWithoutDigits, number);
}
}
string SplitName(string name, out int number)
{
// First, identify whether the name already ends with a number:
int pos = name.Length;
while (pos > 0 && name[pos-1] >= '0' && name[pos-1] <= '9')
pos--;
if (pos < name.Length) {
if (int.TryParse(name.Substring(pos), out number)) {
return name.Substring(0, pos);
}
}
number = 1;
return name;
}
const char maxLoopVariableName = 'n';
public string GetAlternativeName(string oldVariableName)
{
if (oldVariableName.Length == 1 && oldVariableName[0] >= 'i' && oldVariableName[0] <= maxLoopVariableName) {
for (char c = 'i'; c <= maxLoopVariableName; c++) {
if (!typeNames.ContainsKey(c.ToString())) {
typeNames.Add(c.ToString(), 1);
return c.ToString();
}
}
}
int number;
string nameWithoutDigits = SplitName(oldVariableName, out number);
if (!typeNames.ContainsKey(nameWithoutDigits)) {
typeNames.Add(nameWithoutDigits, number - 1);
}
int count = ++typeNames[nameWithoutDigits];
if (count != 1) {
return nameWithoutDigits + count.ToString();
} else {
return nameWithoutDigits;
}
}
string GenerateNameForVariable(ILVariable variable, ILBlock methodBody)
{
string proposedName = null;
if (variable.Type == context.CurrentType.Module.TypeSystem.Int32) {
// test whether the variable might be a loop counter
bool isLoopCounter = false;
foreach (ILWhileLoop loop in methodBody.GetSelfAndChildrenRecursive<ILWhileLoop>()) {
ILExpression expr = loop.Condition;
while (expr != null && expr.Code == ILCode.LogicNot)
expr = expr.Arguments[0];
if (expr != null) {
switch (expr.Code) {
case ILCode.Clt:
case ILCode.Clt_Un:
case ILCode.Cgt:
case ILCode.Cgt_Un:
ILVariable loadVar;
if (expr.Arguments[0].Match(ILCode.Ldloc, out loadVar) && loadVar == variable) {
isLoopCounter = true;
}
break;
}
}
}
if (isLoopCounter) {
// For loop variables, use i,j,k,l,m,n
for (char c = 'i'; c <= maxLoopVariableName; c++) {
if (!typeNames.ContainsKey(c.ToString())) {
proposedName = c.ToString();
break;
}
}
}
}
if (string.IsNullOrEmpty(proposedName)) {
var proposedNameForStores =
(from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>()
where expr.Code == ILCode.Stloc && expr.Operand == variable
select GetNameFromExpression(expr.Arguments.Single())
).Except(fieldNamesInCurrentType).ToList();
if (proposedNameForStores.Count == 1) {
proposedName = proposedNameForStores[0];
}
}
if (string.IsNullOrEmpty(proposedName)) {
var proposedNameForLoads =
(from expr in methodBody.GetSelfAndChildrenRecursive<ILExpression>()
from i in Enumerable.Range(0, expr.Arguments.Count)
let arg = expr.Arguments[i]
where arg.Code == ILCode.Ldloc && arg.Operand == variable
select GetNameForArgument(expr, i)
).Except(fieldNamesInCurrentType).ToList();
if (proposedNameForLoads.Count == 1) {
proposedName = proposedNameForLoads[0];
}
}
if (string.IsNullOrEmpty(proposedName)) {
proposedName = GetNameByType(variable.Type);
}
// remove any numbers from the proposed name
int number;
proposedName = SplitName(proposedName, out number);
if (!typeNames.ContainsKey(proposedName)) {
typeNames.Add(proposedName, 0);
}
int count = ++typeNames[proposedName];
if (count > 1) {
return proposedName + count.ToString();
} else {
return proposedName;
}
}
static string GetNameFromExpression(ILExpression expr)
{
switch (expr.Code) {
case ILCode.Ldfld:
case ILCode.Ldsfld:
return CleanUpVariableName(((FieldReference)expr.Operand).Name);
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
MethodReference mr = (MethodReference)expr.Operand;
if (mr.Name.StartsWith("get_", StringComparison.OrdinalIgnoreCase) && mr.Parameters.Count == 0) {
// use name from properties, but not from indexers
return CleanUpVariableName(mr.Name.Substring(4));
} else if (mr.Name.StartsWith("Get", StringComparison.OrdinalIgnoreCase) && mr.Name.Length >= 4 && char.IsUpper(mr.Name[3])) {
// use name from Get-methods
return CleanUpVariableName(mr.Name.Substring(3));
}
break;
}
return null;
}
static string GetNameForArgument(ILExpression parent, int i)
{
switch (parent.Code) {
case ILCode.Stfld:
case ILCode.Stsfld:
if (i == parent.Arguments.Count - 1) // last argument is stored value
return CleanUpVariableName(((FieldReference)parent.Operand).Name);
else
break;
case ILCode.Call:
case ILCode.Callvirt:
case ILCode.Newobj:
case ILCode.CallGetter:
case ILCode.CallvirtGetter:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
MethodReference methodRef = (MethodReference)parent.Operand;
if (methodRef.Parameters.Count == 1 && i == parent.Arguments.Count - 1) {
// argument might be value of a setter
if (methodRef.Name.StartsWith("set_", StringComparison.OrdinalIgnoreCase)) {
return CleanUpVariableName(methodRef.Name.Substring(4));
} else if (methodRef.Name.StartsWith("Set", StringComparison.OrdinalIgnoreCase) && methodRef.Name.Length >= 4 && char.IsUpper(methodRef.Name[3])) {
return CleanUpVariableName(methodRef.Name.Substring(3));
}
}
MethodDefinition methodDef = methodRef.Resolve();
if (methodDef != null) {
var p = methodDef.Parameters.ElementAtOrDefault((parent.Code != ILCode.Newobj && methodDef.HasThis) ? i - 1 : i);
if (p != null && !string.IsNullOrEmpty(p.Name))
return CleanUpVariableName(p.Name);
}
break;
case ILCode.Ret:
return "result";
}
return null;
}
string GetNameByType(TypeReference type)
{
type = TypeAnalysis.UnpackModifiers(type);
GenericInstanceType git = type as GenericInstanceType;
if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) {
type = ((GenericInstanceType)type).GenericArguments[0];
}
string name;
if (type.IsArray) {
name = "array";
} else if (type.IsPointer || type.IsByReference) {
name = "ptr";
} else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {
name = "ex";
} else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) {
name = 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);
name = CleanUpVariableName(name);
}
return name;
}
static string CleanUpVariableName(string name)
{
// remove the backtick (generics)
int pos = name.IndexOf('`');
if (pos >= 0)
name = name.Substring(0, pos);
// remove field prefix:
if (name.Length > 2 && name.StartsWith("m_", StringComparison.Ordinal))
name = name.Substring(2);
else if (name.Length > 1 && name[0] == '_')
name = name.Substring(1);
if (name.Length == 0)
return "obj";
else
return char.ToLower(name[0]) + name.Substring(1);
}
}
}

193
src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs

@ -0,0 +1,193 @@ @@ -0,0 +1,193 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
public class TextOutputFormatter : IOutputFormatter
{
readonly ITextOutput output;
readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
int braceLevelWithinType = -1;
bool inDocumentationComment = false;
public TextOutputFormatter(ITextOutput output)
{
if (output == null)
throw new ArgumentNullException("output");
this.output = output;
}
public void WriteIdentifier(string identifier)
{
MemberReference memberRef = GetCurrentMemberReference();
if (memberRef != null)
output.WriteReference(identifier, memberRef);
else
output.Write(identifier);
}
MemberReference GetCurrentMemberReference()
{
AstNode node = nodeStack.Peek();
MemberReference memberRef = node.Annotation<MemberReference>();
if (memberRef == null && node.Role == AstNode.Roles.TargetExpression && (node.Parent is InvocationExpression || node.Parent is ObjectCreateExpression)) {
memberRef = node.Parent.Annotation<MemberReference>();
}
return memberRef;
}
public void WriteKeyword(string keyword)
{
output.Write(keyword);
}
public void WriteToken(string token)
{
// Attach member reference to token only if there's no identifier in the current node.
MemberReference memberRef = GetCurrentMemberReference();
if (memberRef != null && nodeStack.Peek().GetChildByRole(AstNode.Roles.Identifier).IsNull)
output.WriteReference(token, memberRef);
else
output.Write(token);
}
public void Space()
{
output.Write(' ');
}
public void OpenBrace(BraceStyle style)
{
if (braceLevelWithinType >= 0 || nodeStack.Peek() is TypeDeclaration)
braceLevelWithinType++;
if (nodeStack.OfType<BlockStatement>().Count() <= 1) {
output.MarkFoldStart(defaultCollapsed: braceLevelWithinType == 1);
}
output.WriteLine();
output.WriteLine("{");
output.Indent();
}
public void CloseBrace(BraceStyle style)
{
output.Unindent();
output.Write('}');
if (nodeStack.OfType<BlockStatement>().Count() <= 1)
output.MarkFoldEnd();
if (braceLevelWithinType >= 0)
braceLevelWithinType--;
}
public void Indent()
{
output.Indent();
}
public void Unindent()
{
output.Unindent();
}
public void NewLine()
{
output.WriteLine();
}
public void WriteComment(CommentType commentType, string content)
{
switch (commentType) {
case CommentType.SingleLine:
output.Write("//");
output.WriteLine(content);
break;
case CommentType.MultiLine:
output.Write("/*");
output.Write(content);
output.Write("*/");
break;
case CommentType.Documentation:
if (!inDocumentationComment)
output.MarkFoldStart("///" + content, true);
output.Write("///");
output.Write(content);
inDocumentationComment = true;
bool isLastLine = !(nodeStack.Peek().NextSibling is Comment);
if (isLastLine) {
inDocumentationComment = false;
output.MarkFoldEnd();
}
output.WriteLine();
break;
}
}
public void StartNode(AstNode node)
{
// code mappings
var ranges = node.Annotation<List<ILRange>>();
if (ranges != null && ranges.Count > 0) {
// find the ancestor that has method mapping as annotation
if (node.Parent != null)
{
var n = node.Ancestors.FirstOrDefault(a => a.Annotation<MemberMapping>() != null);
if (n != null) {
MemberMapping mapping = n.Annotation<MemberMapping>();
// add all ranges
foreach (var range in ranges) {
mapping.MemberCodeMappings.Add(new SourceCodeMapping {
ILInstructionOffset = range,
SourceCodeLine = output.CurrentLine,
MemberMapping = mapping
});
}
}
}
}
// definitions of types and their members
Predicate<AstNode> predicate = n => n is AttributedNode;
if (predicate(node)) {
var n = node as AttributedNode;
int c = 0;
if (n != null)
c = n.Attributes.Count;
node.AddAnnotation(Tuple.Create(output.CurrentLine + c, output.CurrentColumn));
}
nodeStack.Push(node);
}
public void EndNode(AstNode node)
{
if (nodeStack.Pop() != node)
throw new InvalidOperationException();
}
}
}

356
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs

@ -0,0 +1,356 @@ @@ -0,0 +1,356 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Add checked/unchecked blocks.
/// </summary>
public class AddCheckedBlocks : IAstTransform
{
#region Annotation
sealed class CheckedUncheckedAnnotation {
/// <summary>
/// true=checked, false=unchecked
/// </summary>
public bool IsChecked;
}
public static readonly object CheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = true };
public static readonly object UncheckedAnnotation = new CheckedUncheckedAnnotation { IsChecked = false };
#endregion
/*
We treat placing checked/unchecked blocks as an optimization problem, with the following goals:
1. Use minimum number of checked blocks+expressions
2. Prefer checked expressions over checked blocks
3. Make the scope of checked expressions as small as possible
4. Make the scope of checked blocks as large as possible
(where goal 1 has the highest priority)
*/
#region struct Cost
struct Cost
{
// highest possible cost so that the Blocks+Expressions addition doesn't overflow
public static readonly Cost Infinite = new Cost(0x3fffffff, 0x3fffffff);
public readonly int Blocks;
public readonly int Expressions;
public Cost(int blocks, int expressions)
{
this.Blocks = blocks;
this.Expressions = expressions;
}
public static bool operator <(Cost a, Cost b)
{
return a.Blocks + a.Expressions < b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks < b.Blocks;
}
public static bool operator >(Cost a, Cost b)
{
return a.Blocks + a.Expressions > b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks > b.Blocks;
}
public static bool operator <=(Cost a, Cost b)
{
return a.Blocks + a.Expressions < b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks <= b.Blocks;
}
public static bool operator >=(Cost a, Cost b)
{
return a.Blocks + a.Expressions > b.Blocks + b.Expressions
|| a.Blocks + a.Expressions == b.Blocks + b.Expressions && a.Blocks >= b.Blocks;
}
public static Cost operator +(Cost a, Cost b)
{
return new Cost(a.Blocks + b.Blocks, a.Expressions + b.Expressions);
}
public override string ToString()
{
return string.Format("[{0} + {1}]", Blocks, Expressions);
}
}
#endregion
#region class InsertedNode
/// <summary>
/// Holds the blocks and expressions that should be inserted
/// </summary>
abstract class InsertedNode
{
public static InsertedNode operator +(InsertedNode a, InsertedNode b)
{
if (a == null)
return b;
if (b == null)
return a;
return new InsertedNodeList(a, b);
}
public abstract void Insert();
}
class InsertedNodeList : InsertedNode
{
readonly InsertedNode child1, child2;
public InsertedNodeList(AddCheckedBlocks.InsertedNode child1, AddCheckedBlocks.InsertedNode child2)
{
this.child1 = child1;
this.child2 = child2;
}
public override void Insert()
{
child1.Insert();
child2.Insert();
}
}
class InsertedExpression : InsertedNode
{
readonly Expression expression;
readonly bool isChecked;
public InsertedExpression(Expression expression, bool isChecked)
{
this.expression = expression;
this.isChecked = isChecked;
}
public override void Insert()
{
if (isChecked)
expression.ReplaceWith(e => new CheckedExpression { Expression = e });
else
expression.ReplaceWith(e => new UncheckedExpression { Expression = e });
}
}
class ConvertCompoundAssignment : InsertedNode
{
readonly Expression expression;
readonly bool isChecked;
public ConvertCompoundAssignment(Expression expression, bool isChecked)
{
this.expression = expression;
this.isChecked = isChecked;
}
public override void Insert()
{
AssignmentExpression assign = expression.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>().Restore(expression);
expression.ReplaceWith(assign);
if (isChecked)
assign.Right = new CheckedExpression { Expression = assign.Right.Detach() };
else
assign.Right = new UncheckedExpression { Expression = assign.Right.Detach() };
}
}
class InsertedBlock : InsertedNode
{
readonly Statement firstStatement; // inclusive
readonly Statement lastStatement; // exclusive
readonly bool isChecked;
public InsertedBlock(Statement firstStatement, Statement lastStatement, bool isChecked)
{
this.firstStatement = firstStatement;
this.lastStatement = lastStatement;
this.isChecked = isChecked;
}
public override void Insert()
{
BlockStatement newBlock = new BlockStatement();
// Move all statements except for the first
Statement next;
for (Statement stmt = firstStatement.GetNextStatement(); stmt != lastStatement; stmt = next) {
next = stmt.GetNextStatement();
newBlock.Add(stmt.Detach());
}
// Replace the first statement with the new (un)checked block
if (isChecked)
firstStatement.ReplaceWith(new CheckedStatement { Body = newBlock });
else
firstStatement.ReplaceWith(new UncheckedStatement { Body = newBlock });
// now also move the first node into the new block
newBlock.Statements.InsertAfter(null, firstStatement);
}
}
#endregion
#region class Result
/// <summary>
/// Holds the result of an insertion operation.
/// </summary>
class Result
{
public Cost CostInCheckedContext;
public InsertedNode NodesToInsertInCheckedContext;
public Cost CostInUncheckedContext;
public InsertedNode NodesToInsertInUncheckedContext;
}
#endregion
public void Run(AstNode node)
{
BlockStatement block = node as BlockStatement;
if (block == null) {
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Run(child);
}
} else {
Result r = GetResultFromBlock(block);
if (r.NodesToInsertInUncheckedContext != null)
r.NodesToInsertInUncheckedContext.Insert();
}
}
Result GetResultFromBlock(BlockStatement block)
{
// For a block, we are tracking 4 possibilities:
// a) context is checked, no unchecked block open
Cost costCheckedContext = new Cost(0, 0);
InsertedNode nodesCheckedContext = null;
// b) context is checked, an unchecked block is open
Cost costCheckedContextUncheckedBlockOpen = Cost.Infinite;
InsertedNode nodesCheckedContextUncheckedBlockOpen = null;
Statement uncheckedBlockStart = null;
// c) context is unchecked, no checked block open
Cost costUncheckedContext = new Cost(0, 0);
InsertedNode nodesUncheckedContext = null;
// d) context is unchecked, a checked block is open
Cost costUncheckedContextCheckedBlockOpen = Cost.Infinite;
InsertedNode nodesUncheckedContextCheckedBlockOpen = null;
Statement checkedBlockStart = null;
Statement statement = block.Statements.FirstOrDefault();
while (true) {
// Blocks can be closed 'for free'. We use '<=' so that blocks are closed as late as possible (goal 4)
if (costCheckedContextUncheckedBlockOpen <= costCheckedContext) {
costCheckedContext = costCheckedContextUncheckedBlockOpen;
nodesCheckedContext = nodesCheckedContextUncheckedBlockOpen + new InsertedBlock(uncheckedBlockStart, statement, false);
}
if (costUncheckedContextCheckedBlockOpen <= costUncheckedContext) {
costUncheckedContext = costUncheckedContextCheckedBlockOpen;
nodesUncheckedContext = nodesUncheckedContextCheckedBlockOpen + new InsertedBlock(checkedBlockStart, statement, true);
}
if (statement == null)
break;
// Now try opening blocks. We use '<' so that blocks are opened as early as possible. (goal 4)
if (costCheckedContext + new Cost(1, 0) < costCheckedContextUncheckedBlockOpen) {
costCheckedContextUncheckedBlockOpen = costCheckedContext + new Cost(1, 0);
nodesCheckedContextUncheckedBlockOpen = nodesCheckedContext;
uncheckedBlockStart = statement;
}
if (costUncheckedContext + new Cost(1, 0) < costUncheckedContextCheckedBlockOpen) {
costUncheckedContextCheckedBlockOpen = costUncheckedContext + new Cost(1, 0);
nodesUncheckedContextCheckedBlockOpen = nodesUncheckedContext;
checkedBlockStart = statement;
}
// Now handle the statement
Result stmtResult = GetResult(statement);
costCheckedContext += stmtResult.CostInCheckedContext;
nodesCheckedContext += stmtResult.NodesToInsertInCheckedContext;
costCheckedContextUncheckedBlockOpen += stmtResult.CostInUncheckedContext;
nodesCheckedContextUncheckedBlockOpen += stmtResult.NodesToInsertInUncheckedContext;
costUncheckedContext += stmtResult.CostInUncheckedContext;
nodesUncheckedContext += stmtResult.NodesToInsertInUncheckedContext;
costUncheckedContextCheckedBlockOpen += stmtResult.CostInCheckedContext;
nodesUncheckedContextCheckedBlockOpen += stmtResult.NodesToInsertInCheckedContext;
statement = statement.GetNextStatement();
}
return new Result {
CostInCheckedContext = costCheckedContext, NodesToInsertInCheckedContext = nodesCheckedContext,
CostInUncheckedContext = costUncheckedContext, NodesToInsertInUncheckedContext = nodesUncheckedContext
};
}
Result GetResult(AstNode node)
{
if (node is BlockStatement)
return GetResultFromBlock((BlockStatement)node);
Result result = new Result();
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Result childResult = GetResult(child);
result.CostInCheckedContext += childResult.CostInCheckedContext;
result.NodesToInsertInCheckedContext += childResult.NodesToInsertInCheckedContext;
result.CostInUncheckedContext += childResult.CostInUncheckedContext;
result.NodesToInsertInUncheckedContext += childResult.NodesToInsertInUncheckedContext;
}
Expression expr = node as Expression;
if (expr != null) {
CheckedUncheckedAnnotation annotation = expr.Annotation<CheckedUncheckedAnnotation>();
if (annotation != null) {
// If the annotation requires this node to be in a specific context, add a huge cost to the other context
// That huge cost gives us the option to ignore a required checked/unchecked expression when there wouldn't be any
// solution otherwise. (e.g. "for (checked(M().x += 1); true; unchecked(M().x += 2)) {}")
if (annotation.IsChecked)
result.CostInUncheckedContext += new Cost(10000, 0);
else
result.CostInCheckedContext += new Cost(10000, 0);
}
// Embed this node in an checked/unchecked expression:
if (expr.Parent is ExpressionStatement) {
// We cannot use checked/unchecked for top-level-expressions.
// However, we could try converting a compound assignment (checked(a+=b);) or unary operator (checked(a++);)
// back to its old form.
if (expr.Annotation<ReplaceMethodCallsWithOperators.RestoreOriginalAssignOperatorAnnotation>() != null) {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(1, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(1, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new ConvertCompoundAssignment(expr, true);
} else if (result.CostInUncheckedContext + new Cost(1, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(1, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new ConvertCompoundAssignment(expr, false);
}
}
} else {
// We use '<' so that expressions are introduced on the deepest level possible (goal 3)
if (result.CostInCheckedContext + new Cost(0, 1) < result.CostInUncheckedContext) {
result.CostInUncheckedContext = result.CostInCheckedContext + new Cost(0, 1);
result.NodesToInsertInUncheckedContext = result.NodesToInsertInCheckedContext + new InsertedExpression(expr, true);
} else if (result.CostInUncheckedContext + new Cost(0, 1) < result.CostInCheckedContext) {
result.CostInCheckedContext = result.CostInUncheckedContext + new Cost(0, 1);
result.NodesToInsertInCheckedContext = result.NodesToInsertInUncheckedContext + new InsertedExpression(expr, false);
}
}
}
return result;
}
}
}

157
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs

@ -0,0 +1,157 @@ @@ -0,0 +1,157 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Combines query expressions and removes transparent identifiers.
/// </summary>
public class CombineQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public CombineQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
if (!context.Settings.QueryExpressions)
return;
CombineQueries(compilationUnit);
}
static readonly InvocationExpression castPattern = new InvocationExpression {
Target = new MemberReferenceExpression {
Target = new AnyNode("inExpr"),
MemberName = "Cast",
TypeArguments = { new AnyNode("targetType") }
}};
void CombineQueries(AstNode node)
{
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
CombineQueries(child);
}
QueryExpression query = node as QueryExpression;
if (query != null) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
QueryExpression innerQuery = fromClause.Expression as QueryExpression;
if (innerQuery != null) {
if (TryRemoveTransparentIdentifier(query, fromClause, innerQuery)) {
RemoveTransparentIdentifierReferences(query);
} else {
QueryContinuationClause continuation = new QueryContinuationClause();
continuation.PrecedingQuery = innerQuery.Detach();
continuation.Identifier = fromClause.Identifier;
fromClause.ReplaceWith(continuation);
}
} else {
Match m = castPattern.Match(fromClause.Expression);
if (m.Success) {
fromClause.Type = m.Get<AstType>("targetType").Single().Detach();
fromClause.Expression = m.Get<Expression>("inExpr").Single().Detach();
}
}
}
}
static readonly QuerySelectClause selectTransparentIdentifierPattern = new QuerySelectClause {
Expression = new ObjectCreateExpression {
Initializer = new ArrayInitializerExpression {
Elements = {
new NamedNode("nae1", new NamedArgumentExpression { Expression = new IdentifierExpression() }),
new NamedNode("nae2", new NamedArgumentExpression { Expression = new AnyNode() })
}
}
}};
bool IsTransparentIdentifier(string identifier)
{
return identifier.StartsWith("<>", StringComparison.Ordinal) && identifier.Contains("TransparentIdentifier");
}
bool TryRemoveTransparentIdentifier(QueryExpression query, QueryFromClause fromClause, QueryExpression innerQuery)
{
if (!IsTransparentIdentifier(fromClause.Identifier))
return false;
Match match = selectTransparentIdentifierPattern.Match(innerQuery.Clauses.Last());
if (!match.Success)
return false;
QuerySelectClause selectClause = (QuerySelectClause)innerQuery.Clauses.Last();
NamedArgumentExpression nae1 = match.Get<NamedArgumentExpression>("nae1").Single();
NamedArgumentExpression nae2 = match.Get<NamedArgumentExpression>("nae2").Single();
if (nae1.Identifier != ((IdentifierExpression)nae1.Expression).Identifier)
return false;
IdentifierExpression nae2IdentExpr = nae2.Expression as IdentifierExpression;
if (nae2IdentExpr != null && nae2.Identifier == nae2IdentExpr.Identifier) {
// from * in (from x in ... select new { x = x, y = y }) ...
// =>
// from x in ... ...
fromClause.Remove();
selectClause.Remove();
// Move clauses from innerQuery to query
QueryClause insertionPos = null;
foreach (var clause in innerQuery.Clauses) {
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
}
} else {
// from * in (from x in ... select new { x = x, y = expr }) ...
// =>
// from x in ... let y = expr ...
fromClause.Remove();
selectClause.Remove();
// Move clauses from innerQuery to query
QueryClause insertionPos = null;
foreach (var clause in innerQuery.Clauses) {
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
}
query.Clauses.InsertAfter(insertionPos, new QueryLetClause { Identifier = nae2.Identifier, Expression = nae2.Expression.Detach() });
}
return true;
}
/// <summary>
/// Removes all occurrences of transparent identifiers
/// </summary>
void RemoveTransparentIdentifierReferences(AstNode node)
{
foreach (AstNode child in node.Children) {
RemoveTransparentIdentifierReferences(child);
}
MemberReferenceExpression mre = node as MemberReferenceExpression;
if (mre != null) {
IdentifierExpression ident = mre.Target as IdentifierExpression;
if (ident != null && IsTransparentIdentifier(ident.Identifier)) {
IdentifierExpression newIdent = new IdentifierExpression(mre.MemberName);
mre.TypeArguments.MoveTo(newIdent.TypeArguments);
newIdent.CopyAnnotationsFrom(mre);
newIdent.RemoveAnnotations<PropertyDeclaration>(); // remove the reference to the property of the anonymous type
mre.ReplaceWith(newIdent);
return;
}
}
}
}
}

111
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs

@ -0,0 +1,111 @@ @@ -0,0 +1,111 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Base class for AST visitors that need the current type/method context info.
/// </summary>
public abstract class ContextTrackingVisitor<TResult> : DepthFirstAstVisitor<object, TResult>, IAstTransform
{
protected readonly DecompilerContext context;
protected ContextTrackingVisitor(DecompilerContext context)
{
if (context == null)
throw new ArgumentNullException("context");
this.context = context;
}
public override TResult VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
TypeDefinition oldType = context.CurrentType;
try {
context.CurrentType = typeDeclaration.Annotation<TypeDefinition>();
return base.VisitTypeDeclaration(typeDeclaration, data);
} finally {
context.CurrentType = oldType;
}
}
public override TResult VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = methodDeclaration.Annotation<MethodDefinition>();
return base.VisitMethodDeclaration(methodDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = constructorDeclaration.Annotation<MethodDefinition>();
return base.VisitConstructorDeclaration(constructorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = destructorDeclaration.Annotation<MethodDefinition>();
return base.VisitDestructorDeclaration(destructorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = operatorDeclaration.Annotation<MethodDefinition>();
return base.VisitOperatorDeclaration(operatorDeclaration, data);
} finally {
context.CurrentMethod = null;
}
}
public override TResult VisitAccessor(Accessor accessor, object data)
{
Debug.Assert(context.CurrentMethod == null);
try {
context.CurrentMethod = accessor.Annotation<MethodDefinition>();
return base.VisitAccessor(accessor, data);
} finally {
context.CurrentMethod = null;
}
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

176
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs

@ -0,0 +1,176 @@ @@ -0,0 +1,176 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// If the first element of a constructor is a chained constructor call, convert it into a constructor initializer.
/// </summary>
public class ConvertConstructorCallIntoInitializer : DepthFirstAstVisitor<object, object>, IAstTransform
{
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:
invocation.Arguments.MoveTo(ci.Arguments);
// Add the initializer: (unless it is the default 'base()')
if (!(ci.ConstructorInitializerType == ConstructorInitializerType.Base && ci.Arguments.Count == 0))
constructorDeclaration.Initializer = ci.WithAnnotation(invocation.Annotation<MethodReference>());
// Remove the statement:
stmt.Remove();
}
return null;
}
static readonly ExpressionStatement fieldInitializerPattern = new ExpressionStatement {
Expression = new AssignmentExpression {
Left = new NamedNode("fieldAccess", new MemberReferenceExpression { Target = new ThisReferenceExpression() }),
Operator = AssignmentOperatorType.Assign,
Right = new AnyNode("initializer")
}
};
static readonly AstNode thisCallPattern = new ExpressionStatement(new ThisReferenceExpression().Invoke(".ctor", new Repeat(new AnyNode())));
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
// Handle initializers on instance fields
HandleInstanceFieldInitializers(typeDeclaration.Members);
// Now convert base constructor calls to initializers:
base.VisitTypeDeclaration(typeDeclaration, data);
// Remove single empty constructor:
RemoveSingleEmptyConstructor(typeDeclaration);
// Handle initializers on static fields:
HandleStaticFieldInitializers(typeDeclaration.Members);
return null;
}
void HandleInstanceFieldInitializers(IEnumerable<AstNode> members)
{
var instanceCtors = members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
if (instanceCtorsNotChainingWithThis.Length > 0) {
MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
return;
// Recognize field initializers:
// Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
bool allSame;
do {
Match m = fieldInitializerPattern.Match(instanceCtorsNotChainingWithThis[0].Body.FirstOrDefault());
if (!m.Success)
break;
FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null)
break;
AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldOrEventDecl == null)
break;
allSame = true;
for (int i = 1; i < instanceCtorsNotChainingWithThis.Length; i++) {
if (!instanceCtors[0].Body.First().IsMatch(instanceCtorsNotChainingWithThis[i].Body.FirstOrDefault()))
allSame = false;
}
if (allSame) {
foreach (var ctor in instanceCtorsNotChainingWithThis)
ctor.Body.First().Remove();
fieldOrEventDecl.GetChildrenByRole(AstNode.Roles.Variable).Single().Initializer = m.Get<Expression>("initializer").Single().Detach();
}
} while (allSame);
}
}
void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
{
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
if (instanceCtors.Length == 1) {
ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
emptyCtor.Body = new BlockStatement();
if (emptyCtor.IsMatch(instanceCtors[0]))
instanceCtors[0].Remove();
}
}
void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
{
// Convert static constructor into field initializers if the class is BeforeFieldInit
var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
if (staticCtor != null) {
MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
while (true) {
ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (es == null)
break;
AssignmentExpression assignment = es.Expression as AssignmentExpression;
if (assignment == null || assignment.Operator != AssignmentOperatorType.Assign)
break;
FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null || !fieldDef.IsStatic)
break;
FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldDecl == null)
break;
fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
es.Remove();
}
if (staticCtor.Body.Statements.Count == 0)
staticCtor.Remove();
}
}
}
void IAstTransform.Run(AstNode node)
{
// If we're viewing some set of members (fields are direct children of CompilationUnit),
// we also need to handle those:
HandleInstanceFieldInitializers(node.Children);
HandleStaticFieldInitializers(node.Children);
node.AcceptVisitor(this, null);
}
}
}

58
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Transforms decimal constant fields.
/// </summary>
public class DecimalConstantTransform : DepthFirstAstVisitor<object, object>, IAstTransform
{
static readonly PrimitiveType decimalType = new PrimitiveType("decimal");
public override object VisitFieldDeclaration(FieldDeclaration fieldDeclaration, object data)
{
const Modifiers staticReadOnly = Modifiers.Static | Modifiers.Readonly;
if ((fieldDeclaration.Modifiers & staticReadOnly) == staticReadOnly && decimalType.IsMatch(fieldDeclaration.ReturnType)) {
foreach (var attributeSection in fieldDeclaration.Attributes) {
foreach (var attribute in attributeSection.Attributes) {
TypeReference tr = attribute.Type.Annotation<TypeReference>();
if (tr != null && tr.Name == "DecimalConstantAttribute" && tr.Namespace == "System.Runtime.CompilerServices") {
attribute.Remove();
if (attributeSection.Attributes.Count == 0)
attributeSection.Remove();
fieldDeclaration.Modifiers = (fieldDeclaration.Modifiers & ~staticReadOnly) | Modifiers.Const;
return null;
}
}
}
}
return null;
}
public void Run(AstNode compilationUnit)
{
compilationUnit.AcceptVisitor(this, null);
}
}
}

329
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs

@ -0,0 +1,329 @@ @@ -0,0 +1,329 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp.Analysis;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Moves variable declarations to improved positions.
/// </summary>
public class DeclareVariables : IAstTransform
{
sealed class VariableToDeclare
{
public AstType Type;
public string Name;
public AssignmentExpression ReplacedAssignment;
public Statement InsertionPoint;
}
readonly CancellationToken cancellationToken;
List<VariableToDeclare> variablesToDeclare = new List<VariableToDeclare>();
public DeclareVariables(DecompilerContext context)
{
this.cancellationToken = context.CancellationToken;
}
public void Run(AstNode node)
{
Run(node, null);
// Declare all the variables at the end, after all the logic has run.
// This is done so that definite assignment analysis can work on a single representation and doesn't have to be updated
// when we change the AST.
foreach (var v in variablesToDeclare) {
if (v.ReplacedAssignment == null) {
BlockStatement block = (BlockStatement)v.InsertionPoint.Parent;
block.Statements.InsertBefore(
v.InsertionPoint,
new VariableDeclarationStatement((AstType)v.Type.Clone(), v.Name));
}
}
// First do all the insertions, then do all the replacements. This is necessary because a replacement might remove our reference point from the AST.
foreach (var v in variablesToDeclare) {
if (v.ReplacedAssignment != null) {
// We clone the right expression so that it doesn't get removed from the old ExpressionStatement,
// which might be still in use by the definite assignment graph.
VariableDeclarationStatement varDecl = new VariableDeclarationStatement {
Type = (AstType)v.Type.Clone(),
Variables = { new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment) }
};
ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement;
if (es != null) {
// Note: if this crashes with 'Cannot replace the root node', check whether two variables were assigned the same name
es.ReplaceWith(varDecl.CopyAnnotationsFrom(es));
} else {
v.ReplacedAssignment.ReplaceWith(varDecl);
}
}
}
variablesToDeclare = null;
}
void Run(AstNode node, DefiniteAssignmentAnalysis daa)
{
BlockStatement block = node as BlockStatement;
if (block != null) {
var variables = block.Statements.TakeWhile(stmt => stmt is VariableDeclarationStatement)
.Cast<VariableDeclarationStatement>().ToList();
if (variables.Count > 0) {
// remove old variable declarations:
foreach (VariableDeclarationStatement varDecl in variables) {
Debug.Assert(varDecl.Variables.Single().Initializer.IsNull);
varDecl.Remove();
}
if (daa == null) {
// If possible, reuse the DefiniteAssignmentAnalysis that was created for the parent block
daa = new DefiniteAssignmentAnalysis(block, cancellationToken);
}
foreach (VariableDeclarationStatement varDecl in variables) {
string variableName = varDecl.Variables.Single().Name;
bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null;
DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops);
}
}
}
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
Run(child, daa);
}
}
void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops)
{
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
Statement declarationPoint = null;
// Check whether we can move down the variable into the sub-blocks
bool canMoveVariableIntoSubBlocks = FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint);
if (declarationPoint == null) {
// The variable isn't used at all
return;
}
if (canMoveVariableIntoSubBlocks) {
// Declare the variable within the sub-blocks
foreach (Statement stmt in block.Statements) {
ForStatement forStmt = stmt as ForStatement;
if (forStmt != null && forStmt.Initializers.Count == 1) {
// handle the special case of moving a variable into the for initializer
if (TryConvertAssignmentExpressionIntoVariableDeclaration(forStmt.Initializers.Single(), type, variableName))
continue;
}
UsingStatement usingStmt = stmt as UsingStatement;
if (usingStmt != null && usingStmt.ResourceAcquisition is AssignmentExpression) {
// handle the special case of moving a variable into a using statement
if (TryConvertAssignmentExpressionIntoVariableDeclaration((Expression)usingStmt.ResourceAcquisition, type, variableName))
continue;
}
foreach (AstNode child in stmt.Children) {
BlockStatement subBlock = child as BlockStatement;
if (subBlock != null) {
DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops);
} else if (HasNestedBlocks(child)) {
foreach (BlockStatement nestedSubBlock in child.Children.OfType<BlockStatement>()) {
DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops);
}
}
}
}
} else {
// Try converting an assignment expression into a VariableDeclarationStatement
if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) {
// Declare the variable in front of declarationPoint
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint });
}
}
}
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Statement declarationPoint, AstType type, string variableName)
{
// convert the declarationPoint into a VariableDeclarationStatement
ExpressionStatement es = declarationPoint as ExpressionStatement;
if (es != null) {
return TryConvertAssignmentExpressionIntoVariableDeclaration(es.Expression, type, variableName);
}
return false;
}
bool TryConvertAssignmentExpressionIntoVariableDeclaration(Expression expression, AstType type, string variableName)
{
AssignmentExpression ae = expression as AssignmentExpression;
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
IdentifierExpression ident = ae.Left as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae });
return true;
}
}
return false;
}
/// <summary>
/// Finds the declaration point for the variable within the specified block.
/// </summary>
/// <param name="daa">
/// Definite assignment analysis, must be prepared for 'block' or one of its parents.
/// </param>
/// <param name="varDecl">The variable to declare</param>
/// <param name="block">The block in which the variable should be declared</param>
/// <param name="declarationPoint">
/// Output parameter: the first statement within 'block' where the variable needs to be declared.
/// </param>
/// <returns>
/// Returns whether it is possible to move the variable declaration into sub-blocks.
/// </returns>
public static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, VariableDeclarationStatement varDecl, BlockStatement block, out Statement declarationPoint)
{
string variableName = varDecl.Variables.Single().Name;
bool allowPassIntoLoops = varDecl.Variables.Single().Annotation<DelegateConstruction.CapturedVariableAnnotation>() == null;
return FindDeclarationPoint(daa, variableName, allowPassIntoLoops, block, out declarationPoint);
}
static bool FindDeclarationPoint(DefiniteAssignmentAnalysis daa, string variableName, bool allowPassIntoLoops, BlockStatement block, out Statement declarationPoint)
{
// declarationPoint: The point where the variable would be declared, if we decide to declare it in this block
declarationPoint = null;
foreach (Statement stmt in block.Statements) {
if (UsesVariable(stmt, variableName)) {
if (declarationPoint == null)
declarationPoint = stmt;
if (!CanMoveVariableUseIntoSubBlock(stmt, variableName, allowPassIntoLoops)) {
// If it's not possible to move the variable use into a nested block,
// we need to declare the variable in this block
return false;
}
// If we can move the variable into the sub-block, we need to ensure that the remaining code
// does not use the value that was assigned by the first sub-block
Statement nextStatement = stmt.GetNextStatement();
if (nextStatement != null) {
// Analyze the range from the next statement to the end of the block
daa.SetAnalyzedRange(nextStatement, block);
daa.Analyze(variableName);
if (daa.UnassignedVariableUses.Count > 0) {
return false;
}
}
}
}
return true;
}
static bool CanMoveVariableUseIntoSubBlock(Statement stmt, string variableName, bool allowPassIntoLoops)
{
if (!allowPassIntoLoops && (stmt is ForStatement || stmt is ForeachStatement || stmt is DoWhileStatement || stmt is WhileStatement))
return false;
ForStatement forStatement = stmt as ForStatement;
if (forStatement != null && forStatement.Initializers.Count == 1) {
// for-statement is special case: we can move variable declarations into the initializer
ExpressionStatement es = forStatement.Initializers.Single() as ExpressionStatement;
if (es != null) {
AssignmentExpression ae = es.Expression as AssignmentExpression;
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
IdentifierExpression ident = ae.Left as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
return !UsesVariable(ae.Right, variableName);
}
}
}
}
UsingStatement usingStatement = stmt as UsingStatement;
if (usingStatement != null) {
// using-statement is special case: we can move variable declarations into the initializer
AssignmentExpression ae = usingStatement.ResourceAcquisition as AssignmentExpression;
if (ae != null && ae.Operator == AssignmentOperatorType.Assign) {
IdentifierExpression ident = ae.Left as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
return !UsesVariable(ae.Right, variableName);
}
}
}
// We can move the variable into a sub-block only if the variable is used in only that sub-block (and not in expressions such as the loop condition)
for (AstNode child = stmt.FirstChild; child != null; child = child.NextSibling) {
if (!(child is BlockStatement) && UsesVariable(child, variableName)) {
if (HasNestedBlocks(child)) {
// catch clauses/switch sections can contain nested blocks
for (AstNode grandchild = child.FirstChild; grandchild != null; grandchild = grandchild.NextSibling) {
if (!(grandchild is BlockStatement) && UsesVariable(grandchild, variableName))
return false;
}
} else {
return false;
}
}
}
return true;
}
static bool HasNestedBlocks(AstNode node)
{
return node is CatchClause || node is SwitchSection;
}
static bool UsesVariable(AstNode node, string variableName)
{
IdentifierExpression ie = node as IdentifierExpression;
if (ie != null && ie.Identifier == variableName)
return true;
FixedStatement fixedStatement = node as FixedStatement;
if (fixedStatement != null) {
foreach (VariableInitializer v in fixedStatement.Variables) {
if (v.Name == variableName)
return false; // no need to introduce the variable here
}
}
ForeachStatement foreachStatement = node as ForeachStatement;
if (foreachStatement != null) {
if (foreachStatement.VariableName == variableName)
return false; // no need to introduce the variable here
}
UsingStatement usingStatement = node as UsingStatement;
if (usingStatement != null) {
VariableDeclarationStatement varDecl = usingStatement.ResourceAcquisition as VariableDeclarationStatement;
if (varDecl != null) {
foreach (VariableInitializer v in varDecl.Variables) {
if (v.Name == variableName)
return false; // no need to introduce the variable here
}
}
}
CatchClause catchClause = node as CatchClause;
if (catchClause != null && catchClause.VariableName == variableName) {
return false; // no need to introduce the variable here
}
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
if (UsesVariable(child, variableName))
return true;
}
return false;
}
}
}

471
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -0,0 +1,471 @@ @@ -0,0 +1,471 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Converts "new Action(obj, ldftn(func))" into "new Action(obj.func)".
/// For anonymous methods, creates an AnonymousMethodExpression.
/// Also gets rid of any "Display Classes" left over after inlining an anonymous method.
/// </summary>
public class DelegateConstruction : ContextTrackingVisitor<object>
{
internal sealed class Annotation
{
/// <summary>
/// ldftn or ldvirtftn?
/// </summary>
public readonly bool IsVirtual;
public Annotation(bool isVirtual)
{
this.IsVirtual = isVirtual;
}
}
internal sealed class CapturedVariableAnnotation
{
}
List<string> currentlyUsedVariableNames = new List<string>();
public DelegateConstruction(DecompilerContext context) : base(context)
{
}
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))
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.GetElementType() != context.CurrentType) {
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();
mre.Target = obj;
mre.MemberName = methodIdent.Identifier;
methodIdent.TypeArguments.MoveTo(mre.TypeArguments);
mre.AddAnnotation(method);
objectCreateExpression.Arguments.Clear();
objectCreateExpression.Arguments.Add(mre);
return null;
}
}
}
return base.VisitObjectCreateExpression(objectCreateExpression, data);
}
internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method)
{
if (method == null || !(method.Name.StartsWith("<", StringComparison.Ordinal) || method.Name.Contains("$")))
return false;
if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType)))
return false;
return true;
}
bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef)
{
if (!context.Settings.AnonymousMethods)
return false; // anonymous method decompilation is disabled
if (target != null && !(target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression))
return false; // don't copy arbitrary expressions, deal with identifiers only
// Anonymous methods are defined in the same assembly
MethodDefinition method = methodRef.ResolveWithinSameModule();
if (!IsAnonymousMethod(context, method))
return false;
// Create AnonymousMethodExpression and prepare parameters
AnonymousMethodExpression ame = new AnonymousMethodExpression();
ame.CopyAnnotationsFrom(objectCreateExpression); // copy ILRanges etc.
ame.RemoveAnnotations<MethodReference>(); // remove reference to delegate ctor
ame.AddAnnotation(method); // add reference to anonymous method
ame.Parameters.AddRange(AstBuilder.MakeParameters(method, isLambda: true));
ame.HasParameterList = true;
// rename variables so that they don't conflict with the parameters:
foreach (ParameterDeclaration pd in ame.Parameters) {
EnsureVariableNameIsAvailable(objectCreateExpression, pd.Name);
}
// Decompile the anonymous method:
DecompilerContext subContext = context.Clone();
subContext.CurrentMethod = method;
subContext.ReservedVariableNames.AddRange(currentlyUsedVariableNames);
BlockStatement body = AstMethodBodyBuilder.CreateMethodBody(method, subContext, ame.Parameters);
TransformationPipeline.RunTransformationsUntil(body, v => v is DelegateConstruction, subContext);
body.AcceptVisitor(this, null);
bool isLambda = false;
if (ame.Parameters.All(p => p.ParameterModifier == ParameterModifier.None)) {
isLambda = (body.Statements.Count == 1 && body.Statements.Single() is ReturnStatement);
}
// Remove the parameter list from an AnonymousMethodExpression if the original method had no names,
// and the parameters are not used in the method body
if (!isLambda && method.Parameters.All(p => string.IsNullOrEmpty(p.Name))) {
var parameterReferencingIdentifiers =
from ident in body.Descendants.OfType<IdentifierExpression>()
let v = ident.Annotation<ILVariable>()
where v != null && v.IsParameter && method.Parameters.Contains(v.OriginalParameter)
select ident;
if (!parameterReferencingIdentifiers.Any()) {
ame.Parameters.Clear();
ame.HasParameterList = false;
}
}
// 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());
}
if (isLambda) {
LambdaExpression lambda = new LambdaExpression();
lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters);
Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression;
returnExpr.Remove();
lambda.Body = returnExpr;
objectCreateExpression.ReplaceWith(lambda);
} else {
ame.Body = body;
objectCreateExpression.ReplaceWith(ame);
}
return true;
}
internal static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass)
{
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return false;
// check that methodContainingType is within containingType
while (potentialDisplayClass != context.CurrentType) {
potentialDisplayClass = potentialDisplayClass.DeclaringType;
if (potentialDisplayClass == null)
return false;
}
return true;
}
#region Track current variables
public override object VisitMethodDeclaration(MethodDeclaration methodDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(methodDeclaration.Parameters.Select(p => p.Name));
return base.VisitMethodDeclaration(methodDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(operatorDeclaration.Parameters.Select(p => p.Name));
return base.VisitOperatorDeclaration(operatorDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(constructorDeclaration.Parameters.Select(p => p.Name));
return base.VisitConstructorDeclaration(constructorDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, object data)
{
Debug.Assert(currentlyUsedVariableNames.Count == 0);
try {
currentlyUsedVariableNames.AddRange(indexerDeclaration.Parameters.Select(p => p.Name));
return base.VisitIndexerDeclaration(indexerDeclaration, data);
} finally {
currentlyUsedVariableNames.Clear();
}
}
public override object VisitAccessor(Accessor accessor, object data)
{
try {
currentlyUsedVariableNames.Add("value");
return base.VisitAccessor(accessor, data);
} finally {
currentlyUsedVariableNames.RemoveAt(currentlyUsedVariableNames.Count - 1);
}
}
public override object VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, object data)
{
foreach (VariableInitializer v in variableDeclarationStatement.Variables)
currentlyUsedVariableNames.Add(v.Name);
return base.VisitVariableDeclarationStatement(variableDeclarationStatement, data);
}
public override object VisitFixedStatement(FixedStatement fixedStatement, object data)
{
foreach (VariableInitializer v in fixedStatement.Variables)
currentlyUsedVariableNames.Add(v.Name);
return base.VisitFixedStatement(fixedStatement, data);
}
#endregion
static readonly ExpressionStatement displayClassAssignmentPattern =
new ExpressionStatement(new AssignmentExpression(
new NamedNode("variable", new IdentifierExpression()),
new ObjectCreateExpression { Type = new AnyNode("type") }
));
public override object VisitBlockStatement(BlockStatement blockStatement, object data)
{
int numberOfVariablesOutsideBlock = currentlyUsedVariableNames.Count;
base.VisitBlockStatement(blockStatement, data);
foreach (ExpressionStatement stmt in blockStatement.Statements.OfType<ExpressionStatement>().ToArray()) {
Match displayClassAssignmentMatch = displayClassAssignmentPattern.Match(stmt);
if (!displayClassAssignmentMatch.Success)
continue;
ILVariable variable = displayClassAssignmentMatch.Get<AstNode>("variable").Single().Annotation<ILVariable>();
if (variable == null)
continue;
TypeDefinition type = variable.Type.ResolveWithinSameModule();
if (!IsPotentialClosure(context, type))
continue;
if (displayClassAssignmentMatch.Get<AstType>("type").Single().Annotation<TypeReference>().ResolveWithinSameModule() != type)
continue;
// Looks like we found a display class creation. Now let's verify that the variable is used only for field accesses:
bool ok = true;
foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) {
if (identExpr.Identifier == variable.Name && identExpr != displayClassAssignmentMatch.Get("variable").Single()) {
if (!(identExpr.Parent is MemberReferenceExpression && identExpr.Parent.Annotation<FieldReference>() != null))
ok = false;
}
}
if (!ok)
continue;
Dictionary<FieldReference, AstNode> dict = new Dictionary<FieldReference, AstNode>();
// Delete the variable declaration statement:
VariableDeclarationStatement displayClassVarDecl = PatternStatementTransform.FindVariableDeclaration(stmt, variable.Name);
if (displayClassVarDecl != null)
displayClassVarDecl.Remove();
// Delete the assignment statement:
AstNode cur = stmt.NextSibling;
stmt.Remove();
// Delete any following statements as long as they assign parameters to the display class
BlockStatement rootBlock = blockStatement.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? blockStatement;
List<ILVariable> parameterOccurrances = rootBlock.Descendants.OfType<IdentifierExpression>()
.Select(n => n.Annotation<ILVariable>()).Where(p => p != null && p.IsParameter).ToList();
AstNode next;
for (; cur != null; cur = next) {
next = cur.NextSibling;
// Test for the pattern:
// "variableName.MemberName = right;"
ExpressionStatement closureFieldAssignmentPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("left", new MemberReferenceExpression { Target = new IdentifierExpression(variable.Name) }),
new AnyNode("right")
)
);
Match m = closureFieldAssignmentPattern.Match(cur);
if (m.Success) {
FieldDefinition fieldDef = m.Get<MemberReferenceExpression>("left").Single().Annotation<FieldReference>().ResolveWithinSameModule();
AstNode right = m.Get<AstNode>("right").Single();
bool isParameter = false;
bool isDisplayClassParentPointerAssignment = false;
if (right is ThisReferenceExpression) {
isParameter = true;
} else if (right is IdentifierExpression) {
// handle parameters only if the whole method contains no other occurrence except for 'right'
ILVariable v = right.Annotation<ILVariable>();
isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
// parent display class within the same method
// (closure2.localsX = closure1;)
isDisplayClassParentPointerAssignment = true;
}
} else if (right is MemberReferenceExpression) {
// copy of parent display class reference from an outer lambda
// closure2.localsX = this.localsY
MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("right").Single();
do {
// descend into the targets of the mre as long as the field types are closures
FieldDefinition fieldDef2 = mre.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ResolveWithinSameModule())) {
break;
}
// if we finally get to a this reference, it's copying a display class parent pointer
if (mre.Target is ThisReferenceExpression) {
isDisplayClassParentPointerAssignment = true;
}
mre = mre.Target as MemberReferenceExpression;
} while (mre != null);
}
if (isParameter || isDisplayClassParentPointerAssignment) {
dict[fieldDef] = right;
cur.Remove();
} else {
break;
}
} else {
break;
}
}
// Now create variables for all fields of the display class (except for those that we already handled as parameters)
List<Tuple<AstType, string>> variablesToDeclare = new List<Tuple<AstType, string>>();
foreach (FieldDefinition field in type.Fields) {
if (field.IsStatic)
continue; // skip static fields
if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
continue;
string capturedVariableName = field.Name;
if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10)
capturedVariableName = capturedVariableName.Substring(10);
EnsureVariableNameIsAvailable(blockStatement, capturedVariableName);
currentlyUsedVariableNames.Add(capturedVariableName);
variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), capturedVariableName));
dict[field] = new IdentifierExpression(capturedVariableName);
}
// Now figure out where the closure was accessed and use the simpler replacement expression there:
foreach (var identExpr in blockStatement.Descendants.OfType<IdentifierExpression>()) {
if (identExpr.Identifier == variable.Name) {
MemberReferenceExpression mre = (MemberReferenceExpression)identExpr.Parent;
AstNode replacement;
if (dict.TryGetValue(mre.Annotation<FieldReference>().ResolveWithinSameModule(), out replacement)) {
mre.ReplaceWith(replacement.Clone());
}
}
}
// Now insert the variable declarations (we can do this after the replacements only so that the scope detection works):
Statement insertionPoint = blockStatement.Statements.FirstOrDefault();
foreach (var tuple in variablesToDeclare) {
var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2);
newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation());
blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl);
}
}
currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock);
return null;
}
void EnsureVariableNameIsAvailable(AstNode currentNode, string name)
{
int pos = currentlyUsedVariableNames.IndexOf(name);
if (pos < 0) {
// name is still available
return;
}
// Naming conflict. Let's rename the existing variable so that the field keeps the name from metadata.
NameVariables nv = new NameVariables();
// Add currently used variable and parameter names
foreach (string nameInUse in currentlyUsedVariableNames)
nv.AddExistingName(nameInUse);
// variables declared in child nodes of this block
foreach (VariableInitializer vi in currentNode.Descendants.OfType<VariableInitializer>())
nv.AddExistingName(vi.Name);
// parameters in child lambdas
foreach (ParameterDeclaration pd in currentNode.Descendants.OfType<ParameterDeclaration>())
nv.AddExistingName(pd.Name);
string newName = nv.GetAlternativeName(name);
currentlyUsedVariableNames[pos] = newName;
// find top-most block
AstNode topMostBlock = currentNode.Ancestors.OfType<BlockStatement>().LastOrDefault() ?? currentNode;
// rename identifiers
foreach (IdentifierExpression ident in topMostBlock.Descendants.OfType<IdentifierExpression>()) {
if (ident.Identifier == name) {
ident.Identifier = newName;
ILVariable v = ident.Annotation<ILVariable>();
if (v != null)
v.Name = newName;
}
}
// rename variable declarations
foreach (VariableInitializer vi in topMostBlock.Descendants.OfType<VariableInitializer>()) {
if (vi.Name == name) {
vi.Name = newName;
ILVariable v = vi.Annotation<ILVariable>();
if (v != null)
v.Name = newName;
}
}
}
}
}

62
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Converts extension method calls into infix syntax.
/// </summary>
public class IntroduceExtensionMethods : IAstTransform
{
readonly DecompilerContext context;
public IntroduceExtensionMethods(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
foreach (InvocationExpression invocation in compilationUnit.Descendants.OfType<InvocationExpression>()) {
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
MethodReference methodReference = invocation.Annotation<MethodReference>();
if (mre != null && mre.Target is TypeReferenceExpression && methodReference != null && invocation.Arguments.Any()) {
MethodDefinition d = methodReference.Resolve();
if (d != null) {
foreach (var ca in d.CustomAttributes) {
if (ca.AttributeType.Name == "ExtensionAttribute" && ca.AttributeType.Namespace == "System.Runtime.CompilerServices") {
mre.Target = invocation.Arguments.First().Detach();
if (invocation.Arguments.Any()) {
// HACK: removing type arguments should be done indepently from whether a method is an extension method,
// just by testing whether the arguments can be inferred
mre.TypeArguments.Clear();
}
break;
}
}
}
}
}
}
}
}

295
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs

@ -0,0 +1,295 @@ @@ -0,0 +1,295 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Decompiles query expressions.
/// Based on C# 4.0 spec, §7.16.2 Query expression translation
/// </summary>
public class IntroduceQueryExpressions : IAstTransform
{
readonly DecompilerContext context;
public IntroduceQueryExpressions(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
if (!context.Settings.QueryExpressions)
return;
DecompileQueries(compilationUnit);
// After all queries were decompiled, detect degenerate queries (queries not property terminated with 'select' or 'group')
// and fix them, either by adding a degenerate select, or by combining them with another query.
foreach (QueryExpression query in compilationUnit.Descendants.OfType<QueryExpression>()) {
QueryFromClause fromClause = (QueryFromClause)query.Clauses.First();
if (IsDegenerateQuery(query)) {
// introduce select for degenerate query
query.Clauses.Add(new QuerySelectClause { Expression = new IdentifierExpression(fromClause.Identifier) });
}
// See if the data source of this query is a degenerate query,
// and combine the queries if possible.
QueryExpression innerQuery = fromClause.Expression as QueryExpression;
while (IsDegenerateQuery(innerQuery)) {
QueryFromClause innerFromClause = (QueryFromClause)innerQuery.Clauses.First();
if (fromClause.Identifier != innerFromClause.Identifier)
break;
// Replace the fromClause with all clauses from the inner query
fromClause.Remove();
QueryClause insertionPos = null;
foreach (var clause in innerQuery.Clauses) {
query.Clauses.InsertAfter(insertionPos, insertionPos = clause.Detach());
}
fromClause = innerFromClause;
innerQuery = fromClause.Expression as QueryExpression;
}
}
}
bool IsDegenerateQuery(QueryExpression query)
{
if (query == null)
return false;
var lastClause = query.Clauses.LastOrDefault();
return !(lastClause is QuerySelectClause || lastClause is QueryGroupClause);
}
void DecompileQueries(AstNode node)
{
QueryExpression query = DecompileQuery(node as InvocationExpression);
if (query != null)
node.ReplaceWith(query);
for (AstNode child = (query ?? node).FirstChild; child != null; child = child.NextSibling) {
DecompileQueries(child);
}
}
QueryExpression DecompileQuery(InvocationExpression invocation)
{
if (invocation == null)
return null;
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
if (mre == null)
return null;
switch (mre.MemberName) {
case "Select":
{
if (invocation.Arguments.Count != 1)
return null;
string parameterName;
Expression body;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(new QuerySelectClause { Expression = body.Detach() });
return query;
}
return null;
}
case "GroupBy":
{
if (invocation.Arguments.Count == 2) {
string parameterName1, parameterName2;
Expression keySelector, elementSelector;
if (MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName1, out keySelector)
&& MatchSimpleLambda(invocation.Arguments.ElementAt(1), out parameterName2, out elementSelector)
&& parameterName1 == parameterName2)
{
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName1, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryGroupClause { Projection = elementSelector.Detach(), Key = keySelector.Detach() });
return query;
}
} else if (invocation.Arguments.Count == 1) {
string parameterName;
Expression keySelector;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out keySelector)) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryGroupClause { Projection = new IdentifierExpression(parameterName), Key = keySelector.Detach() });
return query;
}
}
return null;
}
case "SelectMany":
{
if (invocation.Arguments.Count != 2)
return null;
string parameterName;
Expression collectionSelector;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(0), out parameterName, out collectionSelector))
return null;
LambdaExpression lambda = invocation.Arguments.ElementAt(1) as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) {
ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
if (p1.Name == parameterName) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = p1.Name, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryFromClause { Identifier = p2.Name, Expression = collectionSelector.Detach() });
query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() });
return query;
}
}
return null;
}
case "Where":
{
if (invocation.Arguments.Count != 1)
return null;
string parameterName;
Expression body;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body)) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(new QueryWhereClause { Condition = body.Detach() });
return query;
}
return null;
}
case "OrderBy":
case "OrderByDescending":
case "ThenBy":
case "ThenByDescending":
{
if (invocation.Arguments.Count != 1)
return null;
string parameterName;
Expression orderExpression;
if (MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out orderExpression)) {
if (ValidateThenByChain(invocation, parameterName)) {
QueryOrderClause orderClause = new QueryOrderClause();
InvocationExpression tmp = invocation;
while (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending") {
// insert new ordering at beginning
orderClause.Orderings.InsertAfter(
null, new QueryOrdering {
Expression = orderExpression.Detach(),
Direction = (mre.MemberName == "ThenBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
});
tmp = (InvocationExpression)mre.Target;
mre = (MemberReferenceExpression)tmp.Target;
MatchSimpleLambda(tmp.Arguments.Single(), out parameterName, out orderExpression);
}
// insert new ordering at beginning
orderClause.Orderings.InsertAfter(
null, new QueryOrdering {
Expression = orderExpression.Detach(),
Direction = (mre.MemberName == "OrderBy" ? QueryOrderingDirection.None : QueryOrderingDirection.Descending)
});
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = parameterName, Expression = mre.Target.Detach() });
query.Clauses.Add(orderClause);
return query;
}
}
return null;
}
case "Join":
case "GroupJoin":
{
if (invocation.Arguments.Count != 4)
return null;
Expression source1 = mre.Target;
Expression source2 = invocation.Arguments.ElementAt(0);
string elementName1, elementName2;
Expression key1, key2;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(1), out elementName1, out key1))
return null;
if (!MatchSimpleLambda(invocation.Arguments.ElementAt(2), out elementName2, out key2))
return null;
LambdaExpression lambda = invocation.Arguments.ElementAt(3) as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 2 && lambda.Body is Expression) {
ParameterDeclaration p1 = lambda.Parameters.ElementAt(0);
ParameterDeclaration p2 = lambda.Parameters.ElementAt(1);
if (p1.Name == elementName1 && (p2.Name == elementName2 || mre.MemberName == "GroupJoin")) {
QueryExpression query = new QueryExpression();
query.Clauses.Add(new QueryFromClause { Identifier = elementName1, Expression = source1.Detach() });
QueryJoinClause joinClause = new QueryJoinClause();
joinClause.JoinIdentifier = elementName2; // join elementName2
joinClause.InExpression = source2.Detach(); // in source2
joinClause.OnExpression = key1.Detach(); // on key1
joinClause.EqualsExpression = key2.Detach(); // equals key2
if (mre.MemberName == "GroupJoin") {
joinClause.IntoIdentifier = p2.Name; // into p2.Name
}
query.Clauses.Add(joinClause);
query.Clauses.Add(new QuerySelectClause { Expression = ((Expression)lambda.Body).Detach() });
return query;
}
}
return null;
}
default:
return null;
}
}
/// <summary>
/// Ensure that all ThenBy's are correct, and that the list of ThenBy's is terminated by an 'OrderBy' invocation.
/// </summary>
bool ValidateThenByChain(InvocationExpression invocation, string expectedParameterName)
{
if (invocation == null || invocation.Arguments.Count != 1)
return false;
MemberReferenceExpression mre = invocation.Target as MemberReferenceExpression;
if (mre == null)
return false;
string parameterName;
Expression body;
if (!MatchSimpleLambda(invocation.Arguments.Single(), out parameterName, out body))
return false;
if (parameterName != expectedParameterName)
return false;
if (mre.MemberName == "OrderBy" || mre.MemberName == "OrderByDescending")
return true;
else if (mre.MemberName == "ThenBy" || mre.MemberName == "ThenByDescending")
return ValidateThenByChain(mre.Target as InvocationExpression, expectedParameterName);
else
return false;
}
/// <summary>Matches simple lambdas of the form "a => b"</summary>
bool MatchSimpleLambda(Expression expr, out string parameterName, out Expression body)
{
LambdaExpression lambda = expr as LambdaExpression;
if (lambda != null && lambda.Parameters.Count == 1 && lambda.Body is Expression) {
ParameterDeclaration p = lambda.Parameters.Single();
if (p.ParameterModifier == ParameterModifier.None) {
parameterName = p.Name;
body = (Expression)lambda.Body;
return true;
}
}
parameterName = null;
body = null;
return false;
}
}
}

106
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs

@ -0,0 +1,106 @@ @@ -0,0 +1,106 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public class IntroduceUnsafeModifier : DepthFirstAstVisitor<object, bool>, IAstTransform
{
public static readonly object PointerArithmeticAnnotation = new PointerArithmetic();
sealed class PointerArithmetic {}
public void Run(AstNode compilationUnit)
{
compilationUnit.AcceptVisitor(this, null);
}
protected override bool VisitChildren(AstNode node, object data)
{
bool result = false;
for (AstNode child = node.FirstChild; child != null; child = child.NextSibling) {
result |= child.AcceptVisitor(this, data);
}
if (result && node is AttributedNode && !(node is Accessor)) {
((AttributedNode)node).Modifiers |= Modifiers.Unsafe;
return false;
}
return result;
}
public override bool VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, object data)
{
base.VisitPointerReferenceExpression(pointerReferenceExpression, data);
return true;
}
public override bool VisitComposedType(ComposedType composedType, object data)
{
if (composedType.PointerRank > 0)
return true;
else
return base.VisitComposedType(composedType, data);
}
public override bool VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, object data)
{
bool result = base.VisitUnaryOperatorExpression(unaryOperatorExpression, data);
if (unaryOperatorExpression.Operator == UnaryOperatorType.Dereference) {
BinaryOperatorExpression bop = unaryOperatorExpression.Expression as BinaryOperatorExpression;
if (bop != null && bop.Operator == BinaryOperatorType.Add && bop.Annotation<PointerArithmetic>() != null) {
// transform "*(ptr + int)" to "ptr[int]"
IndexerExpression indexer = new IndexerExpression();
indexer.Target = bop.Left.Detach();
indexer.Arguments.Add(bop.Right.Detach());
indexer.CopyAnnotationsFrom(unaryOperatorExpression);
indexer.CopyAnnotationsFrom(bop);
unaryOperatorExpression.ReplaceWith(indexer);
}
return true;
} else if (unaryOperatorExpression.Operator == UnaryOperatorType.AddressOf) {
return true;
} else {
return result;
}
}
public override bool VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, object data)
{
bool result = base.VisitMemberReferenceExpression(memberReferenceExpression, data);
UnaryOperatorExpression uoe = memberReferenceExpression.Target as UnaryOperatorExpression;
if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference) {
PointerReferenceExpression pre = new PointerReferenceExpression();
pre.Target = uoe.Expression.Detach();
pre.MemberName = memberReferenceExpression.MemberName;
memberReferenceExpression.TypeArguments.MoveTo(pre.TypeArguments);
pre.CopyAnnotationsFrom(uoe);
pre.CopyAnnotationsFrom(memberReferenceExpression);
memberReferenceExpression.ReplaceWith(pre);
}
return result;
}
public override bool VisitStackAllocExpression(StackAllocExpression stackAllocExpression, object data)
{
base.VisitStackAllocExpression(stackAllocExpression, data);
return true;
}
}
}

360
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs

@ -0,0 +1,360 @@ @@ -0,0 +1,360 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <summary>
/// Introduces using declarations.
/// </summary>
public class IntroduceUsingDeclarations : IAstTransform
{
DecompilerContext context;
public IntroduceUsingDeclarations(DecompilerContext context)
{
this.context = context;
}
public void Run(AstNode compilationUnit)
{
if (!context.Settings.UsingDeclarations)
return;
// First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
importedNamespaces.Add("System"); // always import System, even when not necessary
// Now add using declarations for those namespaces:
foreach (string ns in importedNamespaces.OrderByDescending(n => n)) {
// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
// (always inserting at the start of the list)
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] };
}
compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, CompilationUnit.MemberRole);
}
if (!context.Settings.FullyQualifyAmbiguousTypeNames)
return;
FindAmbiguousTypeNames(context.CurrentModule, internalsVisible: true);
foreach (AssemblyNameReference r in context.CurrentModule.AssemblyReferences) {
AssemblyDefinition d = context.CurrentModule.AssemblyResolver.Resolve(r);
if (d != null)
FindAmbiguousTypeNames(d.MainModule, internalsVisible: false);
}
// verify that the SimpleTypes refer to the correct type (no ambiguities)
compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
}
readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
readonly HashSet<string> importedNamespaces = new HashSet<string>();
// Note that we store type names with `n suffix, so we automatically disambiguate based on number of type parameters.
readonly HashSet<string> availableTypeNames = new HashSet<string>();
readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
{
readonly IntroduceUsingDeclarations transform;
string currentNamespace;
public FindRequiredImports(IntroduceUsingDeclarations transform)
{
this.transform = transform;
this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
}
bool IsParentOfCurrentNamespace(string ns)
{
if (ns.Length == 0)
return true;
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) {
if (currentNamespace.Length == ns.Length)
return true;
if (currentNamespace[ns.Length] == '.')
return true;
}
return false;
}
public override object VisitSimpleType(SimpleType simpleType, object data)
{
TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
transform.importedNamespaces.Add(tr.Namespace);
}
return base.VisitSimpleType(simpleType, data); // also visit type arguments
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (Identifier ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
transform.declaredNamespaces.Add(currentNamespace);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
}
void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
{
foreach (TypeDefinition type in module.Types) {
if (internalsVisible || type.IsPublic) {
if (importedNamespaces.Contains(type.Namespace) || declaredNamespaces.Contains(type.Namespace)) {
if (!availableTypeNames.Add(type.Name))
ambiguousTypeNames.Add(type.Name);
}
}
}
}
sealed class FullyQualifyAmbiguousTypeNamesVisitor : DepthFirstAstVisitor<object, object>
{
readonly IntroduceUsingDeclarations transform;
string currentNamespace;
HashSet<string> currentMemberTypes;
Dictionary<string, MemberReference> currentMembers;
bool isWithinTypeReferenceExpression;
public FullyQualifyAmbiguousTypeNamesVisitor(IntroduceUsingDeclarations transform)
{
this.transform = transform;
this.currentNamespace = transform.context.CurrentType != null ? transform.context.CurrentType.Namespace : string.Empty;
}
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data)
{
string oldNamespace = currentNamespace;
foreach (Identifier ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
}
base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace;
return null;
}
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
HashSet<string> oldMemberTypes = currentMemberTypes;
currentMemberTypes = currentMemberTypes != null ? new HashSet<string>(currentMemberTypes) : new HashSet<string>();
Dictionary<string, MemberReference> oldMembers = currentMembers;
currentMembers = new Dictionary<string, MemberReference>();
TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>();
bool privateMembersVisible = true;
ModuleDefinition internalMembersVisibleInModule = typeDef.Module;
while (typeDef != null) {
foreach (GenericParameter gp in typeDef.GenericParameters) {
currentMemberTypes.Add(gp.Name);
}
foreach (TypeDefinition t in typeDef.NestedTypes) {
if (privateMembersVisible || IsVisible(t, internalMembersVisibleInModule))
currentMemberTypes.Add(t.Name.Substring(t.Name.LastIndexOf('+') + 1));
}
foreach (MethodDefinition method in typeDef.Methods) {
if (privateMembersVisible || IsVisible(method, internalMembersVisibleInModule))
AddCurrentMember(method);
}
foreach (PropertyDefinition property in typeDef.Properties) {
if (privateMembersVisible || IsVisible(property.GetMethod, internalMembersVisibleInModule) || IsVisible(property.SetMethod, internalMembersVisibleInModule))
AddCurrentMember(property);
}
foreach (EventDefinition ev in typeDef.Events) {
if (privateMembersVisible || IsVisible(ev.AddMethod, internalMembersVisibleInModule) || IsVisible(ev.RemoveMethod, internalMembersVisibleInModule))
AddCurrentMember(ev);
}
foreach (FieldDefinition f in typeDef.Fields) {
if (privateMembersVisible || IsVisible(f, internalMembersVisibleInModule))
AddCurrentMember(f);
}
// repeat with base class:
if (typeDef.BaseType != null)
typeDef = typeDef.BaseType.Resolve();
else
typeDef = null;
privateMembersVisible = false;
}
// Now add current members from outer classes:
if (oldMembers != null) {
foreach (var pair in oldMembers) {
// add members from outer classes only if the inner class doesn't define the member
if (!currentMembers.ContainsKey(pair.Key))
currentMembers.Add(pair.Key, pair.Value);
}
}
base.VisitTypeDeclaration(typeDeclaration, data);
currentMembers = oldMembers;
return null;
}
void AddCurrentMember(MemberReference m)
{
MemberReference existingMember;
if (currentMembers.TryGetValue(m.Name, out existingMember)) {
// We keep the existing member assignment if it was from another class (=from a derived class),
// because members in derived classes have precedence over members in base classes.
if (existingMember != null && existingMember.DeclaringType == m.DeclaringType) {
// Use null as value to signalize multiple members with the same name
currentMembers[m.Name] = null;
}
} else {
currentMembers.Add(m.Name, m);
}
}
bool IsVisible(MethodDefinition m, ModuleDefinition internalMembersVisibleInModule)
{
if (m == null)
return false;
switch (m.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.FamANDAssem:
case MethodAttributes.Assembly:
return m.Module == internalMembersVisibleInModule;
case MethodAttributes.Family:
case MethodAttributes.FamORAssem:
case MethodAttributes.Public:
return true;
default:
return false;
}
}
bool IsVisible(FieldDefinition f, ModuleDefinition internalMembersVisibleInModule)
{
if (f == null)
return false;
switch (f.Attributes & FieldAttributes.FieldAccessMask) {
case FieldAttributes.FamANDAssem:
case FieldAttributes.Assembly:
return f.Module == internalMembersVisibleInModule;
case FieldAttributes.Family:
case FieldAttributes.FamORAssem:
case FieldAttributes.Public:
return true;
default:
return false;
}
}
bool IsVisible(TypeDefinition t, ModuleDefinition internalMembersVisibleInModule)
{
if (t == null)
return false;
switch (t.Attributes & TypeAttributes.VisibilityMask) {
case TypeAttributes.NotPublic:
case TypeAttributes.NestedAssembly:
case TypeAttributes.NestedFamANDAssem:
return t.Module == internalMembersVisibleInModule;
case TypeAttributes.NestedFamily:
case TypeAttributes.NestedFamORAssem:
case TypeAttributes.NestedPublic:
case TypeAttributes.Public:
return true;
default:
return false;
}
}
public override object VisitSimpleType(SimpleType simpleType, object data)
{
// Handle type arguments first, so that the fixed-up type arguments get moved over to the MemberType,
// if we're also creating one here.
base.VisitSimpleType(simpleType, data);
TypeReference tr = simpleType.Annotation<TypeReference>();
// Fully qualify any ambiguous type names.
if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
AstType ns;
if (string.IsNullOrEmpty(tr.Namespace)) {
ns = new SimpleType("global");
} else {
string[] parts = tr.Namespace.Split('.');
if (IsAmbiguous(string.Empty, parts[0])) {
// conflict between namespace and type name/member name
ns = new MemberType { Target = new SimpleType("global"), IsDoubleColon = true, MemberName = parts[0] };
} else {
ns = new SimpleType(parts[0]);
}
for (int i = 1; i < parts.Length; i++) {
ns = new MemberType { Target = ns, MemberName = parts[i] };
}
}
MemberType mt = new MemberType();
mt.Target = ns;
mt.IsDoubleColon = string.IsNullOrEmpty(tr.Namespace);
mt.MemberName = simpleType.Identifier;
mt.CopyAnnotationsFrom(simpleType);
simpleType.TypeArguments.MoveTo(mt.TypeArguments);
simpleType.ReplaceWith(mt);
}
return null;
}
public override object VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, object data)
{
isWithinTypeReferenceExpression = true;
base.VisitTypeReferenceExpression(typeReferenceExpression, data);
isWithinTypeReferenceExpression = false;
return null;
}
bool IsAmbiguous(string ns, string name)
{
// If the type name conflicts with an inner class/type parameter, we need to fully-qualify it:
if (currentMemberTypes != null && currentMemberTypes.Contains(name))
return true;
// If the type name conflicts with a field/property etc. on the current class, we need to fully-qualify it,
// if we're inside an expression.
if (isWithinTypeReferenceExpression && currentMembers != null) {
MemberReference mr;
if (currentMembers.TryGetValue(name, out mr)) {
// However, in the special case where the member is a field or property with the same type
// as is requested, then we can use the short name (if it's not otherwise ambiguous)
PropertyDefinition prop = mr as PropertyDefinition;
FieldDefinition field = mr as FieldDefinition;
if (!(prop != null && prop.PropertyType.Namespace == ns && prop.PropertyType.Name == name)
&& !(field != null && field.FieldType.Namespace == ns && field.FieldType.Name == name))
return true;
}
}
// If the type is defined in the current namespace,
// then we can use the short name even if we imported type with same name from another namespace.
if (ns == currentNamespace && !string.IsNullOrEmpty(ns))
return false;
return transform.ambiguousTypeNames.Contains(name);
}
}
}
}

1014
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs

File diff suppressed because it is too large Load Diff

150
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs

@ -0,0 +1,150 @@ @@ -0,0 +1,150 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public class PushNegation: DepthFirstAstVisitor<object, object>, IAstTransform
{
public override object VisitUnaryOperatorExpression(UnaryOperatorExpression unary, object data)
{
// Remove double negation
// !!a
if (unary.Operator == UnaryOperatorType.Not &&
unary.Expression is UnaryOperatorExpression &&
(unary.Expression as UnaryOperatorExpression).Operator == UnaryOperatorType.Not)
{
AstNode newNode = (unary.Expression as UnaryOperatorExpression).Expression;
unary.ReplaceWith(newNode);
return newNode.AcceptVisitor(this, data);
}
// Push through binary operation
// !((a) op (b))
BinaryOperatorExpression binaryOp = unary.Expression as BinaryOperatorExpression;
if (unary.Operator == UnaryOperatorType.Not && binaryOp != null) {
bool successful = true;
switch (binaryOp.Operator) {
case BinaryOperatorType.Equality:
binaryOp.Operator = BinaryOperatorType.InEquality;
break;
case BinaryOperatorType.InEquality:
binaryOp.Operator = BinaryOperatorType.Equality;
break;
case BinaryOperatorType.GreaterThan: // TODO: these are invalid for floats (stupid NaN)
binaryOp.Operator = BinaryOperatorType.LessThanOrEqual;
break;
case BinaryOperatorType.GreaterThanOrEqual:
binaryOp.Operator = BinaryOperatorType.LessThan;
break;
case BinaryOperatorType.LessThanOrEqual:
binaryOp.Operator = BinaryOperatorType.GreaterThan;
break;
case BinaryOperatorType.LessThan:
binaryOp.Operator = BinaryOperatorType.GreaterThanOrEqual;
break;
default:
successful = false;
break;
}
if (successful) {
unary.ReplaceWith(binaryOp);
return binaryOp.AcceptVisitor(this, data);
}
successful = true;
switch (binaryOp.Operator) {
case BinaryOperatorType.ConditionalAnd:
binaryOp.Operator = BinaryOperatorType.ConditionalOr;
break;
case BinaryOperatorType.ConditionalOr:
binaryOp.Operator = BinaryOperatorType.ConditionalAnd;
break;
default:
successful = false;
break;
}
if (successful) {
binaryOp.Left.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
binaryOp.Right.ReplaceWith(e => new UnaryOperatorExpression(UnaryOperatorType.Not, e));
unary.ReplaceWith(binaryOp);
return binaryOp.AcceptVisitor(this, data);
}
}
return base.VisitUnaryOperatorExpression(unary, data);
}
readonly static AstNode asCastIsNullPattern = new BinaryOperatorExpression(
new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
BinaryOperatorType.Equality,
new NullReferenceExpression()
);
readonly static AstNode asCastIsNotNullPattern = new BinaryOperatorExpression(
new AnyNode("expr").ToExpression().CastAs(new AnyNode("type")),
BinaryOperatorType.InEquality,
new NullReferenceExpression()
);
public override object VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, object data)
{
BinaryOperatorType op = binaryOperatorExpression.Operator;
bool? rightOperand = null;
if (binaryOperatorExpression.Right is PrimitiveExpression)
rightOperand = ((PrimitiveExpression)binaryOperatorExpression.Right).Value as bool?;
if (op == BinaryOperatorType.Equality && rightOperand == true || op == BinaryOperatorType.InEquality && rightOperand == false) {
// 'b == true' or 'b != false' is useless
binaryOperatorExpression.Left.AcceptVisitor(this, data);
binaryOperatorExpression.ReplaceWith(binaryOperatorExpression.Left);
return null;
} else if (op == BinaryOperatorType.Equality && rightOperand == false || op == BinaryOperatorType.InEquality && rightOperand == true) {
// 'b == false' or 'b != true' is a negation:
Expression left = binaryOperatorExpression.Left;
left.Remove();
UnaryOperatorExpression uoe = new UnaryOperatorExpression(UnaryOperatorType.Not, left);
binaryOperatorExpression.ReplaceWith(uoe);
return uoe.AcceptVisitor(this, data);
} else {
bool negate = false;
Match m = asCastIsNotNullPattern.Match(binaryOperatorExpression);
if (!m.Success) {
m = asCastIsNullPattern.Match(binaryOperatorExpression);
negate = true;
}
if (m.Success) {
Expression expr = m.Get<Expression>("expr").Single().Detach().IsType(m.Get<AstType>("type").Single().Detach());
if (negate)
expr = new UnaryOperatorExpression(UnaryOperatorType.Not, expr);
binaryOperatorExpression.ReplaceWith(expr);
return expr.AcceptVisitor(this, data);
} else {
return base.VisitBinaryOperatorExpression(binaryOperatorExpression, data);
}
}
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

288
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs

@ -0,0 +1,288 @@ @@ -0,0 +1,288 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.PatternMatching;
using Mono.Cecil;
using Ast = ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
/// <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>, IAstTransform
{
static readonly MemberReferenceExpression typeHandleOnTypeOfPattern = new MemberReferenceExpression {
Target = new Choice {
new TypeOfExpression(new AnyNode()),
new UndocumentedExpression { UndocumentedExpressionType = UndocumentedExpressionType.RefType, Arguments = { new AnyNode() } }
},
MemberName = "TypeHandle"
};
public override object VisitInvocationExpression(InvocationExpression invocationExpression, object data)
{
base.VisitInvocationExpression(invocationExpression, data);
MethodReference methodRef = invocationExpression.Annotation<MethodReference>();
if (methodRef == null)
return null;
var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b"
if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2)
{
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
Expression expr = arguments[0];
for (int i = 1; i < arguments.Length; i++) {
expr = new BinaryOperatorExpression(expr, BinaryOperatorType.Add, arguments[i]);
}
invocationExpression.ReplaceWith(expr);
return null;
}
switch (methodRef.FullName) {
case "System.Type System.Type::GetTypeFromHandle(System.RuntimeTypeHandle)":
if (arguments.Length == 1) {
if (typeHandleOnTypeOfPattern.IsMatch(arguments[0])) {
invocationExpression.ReplaceWith(((MemberReferenceExpression)arguments[0]).Target);
return null;
}
}
break;
}
BinaryOperatorType? bop = GetBinaryOperatorTypeFromMetadataName(methodRef.Name);
if (bop != null && arguments.Length == 2) {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
invocationExpression.ReplaceWith(
new BinaryOperatorExpression(arguments[0], bop.Value, arguments[1]).WithAnnotation(methodRef)
);
return null;
}
UnaryOperatorType? uop = GetUnaryOperatorTypeFromMetadataName(methodRef.Name);
if (uop != null && arguments.Length == 1) {
arguments[0].Remove(); // detach argument
invocationExpression.ReplaceWith(
new UnaryOperatorExpression(uop.Value, arguments[0]).WithAnnotation(methodRef)
);
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) {
invocationExpression.ReplaceWith(arguments[0]);
return null;
}
if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == AstNode.Roles.Condition) {
invocationExpression.ReplaceWith(arguments[0]);
return null;
}
return null;
}
BinaryOperatorType? GetBinaryOperatorTypeFromMetadataName(string name)
{
switch (name) {
case "op_Addition":
return BinaryOperatorType.Add;
case "op_Subtraction":
return BinaryOperatorType.Subtract;
case "op_Multiply":
return BinaryOperatorType.Multiply;
case "op_Division":
return BinaryOperatorType.Divide;
case "op_Modulus":
return BinaryOperatorType.Modulus;
case "op_BitwiseAnd":
return BinaryOperatorType.BitwiseAnd;
case "op_BitwiseOr":
return BinaryOperatorType.BitwiseOr;
case "op_ExlusiveOr":
return BinaryOperatorType.ExclusiveOr;
case "op_LeftShift":
return BinaryOperatorType.ShiftLeft;
case "op_RightShift":
return BinaryOperatorType.ShiftRight;
case "op_Equality":
return BinaryOperatorType.Equality;
case "op_Inequality":
return BinaryOperatorType.InEquality;
case "op_LessThan":
return BinaryOperatorType.LessThan;
case "op_LessThanOrEqual":
return BinaryOperatorType.LessThanOrEqual;
case "op_GreaterThan":
return BinaryOperatorType.GreaterThan;
case "op_GreaterThanOrEqual":
return BinaryOperatorType.GreaterThanOrEqual;
default:
return null;
}
}
UnaryOperatorType? GetUnaryOperatorTypeFromMetadataName(string name)
{
switch (name) {
case "op_LogicalNot":
return UnaryOperatorType.Not;
case "op_OnesComplement":
return UnaryOperatorType.BitNot;
case "op_UnaryNegation":
return UnaryOperatorType.Minus;
case "op_UnaryPlus":
return UnaryOperatorType.Plus;
case "op_Increment":
return UnaryOperatorType.Increment;
case "op_Decrement":
return UnaryOperatorType.Decrement;
default:
return null;
}
}
/// <summary>
/// This annotation is used to convert a compound assignment "a += 2;" or increment operator "a++;"
/// back to the original "a = a + 2;". This is sometimes necessary when the checked/unchecked semantics
/// cannot be guaranteed otherwise (see CheckedUnchecked.ForWithCheckedInitializerAndUncheckedIterator test)
/// </summary>
public class RestoreOriginalAssignOperatorAnnotation
{
readonly BinaryOperatorExpression binaryOperatorExpression;
public RestoreOriginalAssignOperatorAnnotation(BinaryOperatorExpression binaryOperatorExpression)
{
this.binaryOperatorExpression = binaryOperatorExpression;
}
public AssignmentExpression Restore(Expression expression)
{
expression.RemoveAnnotations<RestoreOriginalAssignOperatorAnnotation>();
AssignmentExpression assign = expression as AssignmentExpression;
if (assign == null) {
UnaryOperatorExpression uoe = (UnaryOperatorExpression)expression;
assign = new AssignmentExpression(uoe.Expression.Detach(), new PrimitiveExpression(1));
} else {
assign.Operator = AssignmentOperatorType.Assign;
}
binaryOperatorExpression.Right = assign.Right.Detach();
assign.Right = binaryOperatorExpression;
return assign;
}
}
public override object VisitAssignmentExpression(AssignmentExpression assignment, object data)
{
base.VisitAssignmentExpression(assignment, data);
// Combine "x = x op y" into "x op= y"
BinaryOperatorExpression binary = assignment.Right as BinaryOperatorExpression;
if (binary != null && assignment.Operator == AssignmentOperatorType.Assign) {
if (CanConvertToCompoundAssignment(assignment.Left) && assignment.Left.IsMatch(binary.Left)) {
assignment.Operator = GetAssignmentOperatorForBinaryOperator(binary.Operator);
if (assignment.Operator != AssignmentOperatorType.Assign) {
// If we found a shorter operator, get rid of the BinaryOperatorExpression:
assignment.CopyAnnotationsFrom(binary);
assignment.Right = binary.Right;
assignment.AddAnnotation(new RestoreOriginalAssignOperatorAnnotation(binary));
}
}
}
if (assignment.Operator == AssignmentOperatorType.Add || assignment.Operator == AssignmentOperatorType.Subtract) {
// detect increment/decrement
if (assignment.Right.IsMatch(new PrimitiveExpression(1))) {
// only if it's not a custom operator
if (assignment.Annotation<MethodReference>() == null) {
UnaryOperatorType type;
// When the parent is an expression statement, pre- or post-increment doesn't matter;
// so we can pick post-increment which is more commonly used (for (int i = 0; i < x; i++))
if (assignment.Parent is ExpressionStatement)
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.PostIncrement : UnaryOperatorType.PostDecrement;
else
type = (assignment.Operator == AssignmentOperatorType.Add) ? UnaryOperatorType.Increment : UnaryOperatorType.Decrement;
assignment.ReplaceWith(new UnaryOperatorExpression(type, assignment.Left.Detach()).CopyAnnotationsFrom(assignment));
}
}
}
return null;
}
public static AssignmentOperatorType GetAssignmentOperatorForBinaryOperator(BinaryOperatorType bop)
{
switch (bop) {
case BinaryOperatorType.Add:
return AssignmentOperatorType.Add;
case BinaryOperatorType.Subtract:
return AssignmentOperatorType.Subtract;
case BinaryOperatorType.Multiply:
return AssignmentOperatorType.Multiply;
case BinaryOperatorType.Divide:
return AssignmentOperatorType.Divide;
case BinaryOperatorType.Modulus:
return AssignmentOperatorType.Modulus;
case BinaryOperatorType.ShiftLeft:
return AssignmentOperatorType.ShiftLeft;
case BinaryOperatorType.ShiftRight:
return AssignmentOperatorType.ShiftRight;
case BinaryOperatorType.BitwiseAnd:
return AssignmentOperatorType.BitwiseAnd;
case BinaryOperatorType.BitwiseOr:
return AssignmentOperatorType.BitwiseOr;
case BinaryOperatorType.ExclusiveOr:
return AssignmentOperatorType.ExclusiveOr;
default:
return AssignmentOperatorType.Assign;
}
}
static bool CanConvertToCompoundAssignment(Expression left)
{
MemberReferenceExpression mre = left as MemberReferenceExpression;
if (mre != null)
return IsWithoutSideEffects(mre.Target);
IndexerExpression ie = left as IndexerExpression;
if (ie != null)
return IsWithoutSideEffects(ie.Target) && ie.Arguments.All(IsWithoutSideEffects);
UnaryOperatorExpression uoe = left as UnaryOperatorExpression;
if (uoe != null && uoe.Operator == UnaryOperatorType.Dereference)
return IsWithoutSideEffects(uoe.Expression);
return IsWithoutSideEffects(left);
}
static bool IsWithoutSideEffects(Expression left)
{
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression;
}
void IAstTransform.Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

64
src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs

@ -0,0 +1,64 @@ @@ -0,0 +1,64 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Threading;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Ast.Transforms
{
public interface IAstTransform
{
void Run(AstNode compilationUnit);
}
public static class TransformationPipeline
{
public static IAstTransform[] CreatePipeline(DecompilerContext context)
{
return new IAstTransform[] {
new PushNegation(),
new DelegateConstruction(context),
new PatternStatementTransform(context),
new ReplaceMethodCallsWithOperators(),
new IntroduceUnsafeModifier(),
new AddCheckedBlocks(),
new DeclareVariables(context), // should run after most transforms that modify statements
new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables
new DecimalConstantTransform(),
new IntroduceUsingDeclarations(context),
new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations
new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods
new CombineQueryExpressions(context),
};
}
public static void RunTransformationsUntil(AstNode node, Predicate<IAstTransform> abortCondition, DecompilerContext context)
{
if (node == null)
return;
foreach (var transform in CreatePipeline(context)) {
context.CancellationToken.ThrowIfCancellationRequested();
if (abortCondition != null && abortCondition(transform))
return;
transform.Run(node);
}
}
}
}

516
src/Libraries/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs

@ -0,0 +1,516 @@ @@ -0,0 +1,516 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.Ast
{
public static class TypesHierarchyHelpers
{
public static bool IsBaseType(TypeDefinition baseType, TypeDefinition derivedType, bool resolveTypeArguments)
{
if (resolveTypeArguments)
return BaseTypes(derivedType).Any(t => t.Item == baseType);
else {
var comparableBaseType = baseType.ResolveOrThrow();
while (derivedType.BaseType != null) {
var resolvedBaseType = derivedType.BaseType.ResolveOrThrow();
if (resolvedBaseType == null)
return false;
if (comparableBaseType == resolvedBaseType)
return true;
derivedType = resolvedBaseType;
}
return false;
}
}
/// <summary>
/// Determines whether one method overrides or hides another method.
/// </summary>
/// <param name="parentMethod">The method declared in a base type.</param>
/// <param name="childMethod">The method declared in a derived type.</param>
/// <returns>true if <paramref name="childMethod"/> hides or overrides <paramref name="parentMethod"/>,
/// otherwise false.</returns>
public static bool IsBaseMethod(MethodDefinition parentMethod, MethodDefinition childMethod)
{
if (parentMethod == null)
throw new ArgumentNullException("parentMethod");
if (childMethod == null)
throw new ArgumentNullException("childMethod");
if (parentMethod.Name != childMethod.Name)
return false;
if (parentMethod.HasParameters || childMethod.HasParameters)
if (!parentMethod.HasParameters || !childMethod.HasParameters || parentMethod.Parameters.Count != childMethod.Parameters.Count)
return false;
return FindBaseMethods(childMethod).Any(m => m == parentMethod);// || (parentMethod.HasGenericParameters && m.);
}
/// <summary>
/// Determines whether a property overrides or hides another property.
/// </summary>
/// <param name="parentProperty">The property declared in a base type.</param>
/// <param name="childProperty">The property declared in a derived type.</param>
/// <returns>true if the <paramref name="childProperty"/> hides or overrides <paramref name="parentProperty"/>,
/// otherwise false.</returns>
public static bool IsBaseProperty(PropertyDefinition parentProperty, PropertyDefinition childProperty)
{
if (parentProperty == null)
throw new ArgumentNullException("parentProperty");
if (childProperty == null)
throw new ArgumentNullException("childProperty");
if (parentProperty.Name != childProperty.Name)
return false;
if (parentProperty.HasParameters || childProperty.HasParameters)
if (!parentProperty.HasParameters || !childProperty.HasParameters || parentProperty.Parameters.Count != childProperty.Parameters.Count)
return false;
return FindBaseProperties(childProperty).Any(m => m == parentProperty);
}
public static bool IsBaseEvent(EventDefinition parentEvent, EventDefinition childEvent)
{
if (parentEvent.Name != childEvent.Name)
return false;
return FindBaseEvents(childEvent).Any(m => m == parentEvent);
}
/// <summary>
/// Finds all methods from base types overridden or hidden by the specified method.
/// </summary>
/// <param name="method">The method which overrides or hides methods from base types.</param>
/// <returns>Methods overriden or hidden by the specified method.</returns>
public static IEnumerable<MethodDefinition> FindBaseMethods(MethodDefinition method)
{
if (method == null)
throw new ArgumentNullException("method");
var typeContext = CreateGenericContext(method.DeclaringType);
var gMethod = typeContext.ApplyTo(method);
foreach (var baseType in BaseTypes(method.DeclaringType))
foreach (var baseMethod in baseType.Item.Methods)
if (MatchMethod(baseType.ApplyTo(baseMethod), gMethod) && IsVisibleFromDerived(baseMethod, method.DeclaringType)) {
yield return baseMethod;
if (baseMethod.IsNewSlot == baseMethod.IsVirtual)
yield break;
}
}
/// <summary>
/// Finds all properties from base types overridden or hidden by the specified property.
/// </summary>
/// <param name="property">The property which overrides or hides properties from base types.</param>
/// <returns>Properties overriden or hidden by the specified property.</returns>
public static IEnumerable<PropertyDefinition> FindBaseProperties(PropertyDefinition property)
{
if (property == null)
throw new ArgumentNullException("property");
if ((property.GetMethod ?? property.SetMethod).HasOverrides)
yield break;
var typeContext = CreateGenericContext(property.DeclaringType);
var gProperty = typeContext.ApplyTo(property);
bool isIndexer = property.IsIndexer();
foreach (var baseType in BaseTypes(property.DeclaringType))
foreach (var baseProperty in baseType.Item.Properties)
if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty)
&& IsVisibleFromDerived(baseProperty, property.DeclaringType)) {
if (isIndexer != baseProperty.IsIndexer())
continue;
yield return baseProperty;
var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod;
if (anyPropertyAccessor.IsNewSlot == anyPropertyAccessor.IsVirtual)
yield break;
}
}
public static IEnumerable<EventDefinition> FindBaseEvents(EventDefinition eventDef)
{
if (eventDef == null)
throw new ArgumentNullException("eventDef");
var typeContext = CreateGenericContext(eventDef.DeclaringType);
var gEvent = typeContext.ApplyTo(eventDef);
foreach (var baseType in BaseTypes(eventDef.DeclaringType))
foreach (var baseEvent in baseType.Item.Events)
if (MatchEvent(baseType.ApplyTo(baseEvent), gEvent) && IsVisibleFromDerived(baseEvent, eventDef.DeclaringType)) {
yield return baseEvent;
var anyEventAccessor = baseEvent.AddMethod ?? baseEvent.RemoveMethod;
if (anyEventAccessor.IsNewSlot == anyEventAccessor.IsVirtual)
yield break;
}
}
/// <summary>
/// Determinates whether member of the base type is visible from a derived type.
/// </summary>
/// <param name="baseMember">The member which visibility is checked.</param>
/// <param name="derivedType">The derived type.</param>
/// <returns>true if the member is visible from derived type, othewise false.</returns>
public static bool IsVisibleFromDerived(IMemberDefinition baseMember, TypeDefinition derivedType)
{
if (baseMember == null)
throw new ArgumentNullException("baseMember");
if (derivedType == null)
throw new ArgumentNullException("derivedType");
var visibility = IsVisibleFromDerived(baseMember);
if (visibility.HasValue)
return visibility.Value;
if (baseMember.DeclaringType.Module == derivedType.Module)
return true;
// TODO: Check also InternalsVisibleToAttribute.
return false;
}
private static bool? IsVisibleFromDerived(IMemberDefinition member)
{
MethodAttributes attrs = GetAccessAttributes(member) & MethodAttributes.MemberAccessMask;
if (attrs == MethodAttributes.Private)
return false;
if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem)
return null;
return true;
}
private static MethodAttributes GetAccessAttributes(IMemberDefinition member)
{
var fld = member as FieldDefinition;
if (fld != null)
return (MethodAttributes)fld.Attributes;
var method = member as MethodDefinition;
if (method != null)
return method.Attributes;
var prop = member as PropertyDefinition;
if (prop != null) {
return (prop.GetMethod ?? prop.SetMethod).Attributes;
}
var evnt = member as EventDefinition;
if (evnt != null) {
return (evnt.AddMethod ?? evnt.RemoveMethod).Attributes;
}
var nestedType = member as TypeDefinition;
if (nestedType != null) {
if (nestedType.IsNestedPrivate)
return MethodAttributes.Private;
if (nestedType.IsNestedAssembly || nestedType.IsNestedFamilyAndAssembly)
return MethodAttributes.Assembly;
return MethodAttributes.Public;
}
throw new NotSupportedException();
}
private static bool MatchMethod(GenericContext<MethodDefinition> candidate, GenericContext<MethodDefinition> method)
{
var mCandidate = candidate.Item;
var mMethod = method.Item;
if (mCandidate.Name != mMethod.Name)
return false;
if (mCandidate.HasOverrides)
return false;
if (mCandidate.IsSpecialName != method.Item.IsSpecialName)
return false;
if (mCandidate.HasGenericParameters || mMethod.HasGenericParameters) {
if (!mCandidate.HasGenericParameters || !mMethod.HasGenericParameters || mCandidate.GenericParameters.Count != mMethod.GenericParameters.Count)
return false;
}
if (mCandidate.HasParameters || mMethod.HasParameters) {
if (!mCandidate.HasParameters || !mMethod.HasParameters || mCandidate.Parameters.Count != mMethod.Parameters.Count)
return false;
for (int index = 0; index < mCandidate.Parameters.Count; index++) {
if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), method.ApplyTo(mMethod.Parameters[index])))
return false;
}
}
return true;
}
public static bool MatchInterfaceMethod(MethodDefinition candidate, MethodDefinition method, TypeReference interfaceContextType)
{
var candidateContext = CreateGenericContext(candidate.DeclaringType);
var gCandidate = candidateContext.ApplyTo(candidate);
if (interfaceContextType is GenericInstanceType) {
var methodContext = new GenericContext<TypeDefinition>(interfaceContextType.Resolve(), ((GenericInstanceType)interfaceContextType).GenericArguments);
var gMethod = methodContext.ApplyTo(method);
return MatchMethod(gCandidate, gMethod);
} else {
var methodContext = CreateGenericContext(interfaceContextType.Resolve());
var gMethod = candidateContext.ApplyTo(method);
return MatchMethod(gCandidate, gMethod);
}
}
private static bool MatchProperty(GenericContext<PropertyDefinition> candidate, GenericContext<PropertyDefinition> property)
{
var mCandidate = candidate.Item;
var mProperty = property.Item;
if (mCandidate.Name != mProperty.Name)
return false;
if ((mCandidate.GetMethod ?? mCandidate.SetMethod).HasOverrides)
return false;
if (mCandidate.HasParameters || mProperty.HasParameters) {
if (!mCandidate.HasParameters || !mProperty.HasParameters || mCandidate.Parameters.Count != mProperty.Parameters.Count)
return false;
for (int index = 0; index < mCandidate.Parameters.Count; index++) {
if (!MatchParameters(candidate.ApplyTo(mCandidate.Parameters[index]), property.ApplyTo(mProperty.Parameters[index])))
return false;
}
}
return true;
}
private static bool MatchEvent(GenericContext<EventDefinition> candidate, GenericContext<EventDefinition> ev)
{
var mCandidate = candidate.Item;
var mEvent = ev.Item;
if (mCandidate.Name != mEvent.Name)
return false;
if ((mCandidate.AddMethod ?? mCandidate.RemoveMethod).HasOverrides)
return false;
if (!IsSameType(candidate.ResolveWithContext(mCandidate.EventType), ev.ResolveWithContext(mEvent.EventType)))
return false;
return true;
}
private static bool MatchParameters(GenericContext<ParameterDefinition> baseParameterType, GenericContext<ParameterDefinition> parameterType)
{
if (baseParameterType.Item.IsIn != parameterType.Item.IsIn ||
baseParameterType.Item.IsOut != parameterType.Item.IsOut)
return false;
var baseParam = baseParameterType.ResolveWithContext(baseParameterType.Item.ParameterType);
var param = parameterType.ResolveWithContext(parameterType.Item.ParameterType);
return IsSameType(baseParam, param);
}
private static bool IsSameType(TypeReference tr1, TypeReference tr2)
{
if (tr1 == tr2)
return true;
if (tr1 == null || tr2 == null)
return false;
if (tr1.GetType() != tr2.GetType())
return false;
if (tr1.Name == tr2.Name && tr1.FullName == tr2.FullName)
return true;
return false;
}
private static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(TypeDefinition type)
{
return BaseTypes(CreateGenericContext(type));
}
private static IEnumerable<GenericContext<TypeDefinition>> BaseTypes(GenericContext<TypeDefinition> type)
{
while (type.Item.BaseType != null) {
var baseType = type.Item.BaseType;
var genericBaseType = baseType as GenericInstanceType;
if (genericBaseType != null) {
type = new GenericContext<TypeDefinition>(genericBaseType.ResolveOrThrow(),
genericBaseType.GenericArguments.Select(t => type.ResolveWithContext(t)));
} else
type = new GenericContext<TypeDefinition>(baseType.ResolveOrThrow());
yield return type;
}
}
private static GenericContext<TypeDefinition> CreateGenericContext(TypeDefinition type)
{
return type.HasGenericParameters
? new GenericContext<TypeDefinition>(type, type.GenericParameters)
: new GenericContext<TypeDefinition>(type);
}
struct GenericContext<T> where T : class
{
private static readonly ReadOnlyCollection<TypeReference> Empty = new ReadOnlyCollection<TypeReference>(new List<TypeReference>());
private static readonly GenericParameter UnresolvedGenericTypeParameter =
new DummyGenericParameterProvider(false).DummyParameter;
private static readonly GenericParameter UnresolvedGenericMethodParameter =
new DummyGenericParameterProvider(true).DummyParameter;
public readonly T Item;
public readonly ReadOnlyCollection<TypeReference> TypeArguments;
public GenericContext(T item)
{
if (item == null)
throw new ArgumentNullException("item");
Item = item;
TypeArguments = Empty;
}
public GenericContext(T item, IEnumerable<TypeReference> typeArguments)
{
if (item == null)
throw new ArgumentNullException("item");
Item = item;
var list = new List<TypeReference>();
foreach (var arg in typeArguments) {
var resolved = arg != null ? arg.Resolve() : arg;
list.Add(resolved != null ? resolved : arg);
}
TypeArguments = new ReadOnlyCollection<TypeReference>(list);
}
private GenericContext(T item, ReadOnlyCollection<TypeReference> typeArguments)
{
Item = item;
TypeArguments = typeArguments;
}
public TypeReference ResolveWithContext(TypeReference type)
{
var genericParameter = type as GenericParameter;
if (genericParameter != null)
if (genericParameter.Owner.GenericParameterType == GenericParameterType.Type)
return this.TypeArguments[genericParameter.Position];
else
return genericParameter.Owner.GenericParameterType == GenericParameterType.Type
? UnresolvedGenericTypeParameter : UnresolvedGenericMethodParameter;
var typeSpecification = type as TypeSpecification;
if (typeSpecification != null) {
var resolvedElementType = ResolveWithContext(typeSpecification.ElementType);
return ReplaceElementType(typeSpecification, resolvedElementType);
}
return type.ResolveOrThrow();
}
private TypeReference ReplaceElementType(TypeSpecification ts, TypeReference newElementType)
{
var arrayType = ts as ArrayType;
if (arrayType != null) {
if (newElementType == arrayType.ElementType)
return arrayType;
var newArrayType = new ArrayType(newElementType, arrayType.Rank);
for (int dimension = 0; dimension < arrayType.Rank; dimension++)
newArrayType.Dimensions[dimension] = arrayType.Dimensions[dimension];
return newArrayType;
}
var byReferenceType = ts as ByReferenceType;
if (byReferenceType != null) {
return new ByReferenceType(newElementType);
}
// TODO: should we throw an exception instead calling Resolve method?
return ts.ResolveOrThrow();
}
public GenericContext<T2> ApplyTo<T2>(T2 item) where T2 : class
{
return new GenericContext<T2>(item, this.TypeArguments);
}
private class DummyGenericParameterProvider : IGenericParameterProvider
{
readonly Mono.Cecil.GenericParameterType type;
readonly Mono.Collections.Generic.Collection<GenericParameter> parameters;
public DummyGenericParameterProvider(bool methodTypeParameter)
{
type = methodTypeParameter ? Mono.Cecil.GenericParameterType.Method :
Mono.Cecil.GenericParameterType.Type;
parameters = new Mono.Collections.Generic.Collection<GenericParameter>(1);
parameters.Add(new GenericParameter(this));
}
public GenericParameter DummyParameter
{
get { return parameters[0]; }
}
bool IGenericParameterProvider.HasGenericParameters
{
get { throw new NotImplementedException(); }
}
bool IGenericParameterProvider.IsDefinition
{
get { throw new NotImplementedException(); }
}
ModuleDefinition IGenericParameterProvider.Module
{
get { throw new NotImplementedException(); }
}
Mono.Collections.Generic.Collection<GenericParameter> IGenericParameterProvider.GenericParameters
{
get { return parameters; }
}
GenericParameterType IGenericParameterProvider.GenericParameterType
{
get { return type; }
}
MetadataToken IMetadataTokenProvider.MetadataToken
{
get
{
throw new NotImplementedException();
}
set
{
throw new NotImplementedException();
}
}
}
}
}
}

301
src/Libraries/ICSharpCode.Decompiler/CecilExtensions.cs

@ -0,0 +1,301 @@ @@ -0,0 +1,301 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Cecil helper methods.
/// </summary>
public static class CecilExtensions
{
#region GetPushDelta / GetPopDelta
public static int GetPushDelta(this Instruction instruction)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPush) {
case StackBehaviour.Push0:
return 0;
case StackBehaviour.Push1:
case StackBehaviour.Pushi:
case StackBehaviour.Pushi8:
case StackBehaviour.Pushr4:
case StackBehaviour.Pushr8:
case StackBehaviour.Pushref:
return 1;
case StackBehaviour.Push1_push1:
return 2;
case StackBehaviour.Varpush:
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
return IsVoid (method.ReturnType) ? 0 : 1;
}
throw new NotSupportedException ();
}
public static int? GetPopDelta(this Instruction instruction)
{
OpCode code = instruction.OpCode;
switch (code.StackBehaviourPop) {
case StackBehaviour.Pop0:
return 0;
case StackBehaviour.Popi:
case StackBehaviour.Popref:
case StackBehaviour.Pop1:
return 1;
case StackBehaviour.Pop1_pop1:
case StackBehaviour.Popi_pop1:
case StackBehaviour.Popi_popi:
case StackBehaviour.Popi_popi8:
case StackBehaviour.Popi_popr4:
case StackBehaviour.Popi_popr8:
case StackBehaviour.Popref_pop1:
case StackBehaviour.Popref_popi:
return 2;
case StackBehaviour.Popi_popi_popi:
case StackBehaviour.Popref_popi_popi:
case StackBehaviour.Popref_popi_popi8:
case StackBehaviour.Popref_popi_popr4:
case StackBehaviour.Popref_popi_popr8:
case StackBehaviour.Popref_popi_popref:
return 3;
case StackBehaviour.PopAll:
return null;
case StackBehaviour.Varpop:
if (code == OpCodes.Ret)
return null;
if (code.FlowControl != FlowControl.Call)
break;
IMethodSignature method = (IMethodSignature) instruction.Operand;
int count = method.HasParameters ? method.Parameters.Count : 0;
if (code == OpCodes.Calli || (method.HasThis && code != OpCodes.Newobj))
++count;
return count;
}
throw new NotSupportedException ();
}
public static bool IsVoid(this TypeReference type)
{
while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType;
return type.MetadataType == MetadataType.Void;
}
public static bool IsValueTypeOrVoid(this TypeReference type)
{
while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType;
if (type is ArrayType)
return false;
return type.IsValueType || type.IsVoid();
}
#endregion
/// <summary>
/// Gets the (exclusive) end offset of this instruction.
/// </summary>
public static int GetEndOffset(this Instruction inst)
{
return inst.Offset + inst.GetSize();
}
public static string OffsetToString(int offset)
{
return string.Format("IL_{0:x4}", offset);
}
public static HashSet<MethodDefinition> GetAccessorMethods(this TypeDefinition type)
{
HashSet<MethodDefinition> accessorMethods = new HashSet<MethodDefinition>();
foreach (var property in type.Properties) {
accessorMethods.Add(property.GetMethod);
accessorMethods.Add(property.SetMethod);
if (property.HasOtherMethods) {
foreach (var m in property.OtherMethods)
accessorMethods.Add(m);
}
}
foreach (EventDefinition ev in type.Events) {
accessorMethods.Add(ev.AddMethod);
accessorMethods.Add(ev.RemoveMethod);
accessorMethods.Add(ev.InvokeMethod);
if (ev.HasOtherMethods) {
foreach (var m in ev.OtherMethods)
accessorMethods.Add(m);
}
}
return accessorMethods;
}
public static TypeDefinition ResolveWithinSameModule(this TypeReference type)
{
if (type != null && type.GetElementType().Module == type.Module)
return type.Resolve();
else
return null;
}
public static FieldDefinition ResolveWithinSameModule(this FieldReference field)
{
if (field != null && field.DeclaringType.GetElementType().Module == field.Module)
return field.Resolve();
else
return null;
}
public static MethodDefinition ResolveWithinSameModule(this MethodReference method)
{
if (method != null && method.DeclaringType.GetElementType().Module == method.Module)
return method.Resolve();
else
return null;
}
public static TypeDefinition ResolveOrThrow(this TypeReference typeReference)
{
var resolved = typeReference.Resolve();
if (resolved == null)
throw new ReferenceResolvingException();
return resolved;
}
public static bool IsCompilerGenerated(this ICustomAttributeProvider provider)
{
if (provider != null && provider.HasCustomAttributes) {
foreach (CustomAttribute a in provider.CustomAttributes) {
if (a.AttributeType.FullName == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")
return true;
}
}
return false;
}
public static bool IsCompilerGeneratedOrIsInCompilerGeneratedClass(this IMemberDefinition member)
{
if (member == null)
return false;
if (member.IsCompilerGenerated())
return true;
return IsCompilerGeneratedOrIsInCompilerGeneratedClass(member.DeclaringType);
}
public static bool IsAnonymousType(this TypeReference type)
{
if (type == null)
return false;
if (string.IsNullOrEmpty(type.Namespace) && type.Name.StartsWith("<>", StringComparison.Ordinal) && type.Name.Contains("AnonymousType")) {
TypeDefinition td = type.Resolve();
return td != null && td.IsCompilerGenerated();
}
return false;
}
public static bool ContainsAnonymousType(this TypeReference type)
{
GenericInstanceType git = type as GenericInstanceType;
if (git != null) {
if (IsAnonymousType(git))
return true;
for (int i = 0; i < git.GenericArguments.Count; i++) {
if (git.GenericArguments[i].ContainsAnonymousType())
return true;
}
return false;
}
TypeSpecification typeSpec = type as TypeSpecification;
if (typeSpec != null)
return typeSpec.ElementType.ContainsAnonymousType();
else
return false;
}
public static string GetDefaultMemberName(this TypeDefinition type)
{
CustomAttribute attr;
return type.GetDefaultMemberName(out attr);
}
public static string GetDefaultMemberName(this TypeDefinition type, out CustomAttribute defaultMemberAttribute)
{
if (type.HasCustomAttributes)
foreach (CustomAttribute ca in type.CustomAttributes)
if (ca.Constructor.DeclaringType.Name == "DefaultMemberAttribute" && ca.Constructor.DeclaringType.Namespace == "System.Reflection"
&& ca.Constructor.FullName == @"System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") {
defaultMemberAttribute = ca;
return ca.ConstructorArguments[0].Value as string;
}
defaultMemberAttribute = null;
return null;
}
public static bool IsIndexer(this PropertyDefinition property)
{
CustomAttribute attr;
return property.IsIndexer(out attr);
}
public static bool IsIndexer(this PropertyDefinition property, out CustomAttribute defaultMemberAttribute)
{
defaultMemberAttribute = null;
if (property.HasParameters) {
var accessor = property.GetMethod ?? property.SetMethod;
PropertyDefinition basePropDef = property;
if (accessor.HasOverrides) {
// if the property is explicitly implementing an interface, look up the property in the interface:
MethodDefinition baseAccessor = accessor.Overrides.First().Resolve();
if (baseAccessor != null) {
foreach (PropertyDefinition baseProp in baseAccessor.DeclaringType.Properties) {
if (baseProp.GetMethod == baseAccessor || baseProp.SetMethod == baseAccessor) {
basePropDef = baseProp;
break;
}
}
} else
return false;
}
CustomAttribute attr;
var defaultMemberName = basePropDef.DeclaringType.GetDefaultMemberName(out attr);
if (defaultMemberName == basePropDef.Name) {
defaultMemberAttribute = attr;
return true;
}
}
return false;
}
}
}

346
src/Libraries/ICSharpCode.Decompiler/CodeMappings.cs

@ -0,0 +1,346 @@ @@ -0,0 +1,346 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
public enum DecompiledLanguages
{
IL,
CSharp
}
/// <summary>
/// Base class for decompliler classes : AstBuilder & ReflectionDisassembler.
/// </summary>
public abstract class BaseCodeMappings
{
/// <summary>
/// Gets the code mappings.
/// <remarks>Key is the metadata token.</remarks>
/// </summary>
public Dictionary<int, List<MemberMapping>> CodeMappings { get; protected set; }
/// <summary>
/// Gets the MembeReference that is decompiled (a MethodDefinition, PropertyDefinition etc.)
/// <remarks>Key is the metadata token.</remarks>
/// </summary>
public Dictionary<int, MemberReference> DecompiledMemberReferences { get; protected set; }
/// <summary>
/// Create data in the CodeMappings and DecompiledMemberReferences.
/// </summary>
/// <param name="token">Token of the current method.</param>
/// <param name="member">Current member (MethodDefinition, PropertyDefinition, EventDefinition).</param>
/// <remarks>The token is used in CodeMappings; member (and its token) is used in DecompiledMemberReferences.</remarks>
protected virtual void CreateCodeMappings(int token, MemberReference member)
{
this.CodeMappings.Add(token, new List<MemberMapping>());
int t = member.MetadataToken.ToInt32();
if (!this.DecompiledMemberReferences.ContainsKey(t))
this.DecompiledMemberReferences.Add(t, member);
}
}
/// <summary>
/// Maps the source code to IL.
/// </summary>
public sealed class SourceCodeMapping
{
/// <summary>
/// Gets or sets the source code line number in the output.
/// </summary>
public int SourceCodeLine { get; internal set; }
/// <summary>
/// Gets or sets IL Range offset for the source code line. E.g.: 13-19 &lt;-&gt; 135.
/// </summary>
public ILRange ILInstructionOffset { get; internal set; }
/// <summary>
/// Gets or sets the member mapping this source code mapping belongs to.
/// </summary>
public MemberMapping MemberMapping { get; internal set; }
/// <summary>
/// Retrieves the array that contains the IL range and the missing gaps between ranges.
/// </summary>
/// <returns>The array representation of the step aranges.</returns>
public int[] ToArray(bool isMatch)
{
var currentList = new List<ILRange>();
// add list for the current source code line
currentList.AddRange(ILRange.OrderAndJoint(MemberMapping.MemberCodeMappings
.FindAll(m => m.SourceCodeLine == this.SourceCodeLine)
.ConvertAll<ILRange>(m => m.ILInstructionOffset)));
if (!isMatch) {
// add inverted
currentList.AddRange(MemberMapping.InvertedList);
} else {
// if the current list contains the last mapping, add also the last gap
var lastInverted = MemberMapping.InvertedList.LastOrDefault();
if (lastInverted != null && lastInverted.From == currentList[currentList.Count - 1].To)
currentList.Add(lastInverted);
}
// set the output
var resultList = new List<int>();
foreach (var element in ILRange.OrderAndJoint(currentList)) {
resultList.Add(element.From);
resultList.Add(element.To);
}
return resultList.ToArray();
}
}
/// <summary>
/// Stores the method information and its source code mappings.
/// </summary>
public sealed class MemberMapping
{
IEnumerable<ILRange> invertedList;
/// <summary>
/// Gets or sets the type of the mapping.
/// </summary>
public MemberReference MemberReference { get; internal set; }
/// <summary>
/// Metadata token of the member.
/// </summary>
public int MetadataToken { get; internal set; }
/// <summary>
/// Gets or sets the code size for the member mapping.
/// </summary>
public int CodeSize { get; internal set; }
/// <summary>
/// Gets or sets the source code mappings.
/// </summary>
public List<SourceCodeMapping> MemberCodeMappings { get; internal set; }
/// <summary>
/// Gets the inverted IL Ranges.<br/>
/// E.g.: for (0-9, 11-14, 14-18, 21-25) => (9-11,18-21).
/// </summary>
/// <returns>IL Range inverted list.</returns>
public IEnumerable<ILRange> InvertedList
{
get {
if (invertedList == null) {
var list = MemberCodeMappings.ConvertAll<ILRange>(
s => new ILRange { From = s.ILInstructionOffset.From, To = s.ILInstructionOffset.To });
invertedList = ILRange.OrderAndJoint(ILRange.Invert(list, CodeSize));
}
return invertedList;
}
}
}
/// <summary>
/// Code mappings helper class.
/// </summary>
public static class CodeMappings
{
/// <summary>
/// Create code mapping for a method.
/// </summary>
/// <param name="method">Method to create the mapping for.</param>
/// <param name="codeMappings">Source code mapping storage.</param>
/// <param name="actualMemberReference">The actual member reference.</param>
internal static MemberMapping CreateCodeMapping(
this MethodDefinition member,
List<MemberMapping> codeMappings,
MemberReference actualMemberReference = null)
{
if (member == null || !member.HasBody)
return null;
if (codeMappings == null)
return null;
// create IL/CSharp code mappings - used in debugger
MemberMapping currentMemberMapping = null;
if (codeMappings.Find(map => map.MetadataToken == member.MetadataToken.ToInt32()) == null) {
currentMemberMapping = new MemberMapping() {
MetadataToken = member.MetadataToken.ToInt32(),
MemberCodeMappings = new List<SourceCodeMapping>(),
MemberReference = actualMemberReference ?? member,
CodeSize = member.Body.CodeSize
};
codeMappings.Add(currentMemberMapping);
}
return currentMemberMapping;
}
/// <summary>
/// Gets source code mapping and metadata token based on type name and line number.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="typeName">Member reference name.</param>
/// <param name="lineNumber">Line number.</param>
/// <param name="metadataToken">Metadata token.</param>
/// <returns></returns>
public static SourceCodeMapping GetInstructionByLineNumber(
this List<MemberMapping> codeMappings,
int lineNumber,
out int metadataToken)
{
if (codeMappings == null)
throw new ArgumentException("CodeMappings storage must be valid!");
foreach (var maping in codeMappings) {
var map = maping.MemberCodeMappings.Find(m => m.SourceCodeLine == lineNumber);
if (map != null) {
metadataToken = maping.MetadataToken;
return map;
}
}
metadataToken = 0;
return null;
}
/// <summary>
/// Gets a mapping given a type, a token and an IL offset.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="token">Token.</param>
/// <param name="ilOffset">IL offset.</param>
/// <param name="isMatch">True, if perfect match.</param>
/// <returns>A code mapping.</returns>
public static SourceCodeMapping GetInstructionByTokenAndOffset(
this List<MemberMapping> codeMappings,
int token,
int ilOffset,
out bool isMatch)
{
isMatch = false;
if (codeMappings == null)
throw new ArgumentNullException("CodeMappings storage must be valid!");
var maping = codeMappings.Find(m => m.MetadataToken == token);
if (maping == null)
return null;
// try find an exact match
var map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From <= ilOffset && ilOffset < m.ILInstructionOffset.To);
if (map == null) {
// get the immediate next one
map = maping.MemberCodeMappings.Find(m => m.ILInstructionOffset.From > ilOffset);
isMatch = false;
if (map == null)
map = maping.MemberCodeMappings.LastOrDefault(); // get the last
return map;
}
isMatch = true;
return map;
}
/// <summary>
/// Gets the source code and type name from metadata token and offset.
/// </summary>
/// <param name="codeMappings">Code mappings storage.</param>
/// <param name="token">Metadata token.</param>
/// <param name="ilOffset">IL offset.</param>
/// <param name="typeName">Type definition.</param>
/// <param name="line">Line number.</param>
/// <remarks>It is possible to exist to different types from different assemblies with the same metadata token.</remarks>
public static bool GetInstructionByTokenAndOffset(
this List<MemberMapping> codeMappings,
int token,
int ilOffset,
out MemberReference member,
out int line)
{
member = null;
line = 0;
if (codeMappings == null)
throw new ArgumentException("CodeMappings storage must be valid!");
var mapping = codeMappings.Find(m => m.MetadataToken == token);
if (mapping == null)
return false;
var codeMapping = mapping.MemberCodeMappings.Find(
cm => cm.ILInstructionOffset.From <= ilOffset && ilOffset <= cm.ILInstructionOffset.To - 1);
if (codeMapping == null) {
codeMapping = mapping.MemberCodeMappings.Find(cm => cm.ILInstructionOffset.From > ilOffset);
if (codeMapping == null) {
codeMapping = mapping.MemberCodeMappings.LastOrDefault();
if (codeMapping == null)
return false;
}
}
member = mapping.MemberReference;
line = codeMapping.SourceCodeLine;
return true;
}
}
/// <summary>
/// Decompilation data. Can be used by other applications to store the decompilation data.
/// </summary>
public class DecompileInformation
{
/// <summary>
/// Gets ot sets the code mappings
/// </summary>
public Dictionary<int, List<MemberMapping>> CodeMappings { get; set; }
/// <summary>
/// Gets or sets the local variables.
/// </summary>
public ConcurrentDictionary<int, IEnumerable<ILVariable>> LocalVariables { get; set; }
/// <summary>
/// Gets the list of MembeReferences that are decompiled (TypeDefinitions, MethodDefinitions, etc)
/// </summary>
public Dictionary<int, MemberReference> DecompiledMemberReferences { get; set; }
/// <summary>
/// Gets (or internal sets) the AST nodes.
/// </summary>
public IEnumerable<AstNode> AstNodes { get; set; }
}
}

42
src/Libraries/ICSharpCode.Decompiler/DecompilerException.cs

@ -0,0 +1,42 @@ @@ -0,0 +1,42 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Runtime.Serialization;
using Mono.Cecil;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Desctiption of DecompilerException.
/// </summary>
public class DecompilerException : Exception, ISerializable
{
public MethodDefinition DecompiledMethod { get; set; }
public DecompilerException(MethodDefinition decompiledMethod, Exception innerException)
: base("Error decompiling " + decompiledMethod.FullName + Environment.NewLine, innerException)
{
}
// This constructor is needed for serialization.
protected DecompilerException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
}
}

243
src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs

@ -0,0 +1,243 @@ @@ -0,0 +1,243 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.ComponentModel;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Settings for the decompiler.
/// </summary>
public class DecompilerSettings : INotifyPropertyChanged
{
bool anonymousMethods = true;
/// <summary>
/// Decompile anonymous methods/lambdas.
/// </summary>
public bool AnonymousMethods {
get { return anonymousMethods; }
set {
if (anonymousMethods != value) {
anonymousMethods = value;
OnPropertyChanged("AnonymousMethods");
}
}
}
bool yieldReturn = true;
/// <summary>
/// Decompile enumerators.
/// </summary>
public bool YieldReturn {
get { return yieldReturn; }
set {
if (yieldReturn != value) {
yieldReturn = value;
OnPropertyChanged("YieldReturn");
}
}
}
bool automaticProperties = true;
/// <summary>
/// Decompile automatic properties
/// </summary>
public bool AutomaticProperties {
get { return automaticProperties; }
set {
if (automaticProperties != value) {
automaticProperties = value;
OnPropertyChanged("AutomaticProperties");
}
}
}
bool automaticEvents = true;
/// <summary>
/// Decompile automatic events
/// </summary>
public bool AutomaticEvents {
get { return automaticEvents; }
set {
if (automaticEvents != value) {
automaticEvents = value;
OnPropertyChanged("AutomaticEvents");
}
}
}
bool usingStatement = true;
/// <summary>
/// Decompile using statements.
/// </summary>
public bool UsingStatement {
get { return usingStatement; }
set {
if (usingStatement != value) {
usingStatement = value;
OnPropertyChanged("UsingStatement");
}
}
}
bool forEachStatement = true;
/// <summary>
/// Decompile foreach statements.
/// </summary>
public bool ForEachStatement {
get { return forEachStatement; }
set {
if (forEachStatement != value) {
forEachStatement = value;
OnPropertyChanged("ForEachStatement");
}
}
}
bool lockStatement = true;
/// <summary>
/// Decompile lock statements.
/// </summary>
public bool LockStatement {
get { return lockStatement; }
set {
if (lockStatement != value) {
lockStatement = value;
OnPropertyChanged("LockStatement");
}
}
}
bool switchStatementOnString = true;
public bool SwitchStatementOnString {
get { return switchStatementOnString; }
set {
if (switchStatementOnString != value) {
switchStatementOnString = value;
OnPropertyChanged("SwitchStatementOnString");
}
}
}
bool usingDeclarations = true;
public bool UsingDeclarations {
get { return usingDeclarations; }
set {
if (usingDeclarations != value) {
usingDeclarations = value;
OnPropertyChanged("UsingDeclarations");
}
}
}
bool queryExpressions = true;
public bool QueryExpressions {
get { return queryExpressions; }
set {
if (queryExpressions != value) {
queryExpressions = value;
OnPropertyChanged("QueryExpressions");
}
}
}
bool fullyQualifyAmbiguousTypeNames = true;
public bool FullyQualifyAmbiguousTypeNames {
get { return fullyQualifyAmbiguousTypeNames; }
set {
if (fullyQualifyAmbiguousTypeNames != value) {
fullyQualifyAmbiguousTypeNames = value;
OnPropertyChanged("FullyQualifyAmbiguousTypeNames");
}
}
}
bool useDebugSymbols = true;
/// <summary>
/// Gets/Sets whether to use variable names from debug symbols, if available.
/// </summary>
public bool UseDebugSymbols {
get { return useDebugSymbols; }
set {
if (useDebugSymbols != value) {
useDebugSymbols = value;
OnPropertyChanged("UseDebugSymbols");
}
}
}
bool objectCollectionInitializers = true;
/// <summary>
/// Gets/Sets whether to use C# 3.0 object/collection initializers
/// </summary>
public bool ObjectOrCollectionInitializers {
get { return objectCollectionInitializers; }
set {
if (objectCollectionInitializers != value) {
objectCollectionInitializers = value;
OnPropertyChanged("ObjectCollectionInitializers");
}
}
}
bool showXmlDocumentation = true;
/// <summary>
/// Gets/Sets whether to include XML documentation comments in the decompiled code
/// </summary>
public bool ShowXmlDocumentation {
get { return showXmlDocumentation; }
set {
if (showXmlDocumentation != value) {
showXmlDocumentation = value;
OnPropertyChanged("ShowXmlDocumentation");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
public DecompilerSettings Clone()
{
DecompilerSettings settings = (DecompilerSettings)MemberwiseClone();
settings.PropertyChanged = null;
return settings;
}
}
}

439
src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -0,0 +1,439 @@ @@ -0,0 +1,439 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler
{
public enum ILNameSyntax
{
/// <summary>
/// class/valuetype + TypeName (built-in types use keyword syntax)
/// </summary>
Signature,
/// <summary>
/// Like signature, but always refers to type parameters using their position
/// </summary>
SignatureNoNamedTypeParameters,
/// <summary>
/// [assembly]Full.Type.Name (even for built-in types)
/// </summary>
TypeName,
/// <summary>
/// Name (but built-in types use keyword syntax)
/// </summary>
ShortTypeName
}
public static class DisassemblerHelpers
{
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction)
{
writer.WriteReference(CecilExtensions.OffsetToString(instruction.Offset), instruction);
}
public static void WriteTo(this ExceptionHandler exceptionHandler, ITextOutput writer)
{
writer.Write("Try ");
WriteOffsetReference(writer, exceptionHandler.TryStart);
writer.Write('-');
WriteOffsetReference(writer, exceptionHandler.TryEnd);
writer.Write(' ');
writer.Write(exceptionHandler.HandlerType.ToString());
if (exceptionHandler.FilterStart != null) {
writer.Write(' ');
WriteOffsetReference(writer, exceptionHandler.FilterStart);
writer.Write(" handler ");
}
if (exceptionHandler.CatchType != null) {
writer.Write(' ');
exceptionHandler.CatchType.WriteTo(writer);
}
writer.Write(' ');
WriteOffsetReference(writer, exceptionHandler.HandlerStart);
writer.Write('-');
WriteOffsetReference(writer, exceptionHandler.HandlerEnd);
}
public static void WriteTo(this Instruction instruction, ITextOutput writer)
{
writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction);
writer.Write(": ");
writer.WriteReference(instruction.OpCode.Name, instruction.OpCode);
if (instruction.Operand != null) {
writer.Write(' ');
if (instruction.OpCode == OpCodes.Ldtoken) {
if (instruction.Operand is MethodReference)
writer.Write("method ");
else if (instruction.Operand is FieldReference)
writer.Write("field ");
}
WriteOperand(writer, instruction.Operand);
}
}
static void WriteLabelList(ITextOutput writer, Instruction[] instructions)
{
writer.Write("(");
for(int i = 0; i < instructions.Length; i++) {
if(i != 0) writer.Write(", ");
WriteOffsetReference(writer, instructions[i]);
}
writer.Write(")");
}
static string ToInvariantCultureString(object value)
{
IConvertible convertible = value as IConvertible;
return(null != convertible)
? convertible.ToString(System.Globalization.CultureInfo.InvariantCulture)
: value.ToString();
}
public static void WriteTo(this MethodReference method, ITextOutput writer)
{
if (method.ExplicitThis) {
writer.Write("instance explicit ");
}
else if (method.HasThis) {
writer.Write("instance ");
}
method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' ');
if (method.DeclaringType != null) {
method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::");
}
MethodDefinition md = method as MethodDefinition;
if (md != null && md.IsCompilerControlled) {
writer.WriteReference(Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")), method);
} else {
writer.WriteReference(Escape(method.Name), method);
}
GenericInstanceMethod gim = method as GenericInstanceMethod;
if (gim != null) {
writer.Write('<');
for (int i = 0; i < gim.GenericArguments.Count; i++) {
if (i > 0)
writer.Write(", ");
gim.GenericArguments[i].WriteTo(writer);
}
writer.Write('>');
}
writer.Write("(");
var parameters = method.Parameters;
for(int i = 0; i < parameters.Count; ++i) {
if (i > 0) writer.Write(", ");
parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
}
writer.Write(")");
}
static void WriteTo(this FieldReference field, ITextOutput writer)
{
field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' ');
field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::");
writer.WriteReference(Escape(field.Name), field);
}
static bool IsValidIdentifierCharacter(char c)
{
return c == '_' || c == '$' || c == '@' || c == '?' || c == '`';
}
static bool IsValidIdentifier(string identifier)
{
if (string.IsNullOrEmpty(identifier))
return false;
if (!(char.IsLetter(identifier[0]) || IsValidIdentifierCharacter(identifier[0]))) {
// As a special case, .ctor and .cctor are valid despite starting with a dot
return identifier == ".ctor" || identifier == ".cctor";
}
for (int i = 1; i < identifier.Length; i++) {
if (!(char.IsLetterOrDigit(identifier[i]) || IsValidIdentifierCharacter(identifier[i]) || identifier[i] == '.'))
return false;
}
return true;
}
static readonly HashSet<string> ilKeywords = BuildKeywordList(
"abstract", "algorithm", "alignment", "ansi", "any", "arglist",
"array", "as", "assembly", "assert", "at", "auto", "autochar", "beforefieldinit",
"blob", "blob_object", "bool", "brnull", "brnull.s", "brzero", "brzero.s", "bstr",
"bytearray", "byvalstr", "callmostderived", "carray", "catch", "cdecl", "cf",
"char", "cil", "class", "clsid", "const", "currency", "custom", "date", "decimal",
"default", "demand", "deny", "endmac", "enum", "error", "explicit", "extends", "extern",
"false", "famandassem", "family", "famorassem", "fastcall", "fault", "field", "filetime",
"filter", "final", "finally", "fixed", "float", "float32", "float64", "forwardref",
"fromunmanaged", "handler", "hidebysig", "hresult", "idispatch", "il", "illegal",
"implements", "implicitcom", "implicitres", "import", "in", "inheritcheck", "init",
"initonly", "instance", "int", "int16", "int32", "int64", "int8", "interface", "internalcall",
"iunknown", "lasterr", "lcid", "linkcheck", "literal", "localloc", "lpstr", "lpstruct", "lptstr",
"lpvoid", "lpwstr", "managed", "marshal", "method", "modopt", "modreq", "native", "nested",
"newslot", "noappdomain", "noinlining", "nomachine", "nomangle", "nometadata", "noncasdemand",
"noncasinheritance", "noncaslinkdemand", "noprocess", "not", "not_in_gc_heap", "notremotable",
"notserialized", "null", "nullref", "object", "objectref", "opt", "optil", "out",
"permitonly", "pinned", "pinvokeimpl", "prefix1", "prefix2", "prefix3", "prefix4", "prefix5", "prefix6",
"prefix7", "prefixref", "prejitdeny", "prejitgrant", "preservesig", "private", "privatescope", "protected",
"public", "record", "refany", "reqmin", "reqopt", "reqrefuse", "reqsecobj", "request", "retval",
"rtspecialname", "runtime", "safearray", "sealed", "sequential", "serializable", "special", "specialname",
"static", "stdcall", "storage", "stored_object", "stream", "streamed_object", "string", "struct",
"synchronized", "syschar", "sysstring", "tbstr", "thiscall", "tls", "to", "true", "typedref",
"unicode", "unmanaged", "unmanagedexp", "unsigned", "unused", "userdefined", "value", "valuetype",
"vararg", "variant", "vector", "virtual", "void", "wchar", "winapi", "with", "wrapper",
// These are not listed as keywords in spec, but ILAsm treats them as such
"property", "type", "flags", "callconv", "strict"
);
static HashSet<string> BuildKeywordList(params string[] keywords)
{
HashSet<string> s = new HashSet<string>(keywords);
foreach (var field in typeof(OpCodes).GetFields()) {
s.Add(((OpCode)field.GetValue(null)).Name);
}
return s;
}
public static string Escape(string identifier)
{
if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) {
return identifier;
} else {
// The ECMA specification says that ' inside SQString should be ecaped using an octal escape sequence,
// but we follow Microsoft's ILDasm and use \'.
return "'" + NRefactory.CSharp.OutputVisitor.ConvertString(identifier).Replace("'", "\\'") + "'";
}
}
public static void WriteTo(this TypeReference type, ITextOutput writer, ILNameSyntax syntax = ILNameSyntax.Signature)
{
ILNameSyntax syntaxForElementTypes = syntax == ILNameSyntax.SignatureNoNamedTypeParameters ? syntax : ILNameSyntax.Signature;
if (type is PinnedType) {
((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write(" pinned");
} else if (type is ArrayType) {
ArrayType at = (ArrayType)type;
at.ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('[');
writer.Write(string.Join(", ", at.Dimensions));
writer.Write(']');
} else if (type is GenericParameter) {
writer.Write('!');
if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method)
writer.Write('!');
if (string.IsNullOrEmpty(type.Name) || type.Name[0] == '!' || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
writer.Write(((GenericParameter)type).Position.ToString());
else
writer.Write(Escape(type.Name));
} else if (type is ByReferenceType) {
((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('&');
} else if (type is PointerType) {
((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('*');
} else if (type is GenericInstanceType) {
type.GetElementType().WriteTo(writer, syntaxForElementTypes);
writer.Write('<');
var arguments = ((GenericInstanceType)type).GenericArguments;
for (int i = 0; i < arguments.Count; i++) {
if (i > 0)
writer.Write(", ");
arguments[i].WriteTo(writer, syntaxForElementTypes);
}
writer.Write('>');
} else if (type is OptionalModifierType) {
((OptionalModifierType)type).ElementType.WriteTo(writer, syntax);
writer.Write(" modopt(");
((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
} else if (type is RequiredModifierType) {
((RequiredModifierType)type).ElementType.WriteTo(writer, syntax);
writer.Write(" modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") ");
} else {
string name = PrimitiveTypeName(type.FullName);
if (syntax == ILNameSyntax.ShortTypeName) {
if (name != null)
writer.Write(name);
else
writer.WriteReference(Escape(type.Name), type);
} else if ((syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters) && name != null) {
writer.Write(name);
} else {
if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
writer.Write(type.IsValueType ? "valuetype " : "class ");
if (type.DeclaringType != null) {
type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write('/');
writer.WriteReference(Escape(type.Name), type);
} else {
if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification))
writer.Write("[{0}]", Escape(type.Scope.Name));
writer.WriteReference(Escape(type.FullName), type);
}
}
}
}
public static void WriteOperand(ITextOutput writer, object operand)
{
if (operand == null)
throw new ArgumentNullException("operand");
Instruction targetInstruction = operand as Instruction;
if (targetInstruction != null) {
WriteOffsetReference(writer, targetInstruction);
return;
}
Instruction[] targetInstructions = operand as Instruction[];
if (targetInstructions != null) {
WriteLabelList(writer, targetInstructions);
return;
}
VariableReference variableRef = operand as VariableReference;
if (variableRef != null) {
if (string.IsNullOrEmpty(variableRef.Name))
writer.WriteReference(variableRef.Index.ToString(), variableRef);
else
writer.WriteReference(Escape(variableRef.Name), variableRef);
return;
}
ParameterReference paramRef = operand as ParameterReference;
if (paramRef != null) {
if (string.IsNullOrEmpty(paramRef.Name))
writer.WriteReference(paramRef.Index.ToString(), paramRef);
else
writer.WriteReference(Escape(paramRef.Name), paramRef);
return;
}
MethodReference methodRef = operand as MethodReference;
if (methodRef != null) {
methodRef.WriteTo(writer);
return;
}
TypeReference typeRef = operand as TypeReference;
if (typeRef != null) {
typeRef.WriteTo(writer, ILNameSyntax.TypeName);
return;
}
FieldReference fieldRef = operand as FieldReference;
if (fieldRef != null) {
fieldRef.WriteTo(writer);
return;
}
string s = operand as string;
if (s != null) {
writer.Write("\"" + NRefactory.CSharp.OutputVisitor.ConvertString(s) + "\"");
} else if (operand is char) {
writer.Write(((int)(char)operand).ToString());
} else if (operand is float) {
float val = (float)operand;
if (val == 0) {
writer.Write("0.0");
} else if (float.IsInfinity(val) || float.IsNaN(val)) {
byte[] data = BitConverter.GetBytes(val);
writer.Write('(');
for (int i = 0; i < data.Length; i++) {
if (i > 0)
writer.Write(' ');
writer.Write(data[i].ToString("X2"));
}
writer.Write(')');
} else {
writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
}
} else if (operand is double) {
double val = (double)operand;
if (val == 0) {
writer.Write("0.0");
} else if (double.IsInfinity(val) || double.IsNaN(val)) {
byte[] data = BitConverter.GetBytes(val);
writer.Write('(');
for (int i = 0; i < data.Length; i++) {
if (i > 0)
writer.Write(' ');
writer.Write(data[i].ToString("X2"));
}
writer.Write(')');
} else {
writer.Write(val.ToString("R", System.Globalization.CultureInfo.InvariantCulture));
}
} else if (operand is bool) {
writer.Write((bool)operand ? "true" : "false");
} else {
s = ToInvariantCultureString(operand);
writer.Write(s);
}
}
public static string PrimitiveTypeName(string fullName)
{
switch (fullName) {
case "System.SByte":
return "int8";
case "System.Int16":
return "int16";
case "System.Int32":
return "int32";
case "System.Int64":
return "int64";
case "System.Byte":
return "uint8";
case "System.UInt16":
return "uint16";
case "System.UInt32":
return "uint32";
case "System.UInt64":
return "uint64";
case "System.Single":
return "float32";
case "System.Double":
return "float64";
case "System.Void":
return "void";
case "System.Boolean":
return "bool";
case "System.String":
return "string";
case "System.Char":
return "char";
case "System.Object":
return "object";
case "System.IntPtr":
return "native int";
default:
return null;
}
}
}
}

228
src/Libraries/ICSharpCode.Decompiler/Disassembler/ILStructure.cs

@ -0,0 +1,228 @@ @@ -0,0 +1,228 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler
{
/// <summary>
/// Specifies the type of an IL structure.
/// </summary>
public enum ILStructureType
{
/// <summary>
/// The root block of the method
/// </summary>
Root,
/// <summary>
/// A nested control structure representing a loop.
/// </summary>
Loop,
/// <summary>
/// A nested control structure representing a try block.
/// </summary>
Try,
/// <summary>
/// A nested control structure representing a catch, finally, or fault block.
/// </summary>
Handler,
/// <summary>
/// A nested control structure representing an exception filter block.
/// </summary>
Filter
}
/// <summary>
/// An IL structure.
/// </summary>
public class ILStructure
{
public readonly ILStructureType Type;
/// <summary>
/// Start position of the structure.
/// </summary>
public readonly int StartOffset;
/// <summary>
/// End position of the structure. (exclusive)
/// </summary>
public readonly int EndOffset;
/// <summary>
/// The exception handler associated with the Try, Filter or Handler block.
/// </summary>
public readonly ExceptionHandler ExceptionHandler;
/// <summary>
/// The loop's entry point.
/// </summary>
public readonly Instruction LoopEntryPoint;
/// <summary>
/// The list of child structures.
/// </summary>
public readonly List<ILStructure> Children = new List<ILStructure>();
public ILStructure(MethodBody body)
: this(ILStructureType.Root, 0, body.CodeSize)
{
// Build the tree of exception structures:
for (int i = 0; i < body.ExceptionHandlers.Count; i++) {
ExceptionHandler eh = body.ExceptionHandlers[i];
if (!body.ExceptionHandlers.Take(i).Any(oldEh => oldEh.TryStart == eh.TryStart && oldEh.TryEnd == eh.TryEnd))
AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh));
if (eh.HandlerType == ExceptionHandlerType.Filter)
AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh));
AddNestedStructure(new ILStructure(ILStructureType.Handler, eh.HandlerStart.Offset, eh.HandlerEnd == null ? body.CodeSize : eh.HandlerEnd.Offset, eh));
}
// Very simple loop detection: look for backward branches
List<KeyValuePair<Instruction, Instruction>> allBranches = FindAllBranches(body);
// We go through the branches in reverse so that we find the biggest possible loop boundary first (think loops with "continue;")
for (int i = allBranches.Count - 1; i >= 0; i--) {
int loopEnd = allBranches[i].Key.GetEndOffset();
int loopStart = allBranches[i].Value.Offset;
if (loopStart < loopEnd) {
// We found a backward branch. This is a potential loop.
// Check that is has only one entry point:
Instruction entryPoint = null;
// entry point is first instruction in loop if prev inst isn't an unconditional branch
Instruction prev = allBranches[i].Value.Previous;
if (prev != null && !OpCodeInfo.IsUnconditionalBranch(prev.OpCode))
entryPoint = allBranches[i].Value;
bool multipleEntryPoints = false;
foreach (var pair in allBranches) {
if (pair.Key.Offset < loopStart || pair.Key.Offset >= loopEnd) {
if (loopStart <= pair.Value.Offset && pair.Value.Offset < loopEnd) {
// jump from outside the loop into the loop
if (entryPoint == null)
entryPoint = pair.Value;
else if (pair.Value != entryPoint)
multipleEntryPoints = true;
}
}
}
if (!multipleEntryPoints) {
AddNestedStructure(new ILStructure(ILStructureType.Loop, loopStart, loopEnd, entryPoint));
}
}
}
SortChildren();
}
public ILStructure(ILStructureType type, int startOffset, int endOffset, ExceptionHandler handler = null)
{
Debug.Assert(startOffset < endOffset);
this.Type = type;
this.StartOffset = startOffset;
this.EndOffset = endOffset;
this.ExceptionHandler = handler;
}
public ILStructure(ILStructureType type, int startOffset, int endOffset, Instruction loopEntryPoint)
{
Debug.Assert(startOffset < endOffset);
this.Type = type;
this.StartOffset = startOffset;
this.EndOffset = endOffset;
this.LoopEntryPoint = loopEntryPoint;
}
bool AddNestedStructure(ILStructure newStructure)
{
// special case: don't consider the loop-like structure of "continue;" statements to be nested loops
if (this.Type == ILStructureType.Loop && newStructure.Type == ILStructureType.Loop && newStructure.StartOffset == this.StartOffset)
return false;
// use <= for end-offset comparisons because both end and EndOffset are exclusive
Debug.Assert(StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= EndOffset);
foreach (ILStructure child in this.Children) {
if (child.StartOffset <= newStructure.StartOffset && newStructure.EndOffset <= child.EndOffset) {
return child.AddNestedStructure(newStructure);
} else if (!(child.EndOffset <= newStructure.StartOffset || newStructure.EndOffset <= child.StartOffset)) {
// child and newStructure overlap
if (!(newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset)) {
// Invalid nesting, can't build a tree. -> Don't add the new structure.
return false;
}
}
}
// Move existing structures into the new structure:
for (int i = 0; i < this.Children.Count; i++) {
ILStructure child = this.Children[i];
if (newStructure.StartOffset <= child.StartOffset && child.EndOffset <= newStructure.EndOffset) {
this.Children.RemoveAt(i--);
newStructure.Children.Add(child);
}
}
// Add the structure here:
this.Children.Add(newStructure);
return true;
}
/// <summary>
/// Finds all branches. Returns list of source offset->target offset mapping.
/// Multiple entries for the same source offset are possible (switch statements).
/// The result is sorted by source offset.
/// </summary>
List<KeyValuePair<Instruction, Instruction>> FindAllBranches(MethodBody body)
{
var result = new List<KeyValuePair<Instruction, Instruction>>();
foreach (Instruction inst in body.Instructions) {
switch (inst.OpCode.OperandType) {
case OperandType.InlineBrTarget:
case OperandType.ShortInlineBrTarget:
result.Add(new KeyValuePair<Instruction, Instruction>(inst, (Instruction)inst.Operand));
break;
case OperandType.InlineSwitch:
foreach (Instruction target in (Instruction[])inst.Operand)
result.Add(new KeyValuePair<Instruction, Instruction>(inst, target));
break;
}
}
return result;
}
void SortChildren()
{
Children.Sort((a, b) => a.StartOffset.CompareTo(b.StartOffset));
foreach (ILStructure child in Children)
child.SortChildren();
}
/// <summary>
/// Gets the innermost structure containing the specified offset.
/// </summary>
public ILStructure GetInnermost(int offset)
{
Debug.Assert(StartOffset <= offset && offset < EndOffset);
foreach (ILStructure child in this.Children) {
if (child.StartOffset <= offset && offset < child.EndOffset)
return child.GetInnermost(offset);
}
return this;
}
}
}

237
src/Libraries/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs

@ -0,0 +1,237 @@ @@ -0,0 +1,237 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.Decompiler.ILAst;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler
{
/// <summary>
/// Disassembles a method body.
/// </summary>
public sealed class MethodBodyDisassembler
{
readonly ITextOutput output;
readonly bool detectControlStructure;
readonly CancellationToken cancellationToken;
public MethodBodyDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
{
if (output == null)
throw new ArgumentNullException("output");
this.output = output;
this.detectControlStructure = detectControlStructure;
this.cancellationToken = cancellationToken;
}
public void Disassemble(MethodBody body, MemberMapping methodMapping)
{
// start writing IL code
MethodDefinition method = body.Method;
output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA);
output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize);
output.WriteLine(".maxstack {0}", body.MaxStackSize);
if (method.DeclaringType.Module.Assembly.EntryPoint == method)
output.WriteLine (".entrypoint");
if (method.Body.HasVariables) {
output.Write(".locals ");
if (method.Body.InitLocals)
output.Write("init ");
output.WriteLine("(");
output.Indent();
foreach (var v in method.Body.Variables) {
output.WriteDefinition("[" + v.Index + "] ", v);
v.VariableType.WriteTo(output);
if (!string.IsNullOrEmpty(v.Name)) {
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(v.Name));
}
if (v.Index + 1 < method.Body.Variables.Count)
output.Write(',');
output.WriteLine();
}
output.Unindent();
output.WriteLine(")");
}
output.WriteLine();
if (detectControlStructure && body.Instructions.Count > 0) {
Instruction inst = body.Instructions[0];
HashSet<int> branchTargets = GetBranchTargets(body.Instructions);
WriteStructureBody(new ILStructure(body), branchTargets, ref inst, methodMapping, method.Body.CodeSize);
} else {
foreach (var inst in method.Body.Instructions) {
inst.WriteTo(output);
if (methodMapping != null) {
// add IL code mappings - used in debugger
methodMapping.MemberCodeMappings.Add(
new SourceCodeMapping() {
SourceCodeLine = output.CurrentLine,
ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? method.Body.CodeSize : inst.Next.Offset },
MemberMapping = methodMapping
});
}
output.WriteLine();
}
if (method.Body.HasExceptionHandlers) {
output.WriteLine();
foreach (var eh in method.Body.ExceptionHandlers) {
eh.WriteTo(output);
output.WriteLine();
}
}
}
}
HashSet<int> GetBranchTargets(IEnumerable<Instruction> instructions)
{
HashSet<int> branchTargets = new HashSet<int>();
foreach (var inst in instructions) {
Instruction target = inst.Operand as Instruction;
if (target != null)
branchTargets.Add(target.Offset);
Instruction[] targets = inst.Operand as Instruction[];
if (targets != null)
foreach (Instruction t in targets)
branchTargets.Add(t.Offset);
}
return branchTargets;
}
void WriteStructureHeader(ILStructure s)
{
switch (s.Type) {
case ILStructureType.Loop:
output.Write("// loop start");
if (s.LoopEntryPoint != null) {
output.Write(" (head: ");
DisassemblerHelpers.WriteOffsetReference(output, s.LoopEntryPoint);
output.Write(')');
}
output.WriteLine();
break;
case ILStructureType.Try:
output.WriteLine(".try");
output.WriteLine("{");
break;
case ILStructureType.Handler:
switch (s.ExceptionHandler.HandlerType) {
case Mono.Cecil.Cil.ExceptionHandlerType.Catch:
case Mono.Cecil.Cil.ExceptionHandlerType.Filter:
output.Write("catch");
if (s.ExceptionHandler.CatchType != null) {
output.Write(' ');
s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName);
}
output.WriteLine();
break;
case Mono.Cecil.Cil.ExceptionHandlerType.Finally:
output.WriteLine("finally");
break;
case Mono.Cecil.Cil.ExceptionHandlerType.Fault:
output.WriteLine("fault");
break;
default:
throw new NotSupportedException();
}
output.WriteLine("{");
break;
case ILStructureType.Filter:
output.WriteLine("filter");
output.WriteLine("{");
break;
default:
throw new NotSupportedException();
}
output.Indent();
}
void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize)
{
bool isFirstInstructionInStructure = true;
bool prevInstructionWasBranch = false;
int childIndex = 0;
while (inst != null && inst.Offset < s.EndOffset) {
int offset = inst.Offset;
if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) {
ILStructure child = s.Children[childIndex++];
WriteStructureHeader(child);
WriteStructureBody(child, branchTargets, ref inst, currentMethodMapping, codeSize);
WriteStructureFooter(child);
} else {
if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) {
output.WriteLine(); // put an empty line after branches, and in front of branch targets
}
inst.WriteTo(output);
// add IL code mappings - used in debugger
if (currentMethodMapping != null) {
currentMethodMapping.MemberCodeMappings.Add(
new SourceCodeMapping() {
SourceCodeLine = output.CurrentLine,
ILInstructionOffset = new ILRange { From = inst.Offset, To = inst.Next == null ? codeSize : inst.Next.Offset },
MemberMapping = currentMethodMapping
});
}
output.WriteLine();
prevInstructionWasBranch = inst.OpCode.FlowControl == FlowControl.Branch
|| inst.OpCode.FlowControl == FlowControl.Cond_Branch
|| inst.OpCode.FlowControl == FlowControl.Return
|| inst.OpCode.FlowControl == FlowControl.Throw;
inst = inst.Next;
}
isFirstInstructionInStructure = false;
}
}
void WriteStructureFooter(ILStructure s)
{
output.Unindent();
switch (s.Type) {
case ILStructureType.Loop:
output.WriteLine("// end loop");
break;
case ILStructureType.Try:
output.WriteLine("} // end .try");
break;
case ILStructureType.Handler:
output.WriteLine("} // end handler");
break;
case ILStructureType.Filter:
output.WriteLine("} // end filter");
break;
default:
throw new NotSupportedException();
}
}
}
}

1158
src/Libraries/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

File diff suppressed because it is too large Load Diff

78
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowEdge.cs

@ -0,0 +1,78 @@ @@ -0,0 +1,78 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Describes the type of a control flow egde.
/// </summary>
public enum JumpType
{
/// <summary>
/// A regular control flow edge.
/// </summary>
Normal,
/// <summary>
/// Jump to exception handler (an exception occurred)
/// </summary>
JumpToExceptionHandler,
/// <summary>
/// Jump from try block to leave target:
/// This is not a real jump, as the finally handler is executed first!
/// </summary>
LeaveTry,
/// <summary>
/// Jump at endfinally (to any of the potential leave targets).
/// For any leave-instruction, control flow enters the finally block - the edge to the leave target (LeaveTry) is not a real control flow edge.
/// EndFinally edges are inserted at the end of the finally block, jumping to any of the targets of the leave instruction.
/// This edge type is only used when copying of finally blocks is disabled (with copying, a normal deterministic edge is used at each copy of the endfinally node).
/// </summary>
EndFinally
}
/// <summary>
/// Represents an edge in the control flow graph, pointing from Source to Target.
/// </summary>
public sealed class ControlFlowEdge
{
public readonly ControlFlowNode Source;
public readonly ControlFlowNode Target;
public readonly JumpType Type;
public ControlFlowEdge(ControlFlowNode source, ControlFlowNode target, JumpType type)
{
this.Source = source;
this.Target = target;
this.Type = type;
}
public override string ToString()
{
switch (Type) {
case JumpType.Normal:
return "#" + Target.BlockIndex;
case JumpType.JumpToExceptionHandler:
return "e:#" + Target.BlockIndex;
default:
return Type.ToString() + ":#" + Target.BlockIndex;
}
}
}
}

191
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraph.cs

@ -0,0 +1,191 @@ @@ -0,0 +1,191 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using ICSharpCode.NRefactory.Utils;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Contains the control flow graph.
/// </summary>
/// <remarks>Use ControlFlowGraph builder to create instances of the ControlFlowGraph.</remarks>
public sealed class ControlFlowGraph
{
readonly ReadOnlyCollection<ControlFlowNode> nodes;
public ControlFlowNode EntryPoint {
get { return nodes[0]; }
}
public ControlFlowNode RegularExit {
get { return nodes[1]; }
}
public ControlFlowNode ExceptionalExit {
get { return nodes[2]; }
}
public ReadOnlyCollection<ControlFlowNode> Nodes {
get { return nodes; }
}
internal ControlFlowGraph(ControlFlowNode[] nodes)
{
this.nodes = new ReadOnlyCollection<ControlFlowNode>(nodes);
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
}
public GraphVizGraph ExportGraph()
{
GraphVizGraph graph = new GraphVizGraph();
foreach (ControlFlowNode node in nodes) {
graph.AddNode(new GraphVizNode(node.BlockIndex) { label = node.ToString(), shape = "box" });
}
foreach (ControlFlowNode node in nodes) {
foreach (ControlFlowEdge edge in node.Outgoing) {
GraphVizEdge e = new GraphVizEdge(edge.Source.BlockIndex, edge.Target.BlockIndex);
switch (edge.Type) {
case JumpType.Normal:
break;
case JumpType.LeaveTry:
case JumpType.EndFinally:
e.color = "red";
break;
default:
e.color = "gray";
//e.constraint = false;
break;
}
graph.AddEdge(e);
}
if (node.ImmediateDominator != null) {
graph.AddEdge(new GraphVizEdge(node.ImmediateDominator.BlockIndex, node.BlockIndex) { color = "green", constraint = false });
}
}
return graph;
}
/// <summary>
/// Resets "Visited" to false for all nodes in this graph.
/// </summary>
public void ResetVisited()
{
foreach (ControlFlowNode node in nodes) {
node.Visited = false;
}
}
/// <summary>
/// Computes the dominator tree.
/// </summary>
public void ComputeDominance(CancellationToken cancellationToken = default(CancellationToken))
{
// A Simple, Fast Dominance Algorithm
// Keith D. Cooper, Timothy J. Harvey and Ken Kennedy
EntryPoint.ImmediateDominator = EntryPoint;
bool changed = true;
while (changed) {
changed = false;
ResetVisited();
cancellationToken.ThrowIfCancellationRequested();
// for all nodes b except the entry point
EntryPoint.TraversePreOrder(
b => b.Successors,
b => {
if (b != EntryPoint) {
ControlFlowNode newIdom = b.Predecessors.First(block => block.Visited && block != b);
// for all other predecessors p of b
foreach (ControlFlowNode p in b.Predecessors) {
if (p != b && p.ImmediateDominator != null) {
newIdom = FindCommonDominator(p, newIdom);
}
}
if (b.ImmediateDominator != newIdom) {
b.ImmediateDominator = newIdom;
changed = true;
}
}
});
}
EntryPoint.ImmediateDominator = null;
foreach (ControlFlowNode node in nodes) {
if (node.ImmediateDominator != null)
node.ImmediateDominator.DominatorTreeChildren.Add(node);
}
}
static ControlFlowNode FindCommonDominator(ControlFlowNode b1, ControlFlowNode b2)
{
// Here we could use the postorder numbers to get rid of the hashset, see "A Simple, Fast Dominance Algorithm"
HashSet<ControlFlowNode> path1 = new HashSet<ControlFlowNode>();
while (b1 != null && path1.Add(b1))
b1 = b1.ImmediateDominator;
while (b2 != null) {
if (path1.Contains(b2))
return b2;
else
b2 = b2.ImmediateDominator;
}
throw new Exception("No common dominator found!");
}
/// <summary>
/// Computes dominance frontiers.
/// This method requires that the dominator tree is already computed!
/// </summary>
public void ComputeDominanceFrontier()
{
ResetVisited();
EntryPoint.TraversePostOrder(
b => b.DominatorTreeChildren,
n => {
//logger.WriteLine("Calculating dominance frontier for " + n.Name);
n.DominanceFrontier = new HashSet<ControlFlowNode>();
// DF_local computation
foreach (ControlFlowNode succ in n.Successors) {
if (succ.ImmediateDominator != n) {
//logger.WriteLine(" local: " + succ.Name);
n.DominanceFrontier.Add(succ);
}
}
// DF_up computation
foreach (ControlFlowNode child in n.DominatorTreeChildren) {
foreach (ControlFlowNode p in child.DominanceFrontier) {
if (p.ImmediateDominator != n) {
//logger.WriteLine(" DF_up: " + p.Name + " (child=" + child.Name);
n.DominanceFrontier.Add(p);
}
}
}
});
}
}
}

439
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowGraphBuilder.cs

@ -0,0 +1,439 @@ @@ -0,0 +1,439 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Constructs the Control Flow Graph from a Cecil method body.
/// </summary>
public sealed class ControlFlowGraphBuilder
{
public static ControlFlowGraph Build(MethodBody methodBody)
{
return new ControlFlowGraphBuilder(methodBody).Build();
}
// This option controls how finally blocks are handled:
// false means that the endfinally instruction will jump to any of the leave targets (EndFinally edge type).
// true means that a copy of the whole finally block is created for each leave target. In this case, each endfinally node will be connected with the leave
// target using a normal edge.
bool copyFinallyBlocks = false;
MethodBody methodBody;
int[] offsets; // array index = instruction index; value = IL offset
bool[] hasIncomingJumps; // array index = instruction index
List<ControlFlowNode> nodes = new List<ControlFlowNode>();
ControlFlowNode entryPoint;
ControlFlowNode regularExit;
ControlFlowNode exceptionalExit;
private ControlFlowGraphBuilder(MethodBody methodBody)
{
this.methodBody = methodBody;
offsets = methodBody.Instructions.Select(i => i.Offset).ToArray();
hasIncomingJumps = new bool[methodBody.Instructions.Count];
entryPoint = new ControlFlowNode(0, 0, ControlFlowNodeType.EntryPoint);
nodes.Add(entryPoint);
regularExit = new ControlFlowNode(1, -1, ControlFlowNodeType.RegularExit);
nodes.Add(regularExit);
exceptionalExit = new ControlFlowNode(2, -1, ControlFlowNodeType.ExceptionalExit);
nodes.Add(exceptionalExit);
Debug.Assert(nodes.Count == 3);
}
/// <summary>
/// Determines the index of the instruction (for use with the hasIncomingJumps array)
/// </summary>
int GetInstructionIndex(Instruction inst)
{
int index = Array.BinarySearch(offsets, inst.Offset);
Debug.Assert(index >= 0);
return index;
}
/// <summary>
/// Builds the ControlFlowGraph.
/// </summary>
public ControlFlowGraph Build()
{
CalculateHasIncomingJumps();
CreateNodes();
CreateRegularControlFlow();
CreateExceptionalControlFlow();
if (copyFinallyBlocks)
CopyFinallyBlocksIntoLeaveEdges();
else
TransformLeaveEdges();
return new ControlFlowGraph(nodes.ToArray());
}
#region Step 1: calculate which instructions are the targets of jump instructions.
void CalculateHasIncomingJumps()
{
foreach (Instruction inst in methodBody.Instructions) {
if (inst.OpCode.OperandType == OperandType.InlineBrTarget || inst.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
hasIncomingJumps[GetInstructionIndex((Instruction)inst.Operand)] = true;
} else if (inst.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])inst.Operand)
hasIncomingJumps[GetInstructionIndex(i)] = true;
}
}
foreach (ExceptionHandler eh in methodBody.ExceptionHandlers) {
if (eh.FilterStart != null) {
hasIncomingJumps[GetInstructionIndex(eh.FilterStart)] = true;
}
hasIncomingJumps[GetInstructionIndex(eh.HandlerStart)] = true;
}
}
#endregion
#region Step 2: create nodes
void CreateNodes()
{
// Step 2a: find basic blocks and create nodes for them
for (int i = 0; i < methodBody.Instructions.Count; i++) {
Instruction blockStart = methodBody.Instructions[i];
ExceptionHandler blockStartEH = FindInnermostExceptionHandler(blockStart.Offset);
// try and see how big we can make that block:
for (; i + 1 < methodBody.Instructions.Count; i++) {
Instruction inst = methodBody.Instructions[i];
if (IsBranch(inst.OpCode) || CanThrowException(inst.OpCode))
break;
if (hasIncomingJumps[i + 1])
break;
if (inst.Next != null) {
// ensure that blocks never contain instructions from different try blocks
ExceptionHandler instEH = FindInnermostExceptionHandler(inst.Next.Offset);
if (instEH != blockStartEH)
break;
}
}
nodes.Add(new ControlFlowNode(nodes.Count, blockStart, methodBody.Instructions[i]));
}
// Step 2b: Create special nodes for the exception handling constructs
foreach (ExceptionHandler handler in methodBody.ExceptionHandlers) {
if (handler.HandlerType == ExceptionHandlerType.Filter)
throw new NotSupportedException();
ControlFlowNode endFinallyOrFaultNode = null;
if (handler.HandlerType == ExceptionHandlerType.Finally || handler.HandlerType == ExceptionHandlerType.Fault) {
endFinallyOrFaultNode = new ControlFlowNode(nodes.Count, handler.HandlerEnd.Offset, ControlFlowNodeType.EndFinallyOrFault);
nodes.Add(endFinallyOrFaultNode);
}
nodes.Add(new ControlFlowNode(nodes.Count, handler, endFinallyOrFaultNode));
}
}
#endregion
#region Step 3: create edges for the normal flow of control (assuming no exceptions thrown)
void CreateRegularControlFlow()
{
CreateEdge(entryPoint, methodBody.Instructions[0], JumpType.Normal);
foreach (ControlFlowNode node in nodes) {
if (node.End != null) {
// create normal edges from one instruction to the next
if (!OpCodeInfo.IsUnconditionalBranch(node.End.OpCode))
CreateEdge(node, node.End.Next, JumpType.Normal);
// create edges for branch instructions
if (node.End.OpCode.OperandType == OperandType.InlineBrTarget || node.End.OpCode.OperandType == OperandType.ShortInlineBrTarget) {
if (node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S) {
var handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.NodeType == ControlFlowNodeType.FinallyOrFaultHandler)
CreateEdge(node, (Instruction)node.End.Operand, JumpType.LeaveTry);
else
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
} else {
CreateEdge(node, (Instruction)node.End.Operand, JumpType.Normal);
}
} else if (node.End.OpCode.OperandType == OperandType.InlineSwitch) {
foreach (Instruction i in (Instruction[])node.End.Operand)
CreateEdge(node, i, JumpType.Normal);
}
// create edges for return instructions
if (node.End.OpCode.FlowControl == FlowControl.Return) {
switch (node.End.OpCode.Code) {
case Code.Ret:
CreateEdge(node, regularExit, JumpType.Normal);
break;
case Code.Endfinally:
ControlFlowNode handlerBlock = FindInnermostHandlerBlock(node.End.Offset);
if (handlerBlock.EndFinallyOrFaultNode == null)
throw new InvalidProgramException("Found endfinally in block " + handlerBlock);
CreateEdge(node, handlerBlock.EndFinallyOrFaultNode, JumpType.Normal);
break;
default:
throw new NotSupportedException(node.End.OpCode.ToString());
}
}
}
}
}
#endregion
#region Step 4: create edges for the exceptional control flow (from instructions that might throw, to the innermost containing exception handler)
void CreateExceptionalControlFlow()
{
foreach (ControlFlowNode node in nodes) {
if (node.End != null && CanThrowException(node.End.OpCode)) {
CreateEdge(node, FindInnermostExceptionHandlerNode(node.End.Offset), JumpType.JumpToExceptionHandler);
}
if (node.ExceptionHandler != null) {
if (node.EndFinallyOrFaultNode != null) {
// For Fault and Finally blocks, create edge from "EndFinally" to next exception handler.
// This represents the exception bubbling up after finally block was executed.
CreateEdge(node.EndFinallyOrFaultNode, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
} else {
// For Catch blocks, create edge from "CatchHandler" block (at beginning) to next exception handler.
// This represents the exception bubbling up because it did not match the type of the catch block.
CreateEdge(node, FindParentExceptionHandlerNode(node), JumpType.JumpToExceptionHandler);
}
CreateEdge(node, node.ExceptionHandler.HandlerStart, JumpType.Normal);
}
}
}
ExceptionHandler FindInnermostExceptionHandler(int instructionOffsetInTryBlock)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffsetInTryBlock && instructionOffsetInTryBlock < h.TryEnd.Offset) {
return h;
}
}
return null;
}
ControlFlowNode FindInnermostExceptionHandlerNode(int instructionOffsetInTryBlock)
{
ExceptionHandler h = FindInnermostExceptionHandler(instructionOffsetInTryBlock);
if (h != null)
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
else
return exceptionalExit;
}
ControlFlowNode FindInnermostHandlerBlock(int instructionOffset)
{
foreach (ExceptionHandler h in methodBody.ExceptionHandlers) {
if (h.TryStart.Offset <= instructionOffset && instructionOffset < h.TryEnd.Offset
|| h.HandlerStart.Offset <= instructionOffset && instructionOffset < h.HandlerEnd.Offset)
{
return nodes.Single(n => n.ExceptionHandler == h && n.CopyFrom == null);
}
}
return exceptionalExit;
}
ControlFlowNode FindParentExceptionHandlerNode(ControlFlowNode exceptionHandler)
{
Debug.Assert(exceptionHandler.NodeType == ControlFlowNodeType.CatchHandler
|| exceptionHandler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
int offset = exceptionHandler.ExceptionHandler.TryStart.Offset;
for (int i = exceptionHandler.BlockIndex + 1; i < nodes.Count; i++) {
ExceptionHandler h = nodes[i].ExceptionHandler;
if (h != null && h.TryStart.Offset <= offset && offset < h.TryEnd.Offset)
return nodes[i];
}
return exceptionalExit;
}
#endregion
#region Step 5a: replace LeaveTry edges with EndFinally edges
// this is used only for copyFinallyBlocks==false; see Step 5b otherwise
void TransformLeaveEdges()
{
for (int i = nodes.Count - 1; i >= 0; i--) {
ControlFlowNode node = nodes[i];
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
ControlFlowNode target = node.Outgoing[0].Target;
// remove the edge
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
CreateEdge(node, handler, JumpType.Normal);
CreateEdge(handler.EndFinallyOrFaultNode, target, JumpType.EndFinally);
}
}
}
#endregion
#region Step 5b: copy finally blocks into the LeaveTry edges
void CopyFinallyBlocksIntoLeaveEdges()
{
// We need to process try-finally blocks inside-out.
// We'll do that by going through all instructions in reverse order
for (int i = nodes.Count - 1; i >= 0; i--) {
ControlFlowNode node = nodes[i];
if (node.End != null && node.Outgoing.Count == 1 && node.Outgoing[0].Type == JumpType.LeaveTry) {
Debug.Assert(node.End.OpCode == OpCodes.Leave || node.End.OpCode == OpCodes.Leave_S);
ControlFlowNode target = node.Outgoing[0].Target;
// remove the edge
target.Incoming.Remove(node.Outgoing[0]);
node.Outgoing.Clear();
ControlFlowNode handler = FindInnermostExceptionHandlerNode(node.End.Offset);
Debug.Assert(handler.NodeType == ControlFlowNodeType.FinallyOrFaultHandler);
ControlFlowNode copy = CopyFinallySubGraph(handler, handler.EndFinallyOrFaultNode, target);
CreateEdge(node, copy, JumpType.Normal);
}
}
}
/// <summary>
/// Creates a copy of all nodes pointing to 'end' and replaces those references with references to 'newEnd'.
/// Nodes pointing to the copied node are copied recursively to update those references, too.
/// This recursion stops at 'start'. The modified version of start is returned.
/// </summary>
ControlFlowNode CopyFinallySubGraph(ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
return new CopyFinallySubGraphLogic(this, start, end, newEnd).CopyFinallySubGraph();
}
class CopyFinallySubGraphLogic
{
readonly ControlFlowGraphBuilder builder;
readonly Dictionary<ControlFlowNode, ControlFlowNode> oldToNew = new Dictionary<ControlFlowNode, ControlFlowNode>();
readonly ControlFlowNode start;
readonly ControlFlowNode end;
readonly ControlFlowNode newEnd;
public CopyFinallySubGraphLogic(ControlFlowGraphBuilder builder, ControlFlowNode start, ControlFlowNode end, ControlFlowNode newEnd)
{
this.builder = builder;
this.start = start;
this.end = end;
this.newEnd = newEnd;
}
internal ControlFlowNode CopyFinallySubGraph()
{
foreach (ControlFlowNode n in end.Predecessors) {
CollectNodes(n);
}
foreach (var pair in oldToNew)
ReconstructEdges(pair.Key, pair.Value);
return GetNew(start);
}
void CollectNodes(ControlFlowNode node)
{
if (node == end || node == newEnd)
throw new InvalidOperationException("unexpected cycle involving finally construct");
if (!oldToNew.ContainsKey(node)) {
int newBlockIndex = builder.nodes.Count;
ControlFlowNode copy;
switch (node.NodeType) {
case ControlFlowNodeType.Normal:
copy = new ControlFlowNode(newBlockIndex, node.Start, node.End);
break;
case ControlFlowNodeType.FinallyOrFaultHandler:
copy = new ControlFlowNode(newBlockIndex, node.ExceptionHandler, node.EndFinallyOrFaultNode);
break;
default:
// other nodes shouldn't occur when copying finally blocks
throw new NotSupportedException(node.NodeType.ToString());
}
copy.CopyFrom = node;
builder.nodes.Add(copy);
oldToNew.Add(node, copy);
if (node != start) {
foreach (ControlFlowNode n in node.Predecessors) {
CollectNodes(n);
}
}
}
}
void ReconstructEdges(ControlFlowNode oldNode, ControlFlowNode newNode)
{
foreach (ControlFlowEdge oldEdge in oldNode.Outgoing) {
builder.CreateEdge(newNode, GetNew(oldEdge.Target), oldEdge.Type);
}
}
ControlFlowNode GetNew(ControlFlowNode oldNode)
{
if (oldNode == end)
return newEnd;
ControlFlowNode newNode;
if (oldToNew.TryGetValue(oldNode, out newNode))
return newNode;
return oldNode;
}
}
#endregion
#region CreateEdge methods
void CreateEdge(ControlFlowNode fromNode, Instruction toInstruction, JumpType type)
{
CreateEdge(fromNode, nodes.Single(n => n.Start == toInstruction), type);
}
void CreateEdge(ControlFlowNode fromNode, ControlFlowNode toNode, JumpType type)
{
ControlFlowEdge edge = new ControlFlowEdge(fromNode, toNode, type);
fromNode.Outgoing.Add(edge);
toNode.Incoming.Add(edge);
}
#endregion
#region OpCode info
static bool CanThrowException(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
return OpCodeInfo.Get(opcode).CanThrow;
}
static bool IsBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Cond_Branch:
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
#endregion
}
}

298
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs

@ -0,0 +1,298 @@ @@ -0,0 +1,298 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Type of the control flow node
/// </summary>
public enum ControlFlowNodeType
{
/// <summary>
/// A normal node represents a basic block.
/// </summary>
Normal,
/// <summary>
/// The entry point of the method.
/// </summary>
EntryPoint,
/// <summary>
/// The exit point of the method (every ret instruction branches to this node)
/// </summary>
RegularExit,
/// <summary>
/// This node represents leaving a method irregularly by throwing an exception.
/// </summary>
ExceptionalExit,
/// <summary>
/// This node is used as a header for exception handler blocks.
/// </summary>
CatchHandler,
/// <summary>
/// This node is used as a header for finally blocks and fault blocks.
/// Every leave instruction in the try block leads to the handler of the containing finally block;
/// and exceptional control flow also leads to this handler.
/// </summary>
FinallyOrFaultHandler,
/// <summary>
/// This node is used as footer for finally blocks and fault blocks.
/// Depending on the "copyFinallyBlocks" option used when creating the graph, it is connected with all leave targets using
/// EndFinally edges (when not copying); or with a specific leave target using a normal edge (when copying).
/// For fault blocks, an exception edge is used to represent the "re-throwing" of the exception.
/// </summary>
EndFinallyOrFault
}
/// <summary>
/// Represents a block in the control flow graph.
/// </summary>
public sealed class ControlFlowNode
{
/// <summary>
/// Index of this node in the ControlFlowGraph.Nodes collection.
/// </summary>
public readonly int BlockIndex;
/// <summary>
/// Gets the IL offset of this node.
/// </summary>
public readonly int Offset;
/// <summary>
/// Type of the node.
/// </summary>
public readonly ControlFlowNodeType NodeType;
/// <summary>
/// If this node is a FinallyOrFaultHandler node, this field points to the corresponding EndFinallyOrFault node.
/// Otherwise, this field is null.
/// </summary>
public readonly ControlFlowNode EndFinallyOrFaultNode;
/// <summary>
/// Visited flag, used in various algorithms.
/// Before using it in your algorithm, reset it to false by calling ControlFlowGraph.ResetVisited();
/// </summary>
public bool Visited;
/// <summary>
/// Gets whether this node is reachable. Requires that dominance is computed!
/// </summary>
public bool IsReachable {
get { return ImmediateDominator != null || NodeType == ControlFlowNodeType.EntryPoint; }
}
/// <summary>
/// Signalizes that this node is a copy of another node.
/// </summary>
public ControlFlowNode CopyFrom { get; internal set; }
/// <summary>
/// Gets the immediate dominator (the parent in the dominator tree).
/// Null if dominance has not been calculated; or if the node is unreachable.
/// </summary>
public ControlFlowNode ImmediateDominator { get; internal set; }
/// <summary>
/// List of children in the dominator tree.
/// </summary>
public readonly List<ControlFlowNode> DominatorTreeChildren = new List<ControlFlowNode>();
/// <summary>
/// The dominance frontier of this node.
/// This is the set of nodes for which this node dominates a predecessor, but which are not strictly dominated by this node.
/// </summary>
/// <remarks>
/// b.DominanceFrontier = { y in CFG; (exists p in predecessors(y): b dominates p) and not (b strictly dominates y)}
/// </remarks>
public HashSet<ControlFlowNode> DominanceFrontier;
/// <summary>
/// Start of code block represented by this node. Only set for nodetype == Normal.
/// </summary>
public readonly Instruction Start;
/// <summary>
/// End of the code block represented by this node. Only set for nodetype == Normal.
/// The end is exclusive, the end instruction itself does not belong to this block.
/// </summary>
public readonly Instruction End;
/// <summary>
/// Gets the exception handler associated with this node.
/// Only set for nodetype == CatchHandler or nodetype == FinallyOrFaultHandler.
/// </summary>
public readonly ExceptionHandler ExceptionHandler;
/// <summary>
/// List of incoming control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Incoming = new List<ControlFlowEdge>();
/// <summary>
/// List of outgoing control flow edges.
/// </summary>
public readonly List<ControlFlowEdge> Outgoing = new List<ControlFlowEdge>();
/// <summary>
/// Any user data
/// </summary>
public object UserData;
internal ControlFlowNode(int blockIndex, int offset, ControlFlowNodeType nodeType)
{
this.BlockIndex = blockIndex;
this.Offset = offset;
this.NodeType = nodeType;
}
internal ControlFlowNode(int blockIndex, Instruction start, Instruction end)
{
if (start == null)
throw new ArgumentNullException("start");
if (end == null)
throw new ArgumentNullException("end");
this.BlockIndex = blockIndex;
this.NodeType = ControlFlowNodeType.Normal;
this.Start = start;
this.End = end;
this.Offset = start.Offset;
}
internal ControlFlowNode(int blockIndex, ExceptionHandler exceptionHandler, ControlFlowNode endFinallyOrFaultNode)
{
this.BlockIndex = blockIndex;
this.NodeType = endFinallyOrFaultNode != null ? ControlFlowNodeType.FinallyOrFaultHandler : ControlFlowNodeType.CatchHandler;
this.ExceptionHandler = exceptionHandler;
this.EndFinallyOrFaultNode = endFinallyOrFaultNode;
Debug.Assert((exceptionHandler.HandlerType == ExceptionHandlerType.Finally || exceptionHandler.HandlerType == ExceptionHandlerType.Fault) == (endFinallyOrFaultNode != null));
this.Offset = exceptionHandler.HandlerStart.Offset;
}
/// <summary>
/// Gets all predecessors (=sources of incoming edges)
/// </summary>
public IEnumerable<ControlFlowNode> Predecessors {
get {
return Incoming.Select(e => e.Source);
}
}
/// <summary>
/// Gets all successors (=targets of outgoing edges)
/// </summary>
public IEnumerable<ControlFlowNode> Successors {
get {
return Outgoing.Select(e => e.Target);
}
}
/// <summary>
/// Gets all instructions in this node.
/// Returns an empty list for special nodes that don't have any instructions.
/// </summary>
public IEnumerable<Instruction> Instructions {
get {
Instruction inst = Start;
if (inst != null) {
yield return inst;
while (inst != End) {
inst = inst.Next;
yield return inst;
}
}
}
}
public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
visitAction(this);
foreach (ControlFlowNode t in children(this))
t.TraversePreOrder(children, visitAction);
}
public void TraversePostOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
foreach (ControlFlowNode t in children(this))
t.TraversePostOrder(children, visitAction);
visitAction(this);
}
public override string ToString()
{
StringWriter writer = new StringWriter();
switch (NodeType) {
case ControlFlowNodeType.Normal:
int endOffset = End.GetEndOffset();
writer.Write("Block #{0}: IL_{1:x4} to IL_{2:x4}", BlockIndex, Start.Offset, endOffset);
break;
case ControlFlowNodeType.CatchHandler:
case ControlFlowNodeType.FinallyOrFaultHandler:
writer.Write("Block #{0}: {1}: ", BlockIndex, NodeType);
Disassembler.DisassemblerHelpers.WriteTo(ExceptionHandler, new PlainTextOutput(writer));
break;
default:
writer.Write("Block #{0}: {1}", BlockIndex, NodeType);
break;
}
// if (ImmediateDominator != null) {
// writer.WriteLine();
// writer.Write("ImmediateDominator: #{0}", ImmediateDominator.BlockIndex);
// }
if (DominanceFrontier != null && DominanceFrontier.Any()) {
writer.WriteLine();
writer.Write("DominanceFrontier: " + string.Join(",", DominanceFrontier.OrderBy(d => d.BlockIndex).Select(d => d.BlockIndex.ToString())));
}
foreach (Instruction inst in this.Instructions) {
writer.WriteLine();
Disassembler.DisassemblerHelpers.WriteTo(inst, new PlainTextOutput(writer));
}
return writer.ToString();
}
/// <summary>
/// Gets whether <c>this</c> dominates <paramref name="node"/>.
/// </summary>
public bool Dominates(ControlFlowNode node)
{
// TODO: this can be made O(1) by numbering the dominator tree
ControlFlowNode tmp = node;
while (tmp != null) {
if (tmp == this)
return true;
tmp = tmp.ImmediateDominator;
}
return false;
}
}
}

241
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/ControlStructureDetector.cs

@ -0,0 +1,241 @@ @@ -0,0 +1,241 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Detects the structure of the control flow (exception blocks and loops).
/// </summary>
public class ControlStructureDetector
{
public static ControlStructure DetectStructure(ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers, CancellationToken cancellationToken)
{
ControlStructure root = new ControlStructure(new HashSet<ControlFlowNode>(g.Nodes), g.EntryPoint, ControlStructureType.Root);
// First build a structure tree out of the exception table
DetectExceptionHandling(root, g, exceptionHandlers);
// Then run the loop detection.
DetectLoops(g, root, cancellationToken);
return root;
}
#region Exception Handling
static void DetectExceptionHandling(ControlStructure current, ControlFlowGraph g, IEnumerable<ExceptionHandler> exceptionHandlers)
{
// We rely on the fact that the exception handlers are sorted so that the innermost come first.
// For each exception handler, we determine the nodes and substructures inside that handler, and move them into a new substructure.
// This is always possible because exception handlers are guaranteed (by the CLR spec) to be properly nested and non-overlapping;
// so they directly form the tree that we need.
foreach (ExceptionHandler eh in exceptionHandlers) {
var tryNodes = FindNodes(current, eh.TryStart, eh.TryEnd);
current.Nodes.ExceptWith(tryNodes);
ControlStructure tryBlock = new ControlStructure(
tryNodes,
g.Nodes.Single(n => n.Start == eh.TryStart),
ControlStructureType.Try);
tryBlock.ExceptionHandler = eh;
MoveControlStructures(current, tryBlock, eh.TryStart, eh.TryEnd);
current.Children.Add(tryBlock);
if (eh.FilterStart != null) {
throw new NotSupportedException();
}
var handlerNodes = FindNodes(current, eh.HandlerStart, eh.HandlerEnd);
var handlerNode = current.Nodes.Single(n => n.ExceptionHandler == eh);
handlerNodes.Add(handlerNode);
if (handlerNode.EndFinallyOrFaultNode != null)
handlerNodes.Add(handlerNode.EndFinallyOrFaultNode);
current.Nodes.ExceptWith(handlerNodes);
ControlStructure handlerBlock = new ControlStructure(
handlerNodes, handlerNode, ControlStructureType.Handler);
handlerBlock.ExceptionHandler = eh;
MoveControlStructures(current, handlerBlock, eh.HandlerStart, eh.HandlerEnd);
current.Children.Add(handlerBlock);
}
}
/// <summary>
/// Removes all nodes from start to end (exclusive) from this ControlStructure and moves them to the target structure.
/// </summary>
static HashSet<ControlFlowNode> FindNodes(ControlStructure current, Instruction startInst, Instruction endInst)
{
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
int start = startInst.Offset;
int end = endInst.Offset;
foreach (var node in current.Nodes.ToArray()) {
if (node.Start != null && start <= node.Start.Offset && node.Start.Offset < end) {
result.Add(node);
}
}
return result;
}
static void MoveControlStructures(ControlStructure current, ControlStructure target, Instruction startInst, Instruction endInst)
{
for (int i = 0; i < current.Children.Count; i++) {
var child = current.Children[i];
if (startInst.Offset <= child.EntryPoint.Offset && child.EntryPoint.Offset < endInst.Offset) {
current.Children.RemoveAt(i--);
target.Children.Add(child);
target.AllNodes.UnionWith(child.AllNodes);
}
}
}
#endregion
#region Loop Detection
// Loop detection works like this:
// We find a top-level loop by looking for its entry point, which is characterized by a node dominating its own predecessor.
// Then we determine all other nodes that belong to such a loop (all nodes which lead to the entry point, and are dominated by it).
// Finally, we check whether our result conforms with potential existing exception structures, and create the substructure for the loop if successful.
// This algorithm is applied recursively for any substructures (both detected loops and exception blocks)
// But maybe we should get rid of this complex stuff and instead treat every backward jump as a loop?
// That should still work with the IL produced by compilers, and has the advantage that the detected loop bodies are consecutive IL regions.
static void DetectLoops(ControlFlowGraph g, ControlStructure current, CancellationToken cancellationToken)
{
if (!current.EntryPoint.IsReachable)
return;
g.ResetVisited();
cancellationToken.ThrowIfCancellationRequested();
FindLoops(current, current.EntryPoint);
foreach (ControlStructure loop in current.Children)
DetectLoops(g, loop, cancellationToken);
}
static void FindLoops(ControlStructure current, ControlFlowNode node)
{
if (node.Visited)
return;
node.Visited = true;
if (current.Nodes.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& !(node == current.EntryPoint && current.Type == ControlStructureType.Loop))
{
HashSet<ControlFlowNode> loopContents = new HashSet<ControlFlowNode>();
FindLoopContents(current, loopContents, node, node);
List<ControlStructure> containedChildStructures = new List<ControlStructure>();
bool invalidNesting = false;
foreach (ControlStructure childStructure in current.Children) {
if (childStructure.AllNodes.IsSubsetOf(loopContents)) {
containedChildStructures.Add(childStructure);
} else if (childStructure.AllNodes.Intersect(loopContents).Any()) {
invalidNesting = true;
}
}
if (!invalidNesting) {
current.Nodes.ExceptWith(loopContents);
ControlStructure ctl = new ControlStructure(loopContents, node, ControlStructureType.Loop);
foreach (ControlStructure childStructure in containedChildStructures) {
ctl.Children.Add(childStructure);
current.Children.Remove(childStructure);
ctl.Nodes.ExceptWith(childStructure.AllNodes);
}
current.Children.Add(ctl);
}
}
foreach (var edge in node.Outgoing) {
FindLoops(current, edge.Target);
}
}
static void FindLoopContents(ControlStructure current, HashSet<ControlFlowNode> loopContents, ControlFlowNode loopHead, ControlFlowNode node)
{
if (current.AllNodes.Contains(node) && loopHead.Dominates(node) && loopContents.Add(node)) {
foreach (var edge in node.Incoming) {
FindLoopContents(current, loopContents, loopHead, edge.Source);
}
}
}
#endregion
}
public enum ControlStructureType
{
/// <summary>
/// The root block of the method
/// </summary>
Root,
/// <summary>
/// A nested control structure representing a loop.
/// </summary>
Loop,
/// <summary>
/// A nested control structure representing a try block.
/// </summary>
Try,
/// <summary>
/// A nested control structure representing a catch, finally, or fault block.
/// </summary>
Handler,
/// <summary>
/// A nested control structure representing an exception filter block.
/// </summary>
Filter
}
/// <summary>
/// Represents the structure detected by the <see cref="ControlStructureDetector"/>.
///
/// This is a tree of ControlStructure nodes. Each node contains a set of CFG nodes, and every CFG node is contained in exactly one ControlStructure node.
/// </summary>
public class ControlStructure
{
public readonly ControlStructureType Type;
public readonly List<ControlStructure> Children = new List<ControlStructure>();
/// <summary>
/// The nodes in this control structure.
/// </summary>
public readonly HashSet<ControlFlowNode> Nodes;
/// <summary>
/// The nodes in this control structure and in all child control structures.
/// </summary>
public readonly HashSet<ControlFlowNode> AllNodes;
/// <summary>
/// The entry point of this control structure.
/// </summary>
public readonly ControlFlowNode EntryPoint;
/// <summary>
/// The exception handler associated with this Try,Handler or Finally structure.
/// </summary>
public ExceptionHandler ExceptionHandler;
public ControlStructure(HashSet<ControlFlowNode> nodes, ControlFlowNode entryPoint, ControlStructureType type)
{
if (nodes == null)
throw new ArgumentNullException("nodes");
this.Nodes = nodes;
this.EntryPoint = entryPoint;
this.Type = type;
this.AllNodes = new HashSet<ControlFlowNode>(nodes);
}
}
}

312
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/OpCodeInfo.cs

@ -0,0 +1,312 @@ @@ -0,0 +1,312 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Additional info about opcodes.
/// </summary>
sealed class OpCodeInfo
{
public static bool IsUnconditionalBranch(OpCode opcode)
{
if (opcode.OpCodeType == OpCodeType.Prefix)
return false;
switch (opcode.FlowControl) {
case FlowControl.Branch:
case FlowControl.Throw:
case FlowControl.Return:
return true;
case FlowControl.Next:
case FlowControl.Call:
case FlowControl.Cond_Branch:
return false;
default:
throw new NotSupportedException(opcode.FlowControl.ToString());
}
}
static readonly OpCodeInfo[] knownOpCodes = {
#region Base Instructions
new OpCodeInfo(OpCodes.Add) { CanThrow = false },
new OpCodeInfo(OpCodes.Add_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Add_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.And) { CanThrow = false },
new OpCodeInfo(OpCodes.Arglist) { CanThrow = false },
new OpCodeInfo(OpCodes.Beq) { CanThrow = false },
new OpCodeInfo(OpCodes.Beq_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bge_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bgt_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ble_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Blt_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Bne_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Bne_Un_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Br) { CanThrow = false },
new OpCodeInfo(OpCodes.Br_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Break) { CanThrow = true },
new OpCodeInfo(OpCodes.Brfalse) { CanThrow = false },
new OpCodeInfo(OpCodes.Brfalse_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Brtrue) { CanThrow = false },
new OpCodeInfo(OpCodes.Brtrue_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Call) { CanThrow = true },
new OpCodeInfo(OpCodes.Calli) { CanThrow = true },
new OpCodeInfo(OpCodes.Ceq) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt) { CanThrow = false },
new OpCodeInfo(OpCodes.Cgt_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Ckfinite) { CanThrow = true },
new OpCodeInfo(OpCodes.Clt) { CanThrow = false },
new OpCodeInfo(OpCodes.Clt_Un) { CanThrow = false },
// conv.<to type>
new OpCodeInfo(OpCodes.Conv_I1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U1) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U2) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U4) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U8) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_I) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_U) { CanThrow = false },
new OpCodeInfo(OpCodes.Conv_R_Un) { CanThrow = false },
// conv.ovf.<to type>
new OpCodeInfo(OpCodes.Conv_Ovf_I1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U1) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U2) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U4) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U8) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_I) { CanThrow = true},
new OpCodeInfo(OpCodes.Conv_Ovf_U) { CanThrow = true},
// conv.ovf.<to type>.un
new OpCodeInfo(OpCodes.Conv_Ovf_I1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U1_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U2_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U4_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U8_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_I_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Conv_Ovf_U_Un) { CanThrow = true },
//new OpCodeInfo(OpCodes.Cpblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
new OpCodeInfo(OpCodes.Div) { CanThrow = true },
new OpCodeInfo(OpCodes.Div_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Dup) { CanThrow = true, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Endfilter) { CanThrow = false },
new OpCodeInfo(OpCodes.Endfinally) { CanThrow = false },
//new OpCodeInfo(OpCodes.Initblk) { CanThrow = true }, - no idea whether this might cause trouble for the type system, C# shouldn't use it so I'll disable it
//new OpCodeInfo(OpCodes.Jmp) { CanThrow = true } - We don't support non-local control transfers.
new OpCodeInfo(OpCodes.Ldarg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarg_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldarga) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldarga_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_M1) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_0) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_1) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_2) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_3) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_5) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_6) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_7) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I4_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_I8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R4) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldc_R8) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldftn) { CanThrow = false },
// ldind.<type>
new OpCodeInfo(OpCodes.Ldind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U1) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U2) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_U4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Ldind_Ref) { CanThrow = true },
// the ldloc exceptions described in the spec can only occur on methods without .localsinit - but csc always sets that flag
new OpCodeInfo(OpCodes.Ldloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloc_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Ldloca) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldloca_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldnull) { CanThrow = false },
new OpCodeInfo(OpCodes.Leave) { CanThrow = false },
new OpCodeInfo(OpCodes.Leave_S) { CanThrow = false },
new OpCodeInfo(OpCodes.Localloc) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul) { CanThrow = false },
new OpCodeInfo(OpCodes.Mul_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Mul_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Neg) { CanThrow = false },
new OpCodeInfo(OpCodes.Nop) { CanThrow = false },
new OpCodeInfo(OpCodes.Not) { CanThrow = false },
new OpCodeInfo(OpCodes.Or) { CanThrow = false },
new OpCodeInfo(OpCodes.Pop) { CanThrow = false },
new OpCodeInfo(OpCodes.Rem) { CanThrow = true },
new OpCodeInfo(OpCodes.Rem_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Ret) { CanThrow = false },
new OpCodeInfo(OpCodes.Shl) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr) { CanThrow = false },
new OpCodeInfo(OpCodes.Shr_Un) { CanThrow = false },
new OpCodeInfo(OpCodes.Starg) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Starg_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stind_I1) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I2) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R4) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_R8) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_I) { CanThrow = true },
new OpCodeInfo(OpCodes.Stind_Ref) { CanThrow = true },
new OpCodeInfo(OpCodes.Stloc) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_0) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_1) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_2) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_3) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Stloc_S) { CanThrow = false, IsMoveInstruction = true },
new OpCodeInfo(OpCodes.Sub) { CanThrow = false },
new OpCodeInfo(OpCodes.Sub_Ovf) { CanThrow = true },
new OpCodeInfo(OpCodes.Sub_Ovf_Un) { CanThrow = true },
new OpCodeInfo(OpCodes.Switch) { CanThrow = false },
new OpCodeInfo(OpCodes.Xor) { CanThrow = false },
#endregion
#region Object model instructions
// CanThrow is true by default - most OO instructions can throw, so we don't specify CanThrow all of the time
new OpCodeInfo(OpCodes.Box),
new OpCodeInfo(OpCodes.Callvirt),
new OpCodeInfo(OpCodes.Castclass),
new OpCodeInfo(OpCodes.Cpobj),
new OpCodeInfo(OpCodes.Initobj) { CanThrow = false },
new OpCodeInfo(OpCodes.Isinst) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldelem_Any),
// ldelem.<type>
new OpCodeInfo(OpCodes.Ldelem_I) ,
new OpCodeInfo(OpCodes.Ldelem_I1),
new OpCodeInfo(OpCodes.Ldelem_I2),
new OpCodeInfo(OpCodes.Ldelem_I4),
new OpCodeInfo(OpCodes.Ldelem_I8),
new OpCodeInfo(OpCodes.Ldelem_R4),
new OpCodeInfo(OpCodes.Ldelem_R8),
new OpCodeInfo(OpCodes.Ldelem_Ref),
new OpCodeInfo(OpCodes.Ldelem_U1),
new OpCodeInfo(OpCodes.Ldelem_U2),
new OpCodeInfo(OpCodes.Ldelem_U4),
new OpCodeInfo(OpCodes.Ldelema) ,
new OpCodeInfo(OpCodes.Ldfld) ,
new OpCodeInfo(OpCodes.Ldflda),
new OpCodeInfo(OpCodes.Ldlen) ,
new OpCodeInfo(OpCodes.Ldobj) ,
new OpCodeInfo(OpCodes.Ldsfld),
new OpCodeInfo(OpCodes.Ldsflda),
new OpCodeInfo(OpCodes.Ldstr) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldtoken) { CanThrow = false },
new OpCodeInfo(OpCodes.Ldvirtftn),
new OpCodeInfo(OpCodes.Mkrefany),
new OpCodeInfo(OpCodes.Newarr),
new OpCodeInfo(OpCodes.Newobj),
new OpCodeInfo(OpCodes.Refanytype) { CanThrow = false },
new OpCodeInfo(OpCodes.Refanyval),
new OpCodeInfo(OpCodes.Rethrow),
new OpCodeInfo(OpCodes.Sizeof) { CanThrow = false },
new OpCodeInfo(OpCodes.Stelem_Any),
new OpCodeInfo(OpCodes.Stelem_I1),
new OpCodeInfo(OpCodes.Stelem_I2),
new OpCodeInfo(OpCodes.Stelem_I4),
new OpCodeInfo(OpCodes.Stelem_I8),
new OpCodeInfo(OpCodes.Stelem_R4),
new OpCodeInfo(OpCodes.Stelem_R8),
new OpCodeInfo(OpCodes.Stelem_Ref),
new OpCodeInfo(OpCodes.Stfld),
new OpCodeInfo(OpCodes.Stobj),
new OpCodeInfo(OpCodes.Stsfld),
new OpCodeInfo(OpCodes.Throw),
new OpCodeInfo(OpCodes.Unbox),
new OpCodeInfo(OpCodes.Unbox_Any),
#endregion
};
static readonly Dictionary<Code, OpCodeInfo> knownOpCodeDict = knownOpCodes.ToDictionary(info => info.OpCode.Code);
public static OpCodeInfo Get(OpCode opCode)
{
return Get(opCode.Code);
}
public static OpCodeInfo Get(Code code)
{
OpCodeInfo info;
if (knownOpCodeDict.TryGetValue(code, out info))
return info;
else
throw new NotSupportedException(code.ToString());
}
OpCode opcode;
private OpCodeInfo(OpCode opcode)
{
this.opcode = opcode;
this.CanThrow = true;
}
public OpCode OpCode { get { return opcode; } }
/// <summary>
/// 'Move' kind of instructions have one input (may be stack or local variable) and copy that value to all outputs (again stack or local variable).
/// </summary>
public bool IsMoveInstruction { get; private set; }
/// <summary>
/// Specifies whether this opcode is capable of throwing exceptions.
/// </summary>
public bool CanThrow { get; private set; }
}
}

174
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SimplifyByRefCalls.cs

@ -0,0 +1,174 @@ @@ -0,0 +1,174 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// This is a transformation working on SSA form.
/// It removes ldloca instructions and replaces them with SpecialOpCode.PrepareByOutCall or SpecialOpCode.PrepareByRefCall.
/// This then allows the variable that had its address taken to also be transformed into SSA.
/// </summary>
sealed class SimplifyByRefCalls
{
public static bool MakeByRefCallsSimple(SsaForm ssaForm)
{
SimplifyByRefCalls instance = new SimplifyByRefCalls(ssaForm);
foreach (SsaBlock block in ssaForm.Blocks) {
for (int i = 0; i < block.Instructions.Count; i++) {
SsaInstruction inst = block.Instructions[i];
if (inst.Instruction != null) {
switch (inst.Instruction.OpCode.Code) {
case Code.Call:
case Code.Callvirt:
instance.MakeByRefCallSimple(block, ref i, (IMethodSignature)inst.Instruction.Operand);
break;
case Code.Initobj:
instance.MakeInitObjCallSimple(block, ref i);
break;
case Code.Ldfld:
instance.MakeLoadFieldCallSimple(block, ref i);
break;
}
}
}
}
instance.RemoveRedundantInstructions();
if (instance.couldSimplifySomething)
ssaForm.ComputeVariableUsage();
return instance.couldSimplifySomething;
}
readonly SsaForm ssaForm;
bool couldSimplifySomething;
// the list of ldloca instructions we will remove
readonly List<SsaInstruction> redundantLoadAddressInstructions = new List<SsaInstruction>();
private SimplifyByRefCalls(SsaForm ssaForm)
{
this.ssaForm = ssaForm;
}
void MakeByRefCallSimple(SsaBlock block, ref int instructionIndexInBlock, IMethodSignature targetMethod)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
for (int i = 0; i < inst.Operands.Length; i++) {
SsaVariable operand = inst.Operands[i];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// address is used for this method call only
Instruction loadAddressInstruction = operand.Definition.Instruction;
// find target parameter type:
bool isOut;
if (i == 0 && targetMethod.HasThis) {
isOut = false;
} else {
ParameterDefinition parameter = targetMethod.Parameters[i - (targetMethod.HasThis ? 1 : 0)];
isOut = parameter.IsOut;
}
SsaVariable addressTakenOf = GetVariableFromLoadAddressInstruction(loadAddressInstruction);
// insert "Prepare" instruction on front
SpecialOpCode loadOpCode = isOut ? SpecialOpCode.PrepareByOutCall : SpecialOpCode.PrepareByRefCall;
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
block, null, operand, new SsaVariable[] { addressTakenOf }, specialOpCode: loadOpCode));
// insert "WriteAfterByRefOrOutCall" instruction after call
block.Instructions.Insert(instructionIndexInBlock + 1, new SsaInstruction(
block, null, addressTakenOf, new SsaVariable[] { operand }, specialOpCode: SpecialOpCode.WriteAfterByRefOrOutCall));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
// (later because it might be defined in the current block and we don't want instructionIndex to become invalid)
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
}
SsaVariable GetVariableFromLoadAddressInstruction(Instruction loadAddressInstruction)
{
if (loadAddressInstruction.OpCode == OpCodes.Ldloca) {
return ssaForm.GetOriginalVariable((VariableReference)loadAddressInstruction.Operand);
} else {
Debug.Assert(loadAddressInstruction.OpCode == OpCodes.Ldarga);
return ssaForm.GetOriginalVariable((ParameterReference)loadAddressInstruction.Operand);
}
}
static bool IsLoadAddress(SsaInstruction inst)
{
return inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Ldloca || inst.Instruction.OpCode == OpCodes.Ldarga);
}
void MakeInitObjCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// replace instruction with special "InitObj" instruction
block.Instructions[instructionIndexInBlock] = new SsaInstruction(
inst.ParentBlock, null, GetVariableFromLoadAddressInstruction(operand.Definition.Instruction), null,
specialOpCode: SpecialOpCode.InitObj,
typeOperand: (TypeReference)inst.Instruction.Operand);
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void MakeLoadFieldCallSimple(SsaBlock block, ref int instructionIndexInBlock)
{
SsaInstruction inst = block.Instructions[instructionIndexInBlock];
Debug.Assert(inst.Operands.Length == 1);
SsaVariable operand = inst.Operands[0];
if (operand.IsSingleAssignment && operand.Usage.Count == 1 && IsLoadAddress(operand.Definition)) {
// insert special "PrepareForFieldAccess" instruction in front
block.Instructions.Insert(instructionIndexInBlock++, new SsaInstruction(
inst.ParentBlock, null, operand,
new SsaVariable[] { GetVariableFromLoadAddressInstruction(operand.Definition.Instruction) },
specialOpCode: SpecialOpCode.PrepareForFieldAccess));
couldSimplifySomething = true;
// remove the loadAddressInstruction later
redundantLoadAddressInstructions.Add(operand.Definition);
}
}
void RemoveRedundantInstructions()
{
foreach (SsaInstruction inst in redundantLoadAddressInstructions) {
inst.ParentBlock.Instructions.Remove(inst);
}
}
}
}

60
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaBlock.cs

@ -0,0 +1,60 @@ @@ -0,0 +1,60 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// A block in a control flow graph; with instructions represented by "SsaInstructions" (instructions use variables, no evaluation stack).
/// Usually these variables are in SSA form to make analysis easier.
/// </summary>
public sealed class SsaBlock
{
public readonly List<SsaBlock> Successors = new List<SsaBlock>();
public readonly List<SsaBlock> Predecessors = new List<SsaBlock>();
public readonly ControlFlowNodeType NodeType;
public readonly List<SsaInstruction> Instructions = new List<SsaInstruction>();
/// <summary>
/// The block index in the control flow graph.
/// This correspons to the node index in ControlFlowGraph.Nodes, so it can be used to retrieve the original CFG node and look
/// up additional information (e.g. dominance).
/// </summary>
public readonly int BlockIndex;
internal SsaBlock(ControlFlowNode node)
{
this.NodeType = node.NodeType;
this.BlockIndex = node.BlockIndex;
}
public override string ToString()
{
StringWriter writer = new StringWriter();
writer.Write("Block #{0} ({1})", BlockIndex, NodeType);
foreach (SsaInstruction inst in Instructions) {
writer.WriteLine();
inst.WriteTo(writer);
}
return writer.ToString();
}
}
}

162
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaForm.cs

@ -0,0 +1,162 @@ @@ -0,0 +1,162 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Represents a graph of SsaBlocks.
/// </summary>
public sealed class SsaForm
{
readonly SsaVariable[] parameters;
readonly SsaVariable[] locals;
public readonly ReadOnlyCollection<SsaVariable> OriginalVariables;
public readonly ReadOnlyCollection<SsaBlock> Blocks;
readonly bool methodHasThis;
public SsaBlock EntryPoint {
get { return this.Blocks[0]; }
}
public SsaBlock RegularExit {
get { return this.Blocks[1]; }
}
public SsaBlock ExceptionalExit {
get { return this.Blocks[2]; }
}
internal SsaForm(SsaBlock[] blocks, SsaVariable[] parameters, SsaVariable[] locals, SsaVariable[] stackLocations, bool methodHasThis)
{
this.parameters = parameters;
this.locals = locals;
this.Blocks = new ReadOnlyCollection<SsaBlock>(blocks);
this.OriginalVariables = new ReadOnlyCollection<SsaVariable>(parameters.Concat(locals).Concat(stackLocations).ToList());
this.methodHasThis = methodHasThis;
Debug.Assert(EntryPoint.NodeType == ControlFlowNodeType.EntryPoint);
Debug.Assert(RegularExit.NodeType == ControlFlowNodeType.RegularExit);
Debug.Assert(ExceptionalExit.NodeType == ControlFlowNodeType.ExceptionalExit);
for (int i = 0; i < this.OriginalVariables.Count; i++) {
this.OriginalVariables[i].OriginalVariableIndex = i;
}
}
public GraphVizGraph ExportBlockGraph(Func<SsaBlock, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = b => b.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaBlock block in this.Blocks) {
graph.AddNode(new GraphVizNode(block.BlockIndex) { label = labelProvider(block), shape = "box" });
}
foreach (SsaBlock block in this.Blocks) {
foreach (SsaBlock s in block.Successors) {
graph.AddEdge(new GraphVizEdge(block.BlockIndex, s.BlockIndex));
}
}
return graph;
}
public GraphVizGraph ExportVariableGraph(Func<SsaVariable, string> labelProvider = null)
{
if (labelProvider == null)
labelProvider = v => v.ToString();
GraphVizGraph graph = new GraphVizGraph();
foreach (SsaVariable v in this.AllVariables) {
graph.AddNode(new GraphVizNode(v.Name) { label = labelProvider(v) });
}
int instructionIndex = 0;
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Operands.Length == 0 && inst.Target == null)
continue;
string id = "instruction" + (++instructionIndex);
graph.AddNode(new GraphVizNode(id) { label = inst.ToString(), shape = "box" });
foreach (SsaVariable op in inst.Operands)
graph.AddEdge(new GraphVizEdge(op.Name, id));
if (inst.Target != null)
graph.AddEdge(new GraphVizEdge(id, inst.Target.Name));
}
}
return graph;
}
public SsaVariable GetOriginalVariable(ParameterReference parameter)
{
if (methodHasThis)
return parameters[parameter.Index + 1];
else
return parameters[parameter.Index];
}
public SsaVariable GetOriginalVariable(VariableReference variable)
{
return locals[variable.Index];
}
#region ComputeVariableUsage
public void ComputeVariableUsage()
{
// clear data from previous runs
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage != null)
v.Usage.Clear();
}
if (inst.Target != null && inst.Target.Usage != null)
inst.Target.Usage.Clear();
}
}
foreach (SsaBlock block in this.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
foreach (SsaVariable v in inst.Operands) {
if (v.Usage == null)
v.Usage = new List<SsaInstruction>();
v.Usage.Add(inst);
}
if (inst.Target != null && inst.Target.Usage == null)
inst.Target.Usage = new List<SsaInstruction>();
}
}
}
#endregion
public IEnumerable<SsaVariable> AllVariables {
get {
return (
from block in this.Blocks
from instruction in block.Instructions
where instruction.Target != null
select instruction.Target
).Distinct();
}
}
}
}

257
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaFormBuilder.cs

@ -0,0 +1,257 @@ @@ -0,0 +1,257 @@
// Copyright (c) 2010 Daniel Grunwald
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Constructs "SsaForm" graph for a CFG.
/// This class transforms the method from stack-based IL to a register-based IL language.
/// Then it calls into TransformToSsa to convert the resulting graph to static single assignment form.
/// </summary>
public sealed class SsaFormBuilder
{
public static SsaForm Build(MethodDefinition method)
{
if (method == null)
throw new ArgumentNullException("method");
var cfg = ControlFlowGraphBuilder.Build(method.Body);
cfg.ComputeDominance();
cfg.ComputeDominanceFrontier();
var ssa = BuildRegisterIL(method, cfg);
TransformToSsa.Transform(cfg, ssa);
return ssa;
}
public static SsaForm BuildRegisterIL(MethodDefinition method, ControlFlowGraph cfg)
{
if (method == null)
throw new ArgumentNullException("method");
if (cfg == null)
throw new ArgumentNullException("cfg");
return new SsaFormBuilder(method, cfg).Build();
}
readonly MethodDefinition method;
readonly ControlFlowGraph cfg;
readonly SsaBlock[] blocks; // array index = block index
readonly int[] stackSizeAtBlockStart; // array index = block index
readonly SsaVariable[] parameters; // array index = parameter number
readonly SsaVariable[] locals; // array index = local number
readonly SsaVariable[] stackLocations; // array index = position on the IL evaluation stack
SsaForm ssaForm;
private SsaFormBuilder(MethodDefinition method, ControlFlowGraph cfg)
{
this.method = method;
this.cfg = cfg;
this.blocks = new SsaBlock[cfg.Nodes.Count];
this.stackSizeAtBlockStart = new int[cfg.Nodes.Count];
for (int i = 0; i < stackSizeAtBlockStart.Length; i++) {
stackSizeAtBlockStart[i] = -1;
}
stackSizeAtBlockStart[cfg.EntryPoint.BlockIndex] = 0;
this.parameters = new SsaVariable[method.Parameters.Count + (method.HasThis ? 1 : 0)];
if (method.HasThis)
parameters[0] = new SsaVariable(method.Body.ThisParameter);
for (int i = 0; i < method.Parameters.Count; i++)
parameters[i + (method.HasThis ? 1 : 0)] = new SsaVariable(method.Parameters[i]);
this.locals = new SsaVariable[method.Body.Variables.Count];
for (int i = 0; i < locals.Length; i++)
locals[i] = new SsaVariable(method.Body.Variables[i]);
this.stackLocations = new SsaVariable[method.Body.MaxStackSize];
for (int i = 0; i < stackLocations.Length; i++) {
stackLocations[i] = new SsaVariable(i);
}
}
internal SsaForm Build()
{
CreateGraphStructure();
this.ssaForm = new SsaForm(blocks, parameters, locals, stackLocations, method.HasThis);
CreateInstructions(cfg.EntryPoint.BlockIndex);
CreateSpecialInstructions();
return ssaForm;
}
void CreateGraphStructure()
{
for (int i = 0; i < blocks.Length; i++) {
blocks[i] = new SsaBlock(cfg.Nodes[i]);
}
for (int i = 0; i < blocks.Length; i++) {
foreach (ControlFlowNode node in cfg.Nodes[i].Successors) {
blocks[i].Successors.Add(blocks[node.BlockIndex]);
blocks[node.BlockIndex].Predecessors.Add(blocks[i]);
}
}
}
void CreateInstructions(int blockIndex)
{
ControlFlowNode cfgNode = cfg.Nodes[blockIndex];
SsaBlock block = blocks[blockIndex];
int stackSize = stackSizeAtBlockStart[blockIndex];
Debug.Assert(stackSize >= 0);
List<Instruction> prefixes = new List<Instruction>();
foreach (Instruction inst in cfgNode.Instructions) {
if (inst.OpCode.OpCodeType == OpCodeType.Prefix) {
prefixes.Add(inst);
continue;
}
int popCount = inst.GetPopDelta() ?? stackSize;
stackSize -= popCount;
if (stackSize < 0)
throw new InvalidProgramException("IL stack underflow");
int pushCount = inst.GetPushDelta();
if (stackSize + pushCount > stackLocations.Length)
throw new InvalidProgramException("IL stack overflow");
SsaVariable target;
SsaVariable[] operands;
DetermineOperands(stackSize, inst, popCount, pushCount, out target, out operands);
Instruction[] prefixArray = prefixes.Count > 0 ? prefixes.ToArray() : null;
prefixes.Clear();
// ignore NOP instructions
if (!(inst.OpCode == OpCodes.Nop || inst.OpCode == OpCodes.Pop)) {
block.Instructions.Add(new SsaInstruction(block, inst, target, operands, prefixArray));
}
stackSize += pushCount;
}
foreach (ControlFlowEdge edge in cfgNode.Outgoing) {
int newStackSize;
switch (edge.Type) {
case JumpType.Normal:
newStackSize = stackSize;
break;
case JumpType.EndFinally:
if (stackSize != 0)
throw new NotSupportedException("stacksize must be 0 in endfinally edge");
newStackSize = 0;
break;
case JumpType.JumpToExceptionHandler:
switch (edge.Target.NodeType) {
case ControlFlowNodeType.FinallyOrFaultHandler:
newStackSize = 0;
break;
case ControlFlowNodeType.ExceptionalExit:
case ControlFlowNodeType.CatchHandler:
newStackSize = 1;
break;
default:
throw new NotSupportedException("unsupported target node type: " + edge.Target.NodeType);
}
break;
default:
throw new NotSupportedException("unsupported jump type: " + edge.Type);
}
int nextStackSize = stackSizeAtBlockStart[edge.Target.BlockIndex];
if (nextStackSize == -1) {
stackSizeAtBlockStart[edge.Target.BlockIndex] = newStackSize;
CreateInstructions(edge.Target.BlockIndex);
} else if (nextStackSize != newStackSize) {
throw new InvalidProgramException("Stack size doesn't match");
}
}
}
void DetermineOperands(int stackSize, Instruction inst, int popCount, int pushCount, out SsaVariable target, out SsaVariable[] operands)
{
switch (inst.OpCode.Code) {
case Code.Ldarg:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((ParameterReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Starg:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((ParameterReference)inst.Operand);
break;
case Code.Ldloc:
operands = new SsaVariable[] { ssaForm.GetOriginalVariable((VariableReference)inst.Operand) };
target = stackLocations[stackSize];
break;
case Code.Stloc:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = ssaForm.GetOriginalVariable((VariableReference)inst.Operand);
break;
case Code.Dup:
operands = new SsaVariable[] { stackLocations[stackSize] };
target = stackLocations[stackSize + 1];
break;
default:
operands = new SsaVariable[popCount];
for (int i = 0; i < popCount; i++) {
operands[i] = stackLocations[stackSize + i];
}
switch (pushCount) {
case 0:
target = null;
break;
case 1:
target = stackLocations[stackSize];
break;
default:
throw new NotSupportedException("unsupported pushCount=" + pushCount);
}
break;
}
}
void CreateSpecialInstructions()
{
// Everything needs an initial write for the SSA transformation to work correctly.
foreach (SsaVariable v in parameters) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Parameter));
}
foreach (SsaVariable v in locals) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaVariable v in stackLocations) {
ssaForm.EntryPoint.Instructions.Add(new SsaInstruction(ssaForm.EntryPoint, null, v, null, specialOpCode: SpecialOpCode.Uninitialized));
}
foreach (SsaBlock b in blocks) {
if (b.NodeType == ControlFlowNodeType.CatchHandler) {
b.Instructions.Add(new SsaInstruction(b, null, stackLocations[0], null,
specialOpCode: SpecialOpCode.Exception,
typeOperand: cfg.Nodes[b.BlockIndex].ExceptionHandler.CatchType));
}
}
}
}
}

191
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaInstruction.cs

@ -0,0 +1,191 @@ @@ -0,0 +1,191 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using System.IO;
using ICSharpCode.Decompiler.Disassembler;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
public enum SpecialOpCode
{
/// <summary>
/// No special op code: SsaInstruction has a normal IL instruction
/// </summary>
None,
/// <summary>
/// Φ function: chooses the appropriate variable based on which CFG edge was used to enter this block
/// </summary>
Phi,
/// <summary>
/// Variable is read from before passing it by ref.
/// This instruction constructs a managed reference to the variable.
/// </summary>
PrepareByRefCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The variable is not really read from.
/// </summary>
PrepareByOutCall,
/// <summary>
/// This instruction constructs a managed reference to the variable.
/// The reference is used for a field access on a value type.
/// </summary>
PrepareForFieldAccess,
/// <summary>
/// Variable is written to after passing it by ref or out.
/// </summary>
WriteAfterByRefOrOutCall,
/// <summary>
/// Variable is not initialized.
/// </summary>
Uninitialized,
/// <summary>
/// Value is passed in as parameter
/// </summary>
Parameter,
/// <summary>
/// Value is a caught exception.
/// TypeOperand is set to the exception type.
/// </summary>
Exception,
/// <summary>
/// Initialize a value type. Unlike the real initobj instruction, this one does not take an address
/// but assigns to the target variable.
/// TypeOperand is set to the type being created.
/// </summary>
InitObj
}
public sealed class SsaInstruction
{
public readonly SsaBlock ParentBlock;
public readonly SpecialOpCode SpecialOpCode;
/// <summary>
/// The original IL instruction.
/// May be null for "invented" instructions (SpecialOpCode != None).
/// </summary>
public readonly Instruction Instruction;
/// <summary>
/// Prefixes in front of the IL instruction.
/// </summary>
public readonly Instruction[] Prefixes;
/// <summary>
/// Gets the type operand. This is used only in combination with some special opcodes.
/// </summary>
public readonly TypeReference TypeOperand;
public SsaVariable Target;
public SsaVariable[] Operands;
static readonly SsaVariable[] emptyVariableArray = {};
static readonly Instruction[] emptyInstructionArray = {};
public SsaInstruction(SsaBlock parentBlock, Instruction instruction, SsaVariable target, SsaVariable[] operands,
Instruction[] prefixes = null, SpecialOpCode specialOpCode = SpecialOpCode.None,
TypeReference typeOperand = null)
{
this.ParentBlock = parentBlock;
this.Instruction = instruction;
this.Prefixes = prefixes ?? emptyInstructionArray;
this.Target = target;
this.Operands = operands ?? emptyVariableArray;
this.SpecialOpCode = specialOpCode;
this.TypeOperand = typeOperand;
Debug.Assert((typeOperand != null) == (specialOpCode == SpecialOpCode.Exception || specialOpCode == SpecialOpCode.InitObj));
}
/// <summary>
/// Gets whether this instruction is a simple assignment from one variable to another.
/// </summary>
public bool IsMoveInstruction {
get {
return Target != null && Operands.Length == 1 && Instruction != null && OpCodeInfo.Get(Instruction.OpCode).IsMoveInstruction;
}
}
public void ReplaceVariableInOperands(SsaVariable oldVar, SsaVariable newVar)
{
for (int i = 0; i < this.Operands.Length; i++) {
if (this.Operands[i] == oldVar)
this.Operands[i] = newVar;
}
}
public override string ToString()
{
StringWriter w = new StringWriter();
WriteTo(w);
return w.ToString();
}
public void WriteTo(TextWriter writer)
{
foreach (Instruction prefix in this.Prefixes) {
Disassembler.DisassemblerHelpers.WriteTo(prefix, new PlainTextOutput(writer));
writer.WriteLine();
}
if (Instruction != null && Instruction.Offset >= 0) {
writer.Write(CecilExtensions.OffsetToString(Instruction.Offset));
writer.Write(": ");
}
if (Target != null) {
writer.Write(Target.ToString());
writer.Write(" = ");
}
if (IsMoveInstruction) {
writer.Write(Operands[0].ToString());
if (Instruction != null) {
writer.Write(" (" + Instruction.OpCode.Name + ")");
}
} else {
if (Instruction == null) {
writer.Write(SpecialOpCode.ToString());
} else {
writer.Write(Instruction.OpCode.Name);
if(null != Instruction.Operand) {
writer.Write(' ');
Disassembler.DisassemblerHelpers.WriteOperand(new PlainTextOutput(writer), Instruction.Operand);
writer.Write(' ');
}
}
if (TypeOperand != null) {
writer.Write(' ');
writer.Write(TypeOperand.ToString());
writer.Write(' ');
}
if (Operands.Length > 0) {
writer.Write('(');
for (int i = 0; i < Operands.Length; i++) {
if (i > 0)
writer.Write(", ");
writer.Write(Operands[i].ToString());
}
writer.Write(')');
}
}
}
}
}

138
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaOptimization.cs

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Contains some very simple optimizations that work on the SSA form.
/// </summary>
static class SsaOptimization
{
public static void Optimize(SsaForm ssaForm)
{
DirectlyStoreToVariables(ssaForm);
SimpleCopyPropagation(ssaForm);
RemoveDeadAssignments(ssaForm);
}
/// <summary>
/// When any instructions stores its result in a stack location that's used only once in a 'stloc' or 'starg' instruction,
/// we optimize this to directly store in the target location.
/// As optimization this is redundant (does the same as copy propagation), but it'll make us keep the variables named
/// after locals instead of keeping the temps as using only the simple copy propagation would do.
/// </summary>
public static void DirectlyStoreToVariables(SsaForm ssaForm)
{
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Instruction != null && (inst.Instruction.OpCode == OpCodes.Stloc || inst.Instruction.OpCode == OpCodes.Starg)) {
SsaVariable target = inst.Target;
SsaVariable temp = inst.Operands[0];
if (target.IsSingleAssignment && temp.IsSingleAssignment && temp.Usage.Count == 1 && temp.IsStackLocation) {
temp.Definition.Target = target;
return true;
}
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void SimpleCopyPropagation(SsaForm ssaForm, bool onlyForStackLocations = true)
{
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.IsMoveInstruction && inst.Target.IsSingleAssignment && inst.Operands[0].IsSingleAssignment) {
if (inst.Target.IsStackLocation || !onlyForStackLocations) {
// replace all uses of 'target' with 'operands[0]'.
foreach (SsaInstruction useInstruction in inst.Target.Usage) {
useInstruction.ReplaceVariableInOperands(inst.Target, inst.Operands[0]);
}
}
}
}
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
public static void RemoveDeadAssignments(SsaForm ssaForm)
{
HashSet<SsaVariable> liveVariables = new HashSet<SsaVariable>();
// find variables that are used directly
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (!CanRemoveAsDeadCode(inst)) {
if (inst.Target != null)
liveVariables.Add(inst.Target);
foreach (SsaVariable op in inst.Operands) {
liveVariables.Add(op);
}
}
}
}
Queue<SsaVariable> queue = new Queue<SsaVariable>(liveVariables);
// find variables that are used indirectly
while (queue.Count > 0) {
SsaVariable v = queue.Dequeue();
if (v.IsSingleAssignment) {
foreach (SsaVariable op in v.Definition.Operands) {
if (liveVariables.Add(op))
queue.Enqueue(op);
}
}
}
// remove assignments to all unused variables
foreach (SsaBlock block in ssaForm.Blocks) {
block.Instructions.RemoveAll(
inst => {
if (inst.Target != null && !liveVariables.Contains(inst.Target)) {
Debug.Assert(inst.Target.IsSingleAssignment);
return true;
}
return false;
});
}
ssaForm.ComputeVariableUsage(); // update usage after we modified stuff
}
static bool CanRemoveAsDeadCode(SsaInstruction inst)
{
if (inst.Target != null && !inst.Target.IsSingleAssignment)
return false;
switch (inst.SpecialOpCode) {
case SpecialOpCode.Phi:
case SpecialOpCode.Exception:
case SpecialOpCode.Parameter:
case SpecialOpCode.Uninitialized:
return true;
case SpecialOpCode.None:
return inst.IsMoveInstruction;
default:
return false;
}
}
}
}

91
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/SsaVariable.cs

@ -0,0 +1,91 @@ @@ -0,0 +1,91 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Represents a variable used with the SsaInstruction register-based instructions.
/// Despite what the name suggests, the variable is not necessarily in single-assignment form - take a look at "bool IsSingleAssignment".
/// </summary>
public sealed class SsaVariable
{
public int OriginalVariableIndex;
public readonly string Name;
public readonly bool IsStackLocation;
public readonly ParameterDefinition Parameter;
public readonly VariableDefinition Variable;
public SsaVariable(ParameterDefinition p)
{
this.Name = string.IsNullOrEmpty(p.Name) ? "param" + p.Index : p.Name;
this.Parameter = p;
}
public SsaVariable(VariableDefinition v)
{
this.Name = string.IsNullOrEmpty(v.Name) ? "V_" + v.Index : v.Name;
this.Variable = v;
}
public SsaVariable(int stackLocation)
{
this.Name = "stack" + stackLocation;
this.IsStackLocation = true;
}
public SsaVariable(SsaVariable original, string newName)
{
this.Name = newName;
this.IsStackLocation = original.IsStackLocation;
this.OriginalVariableIndex = original.OriginalVariableIndex;
this.Parameter = original.Parameter;
this.Variable = original.Variable;
}
public override string ToString()
{
return Name;
}
/// <summary>
/// Gets whether this variable has only a single assignment.
/// This field is initialized in TransformToSsa step.
/// </summary>
/// <remarks>Not all variables can be transformed to single assignment form: variables that have their address taken
/// cannot be represented in SSA (although SimplifyByRefCalls will get rid of the address-taking instruction in almost all cases)</remarks>
public bool IsSingleAssignment;
/// <summary>
/// Gets the instruction defining the variable.
/// This field is initialized in TransformToSsa step. It is only set for variables with a single assignment.
/// </summary>
public SsaInstruction Definition;
/// <summary>
/// Gets the places where a variable is used.
/// If a single instruction reads a variable 2 times (e.g. adding to itself), then it must be included 2 times in this list!
/// </summary>
public List<SsaInstruction> Usage;
}
}

254
src/Libraries/ICSharpCode.Decompiler/FlowAnalysis/TransformToSsa.cs

@ -0,0 +1,254 @@ @@ -0,0 +1,254 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.FlowAnalysis
{
/// <summary>
/// Convers a method to static single assignment form.
/// </summary>
sealed class TransformToSsa
{
public static void Transform(ControlFlowGraph cfg, SsaForm ssa, bool optimize = true)
{
TransformToSsa transform = new TransformToSsa(cfg, ssa);
transform.ConvertVariablesToSsa();
SsaOptimization.RemoveDeadAssignments(ssa); // required so that 'MakeByRefCallsSimple' can detect more cases
if (SimplifyByRefCalls.MakeByRefCallsSimple(ssa)) {
transform.ConvertVariablesToSsa();
}
if (optimize)
SsaOptimization.Optimize(ssa);
}
readonly ControlFlowGraph cfg;
readonly SsaForm ssaForm;
readonly List<SsaInstruction>[] writeToOriginalVariables; // array index -> SsaVariable OriginalVariableIndex
readonly bool[] addressTaken; // array index -> SsaVariable OriginalVariableIndex; value = whether ldloca instruction was used with variable
private TransformToSsa(ControlFlowGraph cfg, SsaForm ssaForm)
{
this.cfg = cfg;
this.ssaForm = ssaForm;
this.writeToOriginalVariables = new List<SsaInstruction>[ssaForm.OriginalVariables.Count];
this.addressTaken = new bool[ssaForm.OriginalVariables.Count];
}
#region CollectInformationAboutOriginalVariableUse
void CollectInformationAboutOriginalVariableUse()
{
Debug.Assert(addressTaken.Length == writeToOriginalVariables.Length);
for (int i = 0; i < writeToOriginalVariables.Length; i++) {
Debug.Assert(ssaForm.OriginalVariables[i].OriginalVariableIndex == i);
addressTaken[i] = false;
// writeToOriginalVariables is only used when placing phi functions
// we don't need to do that anymore for variables that are already in SSA form
if (ssaForm.OriginalVariables[i].IsSingleAssignment)
writeToOriginalVariables[i] = null;
else
writeToOriginalVariables[i] = new List<SsaInstruction>();
}
foreach (SsaBlock block in ssaForm.Blocks) {
foreach (SsaInstruction inst in block.Instructions) {
if (inst.Target != null ) {
var list = writeToOriginalVariables[inst.Target.OriginalVariableIndex];
if (list != null)
list.Add(inst);
}
if (inst.Instruction != null) {
if (inst.Instruction.OpCode == OpCodes.Ldloca) {
addressTaken[ssaForm.GetOriginalVariable((VariableDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
} else if (inst.Instruction.OpCode == OpCodes.Ldarga) {
addressTaken[ssaForm.GetOriginalVariable((ParameterDefinition)inst.Instruction.Operand).OriginalVariableIndex] = true;
}
}
}
}
}
#endregion
#region ConvertToSsa
void ConvertVariablesToSsa()
{
CollectInformationAboutOriginalVariableUse();
bool[] processVariable = new bool[ssaForm.OriginalVariables.Count];
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!variable.IsSingleAssignment && !addressTaken[variable.OriginalVariableIndex]) {
PlacePhiFunctions(variable);
processVariable[variable.OriginalVariableIndex] = true;
}
}
RenameVariables(processVariable);
foreach (SsaVariable variable in ssaForm.OriginalVariables) {
if (!addressTaken[variable.OriginalVariableIndex]) {
Debug.Assert(variable.IsSingleAssignment && variable.Definition != null);
}
}
ssaForm.ComputeVariableUsage();
}
#endregion
#region PlacePhiFunctions
void PlacePhiFunctions(SsaVariable variable)
{
cfg.ResetVisited();
HashSet<SsaBlock> blocksWithPhi = new HashSet<SsaBlock>();
Queue<ControlFlowNode> worklist = new Queue<ControlFlowNode>();
foreach (SsaInstruction writeInstruction in writeToOriginalVariables[variable.OriginalVariableIndex]) {
ControlFlowNode cfgNode = cfg.Nodes[writeInstruction.ParentBlock.BlockIndex];
if (!cfgNode.Visited) {
cfgNode.Visited = true;
worklist.Enqueue(cfgNode);
}
}
while (worklist.Count > 0) {
ControlFlowNode cfgNode = worklist.Dequeue();
foreach (ControlFlowNode dfNode in cfgNode.DominanceFrontier) {
// we don't need phi functions in the exit node
if (dfNode.NodeType == ControlFlowNodeType.RegularExit || dfNode.NodeType == ControlFlowNodeType.ExceptionalExit)
continue;
SsaBlock y = ssaForm.Blocks[dfNode.BlockIndex];
if (blocksWithPhi.Add(y)) {
// add a phi instruction in y
SsaVariable[] operands = Enumerable.Repeat(variable, dfNode.Incoming.Count).ToArray();
y.Instructions.Insert(0, new SsaInstruction(y, null, variable, operands, specialOpCode: SpecialOpCode.Phi));
if (!dfNode.Visited) {
dfNode.Visited = true;
worklist.Enqueue(dfNode);
}
}
}
}
}
#endregion
#region RenameVariable
int tempVariableCounter = 1;
void RenameVariables(bool[] processVariable)
{
VariableRenamer r = new VariableRenamer(this, processVariable);
r.Visit(ssaForm.EntryPoint);
}
sealed class VariableRenamer
{
readonly TransformToSsa transform;
readonly ReadOnlyCollection<SsaVariable> inputVariables;
internal readonly Stack<SsaVariable>[] versionStacks;
int[] versionCounters; // specifies for each input variable the next version number
// processVariable = specifies for each input variable whether we should rename it
public VariableRenamer(TransformToSsa transform, bool[] processVariable)
{
this.transform = transform;
this.inputVariables = transform.ssaForm.OriginalVariables;
Debug.Assert(inputVariables.Count == processVariable.Length);
this.versionCounters = new int[inputVariables.Count];
this.versionStacks = new Stack<SsaVariable>[inputVariables.Count];
for (int i = 0; i < versionStacks.Length; i++) {
if (processVariable[i]) {
Debug.Assert(inputVariables[i].IsSingleAssignment == false);
// only create version stacks for the variables that we need to process and that weren't already processed earlier
versionStacks[i] = new Stack<SsaVariable>();
versionStacks[i].Push(inputVariables[i]);
}
}
}
SsaVariable MakeNewVersion(int variableIndex)
{
int versionCounter = ++versionCounters[variableIndex];
SsaVariable x = inputVariables[variableIndex];
if (versionCounter == 1) {
return x;
} else {
if (x.IsStackLocation) {
return new SsaVariable(x, "temp" + (transform.tempVariableCounter++));
} else {
return new SsaVariable(x, x.Name + "_" + versionCounter);
}
}
}
internal void Visit(SsaBlock block)
{
// duplicate top of all stacks
foreach (var stack in versionStacks) {
if (stack != null)
stack.Push(stack.Peek());
}
foreach (SsaInstruction s in block.Instructions) {
// replace all uses of variables being processed with their current version.
if (s.SpecialOpCode != SpecialOpCode.Phi) {
for (int i = 0; i < s.Operands.Length; i++) {
var stack = versionStacks[s.Operands[i].OriginalVariableIndex];
if (stack != null)
s.Operands[i] = stack.Peek();
}
}
// if we're writing to a variable we should process:
if (s.Target != null) {
int targetIndex = s.Target.OriginalVariableIndex;
if (versionStacks[targetIndex] != null) {
s.Target = MakeNewVersion(targetIndex);
s.Target.IsSingleAssignment = true;
s.Target.Definition = s;
// we already pushed our entry for this SsaBlock at the beginning (where we duplicated all stacks),
// so now replace the top element
versionStacks[targetIndex].Pop();
versionStacks[targetIndex].Push(s.Target);
}
}
}
foreach (SsaBlock succ in block.Successors) {
int j = succ.Predecessors.IndexOf(block);
Debug.Assert(j >= 0);
foreach (SsaInstruction f in succ.Instructions) {
if (f.SpecialOpCode == SpecialOpCode.Phi) {
var stack = versionStacks[f.Target.OriginalVariableIndex];
if (stack != null) {
f.Operands[j] = stack.Peek();
}
}
}
}
foreach (ControlFlowNode child in transform.cfg.Nodes[block.BlockIndex].DominatorTreeChildren)
Visit(transform.ssaForm.Blocks[child.BlockIndex]);
// restore stacks:
foreach (var stack in versionStacks) {
if (stack != null)
stack.Pop();
}
}
}
#endregion
}
}

138
src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -0,0 +1,138 @@ @@ -0,0 +1,138 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{984CC812-9470-4A13-AFF9-CC44068D666C}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.Decompiler</RootNamespace>
<AssemblyName>ICSharpCode.Decompiler</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<TargetFrameworkProfile>Client</TargetFrameworkProfile>
<AppDesignerFolder>Properties</AppDesignerFolder>
<AllowUnsafeBlocks>False</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<RegisterForComInterop>False</RegisterForComInterop>
<GenerateSerializationAssemblies>Auto</GenerateSerializationAssemblies>
<BaseAddress>4194304</BaseAddress>
<FileAlignment>4096</FileAlignment>
</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>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\Main\GlobalAssemblyInfo.cs">
<Link>Properties\GlobalAssemblyInfo.cs</Link>
</Compile>
<Compile Include="Ast\AstBuilder.cs" />
<Compile Include="Ast\AstMethodBodyBuilder.cs" />
<Compile Include="Ast\CecilTypeResolveContext.cs" />
<Compile Include="Ast\CommentStatement.cs" />
<Compile Include="Ast\DecompilerContext.cs" />
<Compile Include="Ast\NameVariables.cs" />
<Compile Include="Ast\NRefactoryExtensions.cs" />
<Compile Include="Ast\TextOutputFormatter.cs" />
<Compile Include="Ast\Transforms\AddCheckedBlocks.cs" />
<Compile Include="Ast\Transforms\CombineQueryExpressions.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DecimalConstantTransform.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" />
<Compile Include="Ast\Transforms\IntroduceQueryExpressions.cs" />
<Compile Include="Ast\Transforms\IntroduceUnsafeModifier.cs" />
<Compile Include="Ast\Transforms\IntroduceUsingDeclarations.cs" />
<Compile Include="Ast\Transforms\ReplaceMethodCallsWithOperators.cs" />
<Compile Include="Ast\Transforms\PushNegation.cs" />
<Compile Include="Ast\Transforms\TransformationPipeline.cs" />
<Compile Include="Ast\Transforms\PatternStatementTransform.cs" />
<Compile Include="Ast\TypesHierarchyHelpers.cs" />
<Compile Include="CecilExtensions.cs" />
<Compile Include="CodeMappings.cs" />
<Compile Include="DecompilerException.cs" />
<Compile Include="DecompilerSettings.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" />
<Compile Include="Disassembler\ILStructure.cs" />
<Compile Include="Disassembler\MethodBodyDisassembler.cs" />
<Compile Include="Disassembler\ReflectionDisassembler.cs" />
<Compile Include="FlowAnalysis\ControlFlowEdge.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraph.cs" />
<Compile Include="FlowAnalysis\ControlFlowGraphBuilder.cs" />
<Compile Include="FlowAnalysis\ControlFlowNode.cs" />
<Compile Include="FlowAnalysis\ControlStructureDetector.cs" />
<Compile Include="FlowAnalysis\OpCodeInfo.cs" />
<Compile Include="FlowAnalysis\SimplifyByRefCalls.cs" />
<Compile Include="FlowAnalysis\SsaBlock.cs" />
<Compile Include="FlowAnalysis\SsaForm.cs" />
<Compile Include="FlowAnalysis\SsaFormBuilder.cs" />
<Compile Include="FlowAnalysis\SsaInstruction.cs" />
<Compile Include="FlowAnalysis\SsaOptimization.cs" />
<Compile Include="FlowAnalysis\SsaVariable.cs" />
<Compile Include="FlowAnalysis\TransformToSsa.cs" />
<Compile Include="ILAst\InitializerPeepholeTransforms.cs" />
<Compile Include="ILAst\DefaultDictionary.cs" />
<Compile Include="ILAst\GotoRemoval.cs" />
<Compile Include="ILAst\ILAstBuilder.cs" />
<Compile Include="ILAst\ILAstOptimizer.cs" />
<Compile Include="ILAst\ILAstTypes.cs" />
<Compile Include="ILAst\ILCodes.cs" />
<Compile Include="ILAst\ILInlining.cs" />
<Compile Include="ILAst\LoopsAndConditions.cs" />
<Compile Include="ILAst\PatternMatching.cs" />
<Compile Include="ILAst\PeepholeTransform.cs" />
<Compile Include="ILAst\SimpleControlFlow.cs" />
<Compile Include="ILAst\TypeAnalysis.cs" />
<Compile Include="ILAst\YieldReturnDecompiler.cs" />
<Compile Include="ITextOutput.cs" />
<Compile Include="PlainTextOutput.cs" />
<Compile Include="ReferenceResolvingException.cs" />
<Compile Include="TextOutputWriter.cs" />
<None Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
<ProjectReference Include="..\NewNRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
<Name>ICSharpCode.NRefactory</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Folder Include="Ast" />
<Folder Include="Ast\Transforms" />
<Folder Include="Disassembler" />
<Folder Include="ILAst" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

128
src/Libraries/ICSharpCode.Decompiler/ILAst/DefaultDictionary.cs

@ -0,0 +1,128 @@ @@ -0,0 +1,128 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Generic;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// Dictionary with default values.
/// </summary>
sealed class DefaultDictionary<TKey, TValue> : IDictionary<TKey, TValue>
{
readonly IDictionary<TKey, TValue> dict;
readonly Func<TKey, TValue> defaultProvider;
public DefaultDictionary(TValue defaultValue, IDictionary<TKey, TValue> dictionary = null)
: this(key => defaultValue, dictionary)
{
}
public DefaultDictionary(Func<TKey, TValue> defaultProvider = null, IDictionary<TKey, TValue> dictionary = null)
{
this.dict = dictionary ?? new Dictionary<TKey, TValue>();
this.defaultProvider = defaultProvider ?? (key => default(TValue));
}
public TValue this[TKey key] {
get {
TValue val;
if (dict.TryGetValue(key, out val))
return val;
else
return dict[key] = defaultProvider(key);
}
set {
dict[key] = value;
}
}
public ICollection<TKey> Keys {
get { return dict.Keys; }
}
public ICollection<TValue> Values {
get { return dict.Values; }
}
public int Count {
get { return dict.Count; }
}
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly {
get { return false; }
}
public bool ContainsKey(TKey key)
{
return dict.ContainsKey(key);
}
public void Add(TKey key, TValue value)
{
dict.Add(key, value);
}
public bool Remove(TKey key)
{
return dict.Remove(key);
}
public bool TryGetValue(TKey key, out TValue value)
{
return dict.TryGetValue(key, out value);
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
dict.Add(item);
}
public void Clear()
{
dict.Clear();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return dict.Contains(item);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
dict.CopyTo(array, arrayIndex);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return dict.Remove(item);
}
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
return dict.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return dict.GetEnumerator();
}
}
}

319
src/Libraries/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs

@ -0,0 +1,319 @@ @@ -0,0 +1,319 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Diagnostics;
using System.IO;
using System.Collections.Generic;
using System.Linq;
namespace ICSharpCode.Decompiler.ILAst
{
public class GotoRemoval
{
Dictionary<ILNode, ILNode> parent = new Dictionary<ILNode, ILNode>();
Dictionary<ILNode, ILNode> nextSibling = new Dictionary<ILNode, ILNode>();
public void RemoveGotos(ILBlock method)
{
// Build the navigation data
parent[method] = null;
foreach (ILNode node in method.GetSelfAndChildrenRecursive<ILNode>()) {
ILNode previousChild = null;
foreach (ILNode child in node.GetChildren()) {
if (parent.ContainsKey(child))
throw new Exception("The following expression is linked from several locations: " + child.ToString());
parent[child] = node;
if (previousChild != null)
nextSibling[previousChild] = child;
previousChild = child;
}
if (previousChild != null)
nextSibling[previousChild] = null;
}
// Simplify gotos
bool modified;
do {
modified = false;
foreach (ILExpression gotoExpr in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.Code == ILCode.Br || e.Code == ILCode.Leave)) {
modified |= TrySimplifyGoto(gotoExpr);
}
} while(modified);
RemoveRedundantCode(method);
}
public static void RemoveRedundantCode(ILBlock method)
{
// Remove dead lables and nops
HashSet<ILLabel> liveLabels = new HashSet<ILLabel>(method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets()));
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
block.Body = block.Body.Where(n => !n.Match(ILCode.Nop) && !(n is ILLabel && !liveLabels.Contains((ILLabel)n))).ToList();
}
// Remove redundant continue
foreach(ILWhileLoop loop in method.GetSelfAndChildrenRecursive<ILWhileLoop>()) {
var body = loop.BodyBlock.Body;
if (body.Count > 0 && body.Last().Match(ILCode.LoopContinue)) {
body.RemoveAt(body.Count - 1);
}
}
// Remove redundant break at the end of case
// Remove redundant case blocks altogether
foreach(ILSwitch ilSwitch in method.GetSelfAndChildrenRecursive<ILSwitch>()) {
foreach(ILBlock ilCase in ilSwitch.CaseBlocks) {
Debug.Assert(ilCase.EntryGoto == null);
int count = ilCase.Body.Count;
if (count >= 2) {
if (ilCase.Body[count - 2].IsUnconditionalControlFlow() &&
ilCase.Body[count - 1].Match(ILCode.LoopOrSwitchBreak))
{
ilCase.Body.RemoveAt(count - 1);
}
}
}
var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null);
// If there is no default block, remove empty case blocks
if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) {
ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak));
}
}
// Remove redundant return at the end of method
if (method.Body.Count > 0 && method.Body.Last().Match(ILCode.Ret) && ((ILExpression)method.Body.Last()).Arguments.Count == 0) {
method.Body.RemoveAt(method.Body.Count - 1);
}
// Remove unreachable return statements
bool modified = false;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count - 1;) {
if (block.Body[i].IsUnconditionalControlFlow() && block.Body[i+1].Match(ILCode.Ret)) {
modified = true;
block.Body.RemoveAt(i+1);
} else {
i++;
}
}
}
if (modified) {
// More removals might be possible
new GotoRemoval().RemoveGotos(method);
}
}
IEnumerable<ILNode> GetParents(ILNode node)
{
ILNode current = node;
while(true) {
current = parent[current];
if (current == null)
yield break;
yield return current;
}
}
bool TrySimplifyGoto(ILExpression gotoExpr)
{
Debug.Assert(gotoExpr.Code == ILCode.Br || gotoExpr.Code == ILCode.Leave);
Debug.Assert(gotoExpr.Prefixes == null);
Debug.Assert(gotoExpr.Operand != null);
ILNode target = Enter(gotoExpr, new HashSet<ILNode>());
if (target == null)
return false;
// The gotoExper is marked as visited because we do not want to
// walk over node which we plan to modify
// The simulated path always has to start in the same try-block
// in other for the same finally blocks to be executed.
if (target == Exit(gotoExpr, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.Nop;
gotoExpr.Operand = null;
if (target is ILExpression)
((ILExpression)target).ILRanges.AddRange(gotoExpr.ILRanges);
gotoExpr.ILRanges.Clear();
return true;
}
ILNode breakBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop || n is ILSwitch);
if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopOrSwitchBreak;
gotoExpr.Operand = null;
return true;
}
ILNode continueBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop);
if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopContinue;
gotoExpr.Operand = null;
return true;
}
return false;
}
/// <summary>
/// Get the first expression to be excecuted if the instruction pointer is at the start of the given node.
/// Try blocks may not be entered in any way. If possible, the try block is returned as the node to be executed.
/// </summary>
ILNode Enter(ILNode node, HashSet<ILNode> visitedNodes)
{
if (node == null)
throw new ArgumentNullException();
if (!visitedNodes.Add(node))
return null; // Infinite loop
ILLabel label = node as ILLabel;
if (label != null) {
return Exit(label, visitedNodes);
}
ILExpression expr = node as ILExpression;
if (expr != null) {
if (expr.Code == ILCode.Br || expr.Code == ILCode.Leave) {
ILLabel target = (ILLabel)expr.Operand;
// Early exit - same try-block
if (GetParents(expr).OfType<ILTryCatchBlock>().FirstOrDefault() == GetParents(target).OfType<ILTryCatchBlock>().FirstOrDefault())
return Enter(target, visitedNodes);
// Make sure we are not entering any try-block
var srcTryBlocks = GetParents(expr).OfType<ILTryCatchBlock>().Reverse().ToList();
var dstTryBlocks = GetParents(target).OfType<ILTryCatchBlock>().Reverse().ToList();
// Skip blocks that we are already in
int i = 0;
while(i < srcTryBlocks.Count && i < dstTryBlocks.Count && srcTryBlocks[i] == dstTryBlocks[i]) i++;
if (i == dstTryBlocks.Count) {
return Enter(target, visitedNodes);
} else {
ILTryCatchBlock dstTryBlock = dstTryBlocks[i];
// Check that the goto points to the start
ILTryCatchBlock current = dstTryBlock;
while(current != null) {
foreach(ILNode n in current.TryBlock.Body) {
if (n is ILLabel) {
if (n == target)
return dstTryBlock;
} else if (!n.Match(ILCode.Nop)) {
current = n as ILTryCatchBlock;
break;
}
}
}
return null;
}
} else if (expr.Code == ILCode.Nop) {
return Exit(expr, visitedNodes);
} else if (expr.Code == ILCode.LoopOrSwitchBreak) {
ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch);
return Exit(breakBlock, new HashSet<ILNode>() { expr });
} else if (expr.Code == ILCode.LoopContinue) {
ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop);
return Enter(continueBlock, new HashSet<ILNode>() { expr });
} else {
return expr;
}
}
ILBlock block = node as ILBlock;
if (block != null) {
if (block.EntryGoto != null) {
return Enter(block.EntryGoto, visitedNodes);
} else if (block.Body.Count > 0) {
return Enter(block.Body[0], visitedNodes);
} else {
return Exit(block, visitedNodes);
}
}
ILCondition cond = node as ILCondition;
if (cond != null) {
return cond.Condition;
}
ILWhileLoop loop = node as ILWhileLoop;
if (loop != null) {
if (loop.Condition != null) {
return loop.Condition;
} else {
return Enter(loop.BodyBlock, visitedNodes);
}
}
ILTryCatchBlock tryCatch = node as ILTryCatchBlock;
if (tryCatch != null) {
return tryCatch;
}
ILSwitch ilSwitch = node as ILSwitch;
if (ilSwitch != null) {
return ilSwitch.Condition;
}
throw new NotSupportedException(node.GetType().ToString());
}
/// <summary>
/// Get the first expression to be excecuted if the instruction pointer is at the end of the given node
/// </summary>
ILNode Exit(ILNode node, HashSet<ILNode> visitedNodes)
{
if (node == null)
throw new ArgumentNullException();
ILNode nodeParent = parent[node];
if (nodeParent == null)
return null; // Exited main body
if (nodeParent is ILBlock) {
ILNode nextNode = nextSibling[node];
if (nextNode != null) {
return Enter(nextNode, visitedNodes);
} else {
return Exit(nodeParent, visitedNodes);
}
}
if (nodeParent is ILCondition) {
return Exit(nodeParent, visitedNodes);
}
if (nodeParent is ILTryCatchBlock) {
// Finally blocks are completely ignored.
// We rely on the fact that try blocks can not be entered.
return Exit(nodeParent, visitedNodes);
}
if (nodeParent is ILSwitch) {
return null; // Implicit exit from switch is not allowed
}
if (nodeParent is ILWhileLoop) {
return Enter(nodeParent, visitedNodes);
}
throw new NotSupportedException(nodeParent.GetType().ToString());
}
}
}

823
src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs

@ -0,0 +1,823 @@ @@ -0,0 +1,823 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Cecil = Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public class ILAstBuilder
{
static ByteCode[] EmptyByteCodeArray = new ByteCode[] {};
/// <summary> Immutable </summary>
class StackSlot
{
public readonly ByteCode[] PushedBy; // One of those
public readonly ILVariable LoadFrom; // Where can we get the value from in AST
public StackSlot(ByteCode[] pushedBy, ILVariable loadFrom)
{
this.PushedBy = pushedBy;
this.LoadFrom = loadFrom;
}
public StackSlot(ByteCode pushedBy)
{
this.PushedBy = new[] { pushedBy };
this.LoadFrom = null;
}
public static List<StackSlot> CloneStack(List<StackSlot> stack, int? popCount)
{
if (popCount.HasValue) {
return stack.GetRange(0, stack.Count - popCount.Value);
} else {
return new List<StackSlot>(0);
}
}
}
/// <summary> Immutable </summary>
class VariableSlot
{
public readonly ByteCode[] StoredBy; // One of those
public readonly bool StoredByAll; // Overestimate which is useful for exceptional control flow.
public VariableSlot(ByteCode[] storedBy, bool storedByAll)
{
this.StoredBy = storedBy;
this.StoredByAll = storedByAll;
}
public VariableSlot(ByteCode storedBy)
{
this.StoredBy = new[] { storedBy };
this.StoredByAll = false;
}
public static VariableSlot[] CloneVariableState(VariableSlot[] state)
{
VariableSlot[] clone = new VariableSlot[state.Length];
for (int i = 0; i < clone.Length; i++) {
clone[i] = state[i];
}
return clone;
}
public static VariableSlot[] MakeEmptyState(int varCount)
{
VariableSlot[] emptyVariableState = new VariableSlot[varCount];
for (int i = 0; i < emptyVariableState.Length; i++) {
emptyVariableState[i] = new VariableSlot(EmptyByteCodeArray, false);
}
return emptyVariableState;
}
public static VariableSlot[] MakeFullState(int varCount)
{
VariableSlot[] unknownVariableState = new VariableSlot[varCount];
for (int i = 0; i < unknownVariableState.Length; i++) {
unknownVariableState[i] = new VariableSlot(EmptyByteCodeArray, true);
}
return unknownVariableState;
}
}
class ByteCode
{
public ILLabel Label; // Non-null only if needed
public int Offset;
public int EndOffset;
public ILCode Code;
public object Operand;
public int? PopCount; // Null means pop all
public int PushCount;
public string Name { get { return "IL_" + this.Offset.ToString("X2"); } }
public ByteCode Next;
public Instruction[] Prefixes; // Non-null only if needed
public List<StackSlot> StackBefore; // Unique per bytecode; not shared
public List<ILVariable> StoreTo; // Store result of instruction to those AST variables
public VariableSlot[] VariablesBefore; // Unique per bytecode; not shared
public VariableDefinition OperandAsVariable { get { return (VariableDefinition)this.Operand; } }
public override string ToString()
{
StringBuilder sb = new StringBuilder();
// Label
sb.Append(this.Name);
sb.Append(':');
if (this.Label != null)
sb.Append('*');
// Name
sb.Append(' ');
if (this.Prefixes != null) {
foreach (var prefix in this.Prefixes) {
sb.Append(prefix.OpCode.Name);
sb.Append(' ');
}
}
sb.Append(this.Code.GetName());
if (this.Operand != null) {
sb.Append(' ');
if (this.Operand is Instruction) {
sb.Append("IL_" + ((Instruction)this.Operand).Offset.ToString("X2"));
} else if (this.Operand is Instruction[]) {
foreach(Instruction inst in (Instruction[])this.Operand) {
sb.Append("IL_" + inst.Offset.ToString("X2"));
sb.Append(" ");
}
} else if (this.Operand is ILLabel) {
sb.Append(((ILLabel)this.Operand).Name);
} else if (this.Operand is ILLabel[]) {
foreach(ILLabel label in (ILLabel[])this.Operand) {
sb.Append(label.Name);
sb.Append(" ");
}
} else {
sb.Append(this.Operand.ToString());
}
}
if (this.StackBefore != null) {
sb.Append(" StackBefore={");
bool first = true;
foreach (StackSlot slot in this.StackBefore) {
if (!first) sb.Append(",");
bool first2 = true;
foreach(ByteCode pushedBy in slot.PushedBy) {
if (!first2) sb.Append("|");
sb.AppendFormat("IL_{0:X2}", pushedBy.Offset);
first2 = false;
}
first = false;
}
sb.Append("}");
}
if (this.StoreTo != null && this.StoreTo.Count > 0) {
sb.Append(" StoreTo={");
bool first = true;
foreach (ILVariable stackVar in this.StoreTo) {
if (!first) sb.Append(",");
sb.Append(stackVar.Name);
first = false;
}
sb.Append("}");
}
if (this.VariablesBefore != null) {
sb.Append(" VarsBefore={");
bool first = true;
foreach (VariableSlot varSlot in this.VariablesBefore) {
if (!first) sb.Append(",");
if (varSlot.StoredByAll) {
sb.Append("*");
} else if (varSlot.StoredBy.Length == 0) {
sb.Append("_");
} else {
bool first2 = true;
foreach (ByteCode storedBy in varSlot.StoredBy) {
if (!first2) sb.Append("|");
sb.AppendFormat("IL_{0:X2}", storedBy.Offset);
first2 = false;
}
}
first = false;
}
sb.Append("}");
}
return sb.ToString();
}
}
MethodDefinition methodDef;
bool optimize;
// Virtual instructions to load exception on stack
Dictionary<ExceptionHandler, ByteCode> ldexceptions = new Dictionary<ExceptionHandler, ILAstBuilder.ByteCode>();
public List<ILNode> Build(MethodDefinition methodDef, bool optimize)
{
this.methodDef = methodDef;
this.optimize = optimize;
if (methodDef.Body.Instructions.Count == 0) return new List<ILNode>();
List<ByteCode> body = StackAnalysis(methodDef);
List<ILNode> ast = ConvertToAst(body, new HashSet<ExceptionHandler>(methodDef.Body.ExceptionHandlers));
return ast;
}
List<ByteCode> StackAnalysis(MethodDefinition methodDef)
{
Dictionary<Instruction, ByteCode> instrToByteCode = new Dictionary<Instruction, ByteCode>();
// Create temporary structure for the stack analysis
List<ByteCode> body = new List<ByteCode>(methodDef.Body.Instructions.Count);
List<Instruction> prefixes = null;
foreach(Instruction inst in methodDef.Body.Instructions) {
if (inst.OpCode.OpCodeType == OpCodeType.Prefix) {
if (prefixes == null)
prefixes = new List<Instruction>(1);
prefixes.Add(inst);
continue;
}
ILCode code = (ILCode)inst.OpCode.Code;
object operand = inst.Operand;
ILCodeUtil.ExpandMacro(ref code, ref operand, methodDef.Body);
ByteCode byteCode = new ByteCode() {
Offset = inst.Offset,
EndOffset = inst.Next != null ? inst.Next.Offset : methodDef.Body.CodeSize,
Code = code,
Operand = operand,
PopCount = inst.GetPopDelta(),
PushCount = inst.GetPushDelta()
};
if (prefixes != null) {
instrToByteCode[prefixes[0]] = byteCode;
byteCode.Offset = prefixes[0].Offset;
byteCode.Prefixes = prefixes.ToArray();
prefixes = null;
} else {
instrToByteCode[inst] = byteCode;
}
body.Add(byteCode);
}
for (int i = 0; i < body.Count - 1; i++) {
body[i].Next = body[i + 1];
}
Stack<ByteCode> agenda = new Stack<ByteCode>();
int varCount = methodDef.Body.Variables.Count;
var exceptionHandlerStarts = new HashSet<ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart]));
// Add known states
if(methodDef.Body.HasExceptionHandlers) {
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
ByteCode handlerStart = instrToByteCode[ex.HandlerStart];
handlerStart.StackBefore = new List<StackSlot>();
handlerStart.VariablesBefore = VariableSlot.MakeFullState(varCount);
if (ex.HandlerType == ExceptionHandlerType.Catch || ex.HandlerType == ExceptionHandlerType.Filter) {
// Catch and Filter handlers start with the exeption on the stack
ByteCode ldexception = new ByteCode() {
Code = ILCode.Ldexception,
Operand = ex.CatchType,
PopCount = 0,
PushCount = 1
};
ldexceptions[ex] = ldexception;
handlerStart.StackBefore.Add(new StackSlot(ldexception));
}
agenda.Push(handlerStart);
if (ex.HandlerType == ExceptionHandlerType.Filter)
{
ByteCode filterStart = instrToByteCode[ex.FilterStart];
filterStart.StackBefore = new List<StackSlot>();
filterStart.VariablesBefore = VariableSlot.MakeFullState(varCount);
ByteCode ldexception = new ByteCode() {
Code = ILCode.Ldexception,
Operand = ex.CatchType,
PopCount = 0,
PushCount = 1
};
// TODO: ldexceptions[ex] = ldexception;
filterStart.StackBefore.Add(new StackSlot(ldexception));
agenda.Push(filterStart);
}
}
}
body[0].StackBefore = new List<StackSlot>();
body[0].VariablesBefore = VariableSlot.MakeEmptyState(varCount);
agenda.Push(body[0]);
// Process agenda
while(agenda.Count > 0) {
ByteCode byteCode = agenda.Pop();
// Calculate new stack
List<StackSlot> newStack = StackSlot.CloneStack(byteCode.StackBefore, byteCode.PopCount);
for (int i = 0; i < byteCode.PushCount; i++) {
newStack.Add(new StackSlot(byteCode));
}
// Calculate new variable state
VariableSlot[] newVariableState = VariableSlot.CloneVariableState(byteCode.VariablesBefore);
if (byteCode.Code == ILCode.Stloc) {
int varIndex = ((VariableReference)byteCode.Operand).Index;
newVariableState[varIndex] = new VariableSlot(byteCode);
}
// After the leave, finally block might have touched the variables
if (byteCode.Code == ILCode.Leave) {
newVariableState = VariableSlot.MakeFullState(varCount);
}
// Find all successors
List<ByteCode> branchTargets = new List<ByteCode>();
if (!byteCode.Code.IsUnconditionalControlFlow()) {
if (exceptionHandlerStarts.Contains(byteCode.Next)) {
// Do not fall though down to exception handler
// It is invalid IL as per ECMA-335 §12.4.2.8.1, but some obfuscators produce it
} else {
branchTargets.Add(byteCode.Next);
}
}
if (byteCode.Operand is Instruction[]) {
foreach(Instruction inst in (Instruction[])byteCode.Operand) {
ByteCode target = instrToByteCode[inst];
branchTargets.Add(target);
// The target of a branch must have label
if (target.Label == null) {
target.Label = new ILLabel() { Name = target.Name };
}
}
} else if (byteCode.Operand is Instruction) {
ByteCode target = instrToByteCode[(Instruction)byteCode.Operand];
branchTargets.Add(target);
// The target of a branch must have label
if (target.Label == null) {
target.Label = new ILLabel() { Name = target.Name };
}
}
// Apply the state to successors
foreach (ByteCode branchTarget in branchTargets) {
if (branchTarget.StackBefore == null && branchTarget.VariablesBefore == null) {
if (branchTargets.Count == 1) {
branchTarget.StackBefore = newStack;
branchTarget.VariablesBefore = newVariableState;
} else {
// Do not share data for several bytecodes
branchTarget.StackBefore = StackSlot.CloneStack(newStack, 0);
branchTarget.VariablesBefore = VariableSlot.CloneVariableState(newVariableState);
}
agenda.Push(branchTarget);
} else {
if (branchTarget.StackBefore.Count != newStack.Count) {
throw new Exception("Inconsistent stack size at " + byteCode.Name);
}
// Be careful not to change our new data - it might be reused for several branch targets.
// In general, be careful that two bytecodes never share data structures.
bool modified = false;
// Merge stacks - modify the target
for (int i = 0; i < newStack.Count; i++) {
ByteCode[] oldPushedBy = branchTarget.StackBefore[i].PushedBy;
ByteCode[] newPushedBy = oldPushedBy.Union(newStack[i].PushedBy);
if (newPushedBy.Length > oldPushedBy.Length) {
branchTarget.StackBefore[i] = new StackSlot(newPushedBy, null);
modified = true;
}
}
// Merge variables - modify the target
for (int i = 0; i < newVariableState.Length; i++) {
VariableSlot oldSlot = branchTarget.VariablesBefore[i];
VariableSlot newSlot = newVariableState[i];
// All can not be unioned further
if (!oldSlot.StoredByAll) {
if (newSlot.StoredByAll) {
branchTarget.VariablesBefore[i] = newSlot;
modified = true;
} else {
ByteCode[] oldStoredBy = oldSlot.StoredBy;
ByteCode[] newStoredBy = oldStoredBy.Union(newSlot.StoredBy);
if (newStoredBy.Length > oldStoredBy.Length) {
branchTarget.VariablesBefore[i] = new VariableSlot(newStoredBy, false);
modified = true;
}
}
}
}
if (modified) {
agenda.Push(branchTarget);
}
}
}
}
// Occasionally the compilers or obfuscators generate unreachable code (which migt be intentonally invalid)
// I belive it is safe to just remove it
body.RemoveAll(b => b.StackBefore == null);
// Genertate temporary variables to replace stack
foreach(ByteCode byteCode in body) {
int argIdx = 0;
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) {
ILVariable tmpVar = new ILVariable() { Name = string.Format("arg_{0:X2}_{1}", byteCode.Offset, argIdx), IsGenerated = true };
byteCode.StackBefore[i] = new StackSlot(byteCode.StackBefore[i].PushedBy, tmpVar);
foreach(ByteCode pushedBy in byteCode.StackBefore[i].PushedBy) {
if (pushedBy.StoreTo == null) {
pushedBy.StoreTo = new List<ILVariable>(1);
}
pushedBy.StoreTo.Add(tmpVar);
}
argIdx++;
}
}
// Try to use single temporary variable insted of several if possilbe (especially useful for dup)
// This has to be done after all temporary variables are assigned so we know about all loads
foreach(ByteCode byteCode in body) {
if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) {
var locVars = byteCode.StoreTo;
// For each of the variables, find the location where it is loaded - there should be preciesly one
var loadedBy = locVars.Select(locVar => body.SelectMany(bc => bc.StackBefore).Single(s => s.LoadFrom == locVar)).ToList();
// We now know that all the variables have a single load,
// Let's make sure that they have also a single store - us
if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) {
// Great - we can reduce everything into single variable
ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true };
byteCode.StoreTo = new List<ILVariable>() { tmpVar };
foreach(ByteCode bc in body) {
for (int i = 0; i < bc.StackBefore.Count; i++) {
// Is it one of the variable to be merged?
if (locVars.Contains(bc.StackBefore[i].LoadFrom)) {
// Replace with the new temp variable
bc.StackBefore[i] = new StackSlot(bc.StackBefore[i].PushedBy, tmpVar);
}
}
}
}
}
}
// Split and convert the normal local variables
ConvertLocalVariables(body);
// Convert branch targets to labels
foreach(ByteCode byteCode in body) {
if (byteCode.Operand is Instruction[]) {
List<ILLabel> newOperand = new List<ILLabel>();
foreach(Instruction target in (Instruction[])byteCode.Operand) {
newOperand.Add(instrToByteCode[target].Label);
}
byteCode.Operand = newOperand.ToArray();
} else if (byteCode.Operand is Instruction) {
byteCode.Operand = instrToByteCode[(Instruction)byteCode.Operand].Label;
}
}
// Convert parameters to ILVariables
ConvertParameters(body);
return body;
}
class VariableInfo
{
public ILVariable Variable;
public List<ByteCode> Stores;
public List<ByteCode> Loads;
}
/// <summary>
/// If possible, separates local variables into several independent variables.
/// It should undo any compilers merging.
/// </summary>
void ConvertLocalVariables(List<ByteCode> body)
{
if (optimize) {
int varCount = methodDef.Body.Variables.Count;
for(int variableIndex = 0; variableIndex < varCount; variableIndex++) {
// Find all stores and loads for this variable
List<ByteCode> stores = body.Where(b => b.Code == ILCode.Stloc && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList();
List<ByteCode> loads = body.Where(b => (b.Code == ILCode.Ldloc || b.Code == ILCode.Ldloca) && b.Operand is VariableDefinition && b.OperandAsVariable.Index == variableIndex).ToList();
TypeReference varType = methodDef.Body.Variables[variableIndex].VariableType;
List<VariableInfo> newVars;
bool isPinned = methodDef.Body.Variables[variableIndex].IsPinned;
// If the variable is pinned, use single variable.
// If any of the loads is from "all", use single variable
// If any of the loads is ldloca, fallback to single variable as well
if (isPinned || loads.Any(b => b.VariablesBefore[variableIndex].StoredByAll || b.Code == ILCode.Ldloca)) {
newVars = new List<VariableInfo>(1) { new VariableInfo() {
Variable = new ILVariable() {
Name = "var_" + variableIndex,
Type = isPinned ? ((PinnedType)varType).ElementType : varType,
OriginalVariable = methodDef.Body.Variables[variableIndex]
},
Stores = stores,
Loads = loads
}};
} else {
// Create a new variable for each store
newVars = stores.Select(st => new VariableInfo() {
Variable = new ILVariable() {
Name = "var_" + variableIndex + "_" + st.Offset.ToString("X2"),
Type = varType,
OriginalVariable = methodDef.Body.Variables[variableIndex]
},
Stores = new List<ByteCode>() {st},
Loads = new List<ByteCode>()
}).ToList();
// VB.NET uses the 'init' to allow use of uninitialized variables.
// We do not really care about them too much - if the original variable
// was uninitialized at that point it means that no store was called and
// thus all our new variables must be uninitialized as well.
// So it does not matter which one we load.
// TODO: We should add explicit initialization so that C# code compiles.
// Remember to handle cases where one path inits the variable, but other does not.
// Add loads to the data structure; merge variables if necessary
foreach(ByteCode load in loads) {
ByteCode[] storedBy = load.VariablesBefore[variableIndex].StoredBy;
if (storedBy.Length == 0) {
// Load which always loads the default ('uninitialized') value
// Create a dummy variable just for this load
newVars.Add(new VariableInfo() {
Variable = new ILVariable() {
Name = "var_" + variableIndex + "_" + load.Offset.ToString("X2") + "_default",
Type = varType,
OriginalVariable = methodDef.Body.Variables[variableIndex]
},
Stores = new List<ByteCode>(),
Loads = new List<ByteCode>() { load }
});
} else if (storedBy.Length == 1) {
VariableInfo newVar = newVars.Single(v => v.Stores.Contains(storedBy[0]));
newVar.Loads.Add(load);
} else {
List<VariableInfo> mergeVars = newVars.Where(v => v.Stores.Union(storedBy).Any()).ToList();
VariableInfo mergedVar = new VariableInfo() {
Variable = mergeVars[0].Variable,
Stores = mergeVars.SelectMany(v => v.Stores).ToList(),
Loads = mergeVars.SelectMany(v => v.Loads).ToList()
};
mergedVar.Loads.Add(load);
newVars = newVars.Except(mergeVars).ToList();
newVars.Add(mergedVar);
}
}
}
// Set bytecode operands
foreach(VariableInfo newVar in newVars) {
foreach(ByteCode store in newVar.Stores) {
store.Operand = newVar.Variable;
}
foreach(ByteCode load in newVar.Loads) {
load.Operand = newVar.Variable;
}
}
}
} else {
var variables = methodDef.Body.Variables.Select(v => new ILVariable() { Name = string.IsNullOrEmpty(v.Name) ? "var_" + v.Index : v.Name, Type = v.VariableType, OriginalVariable = v }).ToList();
foreach(ByteCode byteCode in body) {
if (byteCode.Code == ILCode.Ldloc || byteCode.Code == ILCode.Stloc || byteCode.Code == ILCode.Ldloca) {
int index = ((VariableDefinition)byteCode.Operand).Index;
byteCode.Operand = variables[index];
}
}
}
}
public List<ILVariable> Parameters = new List<ILVariable>();
void ConvertParameters(List<ByteCode> body)
{
ILVariable thisParameter = null;
if (methodDef.HasThis) {
TypeReference type = methodDef.DeclaringType;
thisParameter = new ILVariable();
thisParameter.Type = type.IsValueType ? new ByReferenceType(type) : type;
thisParameter.Name = "this";
thisParameter.OriginalParameter = methodDef.Body.ThisParameter;
}
foreach (ParameterDefinition p in methodDef.Parameters) {
this.Parameters.Add(new ILVariable { Type = p.ParameterType, Name = p.Name, OriginalParameter = p });
}
foreach (ByteCode byteCode in body) {
ParameterDefinition p;
switch (byteCode.Code) {
case ILCode.__Ldarg:
p = (ParameterDefinition)byteCode.Operand;
byteCode.Code = ILCode.Ldloc;
byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index];
break;
case ILCode.__Starg:
p = (ParameterDefinition)byteCode.Operand;
byteCode.Code = ILCode.Stloc;
byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index];
break;
case ILCode.__Ldarga:
p = (ParameterDefinition)byteCode.Operand;
byteCode.Code = ILCode.Ldloca;
byteCode.Operand = p.Index < 0 ? thisParameter : this.Parameters[p.Index];
break;
}
}
if (thisParameter != null)
this.Parameters.Add(thisParameter);
}
List<ILNode> ConvertToAst(List<ByteCode> body, HashSet<ExceptionHandler> ehs)
{
List<ILNode> ast = new List<ILNode>();
while (ehs.Any()) {
ILTryCatchBlock tryCatchBlock = new ILTryCatchBlock();
// Find the first and widest scope
int tryStart = ehs.Min(eh => eh.TryStart.Offset);
int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.Offset);
var handlers = ehs.Where(eh => eh.TryStart.Offset == tryStart && eh.TryEnd.Offset == tryEnd).OrderBy(eh => eh.TryStart.Offset).ToList();
// Remember that any part of the body migt have been removed due to unreachability
// Cut all instructions up to the try block
{
int tryStartIdx = 0;
while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++;
ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
}
// Cut the try block
{
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(eh => (tryStart <= eh.TryStart.Offset && eh.TryEnd.Offset < tryEnd) || (tryStart < eh.TryStart.Offset && eh.TryEnd.Offset <= tryEnd)));
ehs.ExceptWith(nestedEHs);
int tryEndIdx = 0;
while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++;
tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
}
// Cut all handlers
tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>();
foreach(ExceptionHandler eh in handlers) {
int handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
int startIdx = 0;
while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++;
int endIdx = 0;
while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++;
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < handlerEndOffset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= handlerEndOffset)));
ehs.ExceptWith(nestedEHs);
List<ILNode> handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs);
if (eh.HandlerType == ExceptionHandlerType.Catch) {
ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() {
ExceptionType = eh.CatchType,
Body = handlerAst
};
// Handle the automatically pushed exception on the stack
ByteCode ldexception = ldexceptions[eh];
if (ldexception.StoreTo.Count == 0) {
throw new Exception("Exception should be consumed by something");
} else if (ldexception.StoreTo.Count == 1) {
ILExpression first = catchBlock.Body[0] as ILExpression;
if (first != null &&
first.Code == ILCode.Pop &&
first.Arguments[0].Code == ILCode.Ldloc &&
first.Arguments[0].Operand == ldexception.StoreTo[0])
{
// The exception is just poped - optimize it all away;
catchBlock.ExceptionVariable = null;
catchBlock.Body.RemoveAt(0);
} else {
catchBlock.ExceptionVariable = ldexception.StoreTo[0];
}
} else {
ILVariable exTemp = new ILVariable() { Name = "ex_" + eh.HandlerStart.Offset.ToString("X2"), IsGenerated = true };
catchBlock.ExceptionVariable = exTemp;
foreach(ILVariable storeTo in ldexception.StoreTo) {
catchBlock.Body.Insert(0, new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, exTemp)));
}
}
tryCatchBlock.CatchBlocks.Add(catchBlock);
} else if (eh.HandlerType == ExceptionHandlerType.Finally) {
tryCatchBlock.FinallyBlock = new ILBlock(handlerAst);
} else if (eh.HandlerType == ExceptionHandlerType.Fault) {
tryCatchBlock.FaultBlock = new ILBlock(handlerAst);
} else {
// TODO: ExceptionHandlerType.Filter
}
}
ehs.ExceptWith(handlers);
ast.Add(tryCatchBlock);
}
// Add whatever is left
ast.AddRange(ConvertToAst(body));
return ast;
}
List<ILNode> ConvertToAst(List<ByteCode> body)
{
List<ILNode> ast = new List<ILNode>();
// Convert stack-based IL code to ILAst tree
foreach(ByteCode byteCode in body) {
ILRange ilRange = new ILRange() { From = byteCode.Offset, To = byteCode.EndOffset };
if (byteCode.StackBefore == null) {
// Unreachable code
continue;
}
ILExpression expr = new ILExpression(byteCode.Code, byteCode.Operand);
expr.ILRanges.Add(ilRange);
if (byteCode.Prefixes != null && byteCode.Prefixes.Length > 0) {
ILExpressionPrefix[] prefixes = new ILExpressionPrefix[byteCode.Prefixes.Length];
for (int i = 0; i < prefixes.Length; i++) {
prefixes[i] = new ILExpressionPrefix((ILCode)byteCode.Prefixes[i].OpCode.Code, byteCode.Prefixes[i].Operand);
}
expr.Prefixes = prefixes;
}
// Label for this instruction
if (byteCode.Label != null) {
ast.Add(byteCode.Label);
}
// Reference arguments using temporary variables
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) {
StackSlot slot = byteCode.StackBefore[i];
expr.Arguments.Add(new ILExpression(ILCode.Ldloc, slot.LoadFrom));
}
// Store the result to temporary variable(s) if needed
if (byteCode.StoreTo == null || byteCode.StoreTo.Count == 0) {
ast.Add(expr);
} else if (byteCode.StoreTo.Count == 1) {
ast.Add(new ILExpression(ILCode.Stloc, byteCode.StoreTo[0], expr));
} else {
ILVariable tmpVar = new ILVariable() { Name = "expr_" + byteCode.Offset.ToString("X2"), IsGenerated = true };
ast.Add(new ILExpression(ILCode.Stloc, tmpVar, expr));
foreach(ILVariable storeTo in byteCode.StoreTo.AsEnumerable().Reverse()) {
ast.Add(new ILExpression(ILCode.Stloc, storeTo, new ILExpression(ILCode.Ldloc, tmpVar)));
}
}
}
return ast;
}
}
public static class ILAstBuilderExtensionMethods
{
public static List<T> CutRange<T>(this List<T> list, int start, int count)
{
List<T> ret = new List<T>(count);
for (int i = 0; i < count; i++) {
ret.Add(list[start + i]);
}
list.RemoveRange(start, count);
return ret;
}
public static T[] Union<T>(this T[] a, T[] b)
{
if (a.Length == 0)
return b;
if (b.Length == 0)
return a;
if (a.Length == 1 && b.Length == 1 && a[0].Equals(b[0]))
return a;
return Enumerable.Union(a, b).ToArray();
}
}
}

830
src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -0,0 +1,830 @@ @@ -0,0 +1,830 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.CSharp;
namespace ICSharpCode.Decompiler.ILAst
{
public enum ILAstOptimizationStep
{
RemoveRedundantCode,
ReduceBranchInstructionSet,
InlineVariables,
CopyPropagation,
YieldReturn,
PropertyAccessInstructions,
SplitToMovableBlocks,
TypeInference,
SimplifyShortCircuit,
SimplifyTernaryOperator,
SimplifyNullCoalescing,
JoinBasicBlocks,
SimplifyShiftOperators,
TransformDecimalCtorToConstant,
SimplifyLdObjAndStObj,
SimplifyCustomShortCircuit,
TransformArrayInitializers,
TransformMultidimensionalArrayInitializers,
TransformObjectInitializers,
MakeAssignmentExpression,
IntroducePostIncrement,
InlineVariables2,
FindLoops,
FindConditions,
FlattenNestedMovableBlocks,
RemoveEndFinally,
RemoveRedundantCode2,
GotoRemoval,
DuplicateReturns,
GotoRemoval2,
ReduceIfNesting,
InlineVariables3,
CachedDelegateInitialization,
IntroduceFixedStatements,
RecombineVariables,
TypeInference2,
RemoveRedundantCode3,
None
}
public partial class ILAstOptimizer
{
int nextLabelIndex = 0;
DecompilerContext context;
TypeSystem typeSystem;
ILBlock method;
public void Optimize(DecompilerContext context, ILBlock method, ILAstOptimizationStep abortBeforeStep = ILAstOptimizationStep.None)
{
this.context = context;
this.typeSystem = context.CurrentMethod.Module.TypeSystem;
this.method = method;
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode) return;
RemoveRedundantCode(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceBranchInstructionSet) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
ReduceBranchInstructionSet(block);
}
// ReduceBranchInstructionSet runs before inlining because the non-aggressive inlining heuristic
// looks at which type of instruction consumes the inlined variable.
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables) return;
// Works better after simple goto removal because of the following debug pattern: stloc X; br Next; Next:; ldloc X
ILInlining inlining1 = new ILInlining(method);
inlining1.InlineAllVariables();
if (abortBeforeStep == ILAstOptimizationStep.CopyPropagation) return;
inlining1.CopyPropagation();
if (abortBeforeStep == ILAstOptimizationStep.YieldReturn) return;
YieldReturnDecompiler.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.PropertyAccessInstructions) return;
IntroducePropertyAccessInstructions(method);
if (abortBeforeStep == ILAstOptimizationStep.SplitToMovableBlocks) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
SplitToBasicBlocks(block);
}
if (abortBeforeStep == ILAstOptimizationStep.TypeInference) return;
// Types are needed for the ternary operator optimization
TypeAnalysis.Run(context, method);
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
bool modified;
do {
modified = false;
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShortCircuit) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyTernaryOperator) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyTernaryOperator);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyNullCoalescing) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyNullCoalescing);
if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShiftOperators) return;
modified |= block.RunOptimization(SimplifyShiftOperators);
if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return;
modified |= block.RunOptimization(TransformDecimalCtorToConstant);
modified |= block.RunOptimization(SimplifyLdcI4ConvI8);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(TransformArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformMultidimensionalArrayInitializers) return;
modified |= block.RunOptimization(TransformMultidimensionalArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) return;
modified |= block.RunOptimization(TransformObjectInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression);
modified |= block.RunOptimization(MakeCompoundAssignments);
if (abortBeforeStep == ILAstOptimizationStep.IntroducePostIncrement) return;
modified |= block.RunOptimization(IntroducePostIncrement);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables2) return;
modified |= new ILInlining(method).InlineAllInBlock(block);
new ILInlining(method).CopyPropagation();
} while(modified);
}
if (abortBeforeStep == ILAstOptimizationStep.FindLoops) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
new LoopsAndConditions(context).FindLoops(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FindConditions) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
new LoopsAndConditions(context).FindConditions(block);
}
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveEndFinally) return;
RemoveEndFinally(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return;
RemoveRedundantCode(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval) return;
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
DuplicateReturnStatements(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) return;
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return;
ReduceIfNesting(method);
if (abortBeforeStep == ILAstOptimizationStep.InlineVariables3) return;
// The 2nd inlining pass is necessary because DuplicateReturns and the introduction of ternary operators
// open up additional inlining possibilities.
new ILInlining(method).InlineAllVariables();
if (abortBeforeStep == ILAstOptimizationStep.CachedDelegateInitialization) return;
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
// TODO: Move before loops
CachedDelegateInitializationWithField(block, ref i);
CachedDelegateInitializationWithLocal(block, ref i);
}
}
if (abortBeforeStep == ILAstOptimizationStep.IntroduceFixedStatements) return;
// we need post-order traversal, not pre-order, for "fixed" to work correctly
foreach (ILBlock block in TreeTraversal.PostOrder<ILNode>(method, n => n.GetChildren()).OfType<ILBlock>()) {
for (int i = block.Body.Count - 1; i >= 0; i--) {
// TODO: Move before loops
if (i < block.Body.Count)
IntroduceFixedStatements(block.Body, i);
}
}
if (abortBeforeStep == ILAstOptimizationStep.RecombineVariables) return;
RecombineVariables(method);
if (abortBeforeStep == ILAstOptimizationStep.TypeInference2) return;
TypeAnalysis.Reset(method);
TypeAnalysis.Run(context, method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode3) return;
GotoRemoval.RemoveRedundantCode(method);
// ReportUnassignedILRanges(method);
}
/// <summary>
/// Removes redundatant Br, Nop, Dup, Pop
/// </summary>
/// <param name="method"></param>
void RemoveRedundantCode(ILBlock method)
{
Dictionary<ILLabel, int> labelRefCount = new Dictionary<ILLabel, int>();
foreach (ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
labelRefCount[target] = labelRefCount.GetOrDefault(target) + 1;
}
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
List<ILNode> body = block.Body;
List<ILNode> newBody = new List<ILNode>(body.Count);
for (int i = 0; i < body.Count; i++) {
ILLabel target;
ILExpression popExpr;
if (body[i].Match(ILCode.Br, out target) && i+1 < body.Count && body[i+1] == target) {
// Ignore the branch
if (labelRefCount[target] == 1)
i++; // Ignore the label as well
} else if (body[i].Match(ILCode.Nop)){
// Ignore nop
} else if (body[i].Match(ILCode.Pop, out popExpr)) {
ILVariable v;
if (!popExpr.Match(ILCode.Ldloc, out v))
throw new Exception("Pop should have just ldloc at this stage");
// Best effort to move the ILRange to previous statement
ILVariable prevVar;
ILExpression prevExpr;
if (i - 1 >= 0 && body[i - 1].Match(ILCode.Stloc, out prevVar, out prevExpr) && prevVar == v)
prevExpr.ILRanges.AddRange(((ILExpression)body[i]).ILRanges);
// Ignore pop
} else {
newBody.Add(body[i]);
}
}
block.Body = newBody;
}
// 'dup' removal
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
for (int i = 0; i < expr.Arguments.Count; i++) {
ILExpression child;
if (expr.Arguments[i].Match(ILCode.Dup, out child)) {
child.ILRanges.AddRange(expr.Arguments[i].ILRanges);
expr.Arguments[i] = child;
}
}
}
}
/// <summary>
/// Reduces the branch codes to just br and brtrue.
/// Moves ILRanges to the branch argument
/// </summary>
void ReduceBranchInstructionSet(ILBlock block)
{
for (int i = 0; i < block.Body.Count; i++) {
ILExpression expr = block.Body[i] as ILExpression;
if (expr != null && expr.Prefixes == null) {
switch(expr.Code) {
case ILCode.Switch:
case ILCode.Brtrue:
expr.Arguments.Single().ILRanges.AddRange(expr.ILRanges);
expr.ILRanges.Clear();
continue;
case ILCode.__Brfalse: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, expr.Arguments.Single())); break;
case ILCode.__Beq: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Ceq, null, expr.Arguments)); break;
case ILCode.__Bne_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Ceq, null, expr.Arguments))); break;
case ILCode.__Bgt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt, null, expr.Arguments)); break;
case ILCode.__Bgt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments)); break;
case ILCode.__Ble: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt, null, expr.Arguments))); break;
case ILCode.__Ble_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Cgt_Un, null, expr.Arguments))); break;
case ILCode.__Blt: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt, null, expr.Arguments)); break;
case ILCode.__Blt_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.Clt_Un, null, expr.Arguments)); break;
case ILCode.__Bge: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt, null, expr.Arguments))); break;
case ILCode.__Bge_Un: block.Body[i] = new ILExpression(ILCode.Brtrue, expr.Operand, new ILExpression(ILCode.LogicNot, null, new ILExpression(ILCode.Clt_Un, null, expr.Arguments))); break;
default:
continue;
}
((ILExpression)block.Body[i]).Arguments.Single().ILRanges.AddRange(expr.ILRanges);
}
}
}
/// <summary>
/// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions.
///
/// CallGetter/CallSetter is used to allow the ILAst to represent "while ((SomeProperty = value) != null)".
///
/// Also simplifies 'newobj(SomeDelegate, target, ldvirtftn(F, target))' to 'newobj(SomeDelegate, target, ldvirtftn(F))'
/// </summary>
void IntroducePropertyAccessInstructions(ILNode node)
{
ILExpression parentExpr = node as ILExpression;
if (parentExpr != null) {
for (int i = 0; i < parentExpr.Arguments.Count; i++) {
ILExpression expr = parentExpr.Arguments[i];
IntroducePropertyAccessInstructions(expr);
IntroducePropertyAccessInstructions(expr, parentExpr, i);
}
} else {
foreach (ILNode child in node.GetChildren()) {
IntroducePropertyAccessInstructions(child);
ILExpression expr = child as ILExpression;
if (expr != null) {
IntroducePropertyAccessInstructions(expr, null, -1);
}
}
}
}
void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent)
{
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand;
if (cecilMethod.DeclaringType is ArrayType) {
switch (cecilMethod.Name) {
case "Get":
expr.Code = ILCode.CallGetter;
break;
case "Set":
expr.Code = ILCode.CallSetter;
break;
case "Address":
ByReferenceType brt = cecilMethod.ReturnType as ByReferenceType;
if (brt != null) {
MethodReference getMethod = new MethodReference("Get", brt.ElementType, cecilMethod.DeclaringType);
foreach (var p in cecilMethod.Parameters)
getMethod.Parameters.Add(p);
getMethod.HasThis = cecilMethod.HasThis;
expr.Operand = getMethod;
}
expr.Code = ILCode.CallGetter;
if (parentExpr != null) {
parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr);
}
break;
}
} else {
MethodDefinition cecilMethodDef = cecilMethod.Resolve();
if (cecilMethodDef != null) {
if (cecilMethodDef.IsGetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
else if (cecilMethodDef.IsSetter)
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
}
}
} else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) {
// Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'.
ILVariable target;
if (expr.Arguments[0].Match(ILCode.Ldloc, out target)
&& expr.Arguments[1].Code == ILCode.Ldvirtftn
&& expr.Arguments[1].Arguments.Count == 1
&& expr.Arguments[1].Arguments[0].MatchLdloc(target))
{
// Remove the 'target' argument from the ldvirtftn instruction.
// It's not needed in the translation to C#, and needs to be eliminated so that the target expression
// can be inlined.
expr.Arguments[1].Arguments.Clear();
}
}
}
/// <summary>
/// Group input into a set of blocks that can be later arbitraliby schufled.
/// The method adds necessary branches to make control flow between blocks
/// explicit and thus order independent.
/// </summary>
void SplitToBasicBlocks(ILBlock block)
{
List<ILNode> basicBlocks = new List<ILNode>();
ILLabel entryLabel = block.Body.FirstOrDefault() as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++) };
ILBasicBlock basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock);
basicBlock.Body.Add(entryLabel);
block.EntryGoto = new ILExpression(ILCode.Br, entryLabel);
if (block.Body.Count > 0) {
if (block.Body[0] != entryLabel)
basicBlock.Body.Add(block.Body[0]);
for (int i = 1; i < block.Body.Count; i++) {
ILNode lastNode = block.Body[i - 1];
ILNode currNode = block.Body[i];
// Start a new basic block if necessary
if (currNode is ILLabel ||
currNode is ILTryCatchBlock || // Counts as label
lastNode.IsConditionalControlFlow() ||
lastNode.IsUnconditionalControlFlow())
{
// Try to reuse the label
ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() };
// Terminate the last block
if (!lastNode.IsUnconditionalControlFlow()) {
// Explicit branch from one block to other
basicBlock.Body.Add(new ILExpression(ILCode.Br, label));
}
// Start the new block
basicBlock = new ILBasicBlock();
basicBlocks.Add(basicBlock);
basicBlock.Body.Add(label);
// Add the node to the basic block
if (currNode != label)
basicBlock.Body.Add(currNode);
} else {
basicBlock.Body.Add(currNode);
}
}
}
block.Body = basicBlocks;
return;
}
void DuplicateReturnStatements(ILBlock method)
{
Dictionary<ILLabel, ILNode> nextSibling = new Dictionary<ILLabel, ILNode>();
// Build navigation data
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count - 1; i++) {
ILLabel curr = block.Body[i] as ILLabel;
if (curr != null) {
nextSibling[curr] = block.Body[i + 1];
}
}
}
// Duplicate returns
foreach(ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
ILLabel targetLabel;
if (block.Body[i].Match(ILCode.Br, out targetLabel) || block.Body[i].Match(ILCode.Leave, out targetLabel)) {
// Skip extra labels
while(nextSibling.ContainsKey(targetLabel) && nextSibling[targetLabel] is ILLabel) {
targetLabel = (ILLabel)nextSibling[targetLabel];
}
// Inline return statement
ILNode target;
List<ILExpression> retArgs;
if (nextSibling.TryGetValue(targetLabel, out target)) {
if (target.Match(ILCode.Ret, out retArgs)) {
ILVariable locVar;
object constValue;
if (retArgs.Count == 0) {
block.Body[i] = new ILExpression(ILCode.Ret, null);
} else if (retArgs.Single().Match(ILCode.Ldloc, out locVar)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldloc, locVar));
} else if (retArgs.Single().Match(ILCode.Ldc_I4, out constValue)) {
block.Body[i] = new ILExpression(ILCode.Ret, null, new ILExpression(ILCode.Ldc_I4, constValue));
}
}
} else {
if (method.Body.Count > 0 && method.Body.Last() == targetLabel) {
// It exits the main method - so it is same as return;
block.Body[i] = new ILExpression(ILCode.Ret, null);
}
}
}
}
}
}
/// <summary>
/// Flattens all nested basic blocks, except the the top level 'node' argument
/// </summary>
void FlattenBasicBlocks(ILNode node)
{
ILBlock block = node as ILBlock;
if (block != null) {
List<ILNode> flatBody = new List<ILNode>();
foreach (ILNode child in block.GetChildren()) {
FlattenBasicBlocks(child);
ILBasicBlock childAsBB = child as ILBasicBlock;
if (childAsBB != null) {
if (!(childAsBB.Body.FirstOrDefault() is ILLabel))
throw new Exception("Basic block has to start with a label. \n" + childAsBB.ToString());
if (childAsBB.Body.LastOrDefault() is ILExpression && !childAsBB.Body.LastOrDefault().IsUnconditionalControlFlow())
throw new Exception("Basci block has to end with unconditional control flow. \n" + childAsBB.ToString());
flatBody.AddRange(childAsBB.GetChildren());
} else {
flatBody.Add(child);
}
}
block.EntryGoto = null;
block.Body = flatBody;
} else if (node is ILExpression) {
// Optimization - no need to check expressions
} else if (node != null) {
// Recursively find all ILBlocks
foreach(ILNode child in node.GetChildren()) {
FlattenBasicBlocks(child);
}
}
}
/// <summary>
/// Replace endfinally with jump to the end of the finally block
/// </summary>
void RemoveEndFinally(ILBlock method)
{
// Go thought the list in reverse so that we do the nested blocks first
foreach(var tryCatch in method.GetSelfAndChildrenRecursive<ILTryCatchBlock>(tc => tc.FinallyBlock != null).Reverse()) {
ILLabel label = new ILLabel() { Name = "EndFinally_" + nextLabelIndex++ };
tryCatch.FinallyBlock.Body.Add(label);
foreach(var block in tryCatch.FinallyBlock.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
if (block.Body[i].Match(ILCode.Endfinally)) {
block.Body[i] = new ILExpression(ILCode.Br, label).WithILRanges(((ILExpression)block.Body[i]).ILRanges);
}
}
}
}
}
/// <summary>
/// Reduce the nesting of conditions.
/// It should be done on flat data that already had most gotos removed
/// </summary>
void ReduceIfNesting(ILNode node)
{
ILBlock block = node as ILBlock;
if (block != null) {
for (int i = 0; i < block.Body.Count; i++) {
ILCondition cond = block.Body[i] as ILCondition;
if (cond != null) {
bool trueExits = cond.TrueBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
bool falseExits = cond.FalseBlock.Body.LastOrDefault().IsUnconditionalControlFlow();
if (trueExits) {
// Move the false block after the condition
block.Body.InsertRange(i + 1, cond.FalseBlock.GetChildren());
cond.FalseBlock = new ILBlock();
} else if (falseExits) {
// Move the true block after the condition
block.Body.InsertRange(i + 1, cond.TrueBlock.GetChildren());
cond.TrueBlock = new ILBlock();
}
// Eliminate empty true block
if (!cond.TrueBlock.GetChildren().Any() && cond.FalseBlock.GetChildren().Any()) {
// Swap bodies
ILBlock tmp = cond.TrueBlock;
cond.TrueBlock = cond.FalseBlock;
cond.FalseBlock = tmp;
cond.Condition = new ILExpression(ILCode.LogicNot, null, cond.Condition);
}
}
}
}
// We are changing the number of blocks so we use plain old recursion to get all blocks
foreach(ILNode child in node.GetChildren()) {
if (child != null && !(child is ILExpression))
ReduceIfNesting(child);
}
}
void RecombineVariables(ILBlock method)
{
// Recombine variables that were split when the ILAst was created
// This ensures that a single IL variable is a single C# variable (gets assigned only one name)
// The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes.
Dictionary<VariableDefinition, ILVariable> dict = new Dictionary<VariableDefinition, ILVariable>();
ReplaceVariables(
method,
delegate(ILVariable v) {
if (v.OriginalVariable == null)
return v;
ILVariable combinedVariable;
if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) {
dict.Add(v.OriginalVariable, v);
combinedVariable = v;
}
return combinedVariable;
});
}
public static void ReplaceVariables(ILNode node, Func<ILVariable, ILVariable> variableMapping)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable v = expr.Operand as ILVariable;
if (v != null)
expr.Operand = variableMapping(v);
foreach (ILExpression child in expr.Arguments)
ReplaceVariables(child, variableMapping);
} else {
var catchBlock = node as ILTryCatchBlock.CatchBlock;
if (catchBlock != null && catchBlock.ExceptionVariable != null) {
catchBlock.ExceptionVariable = variableMapping(catchBlock.ExceptionVariable);
}
foreach (ILNode child in node.GetChildren())
ReplaceVariables(child, variableMapping);
}
}
void ReportUnassignedILRanges(ILBlock method)
{
var unassigned = ILRange.Invert(method.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges), context.CurrentMethod.Body.CodeSize).ToList();
if (unassigned.Count > 0)
Debug.WriteLine(string.Format("Unassigned ILRanges for {0}.{1}: {2}", this.context.CurrentMethod.DeclaringType.Name, this.context.CurrentMethod.Name, string.Join(", ", unassigned.Select(r => r.ToString()))));
}
}
public static class ILAstOptimizerExtensionMethods
{
/// <summary>
/// Perform one pass of a given optimization on this block.
/// This block must consist of only basicblocks.
/// </summary>
public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILBasicBlock, int, bool> optimization)
{
bool modified = false;
List<ILNode> body = block.Body;
for (int i = body.Count - 1; i >= 0; i--) {
if (i < body.Count && optimization(body, (ILBasicBlock)body[i], i)) {
modified = true;
}
}
return modified;
}
public static bool RunOptimization(this ILBlock block, Func<List<ILNode>, ILExpression, int, bool> optimization)
{
bool modified = false;
foreach (ILBasicBlock bb in block.Body) {
for (int i = bb.Body.Count - 1; i >= 0; i--) {
ILExpression expr = bb.Body.ElementAtOrDefault(i) as ILExpression;
if (expr != null && optimization(bb.Body, expr, i)) {
modified = true;
}
}
}
return modified;
}
public static bool IsConditionalControlFlow(this ILNode node)
{
ILExpression expr = node as ILExpression;
return expr != null && expr.Code.IsConditionalControlFlow();
}
public static bool IsUnconditionalControlFlow(this ILNode node)
{
ILExpression expr = node as ILExpression;
return expr != null && expr.Code.IsUnconditionalControlFlow();
}
/// <summary>
/// The expression has no effect on the program and can be removed
/// if its return value is not needed.
/// </summary>
public static bool HasNoSideEffects(this ILExpression expr)
{
// Remember that if expression can throw an exception, it is a side effect
switch(expr.Code) {
case ILCode.Ldloc:
case ILCode.Ldloca:
case ILCode.Ldstr:
case ILCode.Ldnull:
case ILCode.Ldc_I4:
case ILCode.Ldc_I8:
case ILCode.Ldc_R4:
case ILCode.Ldc_R8:
return true;
default:
return false;
}
}
public static bool IsStoreToArray(this ILCode code)
{
switch (code) {
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
public static bool IsLoadFromArray(this ILCode code)
{
switch (code) {
case ILCode.Ldelem_Any:
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
case ILCode.Ldelem_I4:
case ILCode.Ldelem_I8:
case ILCode.Ldelem_U1:
case ILCode.Ldelem_U2:
case ILCode.Ldelem_U4:
case ILCode.Ldelem_R4:
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
return true;
default:
return false;
}
}
/// <summary>
/// Can the expression be used as a statement in C#?
/// </summary>
public static bool CanBeExpressionStatement(this ILExpression expr)
{
switch(expr.Code) {
case ILCode.Call:
case ILCode.Callvirt:
// property getters can't be expression statements, but all other method calls can be
MethodReference mr = (MethodReference)expr.Operand;
return !mr.Name.StartsWith("get_", StringComparison.Ordinal);
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
case ILCode.Newobj:
case ILCode.Newarr:
case ILCode.Stloc:
case ILCode.Stobj:
case ILCode.Stsfld:
case ILCode.Stfld:
case ILCode.Stind_Ref:
case ILCode.Stelem_Any:
case ILCode.Stelem_I:
case ILCode.Stelem_I1:
case ILCode.Stelem_I2:
case ILCode.Stelem_I4:
case ILCode.Stelem_I8:
case ILCode.Stelem_R4:
case ILCode.Stelem_R8:
case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
public static ILExpression WithILRanges(this ILExpression expr, IEnumerable<ILRange> ilranges)
{
expr.ILRanges.AddRange(ilranges);
return expr;
}
public static void RemoveTail(this List<ILNode> body, params ILCode[] codes)
{
for (int i = 0; i < codes.Length; i++) {
if (((ILExpression)body[body.Count - codes.Length + i]).Code != codes[i])
throw new Exception("Tailing code does not match expected.");
}
body.RemoveRange(body.Count - codes.Length, codes.Length);
}
public static V GetOrDefault<K,V>(this Dictionary<K, V> dict, K key)
{
V ret;
dict.TryGetValue(key, out ret);
return ret;
}
public static void RemoveOrThrow<T>(this ICollection<T> collection, T item)
{
if (!collection.Remove(item))
throw new Exception("The item was not found in the collection");
}
public static void RemoveOrThrow<K,V>(this Dictionary<K,V> collection, K key)
{
if (!collection.Remove(key))
throw new Exception("The key was not found in the dictionary");
}
}
}

593
src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs

@ -0,0 +1,593 @@ @@ -0,0 +1,593 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Disassembler;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.CSharp;
using Cecil = Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public abstract class ILNode
{
public IEnumerable<T> GetSelfAndChildrenRecursive<T>(Func<T, bool> predicate = null) where T: ILNode
{
List<T> result = new List<T>(16);
AccumulateSelfAndChildrenRecursive(result, predicate);
return result;
}
void AccumulateSelfAndChildrenRecursive<T>(List<T> list, Func<T, bool> predicate) where T:ILNode
{
// Note: RemoveEndFinally depends on self coming before children
T thisAsT = this as T;
if (thisAsT != null && (predicate == null || predicate(thisAsT)))
list.Add(thisAsT);
foreach (ILNode node in this.GetChildren()) {
if (node != null)
node.AccumulateSelfAndChildrenRecursive(list, predicate);
}
}
public virtual IEnumerable<ILNode> GetChildren()
{
yield break;
}
public override string ToString()
{
StringWriter w = new StringWriter();
WriteTo(new PlainTextOutput(w));
return w.ToString().Replace("\r\n", "; ");
}
public abstract void WriteTo(ITextOutput output);
}
public class ILBlock: ILNode
{
public ILExpression EntryGoto;
public List<ILNode> Body;
public ILBlock(params ILNode[] body)
{
this.Body = new List<ILNode>(body);
}
public ILBlock(List<ILNode> body)
{
this.Body = body;
}
public override IEnumerable<ILNode> GetChildren()
{
if (this.EntryGoto != null)
yield return this.EntryGoto;
foreach(ILNode child in this.Body) {
yield return child;
}
}
public override void WriteTo(ITextOutput output)
{
foreach(ILNode child in this.GetChildren()) {
child.WriteTo(output);
output.WriteLine();
}
}
}
public class ILBasicBlock: ILNode
{
/// <remarks> Body has to start with a label and end with unconditional control flow </remarks>
public List<ILNode> Body = new List<ILNode>();
public override IEnumerable<ILNode> GetChildren()
{
return this.Body;
}
public override void WriteTo(ITextOutput output)
{
foreach(ILNode child in this.GetChildren()) {
child.WriteTo(output);
output.WriteLine();
}
}
}
public class ILLabel: ILNode
{
public string Name;
public override void WriteTo(ITextOutput output)
{
output.WriteDefinition(Name + ":", this);
}
}
public class ILTryCatchBlock: ILNode
{
public class CatchBlock: ILBlock
{
public TypeReference ExceptionType;
public ILVariable ExceptionVariable;
public override void WriteTo(ITextOutput output)
{
output.Write("catch ");
output.WriteReference(ExceptionType.FullName, ExceptionType);
if (ExceptionVariable != null) {
output.Write(' ');
output.Write(ExceptionVariable.Name);
}
output.WriteLine(" {");
output.Indent();
base.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
}
public ILBlock TryBlock;
public List<CatchBlock> CatchBlocks;
public ILBlock FinallyBlock;
public ILBlock FaultBlock;
public override IEnumerable<ILNode> GetChildren()
{
if (this.TryBlock != null)
yield return this.TryBlock;
foreach (var catchBlock in this.CatchBlocks) {
yield return catchBlock;
}
if (this.FaultBlock != null)
yield return this.FaultBlock;
if (this.FinallyBlock != null)
yield return this.FinallyBlock;
}
public override void WriteTo(ITextOutput output)
{
output.WriteLine(".try {");
output.Indent();
TryBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
foreach (CatchBlock block in CatchBlocks) {
block.WriteTo(output);
}
if (FaultBlock != null) {
output.WriteLine("fault {");
output.Indent();
FaultBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
if (FinallyBlock != null) {
output.WriteLine("finally {");
output.Indent();
FinallyBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
}
}
public class ILVariable
{
public string Name;
public bool IsGenerated;
public TypeReference Type;
public VariableDefinition OriginalVariable;
public ParameterDefinition OriginalParameter;
public bool IsPinned {
get { return OriginalVariable != null && OriginalVariable.IsPinned; }
}
public bool IsParameter {
get { return OriginalParameter != null; }
}
public override string ToString()
{
return Name;
}
}
public class ILRange
{
public int From;
public int To; // Exlusive
public override string ToString()
{
return string.Format("{0}-{1}", From.ToString("X"), To.ToString("X"));
}
public static List<ILRange> OrderAndJoint(IEnumerable<ILRange> input)
{
if (input == null)
throw new ArgumentNullException("Input is null!");
List<ILRange> ranges = input.Where(r => r != null).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 static IEnumerable<ILRange> Invert(IEnumerable<ILRange> input, int codeSize)
{
if (input == null)
throw new ArgumentNullException("Input is null!");
if (codeSize <= 0)
throw new ArgumentException("Code size must be grater than 0");
var ordered = OrderAndJoint(input);
if (ordered.Count == 0) {
yield return new ILRange() { From = 0, To = codeSize };
} else {
// Gap before the first element
if (ordered.First().From != 0)
yield return new ILRange() { From = 0, To = ordered.First().From };
// Gaps between elements
for (int i = 0; i < ordered.Count - 1; i++)
yield return new ILRange() { From = ordered[i].To, To = ordered[i + 1].From };
// Gap after the last element
Debug.Assert(ordered.Last().To <= codeSize);
if (ordered.Last().To != codeSize)
yield return new ILRange() { From = ordered.Last().To, To = codeSize };
}
}
}
public class ILExpressionPrefix
{
public readonly ILCode Code;
public readonly object Operand;
public ILExpressionPrefix(ILCode code, object operand = null)
{
this.Code = code;
this.Operand = operand;
}
}
public class ILExpression : ILNode
{
public ILCode Code { get; set; }
public object Operand { get; set; }
public List<ILExpression> Arguments { get; set; }
public ILExpressionPrefix[] Prefixes { get; set; }
// Mapping to the original instructions (useful for debugging)
public List<ILRange> ILRanges { get; set; }
public TypeReference ExpectedType { get; set; }
public TypeReference InferredType { get; set; }
public static readonly object AnyOperand = new object();
public ILExpression(ILCode code, object operand, List<ILExpression> args)
{
if (operand is ILExpression)
throw new ArgumentException("operand");
this.Code = code;
this.Operand = operand;
this.Arguments = new List<ILExpression>(args);
this.ILRanges = new List<ILRange>(1);
}
public ILExpression(ILCode code, object operand, params ILExpression[] args)
{
if (operand is ILExpression)
throw new ArgumentException("operand");
this.Code = code;
this.Operand = operand;
this.Arguments = new List<ILExpression>(args);
this.ILRanges = new List<ILRange>(1);
}
public void AddPrefix(ILExpressionPrefix prefix)
{
ILExpressionPrefix[] arr = this.Prefixes;
if (arr == null)
arr = new ILExpressionPrefix[1];
else
Array.Resize(ref arr, arr.Length + 1);
arr[arr.Length - 1] = prefix;
this.Prefixes = arr;
}
public ILExpressionPrefix GetPrefix(ILCode code)
{
var prefixes = this.Prefixes;
if (prefixes != null) {
foreach (ILExpressionPrefix p in prefixes) {
if (p.Code == code)
return p;
}
}
return null;
}
public override IEnumerable<ILNode> GetChildren()
{
return Arguments;
}
public bool IsBranch()
{
return this.Operand is ILLabel || this.Operand is ILLabel[];
}
public IEnumerable<ILLabel> GetBranchTargets()
{
if (this.Operand is ILLabel) {
return new ILLabel[] { (ILLabel)this.Operand };
} else if (this.Operand is ILLabel[]) {
return (ILLabel[])this.Operand;
} else {
return new ILLabel[] { };
}
}
public override void WriteTo(ITextOutput output)
{
if (Operand is ILVariable && ((ILVariable)Operand).IsGenerated) {
if (Code == ILCode.Stloc && this.InferredType == null) {
output.Write(((ILVariable)Operand).Name);
output.Write(" = ");
Arguments.First().WriteTo(output);
return;
} else if (Code == ILCode.Ldloc) {
output.Write(((ILVariable)Operand).Name);
if (this.InferredType != null) {
output.Write(':');
this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:");
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
}
return;
}
}
if (this.Prefixes != null) {
foreach (var prefix in this.Prefixes) {
output.Write(prefix.Code.GetName());
output.Write(". ");
}
}
output.Write(Code.GetName());
if (this.InferredType != null) {
output.Write(':');
this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:");
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
} else if (this.ExpectedType != null) {
output.Write("[exp:");
this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
output.Write('(');
bool first = true;
if (Operand != null) {
if (Operand is ILLabel) {
output.WriteReference(((ILLabel)Operand).Name, Operand);
} else if (Operand is ILLabel[]) {
ILLabel[] labels = (ILLabel[])Operand;
for (int i = 0; i < labels.Length; i++) {
if (i > 0)
output.Write(", ");
output.WriteReference(labels[i].Name, labels[i]);
}
} else if (Operand is MethodReference) {
MethodReference method = (MethodReference)Operand;
if (method.DeclaringType != null) {
method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write("::");
}
output.WriteReference(method.Name, method);
} else if (Operand is FieldReference) {
FieldReference field = (FieldReference)Operand;
field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write("::");
output.WriteReference(field.Name, field);
} else {
DisassemblerHelpers.WriteOperand(output, Operand);
}
first = false;
}
foreach (ILExpression arg in this.Arguments) {
if (!first) output.Write(", ");
arg.WriteTo(output);
first = false;
}
output.Write(')');
}
}
public class ILWhileLoop : ILNode
{
public ILExpression Condition;
public ILBlock BodyBlock;
public override IEnumerable<ILNode> GetChildren()
{
if (this.Condition != null)
yield return this.Condition;
if (this.BodyBlock != null)
yield return this.BodyBlock;
}
public override void WriteTo(ITextOutput output)
{
output.WriteLine("");
output.Write("loop (");
if (this.Condition != null)
this.Condition.WriteTo(output);
output.WriteLine(") {");
output.Indent();
this.BodyBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
}
public class ILCondition : ILNode
{
public ILExpression Condition;
public ILBlock TrueBlock; // Branch was taken
public ILBlock FalseBlock; // Fall-though
public override IEnumerable<ILNode> GetChildren()
{
if (this.Condition != null)
yield return this.Condition;
if (this.TrueBlock != null)
yield return this.TrueBlock;
if (this.FalseBlock != null)
yield return this.FalseBlock;
}
public override void WriteTo(ITextOutput output)
{
output.Write("if (");
Condition.WriteTo(output);
output.WriteLine(") {");
output.Indent();
TrueBlock.WriteTo(output);
output.Unindent();
output.Write("}");
if (FalseBlock != null) {
output.WriteLine(" else {");
output.Indent();
FalseBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
}
}
public class ILSwitch: ILNode
{
public class CaseBlock: ILBlock
{
public List<int> Values; // null for the default case
public override void WriteTo(ITextOutput output)
{
if (this.Values != null) {
foreach (int i in this.Values) {
output.WriteLine("case {0}:", i);
}
} else {
output.WriteLine("default:");
}
output.Indent();
base.WriteTo(output);
output.Unindent();
}
}
public ILExpression Condition;
public List<CaseBlock> CaseBlocks = new List<CaseBlock>();
public override IEnumerable<ILNode> GetChildren()
{
if (this.Condition != null)
yield return this.Condition;
foreach (ILBlock caseBlock in this.CaseBlocks) {
yield return caseBlock;
}
}
public override void WriteTo(ITextOutput output)
{
output.Write("switch (");
Condition.WriteTo(output);
output.WriteLine(") {");
output.Indent();
foreach (CaseBlock caseBlock in this.CaseBlocks) {
caseBlock.WriteTo(output);
}
output.Unindent();
output.WriteLine("}");
}
}
public class ILFixedStatement : ILNode
{
public List<ILExpression> Initializers = new List<ILExpression>();
public ILBlock BodyBlock;
public override IEnumerable<ILNode> GetChildren()
{
foreach (ILExpression initializer in this.Initializers)
yield return initializer;
if (this.BodyBlock != null)
yield return this.BodyBlock;
}
public override void WriteTo(ITextOutput output)
{
output.Write("fixed (");
for (int i = 0; i < this.Initializers.Count; i++) {
if (i > 0)
output.Write(", ");
this.Initializers[i].WriteTo(output);
}
output.WriteLine(") {");
output.Indent();
this.BodyBlock.WriteTo(output);
output.Unindent();
output.WriteLine("}");
}
}
}

459
src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -0,0 +1,459 @@ @@ -0,0 +1,459 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.ILAst
{
public enum ILCode
{
// For convenience, the start is exactly identical to Mono.Cecil.Cil.Code
// Instructions that should not be used are prepended by __
Nop,
Break,
__Ldarg_0,
__Ldarg_1,
__Ldarg_2,
__Ldarg_3,
__Ldloc_0,
__Ldloc_1,
__Ldloc_2,
__Ldloc_3,
__Stloc_0,
__Stloc_1,
__Stloc_2,
__Stloc_3,
__Ldarg_S,
__Ldarga_S,
__Starg_S,
__Ldloc_S,
__Ldloca_S,
__Stloc_S,
Ldnull,
__Ldc_I4_M1,
__Ldc_I4_0,
__Ldc_I4_1,
__Ldc_I4_2,
__Ldc_I4_3,
__Ldc_I4_4,
__Ldc_I4_5,
__Ldc_I4_6,
__Ldc_I4_7,
__Ldc_I4_8,
__Ldc_I4_S,
Ldc_I4,
Ldc_I8,
Ldc_R4,
Ldc_R8,
Dup,
Pop,
Jmp,
Call,
Calli,
Ret,
__Br_S,
__Brfalse_S,
__Brtrue_S,
__Beq_S,
__Bge_S,
__Bgt_S,
__Ble_S,
__Blt_S,
__Bne_Un_S,
__Bge_Un_S,
__Bgt_Un_S,
__Ble_Un_S,
__Blt_Un_S,
Br,
__Brfalse,
Brtrue,
__Beq,
__Bge,
__Bgt,
__Ble,
__Blt,
__Bne_Un,
__Bge_Un,
__Bgt_Un,
__Ble_Un,
__Blt_Un,
Switch,
__Ldind_I1,
__Ldind_U1,
__Ldind_I2,
__Ldind_U2,
__Ldind_I4,
__Ldind_U4,
__Ldind_I8,
__Ldind_I,
__Ldind_R4,
__Ldind_R8,
Ldind_Ref,
Stind_Ref,
__Stind_I1,
__Stind_I2,
__Stind_I4,
__Stind_I8,
__Stind_R4,
__Stind_R8,
Add,
Sub,
Mul,
Div,
Div_Un,
Rem,
Rem_Un,
And,
Or,
Xor,
Shl,
Shr,
Shr_Un,
Neg,
Not,
Conv_I1,
Conv_I2,
Conv_I4,
Conv_I8,
Conv_R4,
Conv_R8,
Conv_U4,
Conv_U8,
Callvirt,
Cpobj,
Ldobj,
Ldstr,
Newobj,
Castclass,
Isinst,
Conv_R_Un,
Unbox,
Throw,
Ldfld,
Ldflda,
Stfld,
Ldsfld,
Ldsflda,
Stsfld,
Stobj,
Conv_Ovf_I1_Un,
Conv_Ovf_I2_Un,
Conv_Ovf_I4_Un,
Conv_Ovf_I8_Un,
Conv_Ovf_U1_Un,
Conv_Ovf_U2_Un,
Conv_Ovf_U4_Un,
Conv_Ovf_U8_Un,
Conv_Ovf_I_Un,
Conv_Ovf_U_Un,
Box,
Newarr,
Ldlen,
Ldelema,
Ldelem_I1,
Ldelem_U1,
Ldelem_I2,
Ldelem_U2,
Ldelem_I4,
Ldelem_U4,
Ldelem_I8,
Ldelem_I,
Ldelem_R4,
Ldelem_R8,
Ldelem_Ref,
Stelem_I,
Stelem_I1,
Stelem_I2,
Stelem_I4,
Stelem_I8,
Stelem_R4,
Stelem_R8,
Stelem_Ref,
Ldelem_Any,
Stelem_Any,
Unbox_Any,
Conv_Ovf_I1,
Conv_Ovf_U1,
Conv_Ovf_I2,
Conv_Ovf_U2,
Conv_Ovf_I4,
Conv_Ovf_U4,
Conv_Ovf_I8,
Conv_Ovf_U8,
Refanyval,
Ckfinite,
Mkrefany,
Ldtoken,
Conv_U2,
Conv_U1,
Conv_I,
Conv_Ovf_I,
Conv_Ovf_U,
Add_Ovf,
Add_Ovf_Un,
Mul_Ovf,
Mul_Ovf_Un,
Sub_Ovf,
Sub_Ovf_Un,
Endfinally,
Leave,
__Leave_S,
__Stind_I,
Conv_U,
Arglist,
Ceq,
Cgt,
Cgt_Un,
Clt,
Clt_Un,
Ldftn,
Ldvirtftn,
__Ldarg,
__Ldarga,
__Starg,
Ldloc,
Ldloca,
Stloc,
Localloc,
Endfilter,
Unaligned,
Volatile,
Tail,
Initobj,
Constrained,
Cpblk,
Initblk,
No,
Rethrow,
Sizeof,
Refanytype,
Readonly,
// Virtual codes - defined for convenience
Ldexception, // Operand holds the CatchType for catch handler, null for filter
LogicNot,
LogicAnd,
LogicOr,
NullCoalescing,
InitArray, // Array Initializer
// new Class { Prop = 1, Collection = { { 2, 3 }, {4, 5} }}
// is represented as:
// InitObject(newobj Class,
// CallSetter(Prop, InitializedObject, 1),
// InitCollection(CallGetter(Collection, InitializedObject))),
// Call(Add, InitializedObject, 2, 3),
// Call(Add, InitializedObject, 4, 5)))
InitObject, // Object initializer: first arg is newobj/defaultvalue, remaining args are the initializing statements
InitCollection, // Collection initializer: first arg is newobj/defaultvalue, remaining args are the initializing statements
InitializedObject, // Refers the the object being initialized (refers to first arg in parent InitObject or InitCollection instruction)
TernaryOp, // ?:
LoopOrSwitchBreak,
LoopContinue,
Ldc_Decimal,
YieldBreak,
YieldReturn,
/// <summary>
/// Represents the 'default(T)' instruction.
/// </summary>
/// <remarks>Introduced by SimplifyLdObjAndStObj step</remarks>
DefaultValue,
/// <summary>
/// ILExpression with a single child: binary operator.
/// This expression means that the binary operator will also assign the new value to its left-hand side.
/// 'CompoundAssignment' must not be used for local variables, as inlining (and other) optimizations don't know that it modifies the variable.
/// </summary>
/// <remarks>Introduced by MakeCompoundAssignments step</remarks>
CompoundAssignment,
/// <summary>
/// Represents the post-increment operator.
/// The first argument is the address of the variable to increment (ldloca instruction).
/// The second arugment is the amount the variable is incremented by (ldc.i4 instruction)
/// </summary>
/// <remarks>Introduced by IntroducePostIncrement step</remarks>
PostIncrement,
PostIncrement_Ovf, // checked variant of PostIncrement
PostIncrement_Ovf_Un, // checked variant of PostIncrement, for unsigned integers
/// <summary>Calls the getter of a static property (or indexer), or of an instance property on 'base'</summary>
CallGetter,
/// <summary>Calls the getter of an instance property (or indexer)</summary>
CallvirtGetter,
/// <summary>Calls the setter of a static property (or indexer), or of an instance property on 'base'</summary>
/// <remarks>This allows us to represent "while ((SomeProperty = val) != null) {}"</remarks>
CallSetter,
/// <summary>Calls the setter of a instance property (or indexer)</summary>
CallvirtSetter,
/// <summary>Simulates getting the address of the argument instruction.</summary>
/// <remarks>
/// Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays.
/// Also used when inlining a method call on a value type: "stloc(v, ...); call(M, ldloca(v));" becomes "call(M, AddressOf(...))"
/// </remarks>
AddressOf
}
public static class ILCodeUtil
{
public static string GetName(this ILCode code)
{
return code.ToString().ToLowerInvariant().TrimStart('_').Replace('_','.');
}
public static bool IsConditionalControlFlow(this ILCode code)
{
switch(code) {
case ILCode.__Brfalse_S:
case ILCode.__Brtrue_S:
case ILCode.__Beq_S:
case ILCode.__Bge_S:
case ILCode.__Bgt_S:
case ILCode.__Ble_S:
case ILCode.__Blt_S:
case ILCode.__Bne_Un_S:
case ILCode.__Bge_Un_S:
case ILCode.__Bgt_Un_S:
case ILCode.__Ble_Un_S:
case ILCode.__Blt_Un_S:
case ILCode.__Brfalse:
case ILCode.Brtrue:
case ILCode.__Beq:
case ILCode.__Bge:
case ILCode.__Bgt:
case ILCode.__Ble:
case ILCode.__Blt:
case ILCode.__Bne_Un:
case ILCode.__Bge_Un:
case ILCode.__Bgt_Un:
case ILCode.__Ble_Un:
case ILCode.__Blt_Un:
case ILCode.Switch:
return true;
default:
return false;
}
}
public static bool IsUnconditionalControlFlow(this ILCode code)
{
switch(code) {
case ILCode.Br:
case ILCode.__Br_S:
case ILCode.Leave:
case ILCode.__Leave_S:
case ILCode.Ret:
case ILCode.Endfilter:
case ILCode.Endfinally:
case ILCode.Throw:
case ILCode.Rethrow:
case ILCode.LoopContinue:
case ILCode.LoopOrSwitchBreak:
case ILCode.YieldBreak:
return true;
default:
return false;
}
}
public static void ExpandMacro(ref ILCode code, ref object operand, MethodBody methodBody)
{
switch (code) {
case ILCode.__Ldarg_0: code = ILCode.__Ldarg; operand = methodBody.GetParameter(0); break;
case ILCode.__Ldarg_1: code = ILCode.__Ldarg; operand = methodBody.GetParameter(1); break;
case ILCode.__Ldarg_2: code = ILCode.__Ldarg; operand = methodBody.GetParameter(2); break;
case ILCode.__Ldarg_3: code = ILCode.__Ldarg; operand = methodBody.GetParameter(3); break;
case ILCode.__Ldloc_0: code = ILCode.Ldloc; operand = methodBody.Variables[0]; break;
case ILCode.__Ldloc_1: code = ILCode.Ldloc; operand = methodBody.Variables[1]; break;
case ILCode.__Ldloc_2: code = ILCode.Ldloc; operand = methodBody.Variables[2]; break;
case ILCode.__Ldloc_3: code = ILCode.Ldloc; operand = methodBody.Variables[3]; break;
case ILCode.__Stloc_0: code = ILCode.Stloc; operand = methodBody.Variables[0]; break;
case ILCode.__Stloc_1: code = ILCode.Stloc; operand = methodBody.Variables[1]; break;
case ILCode.__Stloc_2: code = ILCode.Stloc; operand = methodBody.Variables[2]; break;
case ILCode.__Stloc_3: code = ILCode.Stloc; operand = methodBody.Variables[3]; break;
case ILCode.__Ldarg_S: code = ILCode.__Ldarg; break;
case ILCode.__Ldarga_S: code = ILCode.__Ldarga; break;
case ILCode.__Starg_S: code = ILCode.__Starg; break;
case ILCode.__Ldloc_S: code = ILCode.Ldloc; break;
case ILCode.__Ldloca_S: code = ILCode.Ldloca; break;
case ILCode.__Stloc_S: code = ILCode.Stloc; break;
case ILCode.__Ldc_I4_M1: code = ILCode.Ldc_I4; operand = -1; break;
case ILCode.__Ldc_I4_0: code = ILCode.Ldc_I4; operand = 0; break;
case ILCode.__Ldc_I4_1: code = ILCode.Ldc_I4; operand = 1; break;
case ILCode.__Ldc_I4_2: code = ILCode.Ldc_I4; operand = 2; break;
case ILCode.__Ldc_I4_3: code = ILCode.Ldc_I4; operand = 3; break;
case ILCode.__Ldc_I4_4: code = ILCode.Ldc_I4; operand = 4; break;
case ILCode.__Ldc_I4_5: code = ILCode.Ldc_I4; operand = 5; break;
case ILCode.__Ldc_I4_6: code = ILCode.Ldc_I4; operand = 6; break;
case ILCode.__Ldc_I4_7: code = ILCode.Ldc_I4; operand = 7; break;
case ILCode.__Ldc_I4_8: code = ILCode.Ldc_I4; operand = 8; break;
case ILCode.__Ldc_I4_S: code = ILCode.Ldc_I4; operand = (int) (sbyte) operand; break;
case ILCode.__Br_S: code = ILCode.Br; break;
case ILCode.__Brfalse_S: code = ILCode.__Brfalse; break;
case ILCode.__Brtrue_S: code = ILCode.Brtrue; break;
case ILCode.__Beq_S: code = ILCode.__Beq; break;
case ILCode.__Bge_S: code = ILCode.__Bge; break;
case ILCode.__Bgt_S: code = ILCode.__Bgt; break;
case ILCode.__Ble_S: code = ILCode.__Ble; break;
case ILCode.__Blt_S: code = ILCode.__Blt; break;
case ILCode.__Bne_Un_S: code = ILCode.__Bne_Un; break;
case ILCode.__Bge_Un_S: code = ILCode.__Bge_Un; break;
case ILCode.__Bgt_Un_S: code = ILCode.__Bgt_Un; break;
case ILCode.__Ble_Un_S: code = ILCode.__Ble_Un; break;
case ILCode.__Blt_Un_S: code = ILCode.__Blt_Un; break;
case ILCode.__Leave_S: code = ILCode.Leave; break;
case ILCode.__Ldind_I: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.IntPtr; break;
case ILCode.__Ldind_I1: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.SByte; break;
case ILCode.__Ldind_I2: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int16; break;
case ILCode.__Ldind_I4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int32; break;
case ILCode.__Ldind_I8: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Int64; break;
case ILCode.__Ldind_U1: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Byte; break;
case ILCode.__Ldind_U2: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.UInt16; break;
case ILCode.__Ldind_U4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.UInt32; break;
case ILCode.__Ldind_R4: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Single; break;
case ILCode.__Ldind_R8: code = ILCode.Ldobj; operand = methodBody.Method.Module.TypeSystem.Double; break;
case ILCode.__Stind_I: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.IntPtr; break;
case ILCode.__Stind_I1: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Byte; break;
case ILCode.__Stind_I2: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int16; break;
case ILCode.__Stind_I4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int32; break;
case ILCode.__Stind_I8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Int64; break;
case ILCode.__Stind_R4: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Single; break;
case ILCode.__Stind_R8: code = ILCode.Stobj; operand = methodBody.Method.Module.TypeSystem.Double; break;
}
}
public static ParameterDefinition GetParameter (this MethodBody self, int index)
{
var method = self.Method;
if (method.HasThis) {
if (index == 0)
return self.ThisParameter;
index--;
}
var parameters = method.Parameters;
if (index < 0 || index >= parameters.Count)
return null;
return parameters [index];
}
}
}

515
src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -0,0 +1,515 @@ @@ -0,0 +1,515 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// Performs inlining transformations.
/// </summary>
public class ILInlining
{
readonly ILBlock method;
internal Dictionary<ILVariable, int> numStloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloc = new Dictionary<ILVariable, int>();
internal Dictionary<ILVariable, int> numLdloca = new Dictionary<ILVariable, int>();
public ILInlining(ILBlock method)
{
this.method = method;
AnalyzeMethod();
}
void AnalyzeMethod()
{
numStloc.Clear();
numLdloc.Clear();
numLdloca.Clear();
// Analyse the whole method
AnalyzeNode(method);
}
void AnalyzeNode(ILNode node)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
numStloc[locVar] = numStloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloc) {
numLdloc[locVar] = numLdloc.GetOrDefault(locVar) + 1;
} else if (expr.Code == ILCode.Ldloca) {
numLdloca[locVar] = numLdloca.GetOrDefault(locVar) + 1;
} else {
throw new NotSupportedException(expr.Code.ToString());
}
}
foreach (ILExpression child in expr.Arguments)
AnalyzeNode(child);
} else {
var catchBlock = node as ILTryCatchBlock.CatchBlock;
if (catchBlock != null && catchBlock.ExceptionVariable != null) {
numStloc[catchBlock.ExceptionVariable] = numStloc.GetOrDefault(catchBlock.ExceptionVariable) + 1;
}
foreach (ILNode child in node.GetChildren())
AnalyzeNode(child);
}
}
public bool InlineAllVariables()
{
bool modified = false;
ILInlining i = new ILInlining(method);
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>())
modified |= i.InlineAllInBlock(block);
return modified;
}
public bool InlineAllInBlock(ILBlock block)
{
bool modified = false;
List<ILNode> body = block.Body;
if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) {
ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable;
if (v != null && v.IsGenerated) {
if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) {
ILVariable v2;
ILExpression ldException;
if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) {
body.RemoveAt(0);
((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2;
modified = true;
}
}
}
}
for(int i = 0; i < body.Count - 1;) {
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(block.Body, i, aggressive: false)) {
modified = true;
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
foreach(ILBasicBlock bb in body.OfType<ILBasicBlock>()) {
modified |= InlineAllInBasicBlock(bb);
}
return modified;
}
public bool InlineAllInBasicBlock(ILBasicBlock bb)
{
bool modified = false;
List<ILNode> body = bb.Body;
for(int i = 0; i < body.Count;) {
ILVariable locVar;
ILExpression expr;
if (body[i].Match(ILCode.Stloc, out locVar, out expr) && InlineOneIfPossible(bb.Body, i, aggressive: false)) {
modified = true;
i = Math.Max(0, i - 1); // Go back one step
} else {
i++;
}
}
return modified;
}
/// <summary>
/// Inlines instructions before pos into block.Body[pos].
/// </summary>
/// <returns>The number of instructions that were inlined.</returns>
public int InlineInto(List<ILNode> body, int pos, bool aggressive)
{
if (pos >= body.Count)
return 0;
int count = 0;
while (--pos >= 0) {
ILExpression expr = body[pos] as ILExpression;
if (expr == null || expr.Code != ILCode.Stloc)
break;
if (InlineOneIfPossible(body, pos, aggressive))
count++;
else
break;
}
return count;
}
/// <summary>
/// Aggressively inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// If inlining was possible; we will continue to inline (non-aggressively) into the the combined instruction.
/// </summary>
/// <remarks>
/// After the operation, pos will point to the new combined instruction.
/// </remarks>
public bool InlineIfPossible(List<ILNode> body, ref int pos)
{
if (InlineOneIfPossible(body, pos, true)) {
pos -= InlineInto(body, pos, false);
return true;
}
return false;
}
/// <summary>
/// Inlines the stloc instruction at block.Body[pos] into the next instruction, if possible.
/// </summary>
public bool InlineOneIfPossible(List<ILNode> body, int pos, bool aggressive)
{
ILVariable v;
ILExpression inlinedExpression;
if (body[pos].Match(ILCode.Stloc, out v, out inlinedExpression) && !v.IsPinned) {
if (InlineIfPossible(v, inlinedExpression, body.ElementAtOrDefault(pos+1), aggressive)) {
// Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges);
// Remove the stloc instruction:
body.RemoveAt(pos);
return true;
} else if (numLdloc.GetOrDefault(v) == 0 && numLdloca.GetOrDefault(v) == 0) {
// The variable is never loaded
if (inlinedExpression.HasNoSideEffects()) {
// Remove completely
body.RemoveAt(pos);
return true;
} else if (inlinedExpression.CanBeExpressionStatement() && v.IsGenerated) {
// Assign the ranges of the stloc instruction:
inlinedExpression.ILRanges.AddRange(((ILExpression)body[pos]).ILRanges);
// Remove the stloc, but keep the inner expression
body[pos] = inlinedExpression;
return true;
}
}
}
return false;
}
/// <summary>
/// Inlines 'expr' into 'next', if possible.
/// </summary>
bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive)
{
// ensure the variable is accessed only a single time
if (numStloc.GetOrDefault(v) != 1)
return false;
int ldloc = numLdloc.GetOrDefault(v);
if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1)
return false;
if (next is ILCondition)
next = ((ILCondition)next).Condition;
else if (next is ILWhileLoop)
next = ((ILWhileLoop)next).Condition;
ILExpression parent;
int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
if (ldloc == 0) {
if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v, inlinedExpression))
return false;
} else {
if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false;
}
// Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges);
if (ldloc == 0) {
// it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types
// comes out correctly
parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression);
} else {
parent.Arguments[pos] = inlinedExpression;
}
return true;
}
return false;
}
/// <summary>
/// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="pos">Index of the load within 'parent'</param>
/// <param name="v">The variable being inlined.</param>
/// <param name="inlinedExpression">The expression being inlined</param>
bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression)
{
if (pos == 0 && v.Type != null && v.Type.IsValueType) {
// Inlining a value type variable is allowed only if the resulting code will maintain the semantics
// that the method is operating on a copy.
// Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers
switch (inlinedExpression.Code) {
case ILCode.Ldloc:
case ILCode.Stloc:
case ILCode.CompoundAssignment:
case ILCode.Ldelem_Any:
case ILCode.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
case ILCode.Ldelem_I4:
case ILCode.Ldelem_I8:
case ILCode.Ldelem_R4:
case ILCode.Ldelem_R8:
case ILCode.Ldelem_Ref:
case ILCode.Ldelem_U1:
case ILCode.Ldelem_U2:
case ILCode.Ldelem_U4:
case ILCode.Ldobj:
case ILCode.Ldind_Ref:
return false;
case ILCode.Ldfld:
case ILCode.Stfld:
case ILCode.Ldsfld:
case ILCode.Stsfld:
// allow inlining field access only if it's a readonly field
FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve();
if (!(f != null && f.IsInitOnly))
return false;
break;
case ILCode.Call:
case ILCode.CallGetter:
// inlining runs both before and after IntroducePropertyAccessInstructions,
// so we have to handle both 'call' and 'callgetter'
MethodReference mr = (MethodReference)inlinedExpression.Operand;
// ensure that it's not an multi-dimensional array getter
if (mr.DeclaringType is ArrayType)
return false;
goto case ILCode.Callvirt;
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
// don't inline foreach loop variables:
mr = (MethodReference)inlinedExpression.Operand;
if (mr.Name == "get_Current" && mr.HasThis)
return false;
break;
case ILCode.Castclass:
case ILCode.Unbox_Any:
// These are valid, but might occur as part of a foreach loop variable.
ILExpression arg = inlinedExpression.Arguments[0];
if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) {
mr = (MethodReference)arg.Operand;
if (mr.Name == "get_Current" && mr.HasThis)
return false; // looks like a foreach loop variable, so don't inline it
}
break;
}
// inline the compiler-generated variable that are used when accessing a member on a value type:
switch (parent.Code) {
case ILCode.Call:
case ILCode.CallGetter:
case ILCode.CallSetter:
case ILCode.Callvirt:
case ILCode.CallvirtGetter:
case ILCode.CallvirtSetter:
MethodReference mr = (MethodReference)parent.Operand;
return mr.HasThis;
case ILCode.Stfld:
case ILCode.Ldfld:
case ILCode.Ldflda:
return true;
}
}
return false;
}
/// <summary>
/// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
/// </summary>
/// <param name="next">The next top-level expression</param>
/// <param name="parent">The direct parent of the load within 'next'</param>
/// <param name="inlinedExpression">The expression being inlined</param>
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression)
{
if (inlinedExpression.Code == ILCode.DefaultValue)
return true;
switch (next.Code) {
case ILCode.Ret:
case ILCode.Brtrue:
return parent == next;
case ILCode.Switch:
return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]);
default:
return false;
}
}
/// <summary>
/// Gets whether 'expressionBeingMoved' can be inlined into 'expr'.
/// </summary>
public bool CanInlineInto(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved)
{
ILExpression parent;
int pos;
return FindLoadInNext(expr, v, expressionBeingMoved, out parent, out pos) == true;
}
/// <summary>
/// Finds the position to inline to.
/// </summary>
/// <returns>true = found; false = cannot continue search; null = not found</returns>
bool? FindLoadInNext(ILExpression expr, ILVariable v, ILExpression expressionBeingMoved, out ILExpression parent, out int pos)
{
parent = null;
pos = 0;
if (expr == null)
return false;
for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated.
// Inlining in that case might result in the inlined expresion not being evaluted.
if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing))
return false;
ILExpression arg = expr.Arguments[i];
if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) {
parent = expr;
pos = i;
return true;
}
bool? r = FindLoadInNext(arg, v, expressionBeingMoved, out parent, out pos);
if (r != null)
return r;
}
if (IsSafeForInlineOver(expr, expressionBeingMoved))
return null; // continue searching
else
return false; // abort, inlining not possible
}
/// <summary>
/// Determines whether it is save to move 'expressionBeingMoved' past 'expr'
/// </summary>
bool IsSafeForInlineOver(ILExpression expr, ILExpression expressionBeingMoved)
{
switch (expr.Code) {
case ILCode.Ldloc:
ILVariable loadedVar = (ILVariable)expr.Operand;
if (numLdloca.GetOrDefault(loadedVar) != 0) {
// abort, inlining is not possible
return false;
}
foreach (ILExpression potentialStore in expressionBeingMoved.GetSelfAndChildrenRecursive<ILExpression>()) {
if (potentialStore.Code == ILCode.Stloc && potentialStore.Operand == loadedVar)
return false;
}
// the expression is loading a non-forbidden variable
return true;
case ILCode.Ldloca:
case ILCode.Ldflda:
case ILCode.Ldsflda:
case ILCode.Ldelema:
// address-loading instructions are safe if their arguments are safe
foreach (ILExpression arg in expr.Arguments) {
if (!IsSafeForInlineOver(arg, expressionBeingMoved))
return false;
}
return true;
default:
// abort, inlining is not possible
return false;
}
}
/// <summary>
/// Runs a very simple form of copy propagation.
/// Copy propagation is used in two cases:
/// 1) assignments from arguments to local variables
/// If the target variable is assigned to only once (so always is that argument) and the argument is never changed (no ldarga/starg),
/// then we can replace the variable with the argument.
/// 2) assignments of address-loading instructions to local variables
/// </summary>
public void CopyPropagation()
{
foreach (ILBlock block in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int i = 0; i < block.Body.Count; i++) {
ILVariable v;
ILExpression copiedExpr;
if (block.Body[i].Match(ILCode.Stloc, out v, out copiedExpr)
&& !v.IsParameter && numStloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0
&& CanPerformCopyPropagation(copiedExpr, v))
{
// un-inline the arguments of the ldArg instruction
ILVariable[] uninlinedArgs = new ILVariable[copiedExpr.Arguments.Count];
for (int j = 0; j < uninlinedArgs.Length; j++) {
uninlinedArgs[j] = new ILVariable { IsGenerated = true, Name = v.Name + "_cp_" + j };
block.Body.Insert(i++, new ILExpression(ILCode.Stloc, uninlinedArgs[j], copiedExpr.Arguments[j]));
}
// perform copy propagation:
foreach (var expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Ldloc && expr.Operand == v) {
expr.Code = copiedExpr.Code;
expr.Operand = copiedExpr.Operand;
for (int j = 0; j < uninlinedArgs.Length; j++) {
expr.Arguments.Add(new ILExpression(ILCode.Ldloc, uninlinedArgs[j]));
}
}
}
block.Body.RemoveAt(i);
if (uninlinedArgs.Length > 0) {
// if we un-inlined stuff; we need to update the usage counters
AnalyzeMethod();
}
InlineInto(block.Body, i, aggressive: false); // maybe inlining gets possible after the removal of block.Body[i]
i -= uninlinedArgs.Length + 1;
}
}
}
}
bool CanPerformCopyPropagation(ILExpression expr, ILVariable copyVariable)
{
switch (expr.Code) {
case ILCode.Ldloca:
case ILCode.Ldelema:
case ILCode.Ldflda:
case ILCode.Ldsflda:
// All address-loading instructions always return the same value for a given operand/argument combination,
// so they can be safely copied.
return true;
case ILCode.Ldloc:
ILVariable v = (ILVariable)expr.Operand;
if (v.IsParameter) {
// Parameters can be copied only if they aren't assigned to (directly or indirectly via ldarga)
return numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 0;
} else {
// Variables are be copied only if both they and the target copy variable are generated,
// and if the variable has only a single assignment
return v.IsGenerated && copyVariable.IsGenerated && numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1;
}
default:
return false;
}
}
}
}

523
src/Libraries/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -0,0 +1,523 @@ @@ -0,0 +1,523 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// IL AST transformation that introduces array, object and collection initializers.
/// </summary>
partial class ILAstOptimizer
{
#region Array Initializers
bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
{
ILVariable v, v3;
ILExpression newarrExpr;
TypeReference elementType;
ILExpression lengthExpr;
int arrayLength;
if (expr.Match(ILCode.Stloc, out v, out newarrExpr) &&
newarrExpr.Match(ILCode.Newarr, out elementType, out lengthExpr) &&
lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) &&
arrayLength > 0) {
ILExpression[] newArr;
int initArrayPos;
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out newArr, out initArrayPos)) {
var arrayType = new ArrayType(elementType, 1);
arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength);
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
body.RemoveAt(initArrayPos);
}
// Put in a limit so that we don't consume too much memory if the code allocates a huge array
// and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
const int maxConsecutiveDefaultValueExpressions = 300;
List<ILExpression> operands = new List<ILExpression>();
int numberOfInstructionsToRemove = 0;
for (int j = pos + 1; j < body.Count; j++) {
ILExpression nextExpr = body[j] as ILExpression;
int arrayPos;
if (nextExpr != null &&
nextExpr.Code.IsStoreToArray() &&
nextExpr.Arguments[0].Match(ILCode.Ldloc, out v3) &&
v == v3 &&
nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
arrayPos >= operands.Count &&
arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions) {
while (operands.Count < arrayPos)
operands.Add(new ILExpression(ILCode.DefaultValue, elementType));
operands.Add(nextExpr.Arguments[2]);
numberOfInstructionsToRemove++;
} else {
break;
}
}
if (operands.Count == arrayLength) {
var arrayType = new ArrayType(elementType, 1);
arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength);
expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
new ILInlining(method).InlineIfPossible(body, ref pos);
return true;
}
}
return false;
}
bool TransformMultidimensionalArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
{
ILVariable v;
ILExpression newarrExpr;
MethodReference ctor;
List<ILExpression> ctorArgs;
ArrayType arrayType;
if (expr.Match(ILCode.Stloc, out v, out newarrExpr) &&
newarrExpr.Match(ILCode.Newobj, out ctor, out ctorArgs) &&
(arrayType = (ctor.DeclaringType as ArrayType)) != null &&
arrayType.Rank == ctorArgs.Count) {
// Clone the type, so we can muck about with the Dimensions
arrayType = new ArrayType(arrayType.ElementType, arrayType.Rank);
var arrayLengths = new int[arrayType.Rank];
for (int i = 0; i < arrayType.Rank; i++) {
if (!ctorArgs[i].Match(ILCode.Ldc_I4, out arrayLengths[i])) return false;
if (arrayLengths[i] <= 0) return false;
arrayType.Dimensions[i] = new ArrayDimension(0, arrayLengths[i]);
}
var totalElements = arrayLengths.Aggregate(1, (t, l) => t * l);
ILExpression[] newArr;
int initArrayPos;
if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, arrayType, totalElements, out newArr, out initArrayPos)) {
var mdArr = Array.CreateInstance(typeof(ILExpression), arrayLengths);
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
body.RemoveAt(initArrayPos);
return true;
}
}
return false;
}
bool ForwardScanInitializeArrayRuntimeHelper(List<ILNode> body, int pos, ILVariable array, TypeReference arrayType, int arrayLength, out ILExpression[] values, out int foundPos)
{
ILVariable v2;
MethodReference methodRef;
ILExpression methodArg1;
ILExpression methodArg2;
FieldReference fieldRef;
if (body.ElementAtOrDefault(pos).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) &&
methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" &&
methodRef.Name == "InitializeArray" &&
methodArg1.Match(ILCode.Ldloc, out v2) &&
array == v2 &&
methodArg2.Match(ILCode.Ldtoken, out fieldRef))
{
FieldDefinition fieldDef = fieldRef.ResolveWithinSameModule();
if (fieldDef != null && fieldDef.InitialValue != null) {
ILExpression[] newArr = new ILExpression[arrayLength];
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType.GetElementType()),
fieldDef.InitialValue, newArr))
{
values = newArr;
foundPos = pos;
return true;
}
}
}
values = null;
foundPos = -1;
return false;
}
static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output)
{
switch (elementType) {
case TypeCode.Boolean:
case TypeCode.Byte:
if (initialValue.Length == output.Length) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)initialValue[j]);
}
return true;
}
return false;
case TypeCode.SByte:
if (initialValue.Length == output.Length) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)unchecked((sbyte)initialValue[j]));
}
return true;
}
return false;
case TypeCode.Int16:
if (initialValue.Length == output.Length * 2) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToInt16(initialValue, j * 2));
}
return true;
}
return false;
case TypeCode.Char:
case TypeCode.UInt16:
if (initialValue.Length == output.Length * 2) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToUInt16(initialValue, j * 2));
}
return true;
}
return false;
case TypeCode.Int32:
case TypeCode.UInt32:
if (initialValue.Length == output.Length * 4) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, BitConverter.ToInt32(initialValue, j * 4));
}
return true;
}
return false;
case TypeCode.Int64:
case TypeCode.UInt64:
if (initialValue.Length == output.Length * 8) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I8, BitConverter.ToInt64(initialValue, j * 8));
}
return true;
}
return false;
case TypeCode.Single:
if (initialValue.Length == output.Length * 4) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_R4, BitConverter.ToSingle(initialValue, j * 4));
}
return true;
}
return false;
case TypeCode.Double:
if (initialValue.Length == output.Length * 8) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_R8, BitConverter.ToDouble(initialValue, j * 8));
}
return true;
}
return false;
default:
return false;
}
}
#endregion
/// <summary>
/// Handles both object and collection initializers.
/// </summary>
bool TransformObjectInitializers(List<ILNode> body, ILExpression expr, int pos)
{
if (!context.Settings.ObjectOrCollectionInitializers)
return false;
Debug.Assert(body[pos] == expr); // should be called for top-level expressions only
ILVariable v;
ILExpression newObjExpr;
TypeReference newObjType;
bool isValueType;
MethodReference ctor;
List<ILExpression> ctorArgs;
if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) {
if (newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) {
// v = newObj(ctor, ctorArgs)
newObjType = ctor.DeclaringType;
isValueType = false;
} else if (newObjExpr.Match(ILCode.DefaultValue, out newObjType)) {
// v = defaultvalue(type)
isValueType = true;
} else {
return false;
}
} else if (expr.Match(ILCode.Call, out ctor, out ctorArgs)) {
// call(SomeStruct::.ctor, ldloca(v), remainingArgs)
if (ctorArgs.Count > 0 && ctorArgs[0].Match(ILCode.Ldloca, out v)) {
isValueType = true;
newObjType = ctor.DeclaringType;
ctorArgs = new List<ILExpression>(ctorArgs);
ctorArgs.RemoveAt(0);
newObjExpr = new ILExpression(ILCode.Newobj, ctor, ctorArgs);
} else {
return false;
}
} else {
return false;
}
if (newObjType.IsValueType != isValueType)
return false;
int originalPos = pos;
// don't use object initializer syntax for closures
if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, newObjType.ResolveWithinSameModule()))
return false;
ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(newObjType), isValueType);
if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements
return false;
int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections
Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1);
// Verify that we can inline 'v' into the next instruction:
if (pos >= body.Count)
return false; // reached end of block, but there should be another instruction which consumes the initialized object
ILInlining inlining = new ILInlining(method);
if (isValueType) {
// one ldloc for the use of the initialized object
if (inlining.numLdloc.GetOrDefault(v) != 1)
return false;
// one ldloca for each initializer argument, and also for the ctor call (if it exists)
if (inlining.numLdloca.GetOrDefault(v) != totalElementCount + (expr.Code == ILCode.Call ? 1 : 0))
return false;
// one stloc for the initial store (if no ctor call was used)
if (inlining.numStloc.GetOrDefault(v) != (expr.Code == ILCode.Call ? 0 : 1))
return false;
} else {
// one ldloc for each initializer argument, and another ldloc for the use of the initialized object
if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1)
return false;
if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0))
return false;
}
ILExpression nextExpr = body[pos] as ILExpression;
if (!inlining.CanInlineInto(nextExpr, v, initializer))
return false;
if (expr.Code == ILCode.Stloc) {
expr.Arguments[0] = initializer;
} else {
Debug.Assert(expr.Code == ILCode.Call);
expr.Code = ILCode.Stloc;
expr.Operand = v;
expr.Arguments.Clear();
expr.Arguments.Add(initializer);
}
// remove all the instructions that were pulled into the initializer
body.RemoveRange(originalPos + 1, pos - originalPos - 1);
// now that we know that it's an object initializer, change all the first arguments to 'InitializedObject'
ChangeFirstArgumentToInitializedObject(initializer);
inlining = new ILInlining(method);
inlining.InlineIfPossible(body, ref originalPos);
return true;
}
/// <summary>
/// Gets whether the type supports collection initializers.
/// </summary>
static bool IsCollectionType(TypeReference tr)
{
if (tr == null)
return false;
TypeDefinition td = tr.Resolve();
while (td != null) {
if (td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
return true;
td = td.BaseType != null ? td.BaseType.Resolve() : null;
}
return false;
}
/// <summary>
/// Gets whether 'expr' represents a setter in an object initializer.
/// ('CallvirtSetter(Property, v, value)')
/// </summary>
static bool IsSetterInObjectInitializer(ILExpression expr)
{
if (expr == null)
return false;
if (expr.Code == ILCode.CallvirtSetter || expr.Code == ILCode.CallSetter || expr.Code == ILCode.Stfld) {
return expr.Arguments.Count == 2;
}
return false;
}
/// <summary>
/// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer.
/// </summary>
static bool IsAddMethodCall(ILExpression expr)
{
MethodReference addMethod;
List<ILExpression> args;
if (expr.Match(ILCode.Callvirt, out addMethod, out args)) {
if (addMethod.Name == "Add" && addMethod.HasThis) {
return args.Count >= 2;
}
}
return false;
}
/// <summary>
/// Parses an object initializer.
/// </summary>
/// <param name="body">ILAst block</param>
/// <param name="pos">
/// Input: position of the instruction assigning to 'v'.
/// Output: first position after the object initializer
/// </param>
/// <param name="v">The variable that holds the object being initialized</param>
/// <param name="newObjExpr">The newobj instruction</param>
/// <returns>InitObject instruction</returns>
ILExpression ParseObjectInitializer(List<ILNode> body, ref int pos, ILVariable v, ILExpression newObjExpr, bool isCollection, bool isValueType)
{
// Take care not to modify any existing ILExpressions in here.
// We just construct new ones around the old ones, any modifications must wait until the whole
// object/collection initializer was analyzed.
ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr);
List<ILExpression> initializerStack = new List<ILExpression>();
initializerStack.Add(objectInitializer);
while (++pos < body.Count) {
ILExpression nextExpr = body[pos] as ILExpression;
if (IsSetterInObjectInitializer(nextExpr)) {
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false, isValueType)) {
CleanupInitializerStackAfterFailedAdjustment(initializerStack);
break;
}
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
} else if (IsAddMethodCall(nextExpr)) {
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true, isValueType)) {
CleanupInitializerStackAfterFailedAdjustment(initializerStack);
break;
}
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
} else {
// can't match any more initializers: end of object initializer
break;
}
}
return objectInitializer;
}
static bool AdjustInitializerStack(List<ILExpression> initializerStack, ILExpression argument, ILVariable v, bool isCollection, bool isValueType)
{
// Argument is of the form 'getter(getter(...(v)))'
// Unpack it into a list of getters:
List<ILExpression> getters = new List<ILExpression>();
while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.CallGetter || argument.Code == ILCode.Ldfld) {
getters.Add(argument);
if (argument.Arguments.Count != 1)
return false;
argument = argument.Arguments[0];
}
// Ensure that the final argument is 'v'
if (isValueType) {
ILVariable loadedVar;
if (!(argument.Match(ILCode.Ldloca, out loadedVar) && loadedVar == v))
return false;
} else {
if (!argument.MatchLdloc(v))
return false;
}
// Now compare the getters with those that are currently active on the initializer stack:
int i;
for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++) {
ILExpression g1 = initializerStack[i].Arguments[0]; // getter stored in initializer
ILExpression g2 = getters[getters.Count - i]; // matching getter from argument
if (g1.Operand != g2.Operand) {
// operands differ, so we abort the comparison
break;
}
}
// Remove all initializers from the stack that were not matched with one from the argument:
initializerStack.RemoveRange(i, initializerStack.Count - i);
// Now create new initializers for the remaining arguments:
for (; i <= getters.Count; i++) {
ILExpression g = getters[getters.Count - i];
MemberReference mr = (MemberReference)g.Operand;
TypeReference returnType;
if (mr is FieldReference)
returnType = TypeAnalysis.GetFieldType((FieldReference)mr);
else
returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, mr);
ILExpression nestedInitializer = new ILExpression(
IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject,
null, g);
// add new initializer to its parent:
ILExpression parentInitializer = initializerStack[initializerStack.Count - 1];
if (parentInitializer.Code == ILCode.InitCollection) {
// can't add children to collection initializer
if (parentInitializer.Arguments.Count == 1) {
// convert empty collection initializer to object initializer
parentInitializer.Code = ILCode.InitObject;
} else {
return false;
}
}
parentInitializer.Arguments.Add(nestedInitializer);
initializerStack.Add(nestedInitializer);
}
ILExpression lastInitializer = initializerStack[initializerStack.Count - 1];
if (isCollection) {
return lastInitializer.Code == ILCode.InitCollection;
} else {
if (lastInitializer.Code == ILCode.InitCollection) {
if (lastInitializer.Arguments.Count == 1) {
// convert empty collection initializer to object initializer
lastInitializer.Code = ILCode.InitObject;
return true;
} else {
return false;
}
} else {
return true;
}
}
}
static void CleanupInitializerStackAfterFailedAdjustment(List<ILExpression> initializerStack)
{
// There might be empty nested initializers left over; so we'll remove those:
while (initializerStack.Count > 1 && initializerStack[initializerStack.Count - 1].Arguments.Count == 1) {
ILExpression parent = initializerStack[initializerStack.Count - 2];
Debug.Assert(parent.Arguments.Last() == initializerStack[initializerStack.Count - 1]);
parent.Arguments.RemoveAt(parent.Arguments.Count - 1);
initializerStack.RemoveAt(initializerStack.Count - 1);
}
}
static void ChangeFirstArgumentToInitializedObject(ILExpression initializer)
{
// Go through all elements in the initializer (so skip the newobj-instr. at the start)
for (int i = 1; i < initializer.Arguments.Count; i++) {
ILExpression element = initializer.Arguments[i];
if (element.Code == ILCode.InitCollection || element.Code == ILCode.InitObject) {
// nested collection/object initializer
ILExpression getCollection = element.Arguments[0];
getCollection.Arguments[0] = new ILExpression(ILCode.InitializedObject, null);
ChangeFirstArgumentToInitializedObject(element); // handle the collection elements
} else {
element.Arguments[0] = new ILExpression(ILCode.InitializedObject, null);
}
}
}
}
}

449
src/Libraries/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs

@ -0,0 +1,449 @@ @@ -0,0 +1,449 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
using ICSharpCode.Decompiler.FlowAnalysis;
namespace ICSharpCode.Decompiler.ILAst
{
/// <summary>
/// Description of LoopsAndConditions.
/// </summary>
public class LoopsAndConditions
{
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
readonly DecompilerContext context;
uint nextLabelIndex = 0;
public LoopsAndConditions(DecompilerContext context)
{
this.context = context;
}
public void FindLoops(ILBlock block)
{
if (block.Body.Count > 0) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominance(context.CancellationToken);
graph.ComputeDominanceFrontier();
block.Body = FindLoops(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint, false);
}
}
public void FindConditions(ILBlock block)
{
if (block.Body.Count > 0) {
ControlFlowGraph graph;
graph = BuildGraph(block.Body, (ILLabel)block.EntryGoto.Operand);
graph.ComputeDominance(context.CancellationToken);
graph.ComputeDominanceFrontier();
block.Body = FindConditions(new HashSet<ControlFlowNode>(graph.Nodes.Skip(3)), graph.EntryPoint);
}
}
ControlFlowGraph BuildGraph(List<ILNode> nodes, ILLabel entryLabel)
{
int index = 0;
List<ControlFlowNode> cfNodes = new List<ControlFlowNode>();
ControlFlowNode entryPoint = new ControlFlowNode(index++, 0, ControlFlowNodeType.EntryPoint);
cfNodes.Add(entryPoint);
ControlFlowNode regularExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.RegularExit);
cfNodes.Add(regularExit);
ControlFlowNode exceptionalExit = new ControlFlowNode(index++, -1, ControlFlowNodeType.ExceptionalExit);
cfNodes.Add(exceptionalExit);
// Create graph nodes
labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
Dictionary<ILNode, ControlFlowNode> astNodeToCfNode = new Dictionary<ILNode, ControlFlowNode>();
foreach(ILBasicBlock node in nodes) {
ControlFlowNode cfNode = new ControlFlowNode(index++, -1, ControlFlowNodeType.Normal);
cfNodes.Add(cfNode);
astNodeToCfNode[node] = cfNode;
cfNode.UserData = node;
// Find all contained labels
foreach(ILLabel label in node.GetSelfAndChildrenRecursive<ILLabel>()) {
labelToCfNode[label] = cfNode;
}
}
// Entry endge
ControlFlowNode entryNode = labelToCfNode[entryLabel];
ControlFlowEdge entryEdge = new ControlFlowEdge(entryPoint, entryNode, JumpType.Normal);
entryPoint.Outgoing.Add(entryEdge);
entryNode.Incoming.Add(entryEdge);
// Create edges
foreach(ILBasicBlock node in nodes) {
ControlFlowNode source = astNodeToCfNode[node];
// Find all branches
foreach(ILLabel target in node.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
ControlFlowNode destination;
// Labels which are out of out scope will not be int the collection
// Insert self edge only if we are sure we are a loop
if (labelToCfNode.TryGetValue(target, out destination) && (destination != source || target == node.Body.FirstOrDefault())) {
ControlFlowEdge edge = new ControlFlowEdge(source, destination, JumpType.Normal);
source.Outgoing.Add(edge);
destination.Incoming.Add(edge);
}
}
}
return new ControlFlowGraph(cfNodes.ToArray());
}
List<ILNode> FindLoops(HashSet<ControlFlowNode> scope, ControlFlowNode entryPoint, bool excludeEntryPoint)
{
List<ILNode> result = new List<ILNode>();
// Do not modify entry data
scope = new HashSet<ControlFlowNode>(scope);
Queue<ControlFlowNode> agenda = new Queue<ControlFlowNode>();
agenda.Enqueue(entryPoint);
while(agenda.Count > 0) {
ControlFlowNode node = agenda.Dequeue();
// If the node is a loop header
if (scope.Contains(node)
&& node.DominanceFrontier.Contains(node)
&& (node != entryPoint || !excludeEntryPoint))
{
HashSet<ControlFlowNode> loopContents = FindLoopContent(scope, node);
// If the first expression is a loop condition
ILBasicBlock basicBlock = (ILBasicBlock)node.UserData;
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
// It has to be just brtrue - any preceding code would introduce goto
if(basicBlock.MatchSingleAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel))
{
ControlFlowNode trueTarget;
labelToCfNode.TryGetValue(trueLabel, out trueTarget);
ControlFlowNode falseTarget;
labelToCfNode.TryGetValue(falseLabel, out falseTarget);
// If one point inside the loop and the other outside
if ((!loopContents.Contains(trueTarget) && loopContents.Contains(falseTarget)) ||
(loopContents.Contains(trueTarget) && !loopContents.Contains(falseTarget)) )
{
loopContents.RemoveOrThrow(node);
scope.RemoveOrThrow(node);
// If false means enter the loop
if (loopContents.Contains(falseTarget) || falseTarget == node)
{
// Negate the condition
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
ILLabel tmp = trueLabel;
trueLabel = falseLabel;
falseLabel = tmp;
}
ControlFlowNode postLoopTarget;
labelToCfNode.TryGetValue(falseLabel, out postLoopTarget);
if (postLoopTarget != null) {
// Pull more nodes into the loop
HashSet<ControlFlowNode> postLoopContents = FindDominatedNodes(scope, postLoopTarget);
var pullIn = scope.Except(postLoopContents).Where(n => node.Dominates(n));
loopContents.UnionWith(pullIn);
}
// Use loop to implement the brtrue
basicBlock.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
basicBlock.Body.Add(new ILWhileLoop() {
Condition = condExpr,
BodyBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, trueLabel),
Body = FindLoops(loopContents, node, false)
}
});
basicBlock.Body.Add(new ILExpression(ILCode.Br, falseLabel));
result.Add(basicBlock);
scope.ExceptWith(loopContents);
}
}
// Fallback method: while(true)
if (scope.Contains(node)) {
result.Add(new ILBasicBlock() {
Body = new List<ILNode>() {
new ILLabel() { Name = "Loop_" + (nextLabelIndex++) },
new ILWhileLoop() {
BodyBlock = new ILBlock() {
EntryGoto = new ILExpression(ILCode.Br, (ILLabel)basicBlock.Body.First()),
Body = FindLoops(loopContents, node, true)
}
},
},
});
scope.ExceptWith(loopContents);
}
}
// Using the dominator tree should ensure we find the the widest loop first
foreach(var child in node.DominatorTreeChildren) {
agenda.Enqueue(child);
}
}
// Add whatever is left
foreach(var node in scope) {
result.Add((ILNode)node.UserData);
}
scope.Clear();
return result;
}
List<ILNode> FindConditions(HashSet<ControlFlowNode> scope, ControlFlowNode entryNode)
{
List<ILNode> result = new List<ILNode>();
// Do not modify entry data
scope = new HashSet<ControlFlowNode>(scope);
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>();
agenda.Add(entryNode);
while(agenda.Any()) {
ControlFlowNode node = agenda.First();
// Attempt for a good order
while(agenda.Contains(node.ImmediateDominator)) {
node = node.ImmediateDominator;
}
agenda.Remove(node);
// Find a block that represents a simple condition
if (scope.Contains(node)) {
ILBasicBlock block = (ILBasicBlock)node.UserData;
{
// Switch
ILLabel[] caseLabels;
ILExpression switchArg;
ILLabel fallLabel;
if (block.MatchLastAndBr(ILCode.Switch, out caseLabels, out switchArg, out fallLabel)) {
// Replace the switch code with ILSwitch
ILSwitch ilSwitch = new ILSwitch() { Condition = switchArg };
block.Body.RemoveTail(ILCode.Switch, ILCode.Br);
block.Body.Add(ilSwitch);
block.Body.Add(new ILExpression(ILCode.Br, fallLabel));
result.Add(block);
// Remove the item so that it is not picked up as content
scope.RemoveOrThrow(node);
// Find the switch offset
int addValue = 0;
List<ILExpression> subArgs;
if (ilSwitch.Condition.Match(ILCode.Sub, out subArgs) && subArgs[1].Match(ILCode.Ldc_I4, out addValue)) {
ilSwitch.Condition = subArgs[0];
}
// Pull in code of cases
ControlFlowNode fallTarget = null;
labelToCfNode.TryGetValue(fallLabel, out fallTarget);
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (fallTarget != null)
frontiers.UnionWith(fallTarget.DominanceFrontier.Except(new [] { fallTarget }));
foreach(ILLabel condLabel in caseLabels) {
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null)
frontiers.UnionWith(condTarget.DominanceFrontier.Except(new [] { condTarget }));
}
for (int i = 0; i < caseLabels.Length; i++) {
ILLabel condLabel = caseLabels[i];
// Find or create new case block
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel);
if (caseBlock == null) {
caseBlock = new ILSwitch.CaseBlock() {
Values = new List<int>(),
EntryGoto = new ILExpression(ILCode.Br, condLabel)
};
ilSwitch.CaseBlocks.Add(caseBlock);
ControlFlowNode condTarget = null;
labelToCfNode.TryGetValue(condLabel, out condTarget);
if (condTarget != null && !frontiers.Contains(condTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, condTarget);
scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, condTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() {
Body = {
new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) },
new ILExpression(ILCode.LoopOrSwitchBreak, null)
}
});
}
}
caseBlock.Values.Add(i + addValue);
}
// Heuristis to determine if we want to use fallthough as default case
if (fallTarget != null && !frontiers.Contains(fallTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, fallTarget);
if (content.Any()) {
var caseBlock = new ILSwitch.CaseBlock() { EntryGoto = new ILExpression(ILCode.Br, fallLabel) };
ilSwitch.CaseBlocks.Add(caseBlock);
block.Body.RemoveTail(ILCode.Br);
scope.ExceptWith(content);
caseBlock.Body.AddRange(FindConditions(content, fallTarget));
// Add explicit break which should not be used by default, but the goto removal might decide to use it
caseBlock.Body.Add(new ILBasicBlock() {
Body = {
new ILLabel() { Name = "SwitchBreak_" + (nextLabelIndex++) },
new ILExpression(ILCode.LoopOrSwitchBreak, null)
}
});
}
}
}
// Two-way branch
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
if(block.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
// Swap bodies since that seems to be the usual C# order
ILLabel temp = trueLabel;
trueLabel = falseLabel;
falseLabel = temp;
condExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
// Convert the brtrue to ILCondition
ILCondition ilCond = new ILCondition() {
Condition = condExpr,
TrueBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, trueLabel) },
FalseBlock = new ILBlock() { EntryGoto = new ILExpression(ILCode.Br, falseLabel) }
};
block.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
block.Body.Add(ilCond);
result.Add(block);
// Remove the item immediately so that it is not picked up as content
scope.RemoveOrThrow(node);
ControlFlowNode trueTarget = null;
labelToCfNode.TryGetValue(trueLabel, out trueTarget);
ControlFlowNode falseTarget = null;
labelToCfNode.TryGetValue(falseLabel, out falseTarget);
// Pull in the conditional code
HashSet<ControlFlowNode> frontiers = new HashSet<ControlFlowNode>();
if (trueTarget != null)
frontiers.UnionWith(trueTarget.DominanceFrontier.Except(new [] { trueTarget }));
if (falseTarget != null)
frontiers.UnionWith(falseTarget.DominanceFrontier.Except(new [] { falseTarget }));
if (trueTarget != null && !frontiers.Contains(trueTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, trueTarget);
scope.ExceptWith(content);
ilCond.TrueBlock.Body.AddRange(FindConditions(content, trueTarget));
}
if (falseTarget != null && !frontiers.Contains(falseTarget)) {
HashSet<ControlFlowNode> content = FindDominatedNodes(scope, falseTarget);
scope.ExceptWith(content);
ilCond.FalseBlock.Body.AddRange(FindConditions(content, falseTarget));
}
}
}
// Add the node now so that we have good ordering
if (scope.Contains(node)) {
result.Add((ILNode)node.UserData);
scope.Remove(node);
}
}
// Using the dominator tree should ensure we find the the widest loop first
foreach(var child in node.DominatorTreeChildren) {
agenda.Add(child);
}
}
// Add whatever is left
foreach(var node in scope) {
result.Add((ILNode)node.UserData);
}
return result;
}
static HashSet<ControlFlowNode> FindDominatedNodes(HashSet<ControlFlowNode> scope, ControlFlowNode head)
{
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>();
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
agenda.Add(head);
while(agenda.Count > 0) {
ControlFlowNode addNode = agenda.First();
agenda.Remove(addNode);
if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
foreach (var successor in addNode.Successors) {
agenda.Add(successor);
}
}
}
return result;
}
static HashSet<ControlFlowNode> FindLoopContent(HashSet<ControlFlowNode> scope, ControlFlowNode head)
{
var viaBackEdges = head.Predecessors.Where(p => head.Dominates(p));
HashSet<ControlFlowNode> agenda = new HashSet<ControlFlowNode>(viaBackEdges);
HashSet<ControlFlowNode> result = new HashSet<ControlFlowNode>();
while(agenda.Count > 0) {
ControlFlowNode addNode = agenda.First();
agenda.Remove(addNode);
if (scope.Contains(addNode) && head.Dominates(addNode) && result.Add(addNode)) {
foreach (var predecessor in addNode.Predecessors) {
agenda.Add(predecessor);
}
}
}
if (scope.Contains(head))
result.Add(head);
return result;
}
}
}

159
src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs

@ -0,0 +1,159 @@ @@ -0,0 +1,159 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public static class PatternMatching
{
public static bool Match(this ILNode node, ILCode code)
{
ILExpression expr = node as ILExpression;
return expr != null && expr.Prefixes == null && expr.Code == code;
}
public static bool Match<T>(this ILNode node, ILCode code, out T operand)
{
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code && expr.Arguments.Count == 0) {
operand = (T)expr.Operand;
return true;
}
operand = default(T);
return false;
}
public static bool Match(this ILNode node, ILCode code, out List<ILExpression> args)
{
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
Debug.Assert(expr.Operand == null);
args = expr.Arguments;
return true;
}
args = null;
return false;
}
public static bool Match(this ILNode node, ILCode code, out ILExpression arg)
{
List<ILExpression> args;
if (node.Match(code, out args) && args.Count == 1) {
arg = args[0];
return true;
}
arg = null;
return false;
}
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out List<ILExpression> args)
{
ILExpression expr = node as ILExpression;
if (expr != null && expr.Prefixes == null && expr.Code == code) {
operand = (T)expr.Operand;
args = expr.Arguments;
return true;
}
operand = default(T);
args = null;
return false;
}
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out ILExpression arg)
{
List<ILExpression> args;
if (node.Match(code, out operand, out args) && args.Count == 1) {
arg = args[0];
return true;
}
arg = null;
return false;
}
public static bool Match<T>(this ILNode node, ILCode code, out T operand, out ILExpression arg1, out ILExpression arg2)
{
List<ILExpression> args;
if (node.Match(code, out operand, out args) && args.Count == 2) {
arg1 = args[0];
arg2 = args[1];
return true;
}
arg1 = null;
arg2 = null;
return false;
}
public static bool MatchSingle<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg)
{
if (bb.Body.Count == 2 &&
bb.Body[0] is ILLabel &&
bb.Body[1].Match(code, out operand, out arg))
{
return true;
}
operand = default(T);
arg = null;
return false;
}
public static bool MatchSingleAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel)
{
if (bb.Body.Count == 3 &&
bb.Body[0] is ILLabel &&
bb.Body[1].Match(code, out operand, out arg) &&
bb.Body[2].Match(ILCode.Br, out brLabel))
{
return true;
}
operand = default(T);
arg = null;
brLabel = null;
return false;
}
public static bool MatchLastAndBr<T>(this ILBasicBlock bb, ILCode code, out T operand, out ILExpression arg, out ILLabel brLabel)
{
if (bb.Body.ElementAtOrDefault(bb.Body.Count - 2).Match(code, out operand, out arg) &&
bb.Body.LastOrDefault().Match(ILCode.Br, out brLabel))
{
return true;
}
operand = default(T);
arg = null;
brLabel = null;
return false;
}
public static bool MatchThis(this ILNode node)
{
ILVariable v;
return node.Match(ILCode.Ldloc, out v) && v.IsParameter && v.OriginalParameter.Index == -1;
}
public static bool MatchLdloc(this ILNode node, ILVariable expectedVar)
{
ILVariable v;
return node.Match(ILCode.Ldloc, out v) && v == expectedVar;
}
}
}

907
src/Libraries/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs

@ -0,0 +1,907 @@ @@ -0,0 +1,907 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.NRefactory.Utils;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public partial class ILAstOptimizer
{
#region TransformDecimalCtorToConstant
static bool TransformDecimalCtorToConstant(List<ILNode> body, ILExpression expr, int pos)
{
MethodReference r;
List<ILExpression> args;
if (expr.Match(ILCode.Newobj, out r, out args) &&
r.DeclaringType.Namespace == "System" &&
r.DeclaringType.Name == "Decimal")
{
if (args.Count == 1) {
int val;
if (args[0].Match(ILCode.Ldc_I4, out val)) {
expr.Code = ILCode.Ldc_Decimal;
expr.Operand = new decimal(val);
expr.InferredType = r.DeclaringType;
expr.Arguments.Clear();
return true;
}
} else if (args.Count == 5) {
int lo, mid, hi, isNegative, scale;
if (expr.Arguments[0].Match(ILCode.Ldc_I4, out lo) &&
expr.Arguments[1].Match(ILCode.Ldc_I4, out mid) &&
expr.Arguments[2].Match(ILCode.Ldc_I4, out hi) &&
expr.Arguments[3].Match(ILCode.Ldc_I4, out isNegative) &&
expr.Arguments[4].Match(ILCode.Ldc_I4, out scale))
{
expr.Code = ILCode.Ldc_Decimal;
expr.Operand = new decimal(lo, mid, hi, isNegative != 0, (byte)scale);
expr.InferredType = r.DeclaringType;
expr.Arguments.Clear();
return true;
}
}
}
bool modified = false;
foreach(ILExpression arg in expr.Arguments) {
modified |= TransformDecimalCtorToConstant(null, arg, -1);
}
return modified;
}
#endregion
#region SimplifyLdObjAndStObj
static bool SimplifyLdObjAndStObj(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
expr = SimplifyLdObjAndStObj(expr, ref modified);
if (modified && body != null)
body[pos] = expr;
for (int i = 0; i < expr.Arguments.Count; i++) {
expr.Arguments[i] = SimplifyLdObjAndStObj(expr.Arguments[i], ref modified);
modified |= SimplifyLdObjAndStObj(null, expr.Arguments[i], -1);
}
return modified;
}
static ILExpression SimplifyLdObjAndStObj(ILExpression expr, ref bool modified)
{
if (expr.Code == ILCode.Initobj) {
expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
modified = true;
} else if (expr.Code == ILCode.Cpobj) {
expr.Code = ILCode.Stobj;
expr.Arguments[1] = new ILExpression(ILCode.Ldobj, expr.Operand, expr.Arguments[1]);
modified = true;
}
ILExpression arg, arg2;
TypeReference type;
ILCode? newCode = null;
if (expr.Match(ILCode.Stobj, out type, out arg, out arg2)) {
switch (arg.Code) {
case ILCode.Ldelema: newCode = ILCode.Stelem_Any; break;
case ILCode.Ldloca: newCode = ILCode.Stloc; break;
case ILCode.Ldflda: newCode = ILCode.Stfld; break;
case ILCode.Ldsflda: newCode = ILCode.Stsfld; break;
}
} else if (expr.Match(ILCode.Ldobj, out type, out arg)) {
switch (arg.Code) {
case ILCode.Ldelema: newCode = ILCode.Ldelem_Any; break;
case ILCode.Ldloca: newCode = ILCode.Ldloc; break;
case ILCode.Ldflda: newCode = ILCode.Ldfld; break;
case ILCode.Ldsflda: newCode = ILCode.Ldsfld; break;
}
}
if (newCode != null) {
arg.Code = newCode.Value;
if (expr.Code == ILCode.Stobj) {
arg.InferredType = expr.InferredType;
arg.ExpectedType = expr.ExpectedType;
arg.Arguments.Add(arg2);
}
arg.ILRanges.AddRange(expr.ILRanges);
modified = true;
return arg;
} else {
return expr;
}
}
#endregion
#region SimplifyLdcI4ConvI8
static bool SimplifyLdcI4ConvI8(List<ILNode> body, ILExpression expr, int pos)
{
ILExpression ldc;
int val;
if (expr.Match(ILCode.Conv_I8, out ldc) && ldc.Match(ILCode.Ldc_I4, out val)) {
expr.Code = ILCode.Ldc_I8;
expr.Operand = (long)val;
expr.Arguments.Clear();
return true;
}
bool modified = false;
foreach(ILExpression arg in expr.Arguments) {
modified |= SimplifyLdcI4ConvI8(null, arg, -1);
}
return modified;
}
#endregion
#region CachedDelegateInitialization
void CachedDelegateInitializationWithField(ILBlock block, ref int i)
{
// if (logicnot(ldsfld(field))) {
// stsfld(field, newobj(Action::.ctor, ldnull(), ldftn(method)))
// } else {
// }
// ...(..., ldsfld(field), ...)
ILCondition c = block.Body[i] as ILCondition;
if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null)
return;
if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0))
return;
if (!c.Condition.Match(ILCode.LogicNot))
return;
ILExpression condition = c.Condition.Arguments.Single() as ILExpression;
if (condition == null || condition.Code != ILCode.Ldsfld)
return;
FieldDefinition field = ((FieldReference)condition.Operand).ResolveWithinSameModule(); // field is defined in current assembly
if (field == null || !field.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return;
ILExpression stsfld = c.TrueBlock.Body[0] as ILExpression;
if (!(stsfld != null && stsfld.Code == ILCode.Stsfld && ((FieldReference)stsfld.Operand).ResolveWithinSameModule() == field))
return;
ILExpression newObj = stsfld.Arguments[0];
if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2))
return;
if (newObj.Arguments[0].Code != ILCode.Ldnull)
return;
if (newObj.Arguments[1].Code != ILCode.Ldftn)
return;
MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly
if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod))
return;
ILNode followingNode = block.Body.ElementAtOrDefault(i + 1);
if (followingNode != null && followingNode.GetSelfAndChildrenRecursive<ILExpression>().Count(
e => e.Code == ILCode.Ldsfld && ((FieldReference)e.Operand).ResolveWithinSameModule() == field) == 1)
{
foreach (ILExpression parent in followingNode.GetSelfAndChildrenRecursive<ILExpression>()) {
for (int j = 0; j < parent.Arguments.Count; j++) {
if (parent.Arguments[j].Code == ILCode.Ldsfld && ((FieldReference)parent.Arguments[j].Operand).ResolveWithinSameModule() == field) {
parent.Arguments[j] = newObj;
block.Body.RemoveAt(i);
i -= new ILInlining(method).InlineInto(block.Body, i, aggressive: false);
return;
}
}
}
}
}
void CachedDelegateInitializationWithLocal(ILBlock block, ref int i)
{
// if (logicnot(ldloc(v))) {
// stloc(v, newobj(Action::.ctor, ldloc(displayClass), ldftn(method)))
// } else {
// }
// ...(..., ldloc(v), ...)
ILCondition c = block.Body[i] as ILCondition;
if (c == null || c.Condition == null && c.TrueBlock == null || c.FalseBlock == null)
return;
if (!(c.TrueBlock.Body.Count == 1 && c.FalseBlock.Body.Count == 0))
return;
if (!c.Condition.Match(ILCode.LogicNot))
return;
ILExpression condition = c.Condition.Arguments.Single() as ILExpression;
if (condition == null || condition.Code != ILCode.Ldloc)
return;
ILVariable v = (ILVariable)condition.Operand;
ILExpression stloc = c.TrueBlock.Body[0] as ILExpression;
if (!(stloc != null && stloc.Code == ILCode.Stloc && (ILVariable)stloc.Operand == v))
return;
ILExpression newObj = stloc.Arguments[0];
if (!(newObj.Code == ILCode.Newobj && newObj.Arguments.Count == 2))
return;
if (newObj.Arguments[0].Code != ILCode.Ldloc)
return;
if (newObj.Arguments[1].Code != ILCode.Ldftn)
return;
MethodDefinition anonymousMethod = ((MethodReference)newObj.Arguments[1].Operand).ResolveWithinSameModule(); // method is defined in current assembly
if (!Ast.Transforms.DelegateConstruction.IsAnonymousMethod(context, anonymousMethod))
return;
ILNode followingNode = block.Body.ElementAtOrDefault(i + 1);
if (followingNode != null && followingNode.GetSelfAndChildrenRecursive<ILExpression>().Count(
e => e.Code == ILCode.Ldloc && (ILVariable)e.Operand == v) == 1)
{
ILInlining inlining = new ILInlining(method);
if (!(inlining.numLdloc.GetOrDefault(v) == 2 && inlining.numStloc.GetOrDefault(v) == 2 && inlining.numLdloca.GetOrDefault(v) == 0))
return;
// Find the store instruction that initializes the local to null:
foreach (ILBlock storeBlock in method.GetSelfAndChildrenRecursive<ILBlock>()) {
for (int j = 0; j < storeBlock.Body.Count; j++) {
ILVariable storedVar;
ILExpression storedExpr;
if (storeBlock.Body[j].Match(ILCode.Stloc, out storedVar, out storedExpr) && storedVar == v && storedExpr.Match(ILCode.Ldnull)) {
// Remove the instruction
storeBlock.Body.RemoveAt(j);
if (storeBlock == block && j < i)
i--;
break;
}
}
}
block.Body[i] = stloc; // remove the 'if (v==null)'
inlining = new ILInlining(method);
inlining.InlineIfPossible(block.Body, ref i);
}
}
#endregion
#region MakeAssignmentExpression
bool MakeAssignmentExpression(List<ILNode> body, ILExpression expr, int pos)
{
// exprVar = ...
// stloc(v, exprVar)
// ->
// exprVar = stloc(v, ...))
ILVariable exprVar;
ILExpression initializer;
if (!(expr.Match(ILCode.Stloc, out exprVar, out initializer) && exprVar.IsGenerated))
return false;
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
ILVariable v;
ILExpression stLocArg;
if (nextExpr.Match(ILCode.Stloc, out v, out stLocArg) && stLocArg.MatchLdloc(exprVar)) {
ILExpression store2 = body.ElementAtOrDefault(pos + 2) as ILExpression;
if (StoreCanBeConvertedToAssignment(store2, exprVar)) {
// expr_44 = ...
// stloc(v1, expr_44)
// anystore(v2, expr_44)
// ->
// stloc(v1, anystore(v2, ...))
ILInlining inlining = new ILInlining(method);
if (inlining.numLdloc.GetOrDefault(exprVar) == 2 && inlining.numStloc.GetOrDefault(exprVar) == 1) {
body.RemoveAt(pos + 2); // remove store2
body.RemoveAt(pos); // remove expr = ...
nextExpr.Arguments[0] = store2;
store2.Arguments[store2.Arguments.Count - 1] = initializer;
inlining.InlineIfPossible(body, ref pos);
return true;
}
}
body.RemoveAt(pos + 1); // remove stloc
nextExpr.Arguments[0] = initializer;
((ILExpression)body[pos]).Arguments[0] = nextExpr;
return true;
} else if ((nextExpr.Code == ILCode.Stsfld || nextExpr.Code == ILCode.CallSetter || nextExpr.Code == ILCode.CallvirtSetter) && nextExpr.Arguments.Count == 1) {
// exprVar = ...
// stsfld(fld, exprVar)
// ->
// exprVar = stsfld(fld, ...))
if (nextExpr.Arguments[0].MatchLdloc(exprVar)) {
body.RemoveAt(pos + 1); // remove stsfld
nextExpr.Arguments[0] = initializer;
((ILExpression)body[pos]).Arguments[0] = nextExpr;
return true;
}
}
return false;
}
bool StoreCanBeConvertedToAssignment(ILExpression store, ILVariable exprVar)
{
if (store == null)
return false;
switch (store.Code) {
case ILCode.Stloc:
case ILCode.Stfld:
case ILCode.Stsfld:
case ILCode.Stobj:
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
break;
default:
if (!store.Code.IsStoreToArray())
return false;
break;
}
return store.Arguments.Last().Code == ILCode.Ldloc && store.Arguments.Last().Operand == exprVar;
}
#endregion
#region MakeCompoundAssignments
bool MakeCompoundAssignments(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = false;
modified |= MakeCompoundAssignment(expr);
// Static fields and local variables are not handled here - those are expressions without side effects
// and get handled by ReplaceMethodCallsWithOperators
// (which does a reversible transform to the short operator form, as the introduction of checked/unchecked might have to revert to the long form).
foreach (ILExpression arg in expr.Arguments) {
modified |= MakeCompoundAssignments(null, arg, -1);
}
if (modified && body != null)
new ILInlining(method).InlineInto(body, pos, aggressive: false);
return modified;
}
bool MakeCompoundAssignment(ILExpression expr)
{
// stelem.any(T, ldloc(array), ldloc(pos), <OP>(ldelem.any(T, ldloc(array), ldloc(pos)), <RIGHT>))
// or
// stobj(T, ldloc(ptr), <OP>(ldobj(T, ldloc(ptr)), <RIGHT>))
ILCode expectedLdelemCode;
switch (expr.Code) {
case ILCode.Stelem_Any:
expectedLdelemCode = ILCode.Ldelem_Any;
break;
case ILCode.Stfld:
expectedLdelemCode = ILCode.Ldfld;
break;
case ILCode.Stobj:
expectedLdelemCode = ILCode.Ldobj;
break;
case ILCode.CallSetter:
expectedLdelemCode = ILCode.CallGetter;
break;
case ILCode.CallvirtSetter:
expectedLdelemCode = ILCode.CallvirtGetter;
break;
default:
return false;
}
// all arguments except the last (so either array+pos, or ptr):
bool hasGeneratedVar = false;
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
ILVariable inputVar;
if (!expr.Arguments[i].Match(ILCode.Ldloc, out inputVar))
return false;
hasGeneratedVar |= inputVar.IsGenerated;
}
// At least one of the variables must be generated; otherwise we just keep the expanded form.
// We do this because we want compound assignments to be represented in ILAst only when strictly necessary;
// other compound assignments will be introduced by ReplaceMethodCallsWithOperator
// (which uses a reversible transformation, see ReplaceMethodCallsWithOperator.RestoreOriginalAssignOperatorAnnotation)
if (!hasGeneratedVar)
return false;
ILExpression op = expr.Arguments.Last();
if (!CanBeRepresentedAsCompoundAssignment(op.Code))
return false;
ILExpression ldelem = op.Arguments[0];
if (ldelem.Code != expectedLdelemCode)
return false;
Debug.Assert(ldelem.Arguments.Count == expr.Arguments.Count - 1);
for (int i = 0; i < ldelem.Arguments.Count; i++) {
if (!ldelem.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand))
return false;
}
expr.Code = ILCode.CompoundAssignment;
expr.Operand = null;
expr.Arguments.RemoveRange(0, ldelem.Arguments.Count);
// result is "CompoundAssignment(<OP>(ldelem.any(...), <RIGHT>))"
return true;
}
static bool CanBeRepresentedAsCompoundAssignment(ILCode code)
{
switch (code) {
case ILCode.Add:
case ILCode.Add_Ovf:
case ILCode.Add_Ovf_Un:
case ILCode.Sub:
case ILCode.Sub_Ovf:
case ILCode.Sub_Ovf_Un:
case ILCode.Mul:
case ILCode.Mul_Ovf:
case ILCode.Mul_Ovf_Un:
case ILCode.Div:
case ILCode.Div_Un:
case ILCode.Rem:
case ILCode.Rem_Un:
case ILCode.And:
case ILCode.Or:
case ILCode.Xor:
case ILCode.Shl:
case ILCode.Shr:
case ILCode.Shr_Un:
return true;
default:
return false;
}
}
#endregion
#region IntroducePostIncrement
bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos)
{
bool modified = IntroducePostIncrementForVariables(body, expr, pos);
Debug.Assert(body[pos] == expr); // IntroducePostIncrementForVariables shouldn't change the expression reference
ILExpression newExpr = IntroducePostIncrementForInstanceFields(expr);
if (newExpr != null) {
modified = true;
body[pos] = newExpr;
new ILInlining(method).InlineIfPossible(body, ref pos);
}
return modified;
}
bool IntroducePostIncrementForVariables(List<ILNode> body, ILExpression expr, int pos)
{
// Works for variables and static fields/properties
// expr = ldloc(i)
// stloc(i, add(expr, ldc.i4(1)))
// ->
// expr = postincrement(1, ldloca(i))
ILVariable exprVar;
ILExpression exprInit;
if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
return false;
//The next expression
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
if (nextExpr == null)
return false;
ILCode loadInstruction = exprInit.Code;
ILCode storeInstruction = nextExpr.Code;
bool recombineVariable = false;
// We only recognise local variables, static fields, and static getters with no arguments
switch (loadInstruction) {
case ILCode.Ldloc:
//Must be a matching store type
if (storeInstruction != ILCode.Stloc)
return false;
ILVariable loadVar = (ILVariable)exprInit.Operand;
ILVariable storeVar = (ILVariable)nextExpr.Operand;
if (loadVar != storeVar) {
if (loadVar.OriginalVariable != null && loadVar.OriginalVariable == storeVar.OriginalVariable)
recombineVariable = true;
else
return false;
}
break;
case ILCode.Ldsfld:
if (storeInstruction != ILCode.Stsfld)
return false;
if (exprInit.Operand != nextExpr.Operand)
return false;
break;
case ILCode.CallGetter:
// non-static getters would have the 'this' argument
if (exprInit.Arguments.Count != 0)
return false;
if (storeInstruction != ILCode.CallSetter)
return false;
if (!IsGetterSetterPair(exprInit.Operand, nextExpr.Operand))
return false;
break;
default:
return false;
}
ILExpression addExpr = nextExpr.Arguments[0];
int incrementAmount;
ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount);
if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar)))
return false;
if (recombineVariable) {
// Split local variable, unsplit these two instances
// replace nextExpr.Operand with exprInit.Operand
ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (ILVariable)exprInit.Operand : oldVar);
}
switch (loadInstruction) {
case ILCode.Ldloc:
exprInit.Code = ILCode.Ldloca;
break;
case ILCode.Ldsfld:
exprInit.Code = ILCode.Ldsflda;
break;
case ILCode.CallGetter:
exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
break;
}
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
body.RemoveAt(pos + 1); // TODO ILRanges
return true;
}
static bool IsGetterSetterPair(object getterOperand, object setterOperand)
{
MethodReference getter = getterOperand as MethodReference;
MethodReference setter = setterOperand as MethodReference;
if (getter == null || setter == null)
return false;
if (!TypeAnalysis.IsSameType(getter.DeclaringType, setter.DeclaringType))
return false;
MethodDefinition getterDef = getter.Resolve();
MethodDefinition setterDef = setter.Resolve();
if (getterDef == null || setterDef == null)
return false;
foreach (PropertyDefinition prop in getterDef.DeclaringType.Properties) {
if (prop.GetMethod == getterDef)
return prop.SetMethod == setterDef;
}
return false;
}
ILExpression IntroducePostIncrementForInstanceFields(ILExpression expr)
{
// stfld(field, ldloc(instance), add(stloc(helperVar, ldfld(field, ldloc(instance))), ldc.i4(1)))
// -> stloc(helperVar, postincrement(1, ldflda(field, ldloc(instance))))
// Also works for array elements and pointers:
// stelem.any(T, ldloc(instance), ldloc(pos), add(stloc(helperVar, ldelem.any(T, ldloc(instance), ldloc(pos))), ldc.i4(1)))
// -> stloc(helperVar, postincrement(1, ldelema(ldloc(instance), ldloc(pos))))
// stobj(T, ldloc(ptr), add(stloc(helperVar, ldobj(T, ldloc(ptr)), ldc.i4(1))))
// -> stloc(helperVar, postIncrement(1, ldloc(ptr)))
// callsetter(set_P, ldloc(instance), add(stloc(helperVar, callgetter(get_P, ldloc(instance))), ldc.i4(1)))
// -> stloc(helperVar, postIncrement(1, propertyaddress. callgetter(get_P, ldloc(instance))))
if (!(expr.Code == ILCode.Stfld || expr.Code.IsStoreToArray() || expr.Code == ILCode.Stobj || expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter))
return null;
// Test that all arguments except the last are ldloc (1 arg for fields and pointers, 2 args for arrays)
for (int i = 0; i < expr.Arguments.Count - 1; i++) {
if (expr.Arguments[i].Code != ILCode.Ldloc)
return null;
}
ILExpression addExpr = expr.Arguments[expr.Arguments.Count - 1];
int incrementAmount;
ILCode incrementCode = GetIncrementCode(addExpr, out incrementAmount);
ILVariable helperVar;
ILExpression initialValue;
if (!(incrementAmount != 0 && addExpr.Arguments[0].Match(ILCode.Stloc, out helperVar, out initialValue)))
return null;
if (expr.Code == ILCode.Stfld) {
if (initialValue.Code != ILCode.Ldfld)
return null;
// There might be two different FieldReference instances, so we compare the field's signatures:
FieldReference getField = (FieldReference)initialValue.Operand;
FieldReference setField = (FieldReference)expr.Operand;
if (!(TypeAnalysis.IsSameType(getField.DeclaringType, setField.DeclaringType)
&& getField.Name == setField.Name && TypeAnalysis.IsSameType(getField.FieldType, setField.FieldType)))
{
return null;
}
} else if (expr.Code == ILCode.Stobj) {
if (!(initialValue.Code == ILCode.Ldobj && initialValue.Operand == expr.Operand))
return null;
} else if (expr.Code == ILCode.CallSetter) {
if (!(initialValue.Code == ILCode.CallGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand)))
return null;
} else if (expr.Code == ILCode.CallvirtSetter) {
if (!(initialValue.Code == ILCode.CallvirtGetter && IsGetterSetterPair(initialValue.Operand, expr.Operand)))
return null;
} else {
if (!initialValue.Code.IsLoadFromArray())
return null;
}
Debug.Assert(expr.Arguments.Count - 1 == initialValue.Arguments.Count);
for (int i = 0; i < initialValue.Arguments.Count; i++) {
if (!initialValue.Arguments[i].MatchLdloc((ILVariable)expr.Arguments[i].Operand))
return null;
}
ILExpression stloc = addExpr.Arguments[0];
if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
initialValue = new ILExpression(ILCode.AddressOf, null, initialValue);
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
} else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);
}
// TODO: ILRanges?
return stloc;
}
ILCode GetIncrementCode(ILExpression addExpr, out int incrementAmount)
{
ILCode incrementCode;
bool decrement = false;
switch (addExpr.Code) {
case ILCode.Add:
incrementCode = ILCode.PostIncrement;
break;
case ILCode.Add_Ovf:
incrementCode = ILCode.PostIncrement_Ovf;
break;
case ILCode.Add_Ovf_Un:
incrementCode = ILCode.PostIncrement_Ovf_Un;
break;
case ILCode.Sub:
incrementCode = ILCode.PostIncrement;
decrement = true;
break;
case ILCode.Sub_Ovf:
incrementCode = ILCode.PostIncrement_Ovf;
decrement = true;
break;
case ILCode.Sub_Ovf_Un:
incrementCode = ILCode.PostIncrement_Ovf_Un;
decrement = true;
break;
default:
incrementAmount = 0;
return ILCode.Nop;
}
if (addExpr.Arguments[1].Match(ILCode.Ldc_I4, out incrementAmount)) {
if (incrementAmount == -1 || incrementAmount == 1) { // TODO pointer increment?
if (decrement)
incrementAmount = -incrementAmount;
return incrementCode;
}
}
incrementAmount = 0;
return ILCode.Nop;
}
#endregion
#region IntroduceFixedStatements
bool IntroduceFixedStatements(List<ILNode> body, int i)
{
ILExpression initValue;
ILVariable pinnedVar;
int initEndPos;
if (!MatchFixedInitializer(body, i, out pinnedVar, out initValue, out initEndPos))
return false;
ILFixedStatement fixedStmt = body.ElementAtOrDefault(initEndPos) as ILFixedStatement;
if (fixedStmt != null) {
ILExpression expr = fixedStmt.BodyBlock.Body.LastOrDefault() as ILExpression;
if (expr != null && expr.Code == ILCode.Stloc && expr.Operand == pinnedVar && IsNullOrZero(expr.Arguments[0])) {
// we found a second initializer for the existing fixed statement
fixedStmt.Initializers.Insert(0, initValue);
body.RemoveRange(i, initEndPos - i);
fixedStmt.BodyBlock.Body.RemoveAt(fixedStmt.BodyBlock.Body.Count - 1);
if (pinnedVar.Type.IsByReference)
pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType);
return true;
}
}
// find where pinnedVar is reset to 0:
int j;
for (j = initEndPos; j < body.Count; j++) {
ILVariable v2;
ILExpression storedVal;
// stloc(pinned_Var, conv.u(ldc.i4(0)))
if (body[j].Match(ILCode.Stloc, out v2, out storedVal) && v2 == pinnedVar) {
if (IsNullOrZero(storedVal)) {
break;
}
}
}
// Create fixed statement from i to j
fixedStmt = new ILFixedStatement();
fixedStmt.Initializers.Add(initValue);
fixedStmt.BodyBlock = new ILBlock(body.GetRange(initEndPos, j - initEndPos)); // from initEndPos to j-1 (inclusive)
body.RemoveRange(i + 1, Math.Min(j, body.Count - 1) - i); // from i+1 to j (inclusive)
body[i] = fixedStmt;
if (pinnedVar.Type.IsByReference)
pinnedVar.Type = new PointerType(((ByReferenceType)pinnedVar.Type).ElementType);
return true;
}
bool IsNullOrZero(ILExpression expr)
{
if (expr.Code == ILCode.Conv_U || expr.Code == ILCode.Conv_I)
expr = expr.Arguments[0];
return (expr.Code == ILCode.Ldc_I4 && (int)expr.Operand == 0) || expr.Code == ILCode.Ldnull;
}
bool MatchFixedInitializer(List<ILNode> body, int i, out ILVariable pinnedVar, out ILExpression initValue, out int nextPos)
{
if (body[i].Match(ILCode.Stloc, out pinnedVar, out initValue) && pinnedVar.IsPinned && !IsNullOrZero(initValue)) {
initValue = (ILExpression)body[i];
nextPos = i + 1;
HandleStringFixing(pinnedVar, body, ref nextPos, ref initValue);
return true;
}
ILCondition ifStmt = body[i] as ILCondition;
ILExpression arrayLoadingExpr;
if (ifStmt != null && MatchFixedArrayInitializerCondition(ifStmt.Condition, out arrayLoadingExpr)) {
ILVariable arrayVariable = (ILVariable)arrayLoadingExpr.Operand;
ILExpression trueValue;
if (ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1
&& ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out pinnedVar, out trueValue)
&& pinnedVar.IsPinned && IsNullOrZero(trueValue))
{
if (ifStmt.FalseBlock != null && ifStmt.FalseBlock.Body.Count == 1 && ifStmt.FalseBlock.Body[0] is ILFixedStatement) {
ILFixedStatement fixedStmt = (ILFixedStatement)ifStmt.FalseBlock.Body[0];
ILVariable stlocVar;
ILExpression falseValue;
if (fixedStmt.Initializers.Count == 1 && fixedStmt.BodyBlock.Body.Count == 0
&& fixedStmt.Initializers[0].Match(ILCode.Stloc, out stlocVar, out falseValue) && stlocVar == pinnedVar)
{
ILVariable loadedVariable;
if (falseValue.Code == ILCode.Ldelema
&& falseValue.Arguments[0].Match(ILCode.Ldloc, out loadedVariable) && loadedVariable == arrayVariable
&& IsNullOrZero(falseValue.Arguments[1]))
{
// OK, we detected the pattern for fixing an array.
// Now check whether the loading expression was a store ot a temp. var
// that can be eliminated.
if (arrayLoadingExpr.Code == ILCode.Stloc) {
ILInlining inlining = new ILInlining(method);
if (inlining.numLdloc.GetOrDefault(arrayVariable) == 2 &&
inlining.numStloc.GetOrDefault(arrayVariable) == 1 && inlining.numLdloca.GetOrDefault(arrayVariable) == 0)
{
arrayLoadingExpr = arrayLoadingExpr.Arguments[0];
}
}
initValue = new ILExpression(ILCode.Stloc, pinnedVar, arrayLoadingExpr);
nextPos = i + 1;
return true;
}
}
}
}
}
initValue = null;
nextPos = -1;
return false;
}
bool MatchFixedArrayInitializerCondition(ILExpression condition, out ILExpression initValue)
{
ILExpression logicAnd;
ILVariable arrayVar;
if (condition.Match(ILCode.LogicNot, out logicAnd) && logicAnd.Code == ILCode.LogicAnd) {
initValue = UnpackDoubleNegation(logicAnd.Arguments[0]);
ILExpression arrayVarInitializer;
if (initValue.Match(ILCode.Ldloc, out arrayVar)
|| initValue.Match(ILCode.Stloc, out arrayVar, out arrayVarInitializer))
{
ILExpression arrayLength = logicAnd.Arguments[1];
if (arrayLength.Code == ILCode.Conv_I4)
arrayLength = arrayLength.Arguments[0];
return arrayLength.Code == ILCode.Ldlen && arrayLength.Arguments[0].MatchLdloc(arrayVar);
}
}
initValue = null;
return false;
}
ILExpression UnpackDoubleNegation(ILExpression expr)
{
ILExpression negated;
if (expr.Match(ILCode.LogicNot, out negated) && negated.Match(ILCode.LogicNot, out negated))
return negated;
else
return expr;
}
bool HandleStringFixing(ILVariable pinnedVar, List<ILNode> body, ref int pos, ref ILExpression fixedStmtInitializer)
{
// fixed (stloc(pinnedVar, ldloc(text))) {
// var1 = var2 = conv.i(ldloc(pinnedVar))
// if (logicnot(logicnot(var1))) {
// var2 = add(var1, call(RuntimeHelpers::get_OffsetToStringData))
// }
// stloc(ptrVar, var2)
// ...
if (pos >= body.Count)
return false;
ILVariable var1, var2;
ILExpression varAssignment, ptrInitialization;
if (!(body[pos].Match(ILCode.Stloc, out var1, out varAssignment) && varAssignment.Match(ILCode.Stloc, out var2, out ptrInitialization)))
return false;
if (!(var1.IsGenerated && var2.IsGenerated))
return false;
if (ptrInitialization.Code == ILCode.Conv_I || ptrInitialization.Code == ILCode.Conv_U)
ptrInitialization = ptrInitialization.Arguments[0];
if (!ptrInitialization.MatchLdloc(pinnedVar))
return false;
ILCondition ifStmt = body[pos + 1] as ILCondition;
if (!(ifStmt != null && ifStmt.TrueBlock != null && ifStmt.TrueBlock.Body.Count == 1 && (ifStmt.FalseBlock == null || ifStmt.FalseBlock.Body.Count == 0)))
return false;
if (!UnpackDoubleNegation(ifStmt.Condition).MatchLdloc(var1))
return false;
ILVariable assignedVar;
ILExpression assignedExpr;
if (!(ifStmt.TrueBlock.Body[0].Match(ILCode.Stloc, out assignedVar, out assignedExpr) && assignedVar == var2 && assignedExpr.Code == ILCode.Add))
return false;
MethodReference calledMethod;
if (!(assignedExpr.Arguments[0].MatchLdloc(var1)))
return false;
if (!(assignedExpr.Arguments[1].Match(ILCode.Call, out calledMethod) || assignedExpr.Arguments[1].Match(ILCode.CallGetter, out calledMethod)))
return false;
if (!(calledMethod.Name == "get_OffsetToStringData" && calledMethod.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers"))
return false;
ILVariable pointerVar;
if (body[pos + 2].Match(ILCode.Stloc, out pointerVar, out assignedExpr) && assignedExpr.MatchLdloc(var2)) {
pos += 3;
fixedStmtInitializer.Operand = pointerVar;
return true;
}
return false;
}
#endregion
#region SimplifyShiftOperators
static bool SimplifyShiftOperators(List<ILNode> body, ILExpression expr, int pos)
{
// C# compiles "a << b" to "a << (b & 31)", so we will remove the "& 31" if possible.
bool modified = false;
SimplifyShiftOperators(expr, ref modified);
return modified;
}
static void SimplifyShiftOperators(ILExpression expr, ref bool modified)
{
for (int i = 0; i < expr.Arguments.Count; i++)
SimplifyShiftOperators(expr.Arguments[i], ref modified);
if (expr.Code != ILCode.Shl && expr.Code != ILCode.Shr && expr.Code != ILCode.Shr_Un)
return;
var a = expr.Arguments[1];
if (a.Code != ILCode.And || a.Arguments[1].Code != ILCode.Ldc_I4 || expr.InferredType == null)
return;
int mask;
switch (expr.InferredType.MetadataType) {
case MetadataType.Int32:
case MetadataType.UInt32: mask = 31; break;
case MetadataType.Int64:
case MetadataType.UInt64: mask = 63; break;
default: return;
}
if ((int)a.Arguments[1].Operand != mask) return;
var res = a.Arguments[0];
res.ILRanges.AddRange(a.ILRanges);
res.ILRanges.AddRange(a.Arguments[1].ILRanges);
expr.Arguments[1] = res;
modified = true;
}
#endregion
}
}

376
src/Libraries/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs

@ -0,0 +1,376 @@ @@ -0,0 +1,376 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public class SimpleControlFlow
{
Dictionary<ILLabel, int> labelGlobalRefCount = new Dictionary<ILLabel, int>();
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>();
DecompilerContext context;
TypeSystem typeSystem;
public SimpleControlFlow(DecompilerContext context, ILBlock method)
{
this.context = context;
this.typeSystem = context.CurrentMethod.Module.TypeSystem;
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) {
labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1;
}
foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) {
foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) {
labelToBasicBlock[label] = bb;
}
}
}
public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
ILVariable trueLocVar = null;
ILExpression trueExpr;
ILLabel trueFall;
ILVariable falseLocVar = null;
ILExpression falseExpr;
ILLabel falseFall;
object unused;
if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) &&
labelGlobalRefCount[trueLabel] == 1 &&
labelGlobalRefCount[falseLabel] == 1 &&
((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) &&
labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) &&
trueLocVar == falseLocVar && trueFall == falseFall) ||
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) &&
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) &&
body.Contains(labelToBasicBlock[trueLabel]) &&
body.Contains(labelToBasicBlock[falseLabel])
)
{
bool isStloc = trueLocVar != null;
ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret;
TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType;
bool retTypeIsBoolean = TypeAnalysis.IsBoolean(retType);
int leftBoolVal;
int rightBoolVal;
ILExpression newExpr;
// a ? true:false is equivalent to a
// a ? false:true is equivalent to !a
// a ? true : b is equivalent to a || b
// a ? b : true is equivalent to !a || b
// a ? b : false is equivalent to a && b
// a ? false : b is equivalent to !a && b
if (retTypeIsBoolean &&
trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) &&
falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) &&
((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0))
)
{
// It can be expressed as trivilal expression
if (leftBoolVal != 0) {
newExpr = condExpr;
} else {
newExpr = new ILExpression(ILCode.LogicNot, null, condExpr);
}
} else if (retTypeIsBoolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) {
// It can be expressed as logical expression
if (leftBoolVal != 0) {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr);
} else {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr);
}
} else if (retTypeIsBoolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) {
// It can be expressed as logical expression
if (rightBoolVal != 0) {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr);
} else {
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
}
} else {
// Ternary operator tends to create long complicated return statements
if (opCode == ILCode.Ret)
return false;
// Only simplify generated variables
if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated)
return false;
// Create ternary expression
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr);
}
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr));
if (isStloc)
head.Body.Add(new ILExpression(ILCode.Br, trueFall));
// Remove the old basic blocks
body.RemoveOrThrow(labelToBasicBlock[trueLabel]);
body.RemoveOrThrow(labelToBasicBlock[falseLabel]);
return true;
}
return false;
}
public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos)
{
// ...
// v = ldloc(leftVar)
// brtrue(endBBLabel, ldloc(leftVar))
// br(rightBBLabel)
//
// rightBBLabel:
// v = rightExpr
// br(endBBLabel)
// ...
// =>
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
ILVariable v, v2;
ILExpression leftExpr, leftExpr2;
ILVariable leftVar;
ILLabel endBBLabel, endBBLabel2;
ILLabel rightBBLabel;
ILBasicBlock rightBB;
ILExpression rightExpr;
if (head.Body.Count >= 3 &&
head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.MatchLdloc(leftVar) &&
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) &&
rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) &&
v == v2 &&
endBBLabel == endBBLabel2 &&
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 &&
body.Contains(rightBB)
)
{
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr)));
head.Body.Add(new ILExpression(ILCode.Br, endBBLabel));
body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]);
return true;
}
return false;
}
public bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
ILExpression condExpr;
ILLabel trueLabel;
ILLabel falseLabel;
if(head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) {
for (int pass = 0; pass < 2; pass++) {
// On the second pass, swap labels and negate expression of the first branch
// It is slightly ugly, but much better then copy-pasting this whole block
ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel;
ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel;
bool negate = (pass == 1);
ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel];
ILExpression nextCondExpr;
ILLabel nextTrueLablel;
ILLabel nextFalseLabel;
if (body.Contains(nextBasicBlock) &&
nextBasicBlock != head &&
labelGlobalRefCount[(ILLabel)nextBasicBlock.Body.First()] == 1 &&
nextBasicBlock.MatchSingleAndBr(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) &&
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel))
{
// Create short cicuit branch
ILExpression logicExpr;
if (otherLablel == nextFalseLabel) {
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr);
} else {
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr);
}
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr));
head.Body.Add(new ILExpression(ILCode.Br, nextFalseLabel));
// Remove the inlined branch from scope
body.RemoveOrThrow(nextBasicBlock);
return true;
}
}
}
return false;
}
public bool SimplifyCustomShortCircuit(List<ILNode> body, ILBasicBlock head, int pos)
{
Debug.Assert(body.Contains(head));
// --- looking for the following pattern ---
// stloc(targetVar, leftVar)
// brtrue(exitLabel, call(op_False, leftVar)
// br(followingBlock)
//
// FollowingBlock:
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
// br(exitLabel)
// ---
if (head.Body.Count < 3)
return false;
// looking for:
// stloc(targetVar, leftVar)
ILVariable targetVar;
ILExpression targetVarInitExpr;
if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr))
return false;
ILVariable leftVar;
if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar))
return false;
// looking for:
// brtrue(exitLabel, call(op_False, leftVar)
// br(followingBlock)
ILExpression callExpr;
ILLabel exitLabel;
ILLabel followingBlock;
if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock))
return false;
if (labelGlobalRefCount[followingBlock] > 1)
return false;
MethodReference opFalse;
ILExpression opFalseArg;
if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg))
return false;
// ignore operators other than op_False and op_True
if (opFalse.Name != "op_False" && opFalse.Name != "op_True")
return false;
if (!opFalseArg.MatchLdloc(leftVar))
return false;
ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock];
// FollowingBlock:
// stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
// br(exitLabel)
ILVariable _targetVar;
ILExpression opBitwiseCallExpr;
ILLabel _exitLabel;
if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel))
return false;
if (_targetVar != targetVar || exitLabel != _exitLabel)
return false;
MethodReference opBitwise;
ILExpression leftVarExpression;
ILExpression rightExpression;
if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression))
return false;
if (!leftVarExpression.MatchLdloc(leftVar))
return false;
// ignore operators other than op_BitwiseAnd and op_BitwiseOr
if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr")
return false;
// insert:
// stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression)
// br(exitLabel)
ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr;
if (op == ILCode.LogicAnd && opFalse.Name != "op_False")
return false;
if (op == ILCode.LogicOr && opFalse.Name != "op_True")
return false;
ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression);
shortCircuitExpr.Operand = opBitwise;
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br);
head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr));
head.Body.Add(new ILExpression(ILCode.Br, exitLabel));
body.Remove(followingBasicBlock);
return true;
}
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right)
{
// Assuming that the inputs are already left associative
if (right.Match(code)) {
// Find the leftmost logical expression
ILExpression current = right;
while(current.Arguments[0].Match(code))
current = current.Arguments[0];
current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]);
return right;
} else {
return new ILExpression(code, null, left, right);
}
}
public bool JoinBasicBlocks(List<ILNode> body, ILBasicBlock head, int pos)
{
ILLabel nextLabel;
ILBasicBlock nextBB;
if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() &&
head.Body.Last().Match(ILCode.Br, out nextLabel) &&
labelGlobalRefCount[nextLabel] == 1 &&
labelToBasicBlock.TryGetValue(nextLabel, out nextBB) &&
body.Contains(nextBB) &&
nextBB.Body.First() == nextLabel &&
!nextBB.Body.OfType<ILTryCatchBlock>().Any()
)
{
head.Body.RemoveTail(ILCode.Br);
nextBB.Body.RemoveAt(0); // Remove label
head.Body.AddRange(nextBB.Body);
body.RemoveOrThrow(nextBB);
return true;
}
return false;
}
}
}

1215
src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

File diff suppressed because it is too large Load Diff

956
src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs

@ -0,0 +1,956 @@ @@ -0,0 +1,956 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
{
public class YieldReturnDecompiler
{
// For a description on the code generated by the C# compiler for yield return:
// http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx
// The idea here is:
// - Figure out whether the current method is instanciating an enumerator
// - Figure out which of the fields is the state field
// - Construct an exception table based on states. This allows us to determine, for each state, what the parent try block is.
/// <summary>
/// This exception is thrown when we find something else than we expect from the C# compiler.
/// This aborts the analysis and makes the whole transform fail.
/// </summary>
class YieldAnalysisFailedException : Exception {}
DecompilerContext context;
TypeDefinition enumeratorType;
MethodDefinition enumeratorCtor;
MethodDefinition disposeMethod;
FieldDefinition stateField;
FieldDefinition currentField;
Dictionary<FieldDefinition, ILVariable> fieldToParameterMap = new Dictionary<FieldDefinition, ILVariable>();
List<ILNode> newBody;
#region Run() method
public static void Run(DecompilerContext context, ILBlock method)
{
if (!context.Settings.YieldReturn)
return; // abort if enumerator decompilation is disabled
var yrd = new YieldReturnDecompiler();
yrd.context = context;
if (!yrd.MatchEnumeratorCreationPattern(method))
return;
yrd.enumeratorType = yrd.enumeratorCtor.DeclaringType;
#if DEBUG
if (Debugger.IsAttached) {
yrd.Run();
} else {
#endif
try {
yrd.Run();
} catch (YieldAnalysisFailedException) {
return;
}
#if DEBUG
}
#endif
method.Body.Clear();
method.EntryGoto = null;
method.Body.AddRange(yrd.newBody);
// Repeat the inlining/copy propagation optimization because the conversion of field access
// to local variables can open up additional inlining possibilities.
ILInlining inlining = new ILInlining(method);
inlining.InlineAllVariables();
inlining.CopyPropagation();
}
void Run()
{
AnalyzeCtor();
AnalyzeCurrentProperty();
ResolveIEnumerableIEnumeratorFieldMapping();
ConstructExceptionTable();
AnalyzeMoveNext();
TranslateFieldsToLocalAccess();
}
#endregion
#region Match the enumerator creation pattern
bool MatchEnumeratorCreationPattern(ILBlock method)
{
if (method.Body.Count == 0)
return false;
ILExpression newObj;
if (method.Body.Count == 1) {
// ret(newobj(...))
if (method.Body[0].Match(ILCode.Ret, out newObj))
return MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor);
else
return false;
}
// stloc(var_1, newobj(..)
ILVariable var1;
if (!method.Body[0].Match(ILCode.Stloc, out var1, out newObj))
return false;
if (!MatchEnumeratorCreationNewObj(newObj, out enumeratorCtor))
return false;
int i;
for (i = 1; i < method.Body.Count; i++) {
// stfld(..., ldloc(var_1), ldloc(parameter))
FieldReference storedField;
ILExpression ldloc, loadParameter;
if (!method.Body[i].Match(ILCode.Stfld, out storedField, out ldloc, out loadParameter))
break;
ILVariable loadedVar, loadedArg;
if (!ldloc.Match(ILCode.Ldloc, out loadedVar) || !loadParameter.Match(ILCode.Ldloc, out loadedArg))
return false;
storedField = GetFieldDefinition(storedField);
if (loadedVar != var1 || storedField == null || !loadedArg.IsParameter)
return false;
fieldToParameterMap[(FieldDefinition)storedField] = loadedArg;
}
ILVariable var2;
ILExpression ldlocForStloc2;
if (i < method.Body.Count && method.Body[i].Match(ILCode.Stloc, out var2, out ldlocForStloc2)) {
// stloc(var_2, ldloc(var_1))
if (ldlocForStloc2.Code != ILCode.Ldloc || ldlocForStloc2.Operand != var1)
return false;
i++;
} else {
// the compiler might skip the above instruction in release builds; in that case, it directly returns stloc.Operand
var2 = var1;
}
ILExpression retArg;
if (i < method.Body.Count && method.Body[i].Match(ILCode.Ret, out retArg)) {
// ret(ldloc(var_2))
if (retArg.Code == ILCode.Ldloc && retArg.Operand == var2) {
return true;
}
}
return false;
}
static FieldDefinition GetFieldDefinition(FieldReference field)
{
return CecilExtensions.ResolveWithinSameModule(field);
}
static MethodDefinition GetMethodDefinition(MethodReference method)
{
return CecilExtensions.ResolveWithinSameModule(method);
}
bool MatchEnumeratorCreationNewObj(ILExpression expr, out MethodDefinition ctor)
{
// newobj(CurrentType/...::.ctor, ldc.i4(-2))
ctor = null;
if (expr.Code != ILCode.Newobj || expr.Arguments.Count != 1)
return false;
if (expr.Arguments[0].Code != ILCode.Ldc_I4)
return false;
int initialState = (int)expr.Arguments[0].Operand;
if (!(initialState == -2 || initialState == 0))
return false;
ctor = GetMethodDefinition(expr.Operand as MethodReference);
if (ctor == null || ctor.DeclaringType.DeclaringType != context.CurrentType)
return false;
return IsCompilerGeneratorEnumerator(ctor.DeclaringType);
}
public static bool IsCompilerGeneratorEnumerator(TypeDefinition type)
{
if (!(type.DeclaringType != null && type.IsCompilerGenerated()))
return false;
foreach (TypeReference i in type.Interfaces) {
if (i.Namespace == "System.Collections" && i.Name == "IEnumerator")
return true;
}
return false;
}
#endregion
#region Figure out what the 'state' field is (analysis of .ctor())
/// <summary>
/// Looks at the enumerator's ctor and figures out which of the fields holds the state.
/// </summary>
void AnalyzeCtor()
{
ILBlock method = CreateILAst(enumeratorCtor);
foreach (ILNode node in method.Body) {
FieldReference field;
ILExpression instExpr;
ILExpression stExpr;
ILVariable arg;
if (node.Match(ILCode.Stfld, out field, out instExpr, out stExpr) &&
instExpr.MatchThis() &&
stExpr.Match(ILCode.Ldloc, out arg) &&
arg.IsParameter && arg.OriginalParameter.Index == 0)
{
stateField = GetFieldDefinition(field);
}
}
if (stateField == null)
throw new YieldAnalysisFailedException();
}
/// <summary>
/// Creates ILAst for the specified method, optimized up to before the 'YieldReturn' step.
/// </summary>
ILBlock CreateILAst(MethodDefinition method)
{
if (method == null || !method.HasBody)
throw new YieldAnalysisFailedException();
ILBlock ilMethod = new ILBlock();
ILAstBuilder astBuilder = new ILAstBuilder();
ilMethod.Body = astBuilder.Build(method, true);
ILAstOptimizer optimizer = new ILAstOptimizer();
optimizer.Optimize(context, ilMethod, ILAstOptimizationStep.YieldReturn);
return ilMethod;
}
#endregion
#region Figure out what the 'current' field is (analysis of get_Current())
/// <summary>
/// Looks at the enumerator's get_Current method and figures out which of the fields holds the current value.
/// </summary>
void AnalyzeCurrentProperty()
{
MethodDefinition getCurrentMethod = enumeratorType.Methods.FirstOrDefault(
m => m.Name.StartsWith("System.Collections.Generic.IEnumerator", StringComparison.Ordinal)
&& m.Name.EndsWith(".get_Current", StringComparison.Ordinal));
ILBlock method = CreateILAst(getCurrentMethod);
if (method.Body.Count == 1) {
// release builds directly return the current field
ILExpression retExpr;
FieldReference field;
ILExpression ldFromObj;
if (method.Body[0].Match(ILCode.Ret, out retExpr) &&
retExpr.Match(ILCode.Ldfld, out field, out ldFromObj) &&
ldFromObj.MatchThis())
{
currentField = GetFieldDefinition(field);
}
} else if (method.Body.Count == 2) {
ILVariable v, v2;
ILExpression stExpr;
FieldReference field;
ILExpression ldFromObj;
ILExpression retExpr;
if (method.Body[0].Match(ILCode.Stloc, out v, out stExpr) &&
stExpr.Match(ILCode.Ldfld, out field, out ldFromObj) &&
ldFromObj.MatchThis() &&
method.Body[1].Match(ILCode.Ret, out retExpr) &&
retExpr.Match(ILCode.Ldloc, out v2) &&
v == v2)
{
currentField = GetFieldDefinition(field);
}
}
if (currentField == null)
throw new YieldAnalysisFailedException();
}
#endregion
#region Figure out the mapping of IEnumerable fields to IEnumerator fields (analysis of GetEnumerator())
void ResolveIEnumerableIEnumeratorFieldMapping()
{
MethodDefinition getEnumeratorMethod = enumeratorType.Methods.FirstOrDefault(
m => m.Name.StartsWith("System.Collections.Generic.IEnumerable", StringComparison.Ordinal)
&& m.Name.EndsWith(".GetEnumerator", StringComparison.Ordinal));
if (getEnumeratorMethod == null)
return; // no mappings (maybe it's just an IEnumerator implementation?)
ILBlock method = CreateILAst(getEnumeratorMethod);
foreach (ILNode node in method.Body) {
FieldReference stField;
ILExpression stToObj;
ILExpression stExpr;
FieldReference ldField;
ILExpression ldFromObj;
if (node.Match(ILCode.Stfld, out stField, out stToObj, out stExpr) &&
stExpr.Match(ILCode.Ldfld, out ldField, out ldFromObj) &&
ldFromObj.MatchThis())
{
FieldDefinition storedField = GetFieldDefinition(stField);
FieldDefinition loadedField = GetFieldDefinition(ldField);
if (storedField != null && loadedField != null) {
ILVariable mappedParameter;
if (fieldToParameterMap.TryGetValue(loadedField, out mappedParameter))
fieldToParameterMap[storedField] = mappedParameter;
}
}
}
}
#endregion
#region Construction of the exception table (analysis of Dispose())
// We construct the exception table by analyzing the enumerator's Dispose() method.
// Assumption: there are no loops/backward jumps
// We 'run' the code, with "state" being a symbolic variable
// so it can form expressions like "state + x" (when there's a sub instruction)
// For each instruction, we maintain a list of value ranges for state for which the instruction is reachable.
// This is (int.MinValue, int.MaxValue) for the first instruction.
// These ranges are propagated depending on the conditional jumps performed by the code.
Dictionary<MethodDefinition, Interval> finallyMethodToStateInterval;
void ConstructExceptionTable()
{
disposeMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "System.IDisposable.Dispose");
ILBlock ilMethod = CreateILAst(disposeMethod);
finallyMethodToStateInterval = new Dictionary<MethodDefinition, Interval>();
InitStateRanges(ilMethod.Body[0]);
AssignStateRanges(ilMethod.Body, ilMethod.Body.Count, forDispose: true);
// Now look at the finally blocks:
foreach (var tryFinally in ilMethod.GetSelfAndChildrenRecursive<ILTryCatchBlock>()) {
Interval interval = ranges[tryFinally.TryBlock.Body[0]].ToEnclosingInterval();
var finallyBody = tryFinally.FinallyBlock.Body;
if (finallyBody.Count != 2)
throw new YieldAnalysisFailedException();
ILExpression call = finallyBody[0] as ILExpression;
if (call == null || call.Code != ILCode.Call || call.Arguments.Count != 1)
throw new YieldAnalysisFailedException();
if (!call.Arguments[0].MatchThis())
throw new YieldAnalysisFailedException();
if (!finallyBody[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
MethodDefinition mdef = GetMethodDefinition(call.Operand as MethodReference);
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, interval);
}
ranges = null;
}
#endregion
#region Assign StateRanges / Symbolic Execution (used for analysis of Dispose() and MoveNext())
#region struct Interval / class StateRange
struct Interval
{
public readonly int Start, End;
public Interval(int start, int end)
{
Debug.Assert(start <= end || (start == 0 && end == -1));
this.Start = start;
this.End = end;
}
public override string ToString()
{
return string.Format("({0} to {1})", Start, End);
}
}
class StateRange
{
readonly List<Interval> data = new List<Interval>();
public StateRange()
{
}
public StateRange(int start, int end)
{
this.data.Add(new Interval(start, end));
}
public bool Contains(int val)
{
foreach (Interval v in data) {
if (v.Start <= val && val <= v.End)
return true;
}
return false;
}
public void UnionWith(StateRange other)
{
data.AddRange(other.data);
}
/// <summary>
/// Unions this state range with (other intersect (minVal to maxVal))
/// </summary>
public void UnionWith(StateRange other, int minVal, int maxVal)
{
foreach (Interval v in other.data) {
int start = Math.Max(v.Start, minVal);
int end = Math.Min(v.End, maxVal);
if (start <= end)
data.Add(new Interval(start, end));
}
}
/// <summary>
/// Merges overlapping interval ranges.
/// </summary>
public void Simplify()
{
if (data.Count < 2)
return;
data.Sort((a, b) => a.Start.CompareTo(b.Start));
Interval prev = data[0];
int prevIndex = 0;
for (int i = 1; i < data.Count; i++) {
Interval next = data[i];
Debug.Assert(prev.Start <= next.Start);
if (next.Start <= prev.End + 1) { // intervals overlapping or touching
prev = new Interval(prev.Start, Math.Max(prev.End, next.End));
data[prevIndex] = prev;
data[i] = new Interval(0, -1); // mark as deleted
} else {
prev = next;
prevIndex = i;
}
}
data.RemoveAll(i => i.Start > i.End); // remove all entries that were marked as deleted
}
public override string ToString()
{
return string.Join(",", data);
}
public Interval ToEnclosingInterval()
{
if (data.Count == 0)
throw new YieldAnalysisFailedException();
return new Interval(data[0].Start, data[data.Count - 1].End);
}
}
#endregion
DefaultDictionary<ILNode, StateRange> ranges;
ILVariable rangeAnalysisStateVariable;
/// <summary>
/// Initializes the state range logic:
/// Clears 'ranges' and sets 'ranges[entryPoint]' to the full range (int.MinValue to int.MaxValue)
/// </summary>
void InitStateRanges(ILNode entryPoint)
{
ranges = new DefaultDictionary<ILNode, StateRange>(n => new StateRange());
ranges[entryPoint] = new StateRange(int.MinValue, int.MaxValue);
rangeAnalysisStateVariable = null;
}
int AssignStateRanges(List<ILNode> body, int bodyLength, bool forDispose)
{
if (bodyLength == 0)
return 0;
for (int i = 0; i < bodyLength; i++) {
StateRange nodeRange = ranges[body[i]];
nodeRange.Simplify();
ILLabel label = body[i] as ILLabel;
if (label != null) {
ranges[body[i + 1]].UnionWith(nodeRange);
continue;
}
ILTryCatchBlock tryFinally = body[i] as ILTryCatchBlock;
if (tryFinally != null) {
if (!forDispose || tryFinally.CatchBlocks.Count != 0 || tryFinally.FaultBlock != null || tryFinally.FinallyBlock == null)
throw new YieldAnalysisFailedException();
ranges[tryFinally.TryBlock].UnionWith(nodeRange);
if (tryFinally.TryBlock.Body.Count != 0) {
ranges[tryFinally.TryBlock.Body[0]].UnionWith(nodeRange);
AssignStateRanges(tryFinally.TryBlock.Body, tryFinally.TryBlock.Body.Count, forDispose);
}
continue;
}
ILExpression expr = body[i] as ILExpression;
if (expr == null)
throw new YieldAnalysisFailedException();
switch (expr.Code) {
case ILCode.Switch:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type != SymbolicValueType.State)
throw new YieldAnalysisFailedException();
ILLabel[] targetLabels = (ILLabel[])expr.Operand;
for (int j = 0; j < targetLabels.Length; j++) {
int state = j - val.Constant;
ranges[targetLabels[j]].UnionWith(nodeRange, state, state);
}
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, -1 - val.Constant);
nextRange.UnionWith(nodeRange, targetLabels.Length - val.Constant, int.MaxValue);
break;
}
case ILCode.Br:
case ILCode.Leave:
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange);
break;
case ILCode.Brtrue:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.StateEquals) {
ranges[(ILLabel)expr.Operand].UnionWith(nodeRange, val.Constant, val.Constant);
StateRange nextRange = ranges[body[i + 1]];
nextRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
nextRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
} else if (val.Type == SymbolicValueType.StateInEquals) {
ranges[body[i + 1]].UnionWith(nodeRange, val.Constant, val.Constant);
StateRange targetRange = ranges[(ILLabel)expr.Operand];
targetRange.UnionWith(nodeRange, int.MinValue, val.Constant - 1);
targetRange.UnionWith(nodeRange, val.Constant + 1, int.MaxValue);
} else {
throw new YieldAnalysisFailedException();
}
break;
}
case ILCode.Nop:
ranges[body[i + 1]].UnionWith(nodeRange);
break;
case ILCode.Ret:
break;
case ILCode.Stloc:
{
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.State && val.Constant == 0 && rangeAnalysisStateVariable == null)
rangeAnalysisStateVariable = (ILVariable)expr.Operand;
else
throw new YieldAnalysisFailedException();
goto case ILCode.Nop;
}
case ILCode.Call:
// in some cases (e.g. foreach over array) the C# compiler produces a finally method outside of try-finally blocks
if (forDispose) {
MethodDefinition mdef = GetMethodDefinition(expr.Operand as MethodReference);
if (mdef == null || finallyMethodToStateInterval.ContainsKey(mdef))
throw new YieldAnalysisFailedException();
finallyMethodToStateInterval.Add(mdef, nodeRange.ToEnclosingInterval());
} else {
throw new YieldAnalysisFailedException();
}
break;
default:
if (forDispose)
throw new YieldAnalysisFailedException();
else
return i;
}
}
return bodyLength;
}
enum SymbolicValueType
{
/// <summary>
/// int: Constant (result of ldc.i4)
/// </summary>
IntegerConstant,
/// <summary>
/// int: State + Constant
/// </summary>
State,
/// <summary>
/// This pointer (result of ldarg.0)
/// </summary>
This,
/// <summary>
/// bool: State == Constant
/// </summary>
StateEquals,
/// <summary>
/// bool: State != Constant
/// </summary>
StateInEquals
}
struct SymbolicValue
{
public readonly int Constant;
public readonly SymbolicValueType Type;
public SymbolicValue(SymbolicValueType type, int constant = 0)
{
this.Type = type;
this.Constant = constant;
}
public override string ToString()
{
return string.Format("[SymbolicValue {0}: {1}]", this.Type, this.Constant);
}
}
SymbolicValue Eval(ILExpression expr)
{
SymbolicValue left, right;
switch (expr.Code) {
case ILCode.Sub:
left = Eval(expr.Arguments[0]);
right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State && left.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException();
if (right.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException();
return new SymbolicValue(left.Type, unchecked ( left.Constant - right.Constant ));
case ILCode.Ldfld:
if (Eval(expr.Arguments[0]).Type != SymbolicValueType.This)
throw new YieldAnalysisFailedException();
if (GetFieldDefinition(expr.Operand as FieldReference) != stateField)
throw new YieldAnalysisFailedException();
return new SymbolicValue(SymbolicValueType.State);
case ILCode.Ldloc:
ILVariable loadedVariable = (ILVariable)expr.Operand;
if (loadedVariable == rangeAnalysisStateVariable)
return new SymbolicValue(SymbolicValueType.State);
else if (loadedVariable.IsParameter && loadedVariable.OriginalParameter.Index < 0)
return new SymbolicValue(SymbolicValueType.This);
else
throw new YieldAnalysisFailedException();
case ILCode.Ldc_I4:
return new SymbolicValue(SymbolicValueType.IntegerConstant, (int)expr.Operand);
case ILCode.Ceq:
left = Eval(expr.Arguments[0]);
right = Eval(expr.Arguments[1]);
if (left.Type != SymbolicValueType.State || right.Type != SymbolicValueType.IntegerConstant)
throw new YieldAnalysisFailedException();
// bool: (state + left.Constant == right.Constant)
// bool: (state == right.Constant - left.Constant)
return new SymbolicValue(SymbolicValueType.StateEquals, unchecked ( right.Constant - left.Constant ));
case ILCode.LogicNot:
SymbolicValue val = Eval(expr.Arguments[0]);
if (val.Type == SymbolicValueType.StateEquals)
return new SymbolicValue(SymbolicValueType.StateInEquals, val.Constant);
else if (val.Type == SymbolicValueType.StateInEquals)
return new SymbolicValue(SymbolicValueType.StateEquals, val.Constant);
else
throw new YieldAnalysisFailedException();
default:
throw new YieldAnalysisFailedException();
}
}
#endregion
#region Analysis of MoveNext()
ILVariable returnVariable;
ILLabel returnLabel;
ILLabel returnFalseLabel;
void AnalyzeMoveNext()
{
MethodDefinition moveNextMethod = enumeratorType.Methods.FirstOrDefault(m => m.Name == "MoveNext");
ILBlock ilMethod = CreateILAst(moveNextMethod);
if (ilMethod.Body.Count == 0)
throw new YieldAnalysisFailedException();
ILExpression lastReturnArg;
if (!ilMethod.Body.Last().Match(ILCode.Ret, out lastReturnArg))
throw new YieldAnalysisFailedException();
// There are two possibilities:
if (lastReturnArg.Code == ILCode.Ldloc) {
// a) the compiler uses a variable for returns (in debug builds, or when there are try-finally blocks)
returnVariable = (ILVariable)lastReturnArg.Operand;
returnLabel = ilMethod.Body.ElementAtOrDefault(ilMethod.Body.Count - 2) as ILLabel;
if (returnLabel == null)
throw new YieldAnalysisFailedException();
} else {
// b) the compiler directly returns constants
returnVariable = null;
returnLabel = null;
// In this case, the last return must return false.
if (lastReturnArg.Code != ILCode.Ldc_I4 || (int)lastReturnArg.Operand != 0)
throw new YieldAnalysisFailedException();
}
ILTryCatchBlock tryFaultBlock = ilMethod.Body[0] as ILTryCatchBlock;
List<ILNode> body;
int bodyLength;
if (tryFaultBlock != null) {
// there are try-finally blocks
if (returnVariable == null) // in this case, we must use a return variable
throw new YieldAnalysisFailedException();
// must be a try-fault block:
if (tryFaultBlock.CatchBlocks.Count != 0 || tryFaultBlock.FinallyBlock != null || tryFaultBlock.FaultBlock == null)
throw new YieldAnalysisFailedException();
ILBlock faultBlock = tryFaultBlock.FaultBlock;
// Ensure the fault block contains the call to Dispose().
if (faultBlock.Body.Count != 2)
throw new YieldAnalysisFailedException();
MethodReference disposeMethodRef;
ILExpression disposeArg;
if (!faultBlock.Body[0].Match(ILCode.Call, out disposeMethodRef, out disposeArg))
throw new YieldAnalysisFailedException();
if (GetMethodDefinition(disposeMethodRef) != disposeMethod || !disposeArg.MatchThis())
throw new YieldAnalysisFailedException();
if (!faultBlock.Body[1].Match(ILCode.Endfinally))
throw new YieldAnalysisFailedException();
body = tryFaultBlock.TryBlock.Body;
bodyLength = body.Count;
} else {
// no try-finally blocks
body = ilMethod.Body;
if (returnVariable == null)
bodyLength = body.Count - 1; // all except for the return statement
else
bodyLength = body.Count - 2; // all except for the return label and statement
}
// Now verify that the last instruction in the body is 'ret(false)'
if (returnVariable != null) {
// If we don't have a return variable, we already verified that above.
// If we do have one, check for 'stloc(returnVariable, ldc.i4(0))'
// Maybe might be a jump to the return label after the stloc:
ILExpression leave = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
if (leave != null && (leave.Code == ILCode.Br || leave.Code == ILCode.Leave) && leave.Operand == returnLabel)
bodyLength--;
ILExpression store0 = body.ElementAtOrDefault(bodyLength - 1) as ILExpression;
if (store0 == null || store0.Code != ILCode.Stloc || store0.Operand != returnVariable)
throw new YieldAnalysisFailedException();
if (store0.Arguments[0].Code != ILCode.Ldc_I4 || (int)store0.Arguments[0].Operand != 0)
throw new YieldAnalysisFailedException();
bodyLength--; // don't conside the stloc instruction to be part of the body
}
// verify that the last element in the body is a label pointing to the 'ret(false)'
returnFalseLabel = body.ElementAtOrDefault(bodyLength - 1) as ILLabel;
if (returnFalseLabel == null)
throw new YieldAnalysisFailedException();
InitStateRanges(body[0]);
int pos = AssignStateRanges(body, bodyLength, forDispose: false);
if (pos > 0 && body[pos - 1] is ILLabel) {
pos--;
} else {
// ensure that the first element at body[pos] is a label:
ILLabel newLabel = new ILLabel();
newLabel.Name = "YieldReturnEntryPoint";
ranges[newLabel] = ranges[body[pos]]; // give the label the range of the instruction at body[pos]
body.Insert(pos, newLabel);
bodyLength++;
}
List<KeyValuePair<ILLabel, StateRange>> labels = new List<KeyValuePair<ILLabel, StateRange>>();
for (int i = pos; i < bodyLength; i++) {
ILLabel label = body[i] as ILLabel;
if (label != null) {
labels.Add(new KeyValuePair<ILLabel, StateRange>(label, ranges[label]));
}
}
ConvertBody(body, pos, bodyLength, labels);
}
#endregion
#region ConvertBody
struct SetState
{
public readonly int NewBodyPos;
public readonly int NewState;
public SetState(int newBodyPos, int newState)
{
this.NewBodyPos = newBodyPos;
this.NewState = newState;
}
}
void ConvertBody(List<ILNode> body, int startPos, int bodyLength, List<KeyValuePair<ILLabel, StateRange>> labels)
{
newBody = new List<ILNode>();
newBody.Add(MakeGoTo(labels, 0));
List<SetState> stateChanges = new List<SetState>();
int currentState = -1;
// Copy all instructions from the old body to newBody.
for (int pos = startPos; pos < bodyLength; pos++) {
ILExpression expr = body[pos] as ILExpression;
if (expr != null && expr.Code == ILCode.Stfld && expr.Arguments[0].MatchThis()) {
// Handle stores to 'state' or 'current'
if (GetFieldDefinition(expr.Operand as FieldReference) == stateField) {
if (expr.Arguments[1].Code != ILCode.Ldc_I4)
throw new YieldAnalysisFailedException();
currentState = (int)expr.Arguments[1].Operand;
stateChanges.Add(new SetState(newBody.Count, currentState));
} else if (GetFieldDefinition(expr.Operand as FieldReference) == currentField) {
newBody.Add(new ILExpression(ILCode.YieldReturn, null, expr.Arguments[1]));
} else {
newBody.Add(body[pos]);
}
} else if (returnVariable != null && expr != null && expr.Code == ILCode.Stloc && expr.Operand == returnVariable) {
// handle store+branch to the returnVariable
ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression;
if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnLabel || expr.Arguments[0].Code != ILCode.Ldc_I4)
throw new YieldAnalysisFailedException();
int val = (int)expr.Arguments[0].Operand;
if (val == 0) {
newBody.Add(MakeGoTo(returnFalseLabel));
} else if (val == 1) {
newBody.Add(MakeGoTo(labels, currentState));
} else {
throw new YieldAnalysisFailedException();
}
} else if (expr != null && expr.Code == ILCode.Ret) {
if (expr.Arguments.Count != 1 || expr.Arguments[0].Code != ILCode.Ldc_I4)
throw new YieldAnalysisFailedException();
// handle direct return (e.g. in release builds)
int val = (int)expr.Arguments[0].Operand;
if (val == 0) {
newBody.Add(MakeGoTo(returnFalseLabel));
} else if (val == 1) {
newBody.Add(MakeGoTo(labels, currentState));
} else {
throw new YieldAnalysisFailedException();
}
} else if (expr != null && expr.Code == ILCode.Call && expr.Arguments.Count == 1 && expr.Arguments[0].MatchThis()) {
MethodDefinition method = GetMethodDefinition(expr.Operand as MethodReference);
if (method == null)
throw new YieldAnalysisFailedException();
Interval interval;
if (method == disposeMethod) {
// Explicit call to dispose is used for "yield break;" within the method.
ILExpression br = body.ElementAtOrDefault(++pos) as ILExpression;
if (br == null || !(br.Code == ILCode.Br || br.Code == ILCode.Leave) || br.Operand != returnFalseLabel)
throw new YieldAnalysisFailedException();
newBody.Add(MakeGoTo(returnFalseLabel));
} else if (finallyMethodToStateInterval.TryGetValue(method, out interval)) {
// Call to Finally-method
int index = stateChanges.FindIndex(ss => ss.NewState >= interval.Start && ss.NewState <= interval.End);
if (index < 0)
throw new YieldAnalysisFailedException();
ILLabel label = new ILLabel();
label.Name = "JumpOutOfTryFinally" + interval.Start + "_" + interval.End;
newBody.Add(new ILExpression(ILCode.Leave, label));
SetState stateChange = stateChanges[index];
// Move all instructions from stateChange.Pos to newBody.Count into a try-block
stateChanges.RemoveRange(index, stateChanges.Count - index); // remove all state changes up to the one we found
ILTryCatchBlock tryFinally = new ILTryCatchBlock();
tryFinally.TryBlock = new ILBlock(newBody.GetRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos));
newBody.RemoveRange(stateChange.NewBodyPos, newBody.Count - stateChange.NewBodyPos); // remove all nodes that we just moved into the try block
tryFinally.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>();
tryFinally.FinallyBlock = ConvertFinallyBlock(method);
newBody.Add(tryFinally);
newBody.Add(label);
}
} else {
newBody.Add(body[pos]);
}
}
newBody.Add(new ILExpression(ILCode.YieldBreak, null));
}
ILExpression MakeGoTo(ILLabel targetLabel)
{
if (targetLabel == returnFalseLabel)
return new ILExpression(ILCode.YieldBreak, null);
else
return new ILExpression(ILCode.Br, targetLabel);
}
ILExpression MakeGoTo(List<KeyValuePair<ILLabel, StateRange>> labels, int state)
{
foreach (var pair in labels) {
if (pair.Value.Contains(state))
return MakeGoTo(pair.Key);
}
throw new YieldAnalysisFailedException();
}
ILBlock ConvertFinallyBlock(MethodDefinition finallyMethod)
{
ILBlock block = CreateILAst(finallyMethod);
// Get rid of assignment to state
FieldReference stfld;
List<ILExpression> args;
if (block.Body.Count > 0 && block.Body[0].Match(ILCode.Stfld, out stfld, out args)) {
if (GetFieldDefinition(stfld) == stateField && args[0].MatchThis())
block.Body.RemoveAt(0);
}
// Convert ret to endfinally
foreach (ILExpression expr in block.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Ret)
expr.Code = ILCode.Endfinally;
}
return block;
}
#endregion
#region TranslateFieldsToLocalAccess
void TranslateFieldsToLocalAccess()
{
var fieldToLocalMap = new DefaultDictionary<FieldDefinition, ILVariable>(f => new ILVariable { Name = f.Name, Type = f.FieldType });
foreach (ILNode node in newBody) {
foreach (ILExpression expr in node.GetSelfAndChildrenRecursive<ILExpression>()) {
FieldDefinition field = GetFieldDefinition(expr.Operand as FieldReference);
if (field != null) {
switch (expr.Code) {
case ILCode.Ldfld:
if (expr.Arguments[0].MatchThis()) {
expr.Code = ILCode.Ldloc;
if (fieldToParameterMap.ContainsKey(field)) {
expr.Operand = fieldToParameterMap[field];
} else {
expr.Operand = fieldToLocalMap[field];
}
expr.Arguments.Clear();
}
break;
case ILCode.Stfld:
if (expr.Arguments[0].MatchThis()) {
expr.Code = ILCode.Stloc;
if (fieldToParameterMap.ContainsKey(field)) {
expr.Operand = fieldToParameterMap[field];
} else {
expr.Operand = fieldToLocalMap[field];
}
expr.Arguments.RemoveAt(0);
}
break;
case ILCode.Ldflda:
if (expr.Arguments[0].MatchThis()) {
expr.Code = ILCode.Ldloca;
if (fieldToParameterMap.ContainsKey(field)) {
expr.Operand = fieldToParameterMap[field];
} else {
expr.Operand = fieldToLocalMap[field];
}
expr.Arguments.Clear();
}
break;
}
}
}
}
}
#endregion
}
}

59
src/Libraries/ICSharpCode.Decompiler/ITextOutput.cs

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
namespace ICSharpCode.Decompiler
{
public interface ITextOutput
{
int CurrentLine { get; }
int CurrentColumn { get; }
void Indent();
void Unindent();
void Write(char ch);
void Write(string text);
void WriteLine();
void WriteDefinition(string text, object definition);
void WriteReference(string text, object reference);
void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false);
void MarkFoldEnd();
}
public static class TextOutputExtensions
{
public static void Write(this ITextOutput output, string format, params object[] args)
{
output.Write(string.Format(format, args));
}
public static void WriteLine(this ITextOutput output, string text)
{
output.Write(text);
output.WriteLine();
}
public static void WriteLine(this ITextOutput output, string format, params object[] args)
{
output.WriteLine(string.Format(format, args));
}
}
}

120
src/Libraries/ICSharpCode.Decompiler/PlainTextOutput.cs

@ -0,0 +1,120 @@ @@ -0,0 +1,120 @@
// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
// software and associated documentation files (the "Software"), to deal in the Software
// without restriction, including without limitation the rights to use, copy, modify, merge,
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
// to whom the Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or
// substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
using System;
using System.IO;
namespace ICSharpCode.Decompiler
{
public sealed class PlainTextOutput : ITextOutput
{
const int TAB_SIZE = 4;
readonly TextWriter writer;
int indent;
bool needsIndent;
int lineNumber = 1;
int columnNumber = 1;
public PlainTextOutput(TextWriter writer)
{
if (writer == null)
throw new ArgumentNullException("writer");
this.writer = writer;
}
public PlainTextOutput()
{
this.writer = new StringWriter();
}
public int CurrentLine {
get { return lineNumber; }
}
public int CurrentColumn {
get { return columnNumber; }
}
public override string ToString()
{
return writer.ToString();
}
public void Indent()
{
indent++;
}
public void Unindent()
{
indent--;
}
void WriteIndent()
{
if (needsIndent) {
needsIndent = false;
for (int i = 0; i < indent; i++) {
writer.Write('\t');
columnNumber += TAB_SIZE - 1;
}
}
}
public void Write(char ch)
{
WriteIndent();
writer.Write(ch);
columnNumber++;
}
public void Write(string text)
{
WriteIndent();
writer.Write(text);
columnNumber += text.Length;
}
public void WriteLine()
{
lineNumber++;
writer.WriteLine();
needsIndent = true;
columnNumber = TAB_SIZE * indent;
}
public void WriteDefinition(string text, object definition)
{
Write(text);
}
public void WriteReference(string text, object reference)
{
Write(text);
}
void ITextOutput.MarkFoldStart(string collapsedText, bool defaultCollapsed)
{
}
void ITextOutput.MarkFoldEnd()
{
}
}
}

27
src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
#region Using directives
using System;
using System.Resources;
using System.Reflection;
using System.Runtime.InteropServices;
#endregion
[assembly: AssemblyTitle("ICSharpCode.Decompiler")]
[assembly: AssemblyDescription("IL decompiler engine")]
[assembly: AssemblyCompany("ic#code")]
[assembly: AssemblyProduct("ILSpy")]
[assembly: AssemblyCopyright("Copyright 2011 AlphaSierraPapa for the SharpDevelop Team")]
[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)]
[assembly: AssemblyVersion("2.0.0.1221")]
[assembly: AssemblyInformationalVersion("2.0.0.1221-26633dc2")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly",
Justification = "AssemblyInformationalVersion does not need to be a parsable version")]

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save