Browse Source

Debug & evaluate third party code.

pull/16/head
Eusebiu Marcu 14 years ago
parent
commit
4f22877fbf
  1. 8
      src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
  2. 184
      src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
  3. 2
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs
  4. 22
      src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
  5. 13
      src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs
  6. 8
      src/AddIns/Debugger/Debugger.Core/Interop/CorDebug.cs
  7. 11
      src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
  8. 48
      src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
  9. 69
      src/AddIns/Debugger/Debugger.Core/Module.cs
  10. 41
      src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs
  11. 7
      src/AddIns/Debugger/Debugger.Core/Options.cs
  12. 31
      src/AddIns/Debugger/Debugger.Core/Process.cs
  13. 57
      src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs
  14. 18
      src/AddIns/Debugger/Debugger.Core/StackFrame.cs
  15. 11
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
  16. 2
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarManager.cs
  17. 4
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs
  18. 8
      src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
  19. 4
      src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj
  20. 26
      src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs
  21. 222
      src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs
  22. 76
      src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs
  23. 454
      src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
  24. 192
      src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  25. 37
      src/Libraries/ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs
  26. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/CommentStatement.cs
  27. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
  28. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
  29. 58
      src/Libraries/ICSharpCode.Decompiler/Ast/NameVariables.cs
  30. 66
      src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
  31. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
  32. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs
  33. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs
  34. 71
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
  35. 58
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs
  36. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs
  37. 66
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  38. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceExtensionMethods.cs
  39. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceQueryExpressions.cs
  40. 19
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUnsafeModifier.cs
  41. 242
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/IntroduceUsingDeclarations.cs
  42. 253
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs
  43. 20
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/PushNegation.cs
  44. 29
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ReplaceMethodCallsWithOperators.cs
  45. 20
      src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/TransformationPipeline.cs
  46. 329
      src/Libraries/ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
  47. 8
      src/Libraries/ICSharpCode.Decompiler/CecilExtensions.cs
  48. 346
      src/Libraries/ICSharpCode.Decompiler/CodeMappings.cs
  49. 19
      src/Libraries/ICSharpCode.Decompiler/DecompilerException.cs
  50. 64
      src/Libraries/ICSharpCode.Decompiler/DecompilerSettings.cs
  51. 245
      src/Libraries/ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  52. 4
      src/Libraries/ICSharpCode.Decompiler/Disassembler/ILStructure.cs
  53. 85
      src/Libraries/ICSharpCode.Decompiler/Disassembler/MethodBodyDisassembler.cs
  54. 718
      src/Libraries/ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  55. 4
      src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  56. 23
      src/Libraries/ICSharpCode.Decompiler/ILAst/DefaultDictionary.cs
  57. 49
      src/Libraries/ICSharpCode.Decompiler/ILAst/GotoRemoval.cs
  58. 73
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs
  59. 166
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  60. 50
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
  61. 42
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs
  62. 176
      src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs
  63. 458
      src/Libraries/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  64. 23
      src/Libraries/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
  65. 19
      src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
  66. 123
      src/Libraries/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
  67. 120
      src/Libraries/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
  68. 222
      src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  69. 21
      src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
  70. 3
      src/Libraries/ICSharpCode.Decompiler/ITextOutput.cs
  71. 17
      src/Libraries/ICSharpCode.Decompiler/PlainTextOutput.cs
  72. 2
      src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs
  73. 27
      src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs
  74. 68
      src/Libraries/ICSharpCode.Decompiler/ReferenceResolvingException.cs
  75. 59
      src/Libraries/ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il
  76. 52
      src/Libraries/ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs
  77. 66
      src/Libraries/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs
  78. 132
      src/Libraries/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs
  79. 41
      src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs
  80. 30
      src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs
  81. 6
      src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs
  82. 480
      src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs
  83. 64
      src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs
  84. 90
      src/Libraries/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs
  85. 93
      src/Libraries/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs
  86. 189
      src/Libraries/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs
  87. 128
      src/Libraries/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs
  88. 105
      src/Libraries/ICSharpCode.Decompiler/Tests/Generics.cs
  89. 110
      src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs
  90. 38
      src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs
  91. 113
      src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
  92. 254
      src/Libraries/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs
  93. 869
      src/Libraries/ICSharpCode.Decompiler/Tests/InitializerTests.cs
  94. 74
      src/Libraries/ICSharpCode.Decompiler/Tests/Loops.cs
  95. 58
      src/Libraries/ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs
  96. 87
      src/Libraries/ICSharpCode.Decompiler/Tests/PInvoke.cs
  97. 81
      src/Libraries/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs
  98. 164
      src/Libraries/ICSharpCode.Decompiler/Tests/QueryExpressions.cs
  99. BIN
      src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe
  100. 132
      src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.il
  101. Some files were not shown because too many files have changed in this diff Show More

8
src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj

@ -360,6 +360,14 @@
<Name>ICSharpCode.AvalonEdit</Name> <Name>ICSharpCode.AvalonEdit</Name>
<Private>False</Private> <Private>False</Private>
</ProjectReference> </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>
</ProjectReference>
<ProjectReference Include="..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj"> <ProjectReference Include="..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">
<Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project> <Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project>
<Name>ICSharpCode.SharpDevelop</Name> <Name>ICSharpCode.SharpDevelop</Name>

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

@ -2,6 +2,7 @@
// This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt) // This code is distributed under the BSD license (for details please see \src\AddIns\Debugger\Debugger.AddIn\license.txt)
using System; using System;
using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Drawing; using System.Drawing;
using System.IO; using System.IO;
@ -15,8 +16,12 @@ using Debugger;
using Debugger.AddIn.Tooltips; using Debugger.AddIn.Tooltips;
using Debugger.AddIn.TreeModel; using Debugger.AddIn.TreeModel;
using Debugger.Interop.CorPublish; using Debugger.Interop.CorPublish;
using Debugger.MetaData;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Core.Services;
using ICSharpCode.Core.WinForms; using ICSharpCode.Core.WinForms;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory; using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast; using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors; using ICSharpCode.NRefactory.Visitors;
@ -25,6 +30,7 @@ using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Gui.OptionPanels; using ICSharpCode.SharpDevelop.Gui.OptionPanels;
using ICSharpCode.SharpDevelop.Project; using ICSharpCode.SharpDevelop.Project;
using Mono.Cecil;
using Process = Debugger.Process; using Process = Debugger.Process;
namespace ICSharpCode.SharpDevelop.Services namespace ICSharpCode.SharpDevelop.Services
@ -81,6 +87,18 @@ namespace ICSharpCode.SharpDevelop.Services
set; set;
} }
public bool IsInExternalCode {
get {
if (debuggedProcess == null)
return true;
if(debuggedProcess.SelectedThread == null)
return true;
return debuggedProcess.SelectedStackFrame.ToString() != debuggedProcess.SelectedThread.MostRecentStackFrame.ToString();
}
}
protected virtual void OnProcessSelected(ProcessEventArgs e) protected virtual void OnProcessSelected(ProcessEventArgs e)
{ {
if (ProcessSelected != null) { if (ProcessSelected != null) {
@ -210,6 +228,8 @@ namespace ICSharpCode.SharpDevelop.Services
DebugStarting(this, EventArgs.Empty); DebugStarting(this, EventArgs.Empty);
try { try {
// set the JIT flag for evaluating optimized code
Process.DebugMode = DebugModeFlag.Debug;
Process process = debugger.Start(processStartInfo.FileName, Process process = debugger.Start(processStartInfo.FileName,
processStartInfo.WorkingDirectory, processStartInfo.WorkingDirectory,
processStartInfo.Arguments); processStartInfo.Arguments);
@ -271,6 +291,8 @@ namespace ICSharpCode.SharpDevelop.Services
DebugStarting(this, EventArgs.Empty); DebugStarting(this, EventArgs.Empty);
try { try {
// set the JIT flag for evaluating optimized code
Process.DebugMode = DebugModeFlag.Debug;
Process process = debugger.Attach(existingProcess); Process process = debugger.Attach(existingProcess);
attached = true; attached = true;
SelectProcess(process); SelectProcess(process);
@ -444,16 +466,56 @@ namespace ICSharpCode.SharpDevelop.Services
// Stepping: // Stepping:
SourceCodeMapping GetCurrentCodeMapping(out bool isMatch)
{
DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation;
isMatch = false;
if (debugInformation == null)
return null;
var frame = debuggedProcess.SelectedThread.MostRecentStackFrame;
int token = frame.MethodInfo.MetadataToken;
// get the mapped instruction from the current line marker or the next one
if (!debugInformation.CodeMappings.ContainsKey(token))
return null;
var mappings = (List<MemberMapping>)debugInformation.CodeMappings[token];
return mappings.GetInstructionByTokenAndOffset(token, 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() public void StepInto()
{ {
if (!IsDebugging) { if (!IsDebugging) {
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepInto}"); MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepInto}");
return; 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}"); MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}");
} else { } else {
debuggedProcess.SelectedStackFrame.AsyncStepInto(); if (IsInExternalCode) {
// get frame info from external code mappings
frame = GetStackFrame();
}
frame.AsyncStepInto();
} }
} }
@ -463,10 +525,17 @@ namespace ICSharpCode.SharpDevelop.Services
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOver}"); MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOver}");
return; 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}"); MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepOver}");
} else { } else {
debuggedProcess.SelectedStackFrame.AsyncStepOver(); if (IsInExternalCode) {
// get frame info from external code mappings
frame = GetStackFrame();
}
frame.AsyncStepOver();
} }
} }
@ -476,10 +545,17 @@ namespace ICSharpCode.SharpDevelop.Services
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOut}"); MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOut}");
return; 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 { } else {
debuggedProcess.SelectedStackFrame.AsyncStepOut(); if (IsInExternalCode) {
// get frame info from external code mappings
frame = GetStackFrame();
}
frame.AsyncStepOut();
} }
} }
@ -505,7 +581,17 @@ namespace ICSharpCode.SharpDevelop.Services
if (!CanEvaluate) { if (!CanEvaluate) {
return null; return null;
} }
return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, debuggedProcess.SelectedStackFrame);
Debugger.StackFrame stackFrame = debuggedProcess.SelectedThread.MostRecentStackFrame;
try {
object data = GetLocalVariableIndex(stackFrame, variableName);
// evaluate expression
return ExpressionEvaluator.Evaluate(variableName, SupportedLanguage.CSharp, stackFrame, data);
} catch {
throw;
}
} }
/// <summary> /// <summary>
@ -550,7 +636,8 @@ namespace ICSharpCode.SharpDevelop.Services
bool CanEvaluate bool CanEvaluate
{ {
get { get {
return debuggedProcess != null && !debuggedProcess.IsRunning && debuggedProcess.SelectedStackFrame != null; return debuggedProcess != null && !debuggedProcess.IsRunning &&
(debuggedProcess.SelectedStackFrame != null || debuggedProcess.SelectedThread.MostRecentStackFrame != null);
} }
} }
@ -566,7 +653,7 @@ namespace ICSharpCode.SharpDevelop.Services
var image = ExpressionNode.GetImageForLocalVariable(out imageName); var image = ExpressionNode.GetImageForLocalVariable(out imageName);
ExpressionNode expressionNode = new ExpressionNode(image, variableName, tooltipExpression); ExpressionNode expressionNode = new ExpressionNode(image, variableName, tooltipExpression);
expressionNode.ImageName = imageName; expressionNode.ImageName = imageName;
return new DebuggerTooltipControl(logicalPosition, expressionNode); return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = !IsInExternalCode };
} catch (GetValueException) { } catch (GetValueException) {
return null; return null;
} }
@ -885,15 +972,64 @@ namespace ICSharpCode.SharpDevelop.Services
public void JumpToCurrentLine() public void JumpToCurrentLine()
{ {
if (debuggedProcess == null || debuggedProcess.SelectedThread == null)
return;
WorkbenchSingleton.MainWindow.Activate(); WorkbenchSingleton.MainWindow.Activate();
DebuggerService.RemoveCurrentLineMarker(); DebuggerService.RemoveCurrentLineMarker();
if (debuggedProcess != null) {
if (!IsInExternalCode) {
SourcecodeSegment nextStatement = debuggedProcess.NextStatement; SourcecodeSegment nextStatement = debuggedProcess.NextStatement;
if (nextStatement != null) { if (nextStatement != null) {
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn); DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
} }
} else {
DecompileInformation externalData = (DecompileInformation)DebuggerService.ExternalDebugInformation;
// use most recent stack frame because we don't have the symbols
var frame = debuggedProcess.SelectedThread.MostRecentStackFrame;
if (frame == null)
return;
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 title = "[" + debugType.Name + "]";
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() StopAttachedProcessDialogResult ShowStopAttachedProcessDialog()
{ {
@ -912,5 +1048,31 @@ namespace ICSharpCode.SharpDevelop.Services
.Where(p => e.Item.Name.IndexOf(p.Name) >= 0) .Where(p => e.Item.Name.IndexOf(p.Name) >= 0)
.ForEach(p => e.Item.LoadSymbolsFromDisk(new []{ Path.GetDirectoryName(p.OutputAssemblyFullPath) })); .ForEach(p => e.Item.LoadSymbolsFromDisk(new []{ Path.GetDirectoryName(p.OutputAssemblyFullPath) }));
} }
public static object GetLocalVariableIndex(Debugger.StackFrame stackFrame, string variableName)
{
// get the target name
int token = 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;
DecompileInformation externalData = (DecompileInformation)DebuggerService.ExternalDebugInformation;
if (externalData == null)
return null;
if (externalData.LocalVariables.TryGetValue(token, out list)) {
var variable = list.FirstOrDefault(v => v.Name == targetName);
if (variable != null && variable.OriginalVariable != null) {
data = new[] { variable.OriginalVariable.Index };
}
}
return data;
}
} }
} }

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

@ -23,7 +23,7 @@ namespace Debugger.AddIn.Tooltips
public DebuggerPopup(DebuggerTooltipControl parentControl, Location logicalPosition, bool showPins = true) public DebuggerPopup(DebuggerTooltipControl parentControl, Location logicalPosition, bool showPins = true)
{ {
this.contentControl = new DebuggerTooltipControl(parentControl, logicalPosition, showPins); this.contentControl = new DebuggerTooltipControl(parentControl, logicalPosition) { ShowPins = showPins };
this.contentControl.containingPopup = this; this.contentControl.containingPopup = this;
this.Child = this.contentControl; this.Child = this.contentControl;
this.IsLeaf = false; this.IsLeaf = false;

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

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

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

@ -5,13 +5,17 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Globalization; using System.Globalization;
using System.Linq;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Windows.Forms; using System.Windows.Forms;
using Debugger.AddIn.Visualizers; using Debugger.AddIn.Visualizers;
using Debugger.MetaData; using Debugger.MetaData;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.Ast; using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging; using ICSharpCode.SharpDevelop.Debugging;
@ -154,6 +158,15 @@ namespace Debugger.AddIn.TreeModel
Value val; Value val;
try { try {
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); val = expression.Evaluate(WindowsDebugger.DebuggedProcess);
} catch (GetValueException e) { } catch (GetValueException e) {
error = e; error = e;

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

@ -63,6 +63,14 @@ namespace Debugger.Interop.CorDebug
CHAIN_THREAD_START = 0x40 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)] [ComImport, TypeLibType((short) 2), Guid("6FEF44D0-39E7-4C77-BE8E-C9F8CF988630"), ClassInterface((short) 0)]
public class CorDebugClass : ICorDebug, CorDebug public class CorDebugClass : ICorDebug, CorDebug
{ {

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

@ -320,6 +320,17 @@ namespace Debugger
EnterCallback(PausedReason.Other, "CreateProcess", pProcess); EnterCallback(PausedReason.Other, "CreateProcess", pProcess);
// Process is added in NDebugger.Start // 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(); ExitCallback();
} }

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

@ -68,7 +68,7 @@ namespace Debugger.MetaData
sb.Append(this.ReturnType.Name); sb.Append(this.ReturnType.Name);
sb.Append(" "); sb.Append(" ");
} else { } else {
sb.Append("void "); sb.Append("System.Void ");
} }
sb.Append(this.DeclaringType.FullName); sb.Append(this.DeclaringType.FullName);
@ -80,7 +80,7 @@ namespace Debugger.MetaData
if (!first) if (!first)
sb.Append(", "); sb.Append(", ");
first = false; first = false;
sb.Append(p.ParameterType.Name); sb.Append(p.ParameterType.FullName);
sb.Append(" "); sb.Append(" ");
sb.Append(p.Name); sb.Append(p.Name);
} }
@ -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/> /// <inheritdoc/>
public override string Name { public override string Name {
get { return methodProps.Name; } get { return methodProps.Name; }
@ -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) public DebugLocalVariableInfo GetLocalVariable(int offset, string name)
{ {
foreach(DebugLocalVariableInfo loc in GetLocalVariables(offset)) { foreach(DebugLocalVariableInfo loc in GetLocalVariables(offset)) {

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

@ -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> /// <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> /// <remarks> Generic types can not be returned, because we do not know how to instanciate them </remarks>
public List<DebugType> GetDefinedTypes() public List<DebugType> GetDefinedTypes()
@ -183,6 +199,8 @@ namespace Debugger
name = System.IO.Path.GetFileName(FullPath); name = System.IO.Path.GetFileName(FullPath);
} }
SetJITCompilerFlags();
LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths); LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths);
ResetJustMyCodeStatus(); ResetJustMyCodeStatus();
} }
@ -282,6 +300,34 @@ namespace Debugger
} }
} }
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 /// <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> /// set to not-user-code as encountered acording to stepping options </summary>
public void ResetJustMyCodeStatus() public void ResetJustMyCodeStatus()
@ -319,6 +365,29 @@ namespace Debugger
{ {
return string.Format("{0}", this.Name); 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] [Serializable]

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

@ -71,29 +71,33 @@ namespace ICSharpCode.NRefactory.Visitors
/// <summary> Evaluate given expression. If you have expression tree already, use overloads of this method.</summary> /// <summary> Evaluate given expression. If you have expression tree already, use overloads of this method.</summary>
/// <returns> Returned value or null for statements </returns> /// <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) public static Value Evaluate(INode code, Process context)
{ {
if (context.SelectedStackFrame != null) { StackFrame stackFrame;
return Evaluate(code, context.SelectedStackFrame); if (context.SelectedStackFrame == null && context.SelectedThread.MostRecentStackFrame == null)
} else if (context.SelectedThread.MostRecentStackFrame != null ) {
return Evaluate(code, context.SelectedThread.MostRecentStackFrame);
} else {
// This can happen when needed 'dll' is missing. This causes an exception dialog to be shown even before the applicaiton starts // 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"); throw new GetValueException("Can not evaluate because the process has no managed stack frames");
if (context.SelectedStackFrame.ToString() == context.SelectedThread.MostRecentStackFrame.ToString()) {
stackFrame = context.SelectedStackFrame;
} 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 == null) throw new ArgumentNullException("context");
if (context.IsInvalid) throw new DebuggerException("The context is no longer valid"); 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) if (val == null)
return null; return null;
return val.Value; return val.Value;
@ -164,7 +168,7 @@ namespace ICSharpCode.NRefactory.Visitors
return Evaluate(expression, true); 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 // Try to get the value from cache
// (the cache is cleared when the process is resumed) // (the cache is cleared when the process is resumed)
@ -177,7 +181,7 @@ namespace ICSharpCode.NRefactory.Visitors
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch(); System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start(); watch.Start();
try { try {
val = (TypedValue)expression.AcceptVisitor(this, null); val = (TypedValue)expression.AcceptVisitor(this, data);
if (val != null && permRef) if (val != null && permRef)
val = new TypedValue(val.Value.GetPermanentReference(), val.Type); val = new TypedValue(val.Value.GetPermanentReference(), val.Type);
} catch (GetValueException e) { } catch (GetValueException e) {
@ -382,6 +386,14 @@ namespace ICSharpCode.NRefactory.Visitors
if (loc != null) if (loc != null)
return new TypedValue(loc.GetValue(context), (DebugType)loc.LocalType); 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 // Instance class members
// Note that the method might be generated instance method that represents anonymous method // Note that the method might be generated instance method that represents anonymous method
TypedValue thisValue = GetThisValue(); TypedValue thisValue = GetThisValue();
@ -554,8 +566,15 @@ namespace ICSharpCode.NRefactory.Visitors
DebugLocalVariableInfo thisVar = context.MethodInfo.GetLocalVariableThis(); DebugLocalVariableInfo thisVar = context.MethodInfo.GetLocalVariableThis();
if (thisVar != null) if (thisVar != null)
return new TypedValue(thisVar.GetValue(context), (DebugType)thisVar.LocalType); return new TypedValue(thisVar.GetValue(context), (DebugType)thisVar.LocalType);
// when symbols are not present
try {
return new TypedValue(context.GetThisValue(), (DebugType)context.MethodInfo.DeclaringType);
} catch (GetValueException) {
// static method
return null; return null;
} }
}
public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data) public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data)
{ {

7
src/AddIns/Debugger/Debugger.Core/Options.cs

@ -5,14 +5,15 @@ namespace Debugger
{ {
public class Options public class Options
{ {
public bool EnableJustMyCode = true; public bool EnableJustMyCode = false;
public bool StepOverNoSymbols = true; public bool StepOverNoSymbols = false;
public bool StepOverDebuggerAttributes = true; public bool StepOverDebuggerAttributes = true;
public bool StepOverAllProperties = false; public bool StepOverAllProperties = false;
public bool StepOverSingleLineProperties = false; public bool StepOverSingleLineProperties = false;
public bool StepOverFieldAccessProperties = true; public bool StepOverFieldAccessProperties = true;
public bool Verbose = false; public bool Verbose = false;
public string[] SymbolsSearchPaths = new string[0]; public string[] SymbolsSearchPaths = new string[0];
public bool SuspendOtherThreads = true; public bool SuspendOtherThreads = false;
public bool EnableEditAndContinue = false;
} }
} }

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

@ -14,6 +14,29 @@ namespace Debugger
{ {
internal enum DebuggeeStateAction { Keep, Clear } 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 public class Process: DebuggerObject
{ {
NDebugger debugger; NDebugger debugger;
@ -28,6 +51,7 @@ namespace Debugger
string workingDirectory; string workingDirectory;
public NDebugger Debugger { public NDebugger Debugger {
get { return debugger; } get { return debugger; }
} }
@ -108,6 +132,8 @@ namespace Debugger
get { return workingDirectory; } get { return workingDirectory; }
} }
public static DebugModeFlag DebugMode { get; set; }
internal Process(NDebugger debugger, ICorDebugProcess corProcess, string workingDirectory) internal Process(NDebugger debugger, ICorDebugProcess corProcess, string workingDirectory)
{ {
this.debugger = debugger; this.debugger = debugger;
@ -665,8 +691,13 @@ namespace Debugger
} }
BreakAtBeginning = false; BreakAtBeginning = false;
} }
if (ModulesAdded != null)
ModulesAdded(this, new ModuleEventArgs(e.Item));
} }
#endregion #endregion
public event EventHandler<ModuleEventArgs> ModulesAdded;
} }
} }

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

@ -16,6 +16,7 @@ namespace Debugger
Module module; Module module;
string filename; string filename;
string typename;
byte[] checkSum; byte[] checkSum;
int startLine; int startLine;
int startColumn; int startColumn;
@ -35,6 +36,10 @@ namespace Debugger
get { return filename; } get { return filename; }
} }
public string Typename {
get { return typename; }
}
public byte[] CheckSum { public byte[] CheckSum {
get { return checkSum; } get { return checkSum; }
} }
@ -336,7 +341,57 @@ namespace Debugger
public override string ToString() 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 ILSpy
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
} }
} }

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

@ -139,8 +139,14 @@ namespace Debugger
} }
} }
public int[] ILRanges { get; set; }
public int SourceCodeLine { get; set; }
SourcecodeSegment GetSegmentForOffet(int offset) SourcecodeSegment GetSegmentForOffet(int offset)
{ {
if (SourceCodeLine != 0)
return SourcecodeSegment.ResolveForIL(this.MethodInfo.DebugModule, corFunction, SourceCodeLine, offset, ILRanges);
return SourcecodeSegment.Resolve(this.MethodInfo.DebugModule, corFunction, offset); return SourcecodeSegment.Resolve(this.MethodInfo.DebugModule, corFunction, offset);
} }
@ -187,17 +193,23 @@ namespace Debugger
void AsyncStep(bool stepIn) 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."); throw new DebuggerException("Unable to step. No symbols loaded.");
} }
int[] stepRanges;
if (ILRanges == null) {
SourcecodeSegment nextSt = NextStatement; SourcecodeSegment nextSt = NextStatement;
if (nextSt == null) { if (nextSt == null) {
throw new DebuggerException("Unable to step. Next statement not aviable"); throw new DebuggerException("Unable to step. Next statement not aviable");
} }
stepRanges = nextSt.StepRanges;
} else {
stepRanges = ILRanges;
}
if (stepIn) { if (stepIn) {
Stepper stepInStepper = Stepper.StepIn(this, nextSt.StepRanges, "normal"); Stepper stepInStepper = Stepper.StepIn(this, stepRanges, "normal");
this.Thread.CurrentStepIn = stepInStepper; this.Thread.CurrentStepIn = stepInStepper;
Stepper clearCurrentStepIn = Stepper.StepOut(this, "clear current step in"); Stepper clearCurrentStepIn = Stepper.StepOut(this, "clear current step in");
clearCurrentStepIn.StepComplete += delegate { clearCurrentStepIn.StepComplete += delegate {
@ -207,7 +219,7 @@ namespace Debugger
}; };
clearCurrentStepIn.Ignore = true; clearCurrentStepIn.Ignore = true;
} else { } else {
Stepper.StepOver(this, nextSt.StepRanges, "normal"); Stepper.StepOver(this, stepRanges, "normal");
} }
AsyncContinue(); AsyncContinue();

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

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

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

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

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

@ -20,9 +20,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
/// </summary> /// </summary>
public class IconBarMargin : AbstractMargin, IDisposable public class IconBarMargin : AbstractMargin, IDisposable
{ {
readonly IconBarManager manager; readonly IBookmarkMargin manager;
public IconBarMargin(IconBarManager manager) public IconBarMargin(IBookmarkMargin manager)
{ {
if (manager == null) if (manager == null)
throw new ArgumentNullException("manager"); throw new ArgumentNullException("manager");

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

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

4
src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj

@ -99,6 +99,10 @@
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
<Name>ICSharpCode.NRefactory</Name> <Name>ICSharpCode.NRefactory</Name>
</ProjectReference> </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"> <ProjectReference Include="..\..\..\Main\Base\Project\ICSharpCode.SharpDevelop.csproj">
<Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project> <Project>{2748AD25-9C63-4E12-877B-4DCE96FBED54}</Project>
<Name>ICSharpCode.SharpDevelop</Name> <Name>ICSharpCode.SharpDevelop</Name>

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

@ -4,15 +4,18 @@
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using ICSharpCode.Core.Services;
using ICSharpCode.SharpDevelop; using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Dom; using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.ILSpyAddIn namespace ICSharpCode.ILSpyAddIn
{ {
public class NavigateToDecompiledEntityService : INavigateToEntityService public class NavigateToDecompiledEntityService : INavigateToEntityService, INavigateToMemberService
{ {
public bool NavigateTo(IEntity entity) public bool NavigateToEntity(IEntity entity)
{ {
if (entity == null) if (entity == null)
throw new ArgumentNullException("entity"); throw new ArgumentNullException("entity");
@ -41,6 +44,12 @@ namespace ICSharpCode.ILSpyAddIn
public static void NavigateTo(string assemblyFile, string typeName, string entityTag) 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>()) { foreach (var vc in WorkbenchSingleton.Workbench.ViewContentCollection.OfType<DecompiledViewContent>()) {
if (string.Equals(vc.AssemblyFile, assemblyFile, StringComparison.OrdinalIgnoreCase) && typeName == vc.FullTypeName) { if (string.Equals(vc.AssemblyFile, assemblyFile, StringComparison.OrdinalIgnoreCase) && typeName == vc.FullTypeName) {
vc.WorkbenchWindow.SelectWindow(); vc.WorkbenchWindow.SelectWindow();
@ -50,5 +59,18 @@ namespace ICSharpCode.ILSpyAddIn
} }
WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag)); WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag));
} }
public bool NavigateToMember(string assemblyFile, string typeName, string entityTag)
{
//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;
}
}
WorkbenchSingleton.Workbench.ShowView(new DecompiledViewContent(assemblyFile, typeName, entityTag));
return true;
}
} }
} }

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

@ -2,33 +2,243 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.AddIn; using ICSharpCode.AvalonEdit.AddIn;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting; using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Rendering;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Editor;
using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
namespace ICSharpCode.ILSpyAddIn.ViewContent namespace ICSharpCode.ILSpyAddIn.ViewContent
{ {
/// <summary> /// <summary>
/// Equivalent to AE.AddIn CodeEditor, but without editing capabilities. /// Equivalent to AE.AddIn CodeEditor, but without editing capabilities.
/// </summary> /// </summary>
public class CodeView : Grid, IDisposable public class CodeView : Grid, IDisposable, ICodeEditor
{ {
readonly SharpDevelopTextEditor textEditor = new SharpDevelopTextEditor(); public event EventHandler DocumentChanged;
readonly AvalonEditTextEditorAdapter adapter = new AvalonEditTextEditorAdapter(new SharpDevelopTextEditor { IsReadOnly = true });
readonly IconBarManager iconBarManager;
readonly IconBarMargin iconMargin;
readonly TextMarkerService textMarkerService;
public CodeView() public CodeView()
{ {
this.Children.Add(textEditor); this.Children.Add(adapter.TextEditor);
textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#"); adapter.TextEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
// add margin
iconMargin = new IconBarMargin(iconBarManager = new IconBarManager());
adapter.TextEditor.TextArea.LeftMargins.Insert(0, iconMargin);
adapter.TextEditor.TextArea.TextView.VisualLinesChanged += delegate { iconMargin.InvalidateVisual(); };
// add marker service
textMarkerService = new TextMarkerService(this);
adapter.TextEditor.TextArea.TextView.BackgroundRenderers.Add(textMarkerService);
adapter.TextEditor.TextArea.TextView.LineTransformers.Add(textMarkerService);
adapter.TextEditor.TextArea.TextView.Services.AddService(typeof(ITextMarkerService), textMarkerService);
// 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 { public TextDocument Document {
get { return textEditor.Document; } get { return adapter.TextEditor.Document; }
set { textEditor.Document = value; } 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 void Dispose() 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);
}
} }
} }

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

@ -2,14 +2,25 @@
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) // This code is distributed under MIT X11 license (for details please see \doc\license.txt)
using System; using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq;
using System.Threading; using System.Threading;
using ICSharpCode.AvalonEdit.Document; using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.Core; using ICSharpCode.Core;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast; using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.ILSpyAddIn.ViewContent; using ICSharpCode.ILSpyAddIn.ViewContent;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Bookmarks;
using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui; using ICSharpCode.SharpDevelop.Gui;
using Mono.Cecil; using Mono.Cecil;
@ -46,7 +57,9 @@ namespace ICSharpCode.ILSpyAddIn
Thread thread = new Thread(DecompilationThread); Thread thread = new Thread(DecompilationThread);
thread.Name = "Decompiler (" + shortTypeName + ")"; thread.Name = "Decompiler (" + shortTypeName + ")";
thread.Start(); thread.Start();
BookmarkManager.Removed += BookmarkManager_Removed;
} }
#endregion #endregion
#region Properties #region Properties
@ -61,6 +74,10 @@ namespace ICSharpCode.ILSpyAddIn
public override object Control { public override object Control {
get { return codeView; } get { return codeView; }
} }
public override bool IsReadOnly {
get { return true; }
}
#endregion #endregion
#region Dispose #region Dispose
@ -68,6 +85,7 @@ namespace ICSharpCode.ILSpyAddIn
{ {
cancellation.Cancel(); cancellation.Cancel();
codeView.Dispose(); codeView.Dispose();
BookmarkManager.Removed -= BookmarkManager_Removed;
base.Dispose(); base.Dispose();
} }
#endregion #endregion
@ -139,6 +157,17 @@ namespace ICSharpCode.ILSpyAddIn
AstBuilder astBuilder = new AstBuilder(context); AstBuilder astBuilder = new AstBuilder(context);
astBuilder.AddType(typeDefinition); astBuilder.AddType(typeDefinition);
astBuilder.GenerateCode(textOutput); 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);
DebuggerService.ExternalDebugInformation = new DecompileInformation {
CodeMappings = astBuilder.CodeMappings,
LocalVariables = astBuilder.LocalVariables,
DecompiledMemberReferences = astBuilder.DecompiledMemberReferences,
AstNodes = nodes
};
} }
void OnDecompilationFinished(StringWriter output) void OnDecompilationFinished(StringWriter output)
@ -150,6 +179,53 @@ namespace ICSharpCode.ILSpyAddIn
this.decompilationFinished = true; this.decompilationFinished = true;
JumpToEntity(this.jumpToEntityTagWhenDecompilationFinished); JumpToEntity(this.jumpToEntityTagWhenDecompilationFinished);
// update UI
UpdateIconMargin(output.ToString());
UpdateDebuggingUI();
}
#endregion
#region Update UI
void UpdateIconMargin(string text)
{
string tempFileName = string.Format("decompiled/{0}.cs", fullTypeName);
codeView.IconBarManager.UpdateClassMemberBookmarks(ParserService.ParseFile(tempFileName, new StringTextBuffer(text)));
}
void UpdateDebuggingUI()
{
if (!DebuggerService.IsDebuggerStarted)
return;
if (DebuggerService.DebugStepInformation != null) {
// get debugging information
DecompileInformation debugInformation = (DecompileInformation)DebuggerService.ExternalDebugInformation;
int token = DebuggerService.DebugStepInformation.Item1;
int ilOffset = DebuggerService.DebugStepInformation.Item2;
int line;
MemberReference member;
if (!debugInformation.CodeMappings.ContainsKey(token))
return;
debugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out member, out line);
// update bookmark & marker
codeView.UnfoldAndScroll(line);
CurrentLineBookmark.SetPosition(this, line, 0, line, 0);
}
}
#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;
}
} }
#endregion #endregion
} }

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

@ -1,4 +1,23 @@
// 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;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Diagnostics; using System.Diagnostics;
@ -7,6 +26,7 @@ using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading; using System.Threading;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast.Transforms; using ICSharpCode.Decompiler.Ast.Transforms;
using ICSharpCode.Decompiler.ILAst; using ICSharpCode.Decompiler.ILAst;
@ -26,10 +46,11 @@ namespace ICSharpCode.Decompiler.Ast
{ {
None = 0, None = 0,
IncludeNamespace = 1, IncludeNamespace = 1,
IncludeTypeParameterDefinitions = 2 IncludeTypeParameterDefinitions = 2,
DoNotUsePrimitiveTypeNames = 4
} }
public class AstBuilder public class AstBuilder : BaseCodeMappings
{ {
DecompilerContext context; DecompilerContext context;
CompilationUnit astCompileUnit = new CompilationUnit(); CompilationUnit astCompileUnit = new CompilationUnit();
@ -42,6 +63,10 @@ namespace ICSharpCode.Decompiler.Ast
throw new ArgumentNullException("context"); throw new ArgumentNullException("context");
this.context = context; this.context = context;
this.DecompileMethodBodies = true; this.DecompileMethodBodies = true;
this.LocalVariables = new ConcurrentDictionary<int, IEnumerable<ILVariable>>();
this.CodeMappings = new Dictionary<int, List<MemberMapping>>();
this.DecompiledMemberReferences = new Dictionary<int, MemberReference>();
} }
public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings) public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings)
@ -121,8 +146,28 @@ namespace ICSharpCode.Decompiler.Ast
public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false) public void AddAssembly(AssemblyDefinition assemblyDefinition, bool onlyAssemblyLevel = false)
{ {
if (assemblyDefinition.Name.Version != null) {
astCompileUnit.AddChild(
new AttributeSection {
AttributeTarget = "assembly",
Attributes = {
new NRefactory.CSharp.Attribute {
Type = new SimpleType("AssemblyVersion")
.WithAnnotation(new TypeReference(
"System.Reflection", "AssemblyVersionAttribute",
assemblyDefinition.MainModule, assemblyDefinition.MainModule.TypeSystem.Corlib)),
Arguments = {
new PrimitiveExpression(assemblyDefinition.Name.Version.ToString())
}
}
}
}, AttributedNode.AttributeRole);
}
ConvertCustomAttributes(astCompileUnit, assemblyDefinition, "assembly"); ConvertCustomAttributes(astCompileUnit, assemblyDefinition, "assembly");
ConvertSecurityAttributes(astCompileUnit, assemblyDefinition, "assembly");
ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module"); ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module");
AddTypeForwarderAttributes(astCompileUnit, assemblyDefinition.MainModule, "assembly");
if (!onlyAssemblyLevel) { if (!onlyAssemblyLevel) {
foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) { foreach (TypeDefinition typeDef in assemblyDefinition.MainModule.Types) {
@ -137,6 +182,30 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
void AddTypeForwarderAttributes(CompilationUnit astCompileUnit, ModuleDefinition module, string target)
{
if (!module.HasExportedTypes)
return;
foreach (ExportedType type in module.ExportedTypes) {
if (type.IsForwarder) {
var forwardedType = CreateTypeOfExpression(new TypeReference(type.Namespace, type.Name, module, type.Scope));
astCompileUnit.AddChild(
new AttributeSection {
AttributeTarget = target,
Attributes = {
new NRefactory.CSharp.Attribute {
Type = new SimpleType("TypeForwardedTo")
.WithAnnotation(new TypeReference(
"System.Runtime.CompilerServices", "TypeForwardedToAttribute",
module, module.TypeSystem.Corlib)),
Arguments = { forwardedType }
}
}
}, AttributedNode.AttributeRole);
}
}
}
NamespaceDeclaration GetCodeNamespace(string name) NamespaceDeclaration GetCodeNamespace(string name)
{ {
if (string.IsNullOrEmpty(name)) { if (string.IsNullOrEmpty(name)) {
@ -192,6 +261,7 @@ namespace ICSharpCode.Decompiler.Ast
/// <returns>TypeDeclaration or DelegateDeclaration.</returns> /// <returns>TypeDeclaration or DelegateDeclaration.</returns>
public AttributedNode CreateType(TypeDefinition typeDef) public AttributedNode CreateType(TypeDefinition typeDef)
{ {
// create type
TypeDefinition oldCurrentType = context.CurrentType; TypeDefinition oldCurrentType = context.CurrentType;
context.CurrentType = typeDef; context.CurrentType = typeDef;
TypeDeclaration astType = new TypeDeclaration(); TypeDeclaration astType = new TypeDeclaration();
@ -219,13 +289,6 @@ namespace ICSharpCode.Decompiler.Ast
astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters)); astType.TypeParameters.AddRange(MakeTypeParameters(genericParameters));
astType.Constraints.AddRange(MakeConstraints(genericParameters)); astType.Constraints.AddRange(MakeConstraints(genericParameters));
// Nested types
foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) {
if (MemberIsHidden(nestedTypeDef, context.Settings))
continue;
astType.AddChild(CreateType(nestedTypeDef), TypeDeclaration.MemberRole);
}
AttributedNode result = astType; AttributedNode result = astType;
if (typeDef.IsEnum) { if (typeDef.IsEnum) {
long expectedEnumMemberValue = 0; long expectedEnumMemberValue = 0;
@ -238,6 +301,7 @@ namespace ICSharpCode.Decompiler.Ast
} }
} else { } else {
EnumMemberDeclaration enumMember = new EnumMemberDeclaration(); EnumMemberDeclaration enumMember = new EnumMemberDeclaration();
enumMember.AddAnnotation(field);
enumMember.Name = CleanName(field.Name); enumMember.Name = CleanName(field.Name);
long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false); long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false);
if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) { if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) {
@ -259,6 +323,7 @@ namespace ICSharpCode.Decompiler.Ast
if (m.Name == "Invoke") { if (m.Name == "Invoke") {
dd.ReturnType = ConvertType(m.ReturnType, m.MethodReturnType); dd.ReturnType = ConvertType(m.ReturnType, m.MethodReturnType);
dd.Parameters.AddRange(MakeParameters(m)); dd.Parameters.AddRange(MakeParameters(m));
ConvertAttributes(dd, m.MethodReturnType, m.Module);
} }
} }
result = dd; result = dd;
@ -302,6 +367,45 @@ namespace ICSharpCode.Decompiler.Ast
return name; return name;
} }
#region Create TypeOf Expression
/// <summary>
/// Creates a typeof-expression for the specified type.
/// </summary>
public static TypeOfExpression CreateTypeOfExpression(TypeReference type)
{
return new TypeOfExpression(AddEmptyTypeArgumentsForUnboundGenerics(ConvertType(type)));
}
static AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type)
{
TypeReference typeRef = type.Annotation<TypeReference>();
if (typeRef == null)
return type;
TypeDefinition typeDef = typeRef.Resolve(); // need to resolve to figure out the number of type parameters
if (typeDef == null || !typeDef.HasGenericParameters)
return type;
SimpleType sType = type as SimpleType;
MemberType mType = type as MemberType;
if (sType != null) {
while (typeDef.GenericParameters.Count > sType.TypeArguments.Count) {
sType.TypeArguments.Add(new SimpleType(""));
}
}
if (mType != null) {
AddEmptyTypeArgumentsForUnboundGenerics(mType.Target);
int outerTypeParamCount = typeDef.DeclaringType == null ? 0 : typeDef.DeclaringType.GenericParameters.Count;
while (typeDef.GenericParameters.Count - outerTypeParamCount > mType.TypeArguments.Count) {
mType.TypeArguments.Add(new SimpleType(""));
}
}
return type;
}
#endregion
#region Convert Type Reference #region Convert Type Reference
/// <summary> /// <summary>
/// Converts a type reference. /// Converts a type reference.
@ -376,6 +480,8 @@ namespace ICSharpCode.Decompiler.Ast
return new PrimitiveType("dynamic"); return new PrimitiveType("dynamic");
} else { } else {
if (ns == "System") { if (ns == "System") {
if ((options & ConvertTypeOptions.DoNotUsePrimitiveTypeNames)
!= ConvertTypeOptions.DoNotUsePrimitiveTypeNames) {
switch (name) { switch (name) {
case "SByte": case "SByte":
return new PrimitiveType("sbyte"); return new PrimitiveType("sbyte");
@ -411,6 +517,7 @@ namespace ICSharpCode.Decompiler.Ast
return new PrimitiveType("object"); return new PrimitiveType("object");
} }
} }
}
name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name); name = ICSharpCode.NRefactory.TypeSystem.ReflectionHelper.SplitTypeParameterCountFromReflectionName(name);
@ -585,6 +692,15 @@ namespace ICSharpCode.Decompiler.Ast
void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef) void AddTypeMembers(TypeDeclaration astType, TypeDefinition typeDef)
{ {
// Nested types
foreach (TypeDefinition nestedTypeDef in typeDef.NestedTypes) {
if (MemberIsHidden(nestedTypeDef, context.Settings))
continue;
var nestedType = CreateType(nestedTypeDef);
SetNewModifier(nestedType);
astType.AddChild(nestedType, TypeDeclaration.MemberRole);
}
// Add fields // Add fields
foreach(FieldDefinition fieldDef in typeDef.Fields) { foreach(FieldDefinition fieldDef in typeDef.Fields) {
if (MemberIsHidden(fieldDef, context.Settings)) continue; if (MemberIsHidden(fieldDef, context.Settings)) continue;
@ -614,22 +730,26 @@ namespace ICSharpCode.Decompiler.Ast
AttributedNode CreateMethod(MethodDefinition methodDef) AttributedNode CreateMethod(MethodDefinition methodDef)
{ {
MethodDeclaration astMethod = new MethodDeclaration(); // Create mapping - used in debugger
CreateCodeMappings(methodDef.MetadataToken.ToInt32(), methodDef);
MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[methodDef.MetadataToken.ToInt32()]);
MethodDeclaration astMethod = new MethodDeclaration().WithAnnotation(methodMapping);
astMethod.AddAnnotation(methodDef); astMethod.AddAnnotation(methodDef);
astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType); astMethod.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType);
astMethod.Name = CleanName(methodDef.Name); astMethod.Name = CleanName(methodDef.Name);
astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters)); astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters));
astMethod.Parameters.AddRange(MakeParameters(methodDef)); astMethod.Parameters.AddRange(MakeParameters(methodDef));
astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters)); // constraints for override and explicit interface implementation methods are inherited from the base method, so they cannot be specified directly
if (!methodDef.IsVirtual || (methodDef.IsNewSlot && !methodDef.IsPrivate)) astMethod.Constraints.AddRange(MakeConstraints(methodDef.GenericParameters));
if (!methodDef.DeclaringType.IsInterface) { if (!methodDef.DeclaringType.IsInterface) {
if (!methodDef.HasOverrides) { if (!methodDef.HasOverrides) {
astMethod.Modifiers = ConvertModifiers(methodDef); astMethod.Modifiers = ConvertModifiers(methodDef);
if (methodDef.IsVirtual ^ !methodDef.IsNewSlot) { if (methodDef.IsVirtual == methodDef.IsNewSlot)
if (TypesHierarchyHelpers.FindBaseMethods(methodDef).Any()) SetNewModifier(astMethod);
astMethod.Modifiers |= Modifiers.New; } else {
}
} else
astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType); astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType);
}
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
} }
ConvertAttributes(astMethod, methodDef); ConvertAttributes(astMethod, methodDef);
@ -699,6 +819,10 @@ namespace ICSharpCode.Decompiler.Ast
ConstructorDeclaration CreateConstructor(MethodDefinition methodDef) ConstructorDeclaration CreateConstructor(MethodDefinition methodDef)
{ {
// Create mapping - used in debugger
CreateCodeMappings(methodDef.MetadataToken.ToInt32(), methodDef);
MemberMapping methodMapping = methodDef.CreateCodeMapping(this.CodeMappings[methodDef.MetadataToken.ToInt32()]);
ConstructorDeclaration astMethod = new ConstructorDeclaration(); ConstructorDeclaration astMethod = new ConstructorDeclaration();
astMethod.AddAnnotation(methodDef); astMethod.AddAnnotation(methodDef);
astMethod.Modifiers = ConvertModifiers(methodDef); astMethod.Modifiers = ConvertModifiers(methodDef);
@ -710,6 +834,10 @@ namespace ICSharpCode.Decompiler.Ast
astMethod.Parameters.AddRange(MakeParameters(methodDef)); astMethod.Parameters.AddRange(MakeParameters(methodDef));
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters); astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
ConvertAttributes(astMethod, methodDef); ConvertAttributes(astMethod, methodDef);
astMethod.WithAnnotation(methodMapping);
if (methodDef.IsStatic && methodDef.DeclaringType.IsBeforeFieldInit && !astMethod.Body.IsNull) {
astMethod.Body.InsertChildAfter(null, new Comment(" Note: this type is marked as 'beforefieldinit'."), AstNode.Roles.Comment);
}
return astMethod; return astMethod;
} }
@ -739,6 +867,7 @@ namespace ICSharpCode.Decompiler.Ast
getterModifiers = ConvertModifiers(propDef.GetMethod); getterModifiers = ConvertModifiers(propDef.GetMethod);
setterModifiers = ConvertModifiers(propDef.SetMethod); setterModifiers = ConvertModifiers(propDef.SetMethod);
astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers); astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers);
try {
if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) { if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) {
foreach (var basePropDef in TypesHierarchyHelpers.FindBaseProperties(propDef)) { foreach (var basePropDef in TypesHierarchyHelpers.FindBaseProperties(propDef)) {
if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) { if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) {
@ -750,38 +879,59 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
} }
if (accessor.IsVirtual ^ !accessor.IsNewSlot) { } catch (ReferenceResolvingException) {
if (TypesHierarchyHelpers.FindBaseProperties(propDef).Any()) // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references.
astProp.Modifiers |= Modifiers.New;
} }
} }
astProp.Name = CleanName(propDef.Name); astProp.Name = CleanName(propDef.Name);
astProp.ReturnType = ConvertType(propDef.PropertyType, propDef); astProp.ReturnType = ConvertType(propDef.PropertyType, propDef);
if (propDef.GetMethod != null) { if (propDef.GetMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(propDef.GetMethod.MetadataToken.ToInt32(), propDef);
MemberMapping methodMapping = propDef.GetMethod.CreateCodeMapping(this.CodeMappings[propDef.GetMethod.MetadataToken.ToInt32()], propDef);
astProp.Getter = new Accessor(); astProp.Getter = new Accessor();
astProp.Getter.Body = CreateMethodBody(propDef.GetMethod); astProp.Getter.Body = CreateMethodBody(propDef.GetMethod);
astProp.AddAnnotation(propDef.GetMethod); astProp.Getter.AddAnnotation(propDef.GetMethod);
ConvertAttributes(astProp.Getter, propDef.GetMethod); ConvertAttributes(astProp.Getter, propDef.GetMethod);
if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask; astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask;
astProp.Getter.WithAnnotation(methodMapping);
} }
if (propDef.SetMethod != null) { if (propDef.SetMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(propDef.SetMethod.MetadataToken.ToInt32(), propDef);
MemberMapping methodMapping = propDef.SetMethod.CreateCodeMapping(this.CodeMappings[propDef.SetMethod.MetadataToken.ToInt32()], propDef);
astProp.Setter = new Accessor(); astProp.Setter = new Accessor();
astProp.Setter.Body = CreateMethodBody(propDef.SetMethod); astProp.Setter.Body = CreateMethodBody(propDef.SetMethod);
astProp.Setter.AddAnnotation(propDef.SetMethod); astProp.Setter.AddAnnotation(propDef.SetMethod);
ConvertAttributes(astProp.Setter, propDef.SetMethod); ConvertAttributes(astProp.Setter, propDef.SetMethod);
ConvertCustomAttributes(astProp.Setter, propDef.SetMethod.Parameters.Last(), "param"); ParameterDefinition lastParam = propDef.SetMethod.Parameters.LastOrDefault();
if (lastParam != null) {
ConvertCustomAttributes(astProp.Setter, lastParam, "param");
if (lastParam.HasMarshalInfo) {
astProp.Setter.Attributes.Add(new AttributeSection(ConvertMarshalInfo(lastParam, propDef.Module)) { AttributeTarget = "param" });
}
}
if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask)) if ((setterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask; astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask;
astProp.Setter.WithAnnotation(methodMapping);
} }
ConvertCustomAttributes(astProp, propDef); ConvertCustomAttributes(astProp, propDef);
MemberDeclaration member = astProp;
if(propDef.IsIndexer()) if(propDef.IsIndexer())
return ConvertPropertyToIndexer(astProp, propDef); member = ConvertPropertyToIndexer(astProp, propDef);
else if(!accessor.HasOverrides && !accessor.DeclaringType.IsInterface)
return astProp; if (accessor.IsVirtual == accessor.IsNewSlot)
SetNewModifier(member);
return member;
} }
IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef) IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef)
@ -821,17 +971,34 @@ namespace ICSharpCode.Decompiler.Ast
astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod); astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
else else
astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType); astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType);
if (eventDef.AddMethod != null) { if (eventDef.AddMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(eventDef.AddMethod.MetadataToken.ToInt32(), eventDef);
MemberMapping methodMapping = eventDef.AddMethod.CreateCodeMapping(this.CodeMappings[eventDef.AddMethod.MetadataToken.ToInt32()], eventDef);
astEvent.AddAccessor = new Accessor { astEvent.AddAccessor = new Accessor {
Body = CreateMethodBody(eventDef.AddMethod) Body = CreateMethodBody(eventDef.AddMethod)
}.WithAnnotation(eventDef.AddMethod); }.WithAnnotation(eventDef.AddMethod);
ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod); ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod);
astEvent.AddAccessor.WithAnnotation(methodMapping);
} }
if (eventDef.RemoveMethod != null) { if (eventDef.RemoveMethod != null) {
// Create mapping - used in debugger
CreateCodeMappings(eventDef.RemoveMethod.MetadataToken.ToInt32(), eventDef);
MemberMapping methodMapping = eventDef.RemoveMethod.CreateCodeMapping(this.CodeMappings[eventDef.RemoveMethod.MetadataToken.ToInt32()], eventDef);
astEvent.RemoveAccessor = new Accessor { astEvent.RemoveAccessor = new Accessor {
Body = CreateMethodBody(eventDef.RemoveMethod) Body = CreateMethodBody(eventDef.RemoveMethod)
}.WithAnnotation(eventDef.RemoveMethod); }.WithAnnotation(eventDef.RemoveMethod);
ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod); ConvertAttributes(astEvent.RemoveAccessor, eventDef.RemoveMethod);
astEvent.RemoveAccessor.WithAnnotation(methodMapping);
}
MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
if (accessor.IsVirtual == accessor.IsNewSlot) {
SetNewModifier(astEvent);
} }
return astEvent; return astEvent;
} }
@ -842,13 +1009,15 @@ namespace ICSharpCode.Decompiler.Ast
BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> parameters = null) BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable<ParameterDeclaration> parameters = null)
{ {
if (DecompileMethodBodies) if (DecompileMethodBodies)
return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters); return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters, LocalVariables);
else else
return null; return null;
} }
FieldDeclaration CreateField(FieldDefinition fieldDef) FieldDeclaration CreateField(FieldDefinition fieldDef)
{ {
this.DecompiledMemberReferences.Add(fieldDef.MetadataToken.ToInt32(), fieldDef);
FieldDeclaration astField = new FieldDeclaration(); FieldDeclaration astField = new FieldDeclaration();
astField.AddAnnotation(fieldDef); astField.AddAnnotation(fieldDef);
VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name)); VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name));
@ -856,20 +1025,29 @@ namespace ICSharpCode.Decompiler.Ast
astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef); astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef);
astField.Modifiers = ConvertModifiers(fieldDef); astField.Modifiers = ConvertModifiers(fieldDef);
if (fieldDef.HasConstant) { if (fieldDef.HasConstant) {
if (fieldDef.Constant == null) { initializer.Initializer = CreateExpressionForConstant(fieldDef.Constant, fieldDef.FieldType, fieldDef.DeclaringType.IsEnum);
initializer.Initializer = new NullReferenceExpression(); }
ConvertAttributes(astField, fieldDef);
SetNewModifier(astField);
return astField;
}
static Expression CreateExpressionForConstant(object constant, TypeReference type, bool isEnumMemberDeclaration = false)
{
if (constant == null) {
if (type.IsValueType && !(type.Namespace == "System" && type.Name == "Nullable`1"))
return new DefaultValueExpression(ConvertType(type));
else
return new NullReferenceExpression();
} else { } else {
TypeCode c = Type.GetTypeCode(fieldDef.Constant.GetType()); TypeCode c = Type.GetTypeCode(constant.GetType());
if (c >= TypeCode.SByte && c <= TypeCode.UInt64 && !fieldDef.DeclaringType.IsEnum) { if (c >= TypeCode.SByte && c <= TypeCode.UInt64 && !isEnumMemberDeclaration) {
initializer.Initializer = MakePrimitive((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, fieldDef.Constant, false), fieldDef.FieldType); return MakePrimitive((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constant, false), type);
} else { } else {
initializer.Initializer = new PrimitiveExpression(fieldDef.Constant); return new PrimitiveExpression(constant);
} }
} }
} }
ConvertAttributes(astField, fieldDef);
return astField;
}
public static IEnumerable<ParameterDeclaration> MakeParameters(MethodDefinition method, bool isLambda = false) public static IEnumerable<ParameterDeclaration> MakeParameters(MethodDefinition method, bool isLambda = false)
{ {
@ -896,12 +1074,16 @@ namespace ICSharpCode.Decompiler.Ast
if (ct != null && ct.PointerRank > 0) if (ct != null && ct.PointerRank > 0)
ct.PointerRank--; ct.PointerRank--;
} }
if (paramDef.HasCustomAttributes) { if (paramDef.HasCustomAttributes) {
foreach (CustomAttribute ca in paramDef.CustomAttributes) { foreach (CustomAttribute ca in paramDef.CustomAttributes) {
if (ca.AttributeType.Name == "ParamArrayAttribute" && ca.AttributeType.Namespace == "System") if (ca.AttributeType.Name == "ParamArrayAttribute" && ca.AttributeType.Namespace == "System")
astParam.ParameterModifier = ParameterModifier.Params; astParam.ParameterModifier = ParameterModifier.Params;
} }
} }
if (paramDef.IsOptional) {
astParam.DefaultExpression = CreateExpressionForConstant(paramDef.Constant, paramDef.ParameterType);
}
ConvertCustomAttributes(astParam, paramDef); ConvertCustomAttributes(astParam, paramDef);
ModuleDefinition module = ((MethodDefinition)paramDef.Method).Module; ModuleDefinition module = ((MethodDefinition)paramDef.Method).Module;
@ -922,6 +1104,7 @@ namespace ICSharpCode.Decompiler.Ast
void ConvertAttributes(AttributedNode attributedNode, TypeDefinition typeDefinition) void ConvertAttributes(AttributedNode attributedNode, TypeDefinition typeDefinition)
{ {
ConvertCustomAttributes(attributedNode, typeDefinition); ConvertCustomAttributes(attributedNode, typeDefinition);
ConvertSecurityAttributes(attributedNode, typeDefinition);
// Handle the non-custom attributes: // Handle the non-custom attributes:
#region SerializableAttribute #region SerializableAttribute
@ -972,11 +1155,12 @@ namespace ICSharpCode.Decompiler.Ast
void ConvertAttributes(AttributedNode attributedNode, MethodDefinition methodDefinition) void ConvertAttributes(AttributedNode attributedNode, MethodDefinition methodDefinition)
{ {
ConvertCustomAttributes(attributedNode, methodDefinition); ConvertCustomAttributes(attributedNode, methodDefinition);
ConvertSecurityAttributes(attributedNode, methodDefinition);
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask; MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
#region DllImportAttribute #region DllImportAttribute
if (methodDefinition.HasPInvokeInfo) { if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) {
PInvokeInfo info = methodDefinition.PInvokeInfo; PInvokeInfo info = methodDefinition.PInvokeInfo;
Ast.Attribute dllImport = CreateNonCustomAttribute(typeof(DllImportAttribute)); Ast.Attribute dllImport = CreateNonCustomAttribute(typeof(DllImportAttribute));
dllImport.Arguments.Add(new PrimitiveExpression(info.Module.Name)); dllImport.Arguments.Add(new PrimitiveExpression(info.Module.Name));
@ -1065,9 +1249,14 @@ namespace ICSharpCode.Decompiler.Ast
} }
#endregion #endregion
ConvertCustomAttributes(attributedNode, methodDefinition.MethodReturnType, "return"); ConvertAttributes(attributedNode, methodDefinition.MethodReturnType, methodDefinition.Module);
if (methodDefinition.MethodReturnType.HasMarshalInfo) { }
var marshalInfo = ConvertMarshalInfo(methodDefinition.MethodReturnType, methodDefinition.Module);
void ConvertAttributes(AttributedNode attributedNode, MethodReturnType methodReturnType, ModuleDefinition module)
{
ConvertCustomAttributes(attributedNode, methodReturnType, "return");
if (methodReturnType.HasMarshalInfo) {
var marshalInfo = ConvertMarshalInfo(methodReturnType, module);
attributedNode.Attributes.Add(new AttributeSection(marshalInfo) { AttributeTarget = "return" }); attributedNode.Attributes.Add(new AttributeSection(marshalInfo) { AttributeTarget = "return" });
} }
} }
@ -1103,6 +1292,37 @@ namespace ICSharpCode.Decompiler.Ast
Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module); Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module);
var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib); var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib);
attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType)); attr.Arguments.Add(MakePrimitive((int)marshalInfo.NativeType, unmanagedType));
FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo;
if (fami != null) {
attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fami.Size));
if (fami.ElementType != NativeType.None)
attr.AddNamedArgument("ArraySubType", MakePrimitive((int)fami.ElementType, unmanagedType));
}
SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo;
if (sami != null && sami.ElementType != VariantType.None) {
var varEnum = new TypeReference("System.Runtime.InteropServices", "VarEnum", module, module.TypeSystem.Corlib);
attr.AddNamedArgument("SafeArraySubType", MakePrimitive((int)sami.ElementType, varEnum));
}
ArrayMarshalInfo ami = marshalInfo as ArrayMarshalInfo;
if (ami != null) {
if (ami.ElementType != NativeType.Max)
attr.AddNamedArgument("ArraySubType", MakePrimitive((int)ami.ElementType, unmanagedType));
if (ami.Size >= 0)
attr.AddNamedArgument("SizeConst", new PrimitiveExpression(ami.Size));
if (ami.SizeParameterMultiplier != 0 && ami.SizeParameterIndex >= 0)
attr.AddNamedArgument("SizeParamIndex", new PrimitiveExpression(ami.SizeParameterIndex));
}
CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo;
if (cmi != null) {
attr.AddNamedArgument("MarshalType", new PrimitiveExpression(cmi.ManagedType.FullName));
if (!string.IsNullOrEmpty(cmi.Cookie))
attr.AddNamedArgument("MarshalCookie", new PrimitiveExpression(cmi.Cookie));
}
FixedSysStringMarshalInfo fssmi = marshalInfo as FixedSysStringMarshalInfo;
if (fssmi != null) {
attr.AddNamedArgument("SizeConst", new PrimitiveExpression(fssmi.Size));
}
return attr; return attr;
} }
#endregion #endregion
@ -1192,6 +1412,65 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
static void ConvertSecurityAttributes(AstNode attributedNode, ISecurityDeclarationProvider secDeclProvider, string attributeTarget = null)
{
if (!secDeclProvider.HasSecurityDeclarations)
return;
var attributes = new List<ICSharpCode.NRefactory.CSharp.Attribute>();
foreach (var secDecl in secDeclProvider.SecurityDeclarations) {
foreach (var secAttribute in secDecl.SecurityAttributes) {
var attribute = new ICSharpCode.NRefactory.CSharp.Attribute();
attribute.AddAnnotation(secAttribute);
attribute.Type = ConvertType(secAttribute.AttributeType);
attributes.Add(attribute);
SimpleType st = attribute.Type as SimpleType;
if (st != null && st.Identifier.EndsWith("Attribute", StringComparison.Ordinal)) {
st.Identifier = st.Identifier.Substring(0, st.Identifier.Length - "Attribute".Length);
}
var module = secAttribute.AttributeType.Module;
var securityActionType = new TypeReference("System.Security.Permissions", "SecurityAction", module, module.TypeSystem.Corlib);
attribute.Arguments.Add(MakePrimitive((int)secDecl.Action, securityActionType));
if (secAttribute.HasProperties) {
TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve();
foreach (var propertyNamedArg in secAttribute.Properties) {
var propertyReference = resolvedAttributeType != null ? resolvedAttributeType.Properties.FirstOrDefault(pr => pr.Name == propertyNamedArg.Name) : null;
var propertyName = new IdentifierExpression(propertyNamedArg.Name).WithAnnotation(propertyReference);
var argumentValue = ConvertArgumentValue(propertyNamedArg.Argument);
attribute.Arguments.Add(new AssignmentExpression(propertyName, argumentValue));
}
}
if (secAttribute.HasFields) {
TypeDefinition resolvedAttributeType = secAttribute.AttributeType.Resolve();
foreach (var fieldNamedArg in secAttribute.Fields) {
var fieldReference = resolvedAttributeType != null ? resolvedAttributeType.Fields.FirstOrDefault(f => f.Name == fieldNamedArg.Name) : null;
var fieldName = new IdentifierExpression(fieldNamedArg.Name).WithAnnotation(fieldReference);
var argumentValue = ConvertArgumentValue(fieldNamedArg.Argument);
attribute.Arguments.Add(new AssignmentExpression(fieldName, argumentValue));
}
}
}
}
if (attributeTarget == "module" || attributeTarget == "assembly") {
// use separate section for each attribute
foreach (var attribute in attributes) {
var section = new AttributeSection();
section.AttributeTarget = attributeTarget;
section.Attributes.Add(attribute);
attributedNode.AddChild(section, AttributedNode.AttributeRole);
}
} else if (attributes.Count > 0) {
// use single section for all attributes
var section = new AttributeSection();
section.AttributeTarget = attributeTarget;
section.Attributes.AddRange(attributes);
attributedNode.AddChild(section, AttributedNode.AttributeRole);
}
}
private static Expression ConvertArgumentValue(CustomAttributeArgument argument) private static Expression ConvertArgumentValue(CustomAttributeArgument argument)
{ {
if (argument.Value is CustomAttributeArgument[]) { if (argument.Value is CustomAttributeArgument[]) {
@ -1212,9 +1491,7 @@ namespace ICSharpCode.Decompiler.Ast
if (type != null && type.IsEnum) { if (type != null && type.IsEnum) {
return MakePrimitive(Convert.ToInt64(argument.Value), type); return MakePrimitive(Convert.ToInt64(argument.Value), type);
} else if (argument.Value is TypeReference) { } else if (argument.Value is TypeReference) {
return new TypeOfExpression() { return CreateTypeOfExpression((TypeReference)argument.Value);
Type = ConvertType((TypeReference)argument.Value),
};
} else { } else {
return new PrimitiveExpression(argument.Value); return new PrimitiveExpression(argument.Value);
} }
@ -1233,13 +1510,13 @@ namespace ICSharpCode.Decompiler.Ast
{ // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs) { // cannot rely on type.IsValueType, it's not set for typerefs (but is set for typespecs)
TypeDefinition enumDefinition = type.Resolve(); TypeDefinition enumDefinition = type.Resolve();
if (enumDefinition != null && enumDefinition.IsEnum) { if (enumDefinition != null && enumDefinition.IsEnum) {
TypeCode enumBaseTypeCode = TypeCode.Int32;
foreach (FieldDefinition field in enumDefinition.Fields) { foreach (FieldDefinition field in enumDefinition.Fields) {
if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val)) if (field.IsStatic && object.Equals(CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false), val))
return ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); return ConvertType(type).Member(field.Name).WithAnnotation(field);
else if (!field.IsStatic && field.IsRuntimeSpecialName) else if (!field.IsStatic && field.IsRuntimeSpecialName)
type = field.FieldType; // use primitive type of the enum enumBaseTypeCode = TypeAnalysis.GetTypeCode(field.FieldType); // use primitive type of the enum
} }
TypeCode enumBaseTypeCode = TypeAnalysis.GetTypeCode(type);
if (IsFlagsEnum(enumDefinition)) { if (IsFlagsEnum(enumDefinition)) {
long enumValue = val; long enumValue = val;
Expression expr = null; Expression expr = null;
@ -1266,7 +1543,7 @@ namespace ICSharpCode.Decompiler.Ast
continue; // skip None enum value continue; // skip None enum value
if ((fieldValue & enumValue) == fieldValue) { if ((fieldValue & enumValue) == fieldValue) {
var fieldExpression = ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field);
if (expr == null) if (expr == null)
expr = fieldExpression; expr = fieldExpression;
else else
@ -1275,7 +1552,7 @@ namespace ICSharpCode.Decompiler.Ast
enumValue &= ~fieldValue; enumValue &= ~fieldValue;
} }
if ((fieldValue & negatedEnumValue) == fieldValue) { if ((fieldValue & negatedEnumValue) == fieldValue) {
var fieldExpression = ConvertType(enumDefinition).Member(field.Name).WithAnnotation(field); var fieldExpression = ConvertType(type).Member(field.Name).WithAnnotation(field);
if (negatedExpr == null) if (negatedExpr == null)
negatedExpr = fieldExpression; negatedExpr = fieldExpression;
else else
@ -1293,7 +1570,7 @@ namespace ICSharpCode.Decompiler.Ast
return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr); return new UnaryOperatorExpression(UnaryOperatorType.BitNot, negatedExpr);
} }
} }
return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(enumDefinition)); return new Ast.PrimitiveExpression(CSharpPrimitiveCast.Cast(enumBaseTypeCode, val, false)).CastTo(ConvertType(type));
} }
} }
TypeCode code = TypeAnalysis.GetTypeCode(type); TypeCode code = TypeAnalysis.GetTypeCode(type);
@ -1309,5 +1586,88 @@ namespace ICSharpCode.Decompiler.Ast
return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute"); return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute");
} }
/// <summary>
/// Sets new modifier if the member hides some other member from a base type.
/// </summary>
/// <param name="member">The node of the member which new modifier state should be determined.</param>
static void SetNewModifier(AttributedNode member)
{
try {
bool addNewModifier = false;
if (member is IndexerDeclaration) {
var propertyDef = member.Annotation<PropertyDefinition>();
var baseProperties =
TypesHierarchyHelpers.FindBaseProperties(propertyDef);
addNewModifier = baseProperties.Any();
} else
addNewModifier = HidesBaseMember(member);
if (addNewModifier)
member.Modifiers |= Modifiers.New;
}
catch (ReferenceResolvingException) {
// TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references.
}
}
private static bool HidesBaseMember(AttributedNode member)
{
var memberDefinition = member.Annotation<IMemberDefinition>();
bool addNewModifier = false;
var methodDefinition = memberDefinition as MethodDefinition;
if (methodDefinition != null) {
addNewModifier = HidesByName(memberDefinition, includeBaseMethods: false);
if (!addNewModifier)
addNewModifier = TypesHierarchyHelpers.FindBaseMethods(methodDefinition).Any();
} else
addNewModifier = HidesByName(memberDefinition, includeBaseMethods: true);
return addNewModifier;
}
/// <summary>
/// Determines whether any base class member has the same name as the given member.
/// </summary>
/// <param name="member">The derived type's member.</param>
/// <param name="includeBaseMethods">true if names of methods declared in base types should also be checked.</param>
/// <returns>true if any base member has the same name as given member, otherwise false.</returns>
static bool HidesByName(IMemberDefinition member, bool includeBaseMethods)
{
Debug.Assert(!(member is PropertyDefinition) || !((PropertyDefinition)member).IsIndexer());
if (member.DeclaringType.BaseType != null) {
var baseTypeRef = member.DeclaringType.BaseType;
while (baseTypeRef != null) {
var baseType = baseTypeRef.ResolveOrThrow();
if (baseType.HasProperties && AnyIsHiddenBy(baseType.Properties, member, m => !m.IsIndexer()))
return true;
if (baseType.HasEvents && AnyIsHiddenBy(baseType.Events, member))
return true;
if (baseType.HasFields && AnyIsHiddenBy(baseType.Fields, member))
return true;
if (includeBaseMethods && baseType.HasMethods
&& AnyIsHiddenBy(baseType.Methods, member, m => !m.IsSpecialName))
return true;
if (baseType.HasNestedTypes && AnyIsHiddenBy(baseType.NestedTypes, member))
return true;
baseTypeRef = baseType.BaseType;
}
}
return false;
}
static bool AnyIsHiddenBy<T>(IEnumerable<T> members, IMemberDefinition derived, Predicate<T> condition = null)
where T : IMemberDefinition
{
return members.Any(m => m.Name == derived.Name
&& (condition == null || condition(m))
&& TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType));
}
/// <summary>
/// Gets the local variables for the current decompiled type, method, etc.
/// <remarks>The key is the metadata token.</remarks>
/// </summary>
public ConcurrentDictionary<int, IEnumerable<ILVariable>> LocalVariables { get; private set; }
} }
} }

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

@ -1,3 +1,21 @@
// 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;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
@ -32,9 +50,16 @@ namespace ICSharpCode.Decompiler.Ast
/// <param name="context">Decompilation context.</param> /// <param name="context">Decompilation context.</param>
/// <param name="parameters">Parameter declarations of the method being decompiled. /// <param name="parameters">Parameter declarations of the method being decompiled.
/// These are used to update the parameter names when the decompiler generates names for the parameters.</param> /// These are used to update the parameter names when the decompiler generates names for the parameters.</param>
/// <param name="localVariables">Local variables storage that will be filled/updated with the local variables.</param>
/// <returns>Block for the method body</returns> /// <returns>Block for the method body</returns>
public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context, IEnumerable<ParameterDeclaration> parameters = null) public static BlockStatement CreateMethodBody(MethodDefinition methodDef,
DecompilerContext context,
IEnumerable<ParameterDeclaration> parameters = null,
ConcurrentDictionary<int, IEnumerable<ILVariable>> localVariables = null)
{ {
if (localVariables == null)
localVariables = new ConcurrentDictionary<int, IEnumerable<ILVariable>>();
MethodDefinition oldCurrentMethod = context.CurrentMethod; MethodDefinition oldCurrentMethod = context.CurrentMethod;
Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef); Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
context.CurrentMethod = methodDef; context.CurrentMethod = methodDef;
@ -44,10 +69,10 @@ namespace ICSharpCode.Decompiler.Ast
builder.context = context; builder.context = context;
builder.typeSystem = methodDef.Module.TypeSystem; builder.typeSystem = methodDef.Module.TypeSystem;
if (Debugger.IsAttached) { if (Debugger.IsAttached) {
return builder.CreateMethodBody(parameters); return builder.CreateMethodBody(parameters, localVariables);
} else { } else {
try { try {
return builder.CreateMethodBody(parameters); return builder.CreateMethodBody(parameters, localVariables);
} catch (OperationCanceledException) { } catch (OperationCanceledException) {
throw; throw;
} catch (Exception ex) { } catch (Exception ex) {
@ -59,10 +84,14 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters) public BlockStatement CreateMethodBody(IEnumerable<ParameterDeclaration> parameters,
ConcurrentDictionary<int, IEnumerable<ILVariable>> localVariables)
{ {
if (methodDef.Body == null) return null; if (methodDef.Body == null) return null;
if (localVariables == null)
throw new ArgumentException("localVariables must be instantiated");
context.CancellationToken.ThrowIfCancellationRequested(); context.CancellationToken.ThrowIfCancellationRequested();
ILBlock ilMethod = new ILBlock(); ILBlock ilMethod = new ILBlock();
ILAstBuilder astBuilder = new ILAstBuilder(); ILAstBuilder astBuilder = new ILAstBuilder();
@ -102,6 +131,10 @@ namespace ICSharpCode.Decompiler.Ast
astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl);
} }
// store the variables - used for debugger
int token = methodDef.MetadataToken.ToInt32();
localVariables.AddOrUpdate(token, allVariables, (key, oldValue) => allVariables);
return astBlock; return astBlock;
} }
@ -215,10 +248,20 @@ namespace ICSharpCode.Decompiler.Ast
{ {
AstNode node = TransformByteCode(expr); AstNode node = TransformByteCode(expr);
Expression astExpr = node as Expression; Expression astExpr = node as Expression;
// get IL ranges - used in debugger
List<ILRange> ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive<ILExpression>().SelectMany(e => e.ILRanges));
AstNode result;
if (astExpr != null) if (astExpr != null)
return Convert(astExpr, expr.InferredType, expr.ExpectedType); result = Convert(astExpr, expr.InferredType, expr.ExpectedType);
else else
return node; result = node;
if (result != null)
return result.WithAnnotation(ilRanges);
return result;
} }
AstNode TransformByteCode(ILExpression byteCode) AstNode TransformByteCode(ILExpression byteCode)
@ -306,8 +349,7 @@ namespace ICSharpCode.Decompiler.Ast
} }
#endregion #endregion
#region Arrays #region Arrays
case ILCode.Newarr: case ILCode.Newarr: {
case ILCode.InitArray: {
var ace = new Ast.ArrayCreateExpression(); var ace = new Ast.ArrayCreateExpression();
ace.Type = operandAsTypeRef; ace.Type = operandAsTypeRef;
ComposedType ct = operandAsTypeRef as ComposedType; ComposedType ct = operandAsTypeRef as ComposedType;
@ -323,6 +365,38 @@ namespace ICSharpCode.Decompiler.Ast
} }
return ace; return ace;
} }
case ILCode.InitArray: {
var ace = new Ast.ArrayCreateExpression();
ace.Type = operandAsTypeRef;
ComposedType ct = operandAsTypeRef as ComposedType;
var arrayType = (ArrayType) operand;
if (ct != null)
{
// change "new (int[,])[10] to new int[10][,]"
ct.ArraySpecifiers.MoveTo(ace.AdditionalArraySpecifiers);
ace.Initializer = new ArrayInitializerExpression();
var first = ace.AdditionalArraySpecifiers.First();
first.Remove();
ace.Arguments.AddRange(Enumerable.Repeat(0, first.Dimensions).Select(i => new EmptyExpression()));
}
var newArgs = new List<Expression>();
foreach (var arrayDimension in arrayType.Dimensions.Skip(1).Reverse())
{
int length = (int)arrayDimension.UpperBound - (int)arrayDimension.LowerBound;
for (int j = 0; j < args.Count; j += length)
{
var child = new ArrayInitializerExpression();
child.Elements.AddRange(args.GetRange(j, length));
newArgs.Add(child);
}
var temp = args;
args = newArgs;
newArgs = temp;
newArgs.Clear();
}
ace.Initializer.Elements.AddRange(args);
return ace;
}
case ILCode.Ldlen: return arg1.Member("Length"); case ILCode.Ldlen: return arg1.Member("Length");
case ILCode.Ldelem_I: case ILCode.Ldelem_I:
case ILCode.Ldelem_I1: case ILCode.Ldelem_I1:
@ -460,9 +534,12 @@ namespace ICSharpCode.Decompiler.Ast
return arg1; return arg1;
else else
return arg1.CastTo(operandAsTypeRef); return arg1.CastTo(operandAsTypeRef);
case ILCode.Isinst: return arg1.CastAs(operandAsTypeRef); case ILCode.Isinst:
case ILCode.Box: return arg1; return arg1.CastAs(operandAsTypeRef);
case ILCode.Unbox: return InlineAssembly(byteCode, args); case ILCode.Box:
return arg1;
case ILCode.Unbox:
return MakeRef(arg1.CastTo(operandAsTypeRef));
#endregion #endregion
#region Indirect #region Indirect
case ILCode.Ldind_Ref: case ILCode.Ldind_Ref:
@ -514,11 +591,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Endfilter: return InlineAssembly(byteCode, args); case ILCode.Endfilter: return InlineAssembly(byteCode, args);
case ILCode.Endfinally: return null; case ILCode.Endfinally: return null;
case ILCode.Initblk: return InlineAssembly(byteCode, args); case ILCode.Initblk: return InlineAssembly(byteCode, args);
case ILCode.Initobj: case ILCode.Initobj: return InlineAssembly(byteCode, args);
if (args[0] is DirectionExpression)
return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), MakeDefaultValue((TypeReference)operand));
else
return InlineAssembly(byteCode, args);
case ILCode.DefaultValue: case ILCode.DefaultValue:
return MakeDefaultValue((TypeReference)operand); return MakeDefaultValue((TypeReference)operand);
case ILCode.Jmp: return InlineAssembly(byteCode, args); case ILCode.Jmp: return InlineAssembly(byteCode, args);
@ -577,7 +650,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand); case ILCode.Ldstr: return new Ast.PrimitiveExpression(operand);
case ILCode.Ldtoken: case ILCode.Ldtoken:
if (operand is Cecil.TypeReference) { if (operand is Cecil.TypeReference) {
return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle"); return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle");
} else { } else {
return InlineAssembly(byteCode, args); return InlineAssembly(byteCode, args);
} }
@ -678,24 +751,81 @@ namespace ICSharpCode.Decompiler.Ast
return new Ast.YieldBreakStatement(); return new Ast.YieldBreakStatement();
case ILCode.YieldReturn: case ILCode.YieldReturn:
return new Ast.YieldStatement { Expression = arg1 }; return new Ast.YieldStatement { Expression = arg1 };
case ILCode.InitCollection: { case ILCode.InitObject:
ObjectCreateExpression oce = (ObjectCreateExpression)arg1; case ILCode.InitCollection:
oce.Initializer = new ArrayInitializerExpression(); {
ArrayInitializerExpression initializer = new ArrayInitializerExpression();
for (int i = 1; i < args.Count; i++) { for (int i = 1; i < args.Count; i++) {
ArrayInitializerExpression aie = args[i] as ArrayInitializerExpression; Match m = objectInitializerPattern.Match(args[i]);
if (aie != null && aie.Elements.Count == 1) if (m.Success) {
oce.Initializer.Elements.Add(aie.Elements.Single().Detach()); MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("left").Single();
else initializer.Elements.Add(
oce.Initializer.Elements.Add(args[i]); new NamedArgumentExpression {
Identifier = mre.MemberName,
Expression = m.Get<Expression>("right").Single().Detach()
}.CopyAnnotationsFrom(mre));
} else {
m = collectionInitializerPattern.Match(args[i]);
if (m.Success) {
if (m.Get("arg").Count() == 1) {
initializer.Elements.Add(m.Get<Expression>("arg").Single().Detach());
} else {
ArrayInitializerExpression argList = new ArrayInitializerExpression();
foreach (var expr in m.Get<Expression>("arg")) {
argList.Elements.Add(expr.Detach());
}
initializer.Elements.Add(argList);
}
} else {
initializer.Elements.Add(args[i]);
}
}
} }
ObjectCreateExpression oce = arg1 as ObjectCreateExpression;
DefaultValueExpression dve = arg1 as DefaultValueExpression;
if (oce != null) {
oce.Initializer = initializer;
return oce; return oce;
} else if (dve != null) {
oce = new ObjectCreateExpression(dve.Type.Detach());
oce.CopyAnnotationsFrom(dve);
oce.Initializer = initializer;
return oce;
} else {
return new AssignmentExpression(arg1, initializer);
} }
case ILCode.InitCollectionAddMethod: {
var collectionInit = new ArrayInitializerExpression();
collectionInit.Elements.AddRange(args);
return collectionInit;
} }
default: throw new Exception("Unknown OpCode: " + byteCode.Code); case ILCode.InitializedObject:
return new InitializedObjectExpression();
case ILCode.AddressOf:
return MakeRef(arg1);
default:
throw new Exception("Unknown OpCode: " + byteCode.Code);
}
}
static readonly AstNode objectInitializerPattern = new AssignmentExpression(
new MemberReferenceExpression {
Target = new InitializedObjectExpression()
}.WithName("left"),
new AnyNode("right")
);
static readonly AstNode collectionInitializerPattern = new InvocationExpression {
Target = new MemberReferenceExpression {
Target = new InitializedObjectExpression(),
MemberName = "Add"
},
Arguments = { new Repeat(new AnyNode("arg")) }
};
sealed class InitializedObjectExpression : IdentifierExpression
{
public InitializedObjectExpression() : base("__initialized_object__") {}
protected override bool DoMatch(AstNode other, Match match)
{
return other is InitializedObjectExpression;
} }
} }
@ -810,8 +940,6 @@ namespace ICSharpCode.Decompiler.Ast
if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) { if (cecilMethod.Name == "Get" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return target.Indexer(methodArgs); return target.Indexer(methodArgs);
} else if (cecilMethod.Name == "Address" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 1) {
return MakeRef(target.Indexer(methodArgs));
} else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) { } else if (cecilMethod.Name == "Set" && cecilMethod.DeclaringType is ArrayType && methodArgs.Count > 2) {
return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last()); return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last());
} }

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

@ -1,9 +1,25 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.TypeSystem; using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil; using Mono.Cecil;
@ -12,7 +28,7 @@ namespace ICSharpCode.Decompiler.Ast
/// <summary> /// <summary>
/// ITypeResolveContext implementation that lazily loads types from Cecil. /// ITypeResolveContext implementation that lazily loads types from Cecil.
/// </summary> /// </summary>
public class CecilTypeResolveContext : ISynchronizedTypeResolveContext, IProjectContent public class CecilTypeResolveContext : AbstractAnnotatable, ISynchronizedTypeResolveContext, IProjectContent
{ {
readonly ModuleDefinition module; readonly ModuleDefinition module;
readonly string[] namespaces; readonly string[] namespaces;
@ -76,7 +92,7 @@ namespace ICSharpCode.Decompiler.Ast
public IList<IAttribute> AssemblyAttributes { get; private set; } public IList<IAttribute> AssemblyAttributes { get; private set; }
public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer) public ITypeDefinition GetTypeDefinition(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
{ {
if (typeParameterCount > 0) if (typeParameterCount > 0)
name = name + "`" + typeParameterCount.ToString(); name = name + "`" + typeParameterCount.ToString();
@ -98,14 +114,14 @@ namespace ICSharpCode.Decompiler.Ast
return null; return null;
} }
public IEnumerable<ITypeDefinition> GetClasses() public IEnumerable<ITypeDefinition> GetTypes()
{ {
foreach (TypeDefinition cecilType in module.Types) { foreach (TypeDefinition cecilType in module.Types) {
yield return GetClass(cecilType); yield return GetClass(cecilType);
} }
} }
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer) public IEnumerable<ITypeDefinition> GetTypes(string nameSpace, StringComparer nameComparer)
{ {
foreach (TypeDefinition cecilType in module.Types) { foreach (TypeDefinition cecilType in module.Types) {
if (nameComparer.Equals(nameSpace, cecilType.Namespace)) if (nameComparer.Equals(nameSpace, cecilType.Namespace))
@ -144,5 +160,14 @@ namespace ICSharpCode.Decompiler.Ast
{ {
// exit from Synchronize() block // exit from Synchronize() block
} }
IEnumerable<IParsedFile> IProjectContent.Files {
get { return new IParsedFile[0]; }
}
IParsedFile IProjectContent.GetFile(string fileName)
{
return null;
}
} }
} }

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Linq; using System.Linq;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -42,8 +57,23 @@ namespace ICSharpCode.Decompiler.Ast
foreach (var p in parameters) foreach (var p in parameters)
nv.AddExistingName(p.Name); nv.AddExistingName(p.Name);
foreach (var v in variables) { foreach (var v in variables) {
if (v.IsGenerated) if (v.IsGenerated) {
// don't introduce names for variables generated by ILSpy - keep "expr"/"arg"
nv.AddExistingName(v.Name); 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: // Now generate names:
foreach (ILVariable p in parameters) { foreach (ILVariable p in parameters) {
@ -51,10 +81,22 @@ namespace ICSharpCode.Decompiler.Ast
p.Name = nv.GenerateNameForVariable(p, methodBody); p.Name = nv.GenerateNameForVariable(p, methodBody);
} }
foreach (ILVariable varDef in variables) { foreach (ILVariable varDef in variables) {
if (!varDef.IsGenerated) { if (string.IsNullOrEmpty(varDef.Name))
varDef.Name = nv.GenerateNameForVariable(varDef, methodBody); 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; DecompilerContext context;
@ -107,10 +149,10 @@ namespace ICSharpCode.Decompiler.Ast
string nameWithoutDigits = SplitName(oldVariableName, out number); string nameWithoutDigits = SplitName(oldVariableName, out number);
if (!typeNames.ContainsKey(nameWithoutDigits)) { if (!typeNames.ContainsKey(nameWithoutDigits)) {
typeNames.Add(nameWithoutDigits, 0); typeNames.Add(nameWithoutDigits, number - 1);
} }
int count = ++typeNames[nameWithoutDigits]; int count = ++typeNames[nameWithoutDigits];
if (count > 1) { if (count != 1) {
return nameWithoutDigits + count.ToString(); return nameWithoutDigits + count.ToString();
} else { } else {
return nameWithoutDigits; return nameWithoutDigits;
@ -255,6 +297,8 @@ namespace ICSharpCode.Decompiler.Ast
string GetNameByType(TypeReference type) string GetNameByType(TypeReference type)
{ {
type = TypeAnalysis.UnpackModifiers(type);
GenericInstanceType git = type as GenericInstanceType; GenericInstanceType git = type as GenericInstanceType;
if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) { if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) {
type = ((GenericInstanceType)type).GenericArguments[0]; type = ((GenericInstanceType)type).GenericArguments[0];
@ -265,6 +309,8 @@ namespace ICSharpCode.Decompiler.Ast
name = "array"; name = "array";
} else if (type.IsPointer || type.IsByReference) { } else if (type.IsPointer || type.IsByReference) {
name = "ptr"; name = "ptr";
} else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {
name = "ex";
} else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) { } else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) {
name = type.Name; name = type.Name;
// remove the 'I' for interfaces // remove the 'I' for interfaces

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

@ -1,10 +1,27 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil; using Mono.Cecil;
@ -15,6 +32,7 @@ namespace ICSharpCode.Decompiler.Ast
readonly ITextOutput output; readonly ITextOutput output;
readonly Stack<AstNode> nodeStack = new Stack<AstNode>(); readonly Stack<AstNode> nodeStack = new Stack<AstNode>();
int braceLevelWithinType = -1; int braceLevelWithinType = -1;
bool inDocumentationComment = false;
public TextOutputFormatter(ITextOutput output) public TextOutputFormatter(ITextOutput output)
{ {
@ -113,14 +131,56 @@ namespace ICSharpCode.Decompiler.Ast
output.Write("*/"); output.Write("*/");
break; break;
case CommentType.Documentation: case CommentType.Documentation:
if (!inDocumentationComment)
output.MarkFoldStart("///" + content, true);
output.Write("///"); output.Write("///");
output.WriteLine(content); output.Write(content);
inDocumentationComment = true;
bool isLastLine = !(nodeStack.Peek().NextSibling is Comment);
if (isLastLine) {
inDocumentationComment = false;
output.MarkFoldEnd();
}
output.WriteLine();
break; break;
} }
} }
public void StartNode(AstNode node) 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); nodeStack.Push(node);
} }

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Linq; using System.Linq;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Linq; using System.Linq;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Diagnostics; using System.Diagnostics;

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

@ -1,7 +1,23 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.PatternMatching;
@ -54,9 +70,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data) public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{ {
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray(); // 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(); var instanceCtorsNotChainingWithThis = instanceCtors.Where(ctor => !thisCallPattern.IsMatch(ctor.Body.Statements.FirstOrDefault())).ToArray();
if (instanceCtorsNotChainingWithThis.Length > 0 && typeDeclaration.ClassType == NRefactory.TypeSystem.ClassType.Class) { if (instanceCtorsNotChainingWithThis.Length > 0) {
MethodDefinition ctorMethodDef = instanceCtorsNotChainingWithThis[0].Annotation<MethodDefinition>();
if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
return;
// Recognize field initializers: // Recognize field initializers:
// Convert first statement in all ctors (if all ctors have the same statement) into a field initializer. // Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
bool allSame; bool allSame;
@ -68,7 +104,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule(); FieldDefinition fieldDef = m.Get<AstNode>("fieldAccess").Single().Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null) if (fieldDef == null)
break; break;
AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef); AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldOrEventDecl == null) if (fieldOrEventDecl == null)
break; break;
@ -84,11 +120,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
} while (allSame); } while (allSame);
} }
}
// Now convert base constructor calls to initializers: void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
base.VisitTypeDeclaration(typeDeclaration, data); {
var instanceCtors = typeDeclaration.Members.OfType<ConstructorDeclaration>().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
// Remove single empty constructor:
if (instanceCtors.Length == 1) { if (instanceCtors.Length == 1) {
ConstructorDeclaration emptyCtor = new ConstructorDeclaration(); ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public); emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
@ -96,12 +132,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (emptyCtor.IsMatch(instanceCtors[0])) if (emptyCtor.IsMatch(instanceCtors[0]))
instanceCtors[0].Remove(); instanceCtors[0].Remove();
} }
}
void HandleStaticFieldInitializers(IEnumerable<AstNode> members)
{
// Convert static constructor into field initializers if the class is BeforeFieldInit // Convert static constructor into field initializers if the class is BeforeFieldInit
var staticCtor = typeDeclaration.Members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static); var staticCtor = members.OfType<ConstructorDeclaration>().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
if (staticCtor != null) { if (staticCtor != null) {
TypeDefinition typeDef = typeDeclaration.Annotation<TypeDefinition>(); MethodDefinition ctorMethodDef = staticCtor.Annotation<MethodDefinition>();
if (typeDef != null && typeDef.IsBeforeFieldInit) { if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
while (true) { while (true) {
ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement; ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (es == null) if (es == null)
@ -112,7 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule(); FieldDefinition fieldDef = assignment.Left.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef == null || !fieldDef.IsStatic) if (fieldDef == null || !fieldDef.IsStatic)
break; break;
FieldDeclaration fieldDecl = typeDeclaration.Members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef); FieldDeclaration fieldDecl = members.OfType<FieldDeclaration>().FirstOrDefault(f => f.Annotation<FieldDefinition>() == fieldDef);
if (fieldDecl == null) if (fieldDecl == null)
break; break;
fieldDecl.Variables.Single().Initializer = assignment.Right.Detach(); fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
@ -122,11 +161,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
staticCtor.Remove(); staticCtor.Remove();
} }
} }
return null;
} }
void IAstTransform.Run(AstNode node) 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); node.AcceptVisitor(this, null);
} }
} }

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

@ -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);
}
}
}

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -46,7 +61,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data) public override object VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, object data)
{ {
if (objectCreateExpression.Arguments.Count() == 2) { if (objectCreateExpression.Arguments.Count == 2) {
Expression obj = objectCreateExpression.Arguments.First(); Expression obj = objectCreateExpression.Arguments.First();
Expression func = objectCreateExpression.Arguments.Last(); Expression func = objectCreateExpression.Arguments.Last();
Annotation annotation = func.Annotation<Annotation>(); Annotation annotation = func.Annotation<Annotation>();
@ -100,7 +115,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method) internal static bool IsAnonymousMethod(DecompilerContext context, MethodDefinition method)
{ {
if (method == null || !method.Name.StartsWith("<", StringComparison.Ordinal)) if (method == null || !(method.Name.StartsWith("<", StringComparison.Ordinal) || method.Name.Contains("$")))
return false; return false;
if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType))) if (!(method.IsCompilerGenerated() || IsPotentialClosure(context, method.DeclaringType)))
return false; return false;
@ -111,6 +126,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
{ {
if (!context.Settings.AnonymousMethods) if (!context.Settings.AnonymousMethods)
return false; // anonymous method decompilation is disabled 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 // Anonymous methods are defined in the same assembly
MethodDefinition method = methodRef.ResolveWithinSameModule(); MethodDefinition method = methodRef.ResolveWithinSameModule();
@ -119,6 +136,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// Create AnonymousMethodExpression and prepare parameters // Create AnonymousMethodExpression and prepare parameters
AnonymousMethodExpression ame = new AnonymousMethodExpression(); 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.Parameters.AddRange(AstBuilder.MakeParameters(method, isLambda: true));
ame.HasParameterList = true; ame.HasParameterList = true;
@ -163,6 +183,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
if (isLambda) { if (isLambda) {
LambdaExpression lambda = new LambdaExpression(); LambdaExpression lambda = new LambdaExpression();
lambda.CopyAnnotationsFrom(ame);
ame.Parameters.MoveTo(lambda.Parameters); ame.Parameters.MoveTo(lambda.Parameters);
Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression; Expression returnExpr = ((ReturnStatement)body.Statements.Single()).Expression;
returnExpr.Remove(); returnExpr.Remove();
@ -175,7 +196,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return true; return true;
} }
static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass) internal static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass)
{ {
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass()) if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return false; return false;
@ -328,12 +349,30 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (right is ThisReferenceExpression) { if (right is ThisReferenceExpression) {
isParameter = true; isParameter = true;
} else if (right is IdentifierExpression) { } else if (right is IdentifierExpression) {
// handle parameters only if the whole method contains no other occurrance except for 'right' // handle parameters only if the whole method contains no other occurrence except for 'right'
ILVariable v = right.Annotation<ILVariable>(); ILVariable v = right.Annotation<ILVariable>();
isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1; isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
if (!isParameter && TypeAnalysis.IsSameType(v.Type, fieldDef.FieldType) && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) { if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
// parent display class within the same method
// (closure2.localsX = closure1;)
isDisplayClassParentPointerAssignment = true; 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) { if (isParameter || isDisplayClassParentPointerAssignment) {
dict[fieldDef] = right; dict[fieldDef] = right;
@ -349,12 +388,17 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// Now create variables for all fields of the display class (except for those that we already handled as parameters) // 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>>(); List<Tuple<AstType, string>> variablesToDeclare = new List<Tuple<AstType, string>>();
foreach (FieldDefinition field in type.Fields) { 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 if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
continue; continue;
EnsureVariableNameIsAvailable(blockStatement, field.Name); string capturedVariableName = field.Name;
currentlyUsedVariableNames.Add(field.Name); if (capturedVariableName.StartsWith("$VB$Local_", StringComparison.Ordinal) && capturedVariableName.Length > 10)
variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), field.Name)); capturedVariableName = capturedVariableName.Substring(10);
dict[field] = new IdentifierExpression(field.Name); 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: // Now figure out where the closure was accessed and use the simpler replacement expression there:

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Linq; using System.Linq;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Diagnostics; using System.Diagnostics;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -12,14 +27,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
/// <summary> /// <summary>
/// Introduces using declarations. /// Introduces using declarations.
/// </summary> /// </summary>
public class IntroduceUsingDeclarations : DepthFirstAstVisitor<object, object>, IAstTransform public class IntroduceUsingDeclarations : IAstTransform
{ {
DecompilerContext context; DecompilerContext context;
public IntroduceUsingDeclarations(DecompilerContext context) public IntroduceUsingDeclarations(DecompilerContext context)
{ {
this.context = context; this.context = context;
currentNamespace = context.CurrentType != null ? context.CurrentType.Namespace : string.Empty;
} }
public void Run(AstNode compilationUnit) public void Run(AstNode compilationUnit)
@ -28,7 +42,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return; return;
// First determine all the namespaces that need to be imported: // First determine all the namespaces that need to be imported:
compilationUnit.AcceptVisitor(this, null); compilationUnit.AcceptVisitor(new FindRequiredImports(this), null);
importedNamespaces.Add("System"); // always import System, even when not necessary importedNamespaces.Add("System"); // always import System, even when not necessary
@ -55,16 +69,27 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
// verify that the SimpleTypes refer to the correct type (no ambiguities) // verify that the SimpleTypes refer to the correct type (no ambiguities)
FullyQualifyAmbiguousTypeNames(compilationUnit); compilationUnit.AcceptVisitor(new FullyQualifyAmbiguousTypeNamesVisitor(this), null);
} }
readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty }; readonly HashSet<string> declaredNamespaces = new HashSet<string>() { string.Empty };
readonly HashSet<string> importedNamespaces = new HashSet<string>(); 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> availableTypeNames = new HashSet<string>();
readonly HashSet<string> ambiguousTypeNames = new HashSet<string>(); readonly HashSet<string> ambiguousTypeNames = new HashSet<string>();
sealed class FindRequiredImports : DepthFirstAstVisitor<object, object>
{
readonly IntroduceUsingDeclarations transform;
string currentNamespace; 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) bool IsParentOfCurrentNamespace(string ns)
{ {
if (ns.Length == 0) if (ns.Length == 0)
@ -82,7 +107,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
{ {
TypeReference tr = simpleType.Annotation<TypeReference>(); TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) { if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) {
importedNamespaces.Add(tr.Namespace); transform.importedNamespaces.Add(tr.Namespace);
} }
return base.VisitSimpleType(simpleType, data); // also visit type arguments return base.VisitSimpleType(simpleType, data); // also visit type arguments
} }
@ -92,12 +117,13 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
string oldNamespace = currentNamespace; string oldNamespace = currentNamespace;
foreach (Identifier ident in namespaceDeclaration.Identifiers) { foreach (Identifier ident in namespaceDeclaration.Identifiers) {
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name); currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name);
declaredNamespaces.Add(currentNamespace); transform.declaredNamespaces.Add(currentNamespace);
} }
base.VisitNamespaceDeclaration(namespaceDeclaration, data); base.VisitNamespaceDeclaration(namespaceDeclaration, data);
currentNamespace = oldNamespace; currentNamespace = oldNamespace;
return null; return null;
} }
}
void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible) void FindAmbiguousTypeNames(ModuleDefinition module, bool internalsVisible)
{ {
@ -111,17 +137,176 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
} }
void FullyQualifyAmbiguousTypeNames(AstNode compilationUnit) 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)
{ {
foreach (SimpleType simpleType in compilationUnit.Descendants.OfType<SimpleType>()) { // 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>(); TypeReference tr = simpleType.Annotation<TypeReference>();
if (tr != null && ambiguousTypeNames.Contains(tr.Name)) { // Fully qualify any ambiguous type names.
if (tr != null && IsAmbiguous(tr.Namespace, tr.Name)) {
AstType ns; AstType ns;
if (string.IsNullOrEmpty(tr.Namespace)) { if (string.IsNullOrEmpty(tr.Namespace)) {
ns = new SimpleType("global"); ns = new SimpleType("global");
} else { } else {
string[] parts = tr.Namespace.Split('.'); 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]); ns = new SimpleType(parts[0]);
}
for (int i = 1; i < parts.Length; i++) { for (int i = 1; i < parts.Length; i++) {
ns = new MemberType { Target = ns, MemberName = parts[i] }; ns = new MemberType { Target = ns, MemberName = parts[i] };
} }
@ -134,6 +319,41 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
simpleType.TypeArguments.MoveTo(mt.TypeArguments); simpleType.TypeArguments.MoveTo(mt.TypeArguments);
simpleType.ReplaceWith(mt); 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);
} }
} }
} }

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -47,6 +62,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
result = TransformUsings(expressionStatement); result = TransformUsings(expressionStatement);
if (result != null) if (result != null)
return result; return result;
result = TransformNonGenericForEach(expressionStatement);
if (result != null)
return result;
} }
result = TransformFor(expressionStatement); result = TransformFor(expressionStatement);
if (result != null) if (result != null)
@ -118,7 +136,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
#endregion #endregion
/// <summary> /// <summary>
/// $type $variable = $initializer; /// $variable = $initializer;
/// </summary> /// </summary>
static readonly AstNode variableAssignPattern = new ExpressionStatement( static readonly AstNode variableAssignPattern = new ExpressionStatement(
new AssignmentExpression( new AssignmentExpression(
@ -136,7 +154,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
static readonly AstNode usingTryCatchPattern = new TryCatchStatement { static readonly AstNode usingTryCatchPattern = new TryCatchStatement {
TryBlock = new AnyNode("body"), TryBlock = new AnyNode(),
FinallyBlock = new BlockStatement { FinallyBlock = new BlockStatement {
new Choice { new Choice {
{ "valueType", { "valueType",
@ -162,45 +180,61 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
{ {
Match m1 = variableAssignPattern.Match(node); Match m1 = variableAssignPattern.Match(node);
if (!m1.Success) return null; if (!m1.Success) return null;
AstNode tryCatch = node.NextSibling; TryCatchStatement tryCatch = node.NextSibling as TryCatchStatement;
Match m2 = usingTryCatchPattern.Match(tryCatch); Match m2 = usingTryCatchPattern.Match(tryCatch);
if (!m2.Success) return null; if (!m2.Success) return null;
string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier; string variableName = m1.Get<IdentifierExpression>("variable").Single().Identifier;
if (variableName == m2.Get<IdentifierExpression>("ident").Single().Identifier) { if (variableName != m2.Get<IdentifierExpression>("ident").Single().Identifier)
return null;
if (m2.Has("valueType")) { if (m2.Has("valueType")) {
// if there's no if(x!=null), then it must be a value type // if there's no if(x!=null), then it must be a value type
ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>(); ILVariable v = m1.Get<AstNode>("variable").Single().Annotation<ILVariable>();
if (v == null || v.Type == null || !v.Type.IsValueType) if (v == null || v.Type == null || !v.Type.IsValueType)
return null; return null;
} }
// There are two variants of the using statement:
// "using (var a = init)" and "using (expr)".
// The former declares a read-only variable 'a', and the latter declares an unnamed read-only variable
// to store the original value of 'expr'.
// This means that in order to introduce a using statement, in both cases we need to detect a read-only
// variable that is used only within that block.
if (HasAssignment(tryCatch, variableName))
return null;
VariableDeclarationStatement varDecl = FindVariableDeclaration(node, variableName);
if (varDecl == null || !(varDecl.Parent is BlockStatement))
return null;
// Validate that the variable is not used after the using statement:
if (!IsVariableValueUnused(varDecl, tryCatch))
return null;
node.Remove(); node.Remove();
BlockStatement body = m2.Get<BlockStatement>("body").Single();
UsingStatement usingStatement = new UsingStatement(); UsingStatement usingStatement = new UsingStatement();
usingStatement.ResourceAcquisition = node.Expression.Detach(); usingStatement.EmbeddedStatement = tryCatch.TryBlock.Detach();
usingStatement.EmbeddedStatement = body.Detach();
tryCatch.ReplaceWith(usingStatement); tryCatch.ReplaceWith(usingStatement);
// Move the variable declaration into the resource acquisition, if possible
// This is necessary for the foreach-pattern to work on the result of the using-pattern // If possible, we'll eliminate the variable completely:
VariableDeclarationStatement varDecl = FindVariableDeclaration(usingStatement, variableName); if (usingStatement.EmbeddedStatement.Descendants.OfType<IdentifierExpression>().Any(ident => ident.Identifier == variableName)) {
if (varDecl != null && varDecl.Parent is BlockStatement) { // variable is used, so we'll create a variable declaration
Statement declarationPoint;
if (CanMoveVariableDeclarationIntoStatement(varDecl, usingStatement, out declarationPoint)) {
// Moving the variable into the UsingStatement is allowed:
usingStatement.ResourceAcquisition = new VariableDeclarationStatement { usingStatement.ResourceAcquisition = new VariableDeclarationStatement {
Type = (AstType)varDecl.Type.Clone(), Type = (AstType)varDecl.Type.Clone(),
Variables = { Variables = {
new VariableInitializer { new VariableInitializer {
Name = variableName, Name = variableName,
Initializer = m1.Get<Expression>("initializer").Single().Detach() Initializer = m1.Get<Expression>("initializer").Single().Detach()
}.CopyAnnotationsFrom(usingStatement.ResourceAcquisition) }.CopyAnnotationsFrom(node.Expression)
} }
}.CopyAnnotationsFrom(node); }.CopyAnnotationsFrom(node);
} } else {
// the variable is never used; eliminate it:
usingStatement.ResourceAcquisition = m1.Get<Expression>("initializer").Single().Detach();
} }
return usingStatement; return usingStatement;
} }
return null;
}
internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier) internal static VariableDeclarationStatement FindVariableDeclaration(AstNode node, string identifier)
{ {
@ -217,6 +251,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null; return null;
} }
/// <summary>
/// Gets whether the old variable value (assigned inside 'targetStatement' or earlier)
/// is read anywhere in the remaining scope of the variable declaration.
/// </summary>
bool IsVariableValueUnused(VariableDeclarationStatement varDecl, Statement targetStatement)
{
Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent));
BlockStatement block = (BlockStatement)varDecl.Parent;
DefiniteAssignmentAnalysis daa = new DefiniteAssignmentAnalysis(block, context.CancellationToken);
daa.SetAnalyzedRange(targetStatement, block, startInclusive: false);
daa.Analyze(varDecl.Variables.Single().Name);
return daa.UnassignedVariableUses.Count == 0;
}
// I used this in the first implementation of the using-statement transform, but now no longer
// because there were problems when multiple using statements were using the same variable
// - no single using statement could be transformed without making the C# code invalid,
// but transforming both would work.
// We now use 'IsVariableValueUnused' which will perform the transform
// even if it results in two variables with the same name and overlapping scopes.
// (this issue could be fixed later by renaming one of the variables)
// I'm not sure whether the other consumers of 'CanMoveVariableDeclarationIntoStatement' should be changed the same way.
bool CanMoveVariableDeclarationIntoStatement(VariableDeclarationStatement varDecl, Statement targetStatement, out Statement declarationPoint) bool CanMoveVariableDeclarationIntoStatement(VariableDeclarationStatement varDecl, Statement targetStatement, out Statement declarationPoint)
{ {
Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent)); Debug.Assert(targetStatement.Ancestors.Contains(varDecl.Parent));
@ -233,10 +290,28 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
return true; return true;
} }
/// <summary>
/// Gets whether there is an assignment to 'variableName' anywhere within the given node.
/// </summary>
bool HasAssignment(AstNode root, string variableName)
{
foreach (AstNode node in root.DescendantsAndSelf) {
IdentifierExpression ident = node as IdentifierExpression;
if (ident != null && ident.Identifier == variableName) {
if (ident.Parent is AssignmentExpression && ident.Role == AssignmentExpression.LeftRole
|| ident.Parent is DirectionExpression)
{
return true;
}
}
}
return false;
}
#endregion #endregion
#region foreach #region foreach (generic)
static readonly UsingStatement foreachPattern = new UsingStatement { static readonly UsingStatement genericForeachPattern = new UsingStatement {
ResourceAcquisition = new VariableDeclarationStatement { ResourceAcquisition = new VariableDeclarationStatement {
Type = new AnyNode("enumeratorType"), Type = new AnyNode("enumeratorType"),
Variables = { Variables = {
@ -270,7 +345,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public ForeachStatement TransformForeach(UsingStatement node) public ForeachStatement TransformForeach(UsingStatement node)
{ {
Match m = foreachPattern.Match(node); Match m = genericForeachPattern.Match(node);
if (!m.Success) if (!m.Success)
return null; return null;
if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) { if (!(node.Parent is BlockStatement) && m.Has("variablesOutsideLoop")) {
@ -308,8 +383,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
InExpression = m.Get<Expression>("collection").Single().Detach(), InExpression = m.Get<Expression>("collection").Single().Detach(),
EmbeddedStatement = newBody EmbeddedStatement = newBody
}; };
if (foreachStatement.InExpression is BaseReferenceExpression) if (foreachStatement.InExpression is BaseReferenceExpression) {
foreachStatement.InExpression = new ThisReferenceExpression(); foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
}
node.ReplaceWith(foreachStatement); node.ReplaceWith(foreachStatement);
foreach (Statement stmt in m.Get<Statement>("variablesOutsideLoop")) { foreach (Statement stmt in m.Get<Statement>("variablesOutsideLoop")) {
((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach()); ((BlockStatement)foreachStatement.Parent).Statements.InsertAfter(null, stmt.Detach());
@ -318,6 +394,117 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
} }
#endregion #endregion
#region foreach (non-generic)
ExpressionStatement getEnumeratorPattern = new ExpressionStatement(
new AssignmentExpression(
new NamedNode("left", new IdentifierExpression()),
new AnyNode("collection").ToExpression().Invoke("GetEnumerator")
));
TryCatchStatement nonGenericForeachPattern = new TryCatchStatement {
TryBlock = new BlockStatement {
new WhileStatement {
Condition = new IdentifierExpression().WithName("enumerator").Invoke("MoveNext"),
EmbeddedStatement = new BlockStatement {
new AssignmentExpression(
new IdentifierExpression().WithName("itemVar"),
new Choice {
new Backreference("enumerator").ToExpression().Member("Current"),
new CastExpression {
Type = new AnyNode("castType"),
Expression = new Backreference("enumerator").ToExpression().Member("Current")
}
}
),
new Repeat(new AnyNode("stmt")).ToStatement()
}
}.WithName("loop")
},
FinallyBlock = new BlockStatement {
new AssignmentExpression(
new IdentifierExpression().WithName("disposable"),
new Backreference("enumerator").ToExpression().CastAs(new TypePattern(typeof(IDisposable)))
),
new IfElseStatement {
Condition = new BinaryOperatorExpression {
Left = new Backreference("disposable"),
Operator = BinaryOperatorType.InEquality,
Right = new NullReferenceExpression()
},
TrueStatement = new BlockStatement {
new Backreference("disposable").ToExpression().Invoke("Dispose")
}
}
}};
public ForeachStatement TransformNonGenericForEach(ExpressionStatement node)
{
Match m1 = getEnumeratorPattern.Match(node);
if (!m1.Success) return null;
AstNode tryCatch = node.NextSibling;
Match m2 = nonGenericForeachPattern.Match(tryCatch);
if (!m2.Success) return null;
IdentifierExpression enumeratorVar = m2.Get<IdentifierExpression>("enumerator").Single();
IdentifierExpression itemVar = m2.Get<IdentifierExpression>("itemVar").Single();
WhileStatement loop = m2.Get<WhileStatement>("loop").Single();
// verify that the getEnumeratorPattern assigns to the same variable as the nonGenericForeachPattern is reading from
if (!enumeratorVar.IsMatch(m1.Get("left").Single()))
return null;
VariableDeclarationStatement enumeratorVarDecl = FindVariableDeclaration(loop, enumeratorVar.Identifier);
if (enumeratorVarDecl == null || !(enumeratorVarDecl.Parent is BlockStatement))
return null;
// Find the declaration of the item variable:
// Because we look only outside the loop, we won't make the mistake of moving a captured variable across the loop boundary
VariableDeclarationStatement itemVarDecl = FindVariableDeclaration(loop, itemVar.Identifier);
if (itemVarDecl == null || !(itemVarDecl.Parent is BlockStatement))
return null;
// Now verify that we can move the variable declaration in front of the loop:
Statement declarationPoint;
CanMoveVariableDeclarationIntoStatement(itemVarDecl, loop, out declarationPoint);
// We ignore the return value because we don't care whether we can move the variable into the loop
// (that is possible only with non-captured variables).
// We just care that we can move it in front of the loop:
if (declarationPoint != loop)
return null;
ForeachStatement foreachStatement = new ForeachStatement();
foreachStatement.VariableType = itemVarDecl.Type.Clone();
foreachStatement.VariableName = itemVar.Identifier;
BlockStatement body = new BlockStatement();
foreachStatement.EmbeddedStatement = body;
((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement);
body.Add(node.Detach());
body.Add((Statement)tryCatch.Detach());
// Now that we moved the whole try-catch into the foreach loop; verify that we can
// move the enumerator into the foreach loop:
CanMoveVariableDeclarationIntoStatement(enumeratorVarDecl, foreachStatement, out declarationPoint);
if (declarationPoint != foreachStatement) {
// oops, the enumerator variable can't be moved into the foreach loop
// Undo our AST changes:
((BlockStatement)foreachStatement.Parent).Statements.InsertBefore(foreachStatement, node.Detach());
foreachStatement.ReplaceWith(tryCatch);
return null;
}
// Now create the correct body for the foreach statement:
foreachStatement.InExpression = m1.Get<Expression>("collection").Single().Detach();
if (foreachStatement.InExpression is BaseReferenceExpression) {
foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression);
}
body.Statements.Clear();
body.Statements.AddRange(m2.Get<Statement>("stmt").Select(stmt => stmt.Detach()));
return foreachStatement;
}
#endregion
#region for #region for
static readonly WhileStatement forPattern = new WhileStatement { static readonly WhileStatement forPattern = new WhileStatement {
Condition = new BinaryOperatorExpression { Condition = new BinaryOperatorExpression {
@ -515,12 +702,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
Match m = switchOnStringPattern.Match(node); Match m = switchOnStringPattern.Match(node);
if (!m.Success) if (!m.Success)
return null; return null;
if (m.Has("nonNullDefaultStmt") && !m.Has("nullStmt"))
return null;
// switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment // switchVar must be the same as switchExpr; or switchExpr must be an assignment and switchVar the left side of that assignment
if (!m.Get("switchVar").Single().IsMatch(m.Get("switchExpr").Single())) { if (!m.Get("switchVar").Single().IsMatch(m.Get("switchExpr").Single())) {
AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression; AssignmentExpression assign = m.Get("switchExpr").Single() as AssignmentExpression;
if (!m.Get("switchVar").Single().IsMatch(assign.Left)) if (!(assign != null && m.Get("switchVar").Single().IsMatch(assign.Left)))
return null; return null;
} }
FieldReference cachedDictField = m.Get<AstNode>("cachedDict").Single().Annotation<FieldReference>(); FieldReference cachedDictField = m.Get<AstNode>("cachedDict").Single().Annotation<FieldReference>();
@ -551,16 +736,22 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
block.Statements.Add(new BreakStatement()); block.Statements.Add(new BreakStatement());
section.Statements.Add(block.Detach()); section.Statements.Add(block.Detach());
sw.SwitchSections.Add(section); sw.SwitchSections.Add(section);
} else if (m.Has("nonNullDefaultStmt")) {
sw.SwitchSections.Add(
new SwitchSection {
CaseLabels = { new CaseLabel { Expression = new NullReferenceExpression() } },
Statements = { new BlockStatement { new BreakStatement() } }
});
}
if (m.Has("nonNullDefaultStmt")) { if (m.Has("nonNullDefaultStmt")) {
section = new SwitchSection(); SwitchSection section = new SwitchSection();
section.CaseLabels.Add(new CaseLabel()); section.CaseLabels.Add(new CaseLabel());
block = new BlockStatement(); BlockStatement block = new BlockStatement();
block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach())); block.Statements.AddRange(m.Get<Statement>("nonNullDefaultStmt").Select(s => s.Detach()));
block.Add(new BreakStatement()); block.Add(new BreakStatement());
section.Statements.Add(block); section.Statements.Add(block);
sw.SwitchSections.Add(section); sw.SwitchSections.Add(section);
} }
}
node.ReplaceWith(sw); node.ReplaceWith(sw);
return sw; return sw;
} }

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

@ -1,4 +1,22 @@
using System; // 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.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp;

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

@ -1,4 +1,22 @@
using System; // 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.Generic;
using System.Linq; using System.Linq;
using ICSharpCode.NRefactory.PatternMatching; using ICSharpCode.NRefactory.PatternMatching;
@ -32,7 +50,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
var arguments = invocationExpression.Arguments.ToArray(); var arguments = invocationExpression.Arguments.ToArray();
// Reduce "String.Concat(a, b)" to "a + b" // Reduce "String.Concat(a, b)" to "a + b"
if (methodRef != null && methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2) if (methodRef.Name == "Concat" && methodRef.DeclaringType.FullName == "System.String" && arguments.Length >= 2)
{ {
invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression invocationExpression.Arguments.Clear(); // detach arguments from invocationExpression
Expression expr = arguments[0]; Expression expr = arguments[0];
@ -79,7 +97,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return null; return null;
} }
if (methodRef.Name == "op_Implicit" && arguments.Length == 1) { if (methodRef.Name == "op_Implicit" && arguments.Length == 1) {
arguments[0].Remove(); // detach argument invocationExpression.ReplaceWith(arguments[0]);
return null;
}
if (methodRef.Name == "op_True" && arguments.Length == 1 && invocationExpression.Role == AstNode.Roles.Condition) {
invocationExpression.ReplaceWith(arguments[0]); invocationExpression.ReplaceWith(arguments[0]);
return null; return null;
} }
@ -256,7 +277,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
static bool IsWithoutSideEffects(Expression left) static bool IsWithoutSideEffects(Expression left)
{ {
return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression; return left is ThisReferenceExpression || left is IdentifierExpression || left is TypeReferenceExpression || left is BaseReferenceExpression;
} }
void IAstTransform.Run(AstNode node) void IAstTransform.Run(AstNode node)

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Threading; using System.Threading;
@ -25,6 +40,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
new AddCheckedBlocks(), new AddCheckedBlocks(),
new DeclareVariables(context), // should run after most transforms that modify statements new DeclareVariables(context), // should run after most transforms that modify statements
new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables new ConvertConstructorCallIntoInitializer(), // must run after DeclareVariables
new DecimalConstantTransform(),
new IntroduceUsingDeclarations(context), new IntroduceUsingDeclarations(context),
new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations new IntroduceExtensionMethods(context), // must run after IntroduceUsingDeclarations
new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods new IntroduceQueryExpressions(context), // must run after IntroduceExtensionMethods

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

@ -1,4 +1,22 @@
using System; // 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.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
@ -14,9 +32,9 @@ namespace ICSharpCode.Decompiler.Ast
if (resolveTypeArguments) if (resolveTypeArguments)
return BaseTypes(derivedType).Any(t => t.Item == baseType); return BaseTypes(derivedType).Any(t => t.Item == baseType);
else { else {
var comparableBaseType = baseType.Resolve(); var comparableBaseType = baseType.ResolveOrThrow();
while (derivedType.BaseType != null) { while (derivedType.BaseType != null) {
var resolvedBaseType = derivedType.BaseType.Resolve(); var resolvedBaseType = derivedType.BaseType.ResolveOrThrow();
if (resolvedBaseType == null) if (resolvedBaseType == null)
return false; return false;
if (comparableBaseType == resolvedBaseType) if (comparableBaseType == resolvedBaseType)
@ -27,20 +45,44 @@ namespace ICSharpCode.Decompiler.Ast
} }
} }
/// <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) 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) if (parentMethod.Name != childMethod.Name)
return false; return false;
if (parentMethod.HasParameters || childMethod.HasParameters) if (parentMethod.HasParameters || childMethod.HasParameters)
if(!parentMethod.HasParameters || ! childMethod.HasParameters || parentMethod.Parameters.Count != childMethod.Parameters.Count) if (!parentMethod.HasParameters || !childMethod.HasParameters || parentMethod.Parameters.Count != childMethod.Parameters.Count)
return false; return false;
return FindBaseMethods(childMethod).Any(m => m == parentMethod);// || (parentMethod.HasGenericParameters && m.); 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) 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) if (parentProperty.Name != childProperty.Name)
return false; return false;
@ -51,52 +93,148 @@ namespace ICSharpCode.Decompiler.Ast
return FindBaseProperties(childProperty).Any(m => m == parentProperty); 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) public static IEnumerable<MethodDefinition> FindBaseMethods(MethodDefinition method)
{ {
if (method == null)
throw new ArgumentNullException("method");
var typeContext = CreateGenericContext(method.DeclaringType); var typeContext = CreateGenericContext(method.DeclaringType);
var gMethod = typeContext.ApplyTo(method); var gMethod = typeContext.ApplyTo(method);
foreach (var baseType in BaseTypes(method.DeclaringType)) foreach (var baseType in BaseTypes(method.DeclaringType))
foreach (var baseMethod in baseType.Item.Methods) foreach (var baseMethod in baseType.Item.Methods)
if (MatchMethod(baseType.ApplyTo(baseMethod), gMethod) && IsVisbleFrom(baseMethod, method)) { if (MatchMethod(baseType.ApplyTo(baseMethod), gMethod) && IsVisibleFromDerived(baseMethod, method.DeclaringType)) {
yield return baseMethod; yield return baseMethod;
if (!(baseMethod.IsNewSlot ^ baseMethod.IsVirtual)) if (baseMethod.IsNewSlot == baseMethod.IsVirtual)
yield break; 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) 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 typeContext = CreateGenericContext(property.DeclaringType);
var gProperty = typeContext.ApplyTo(property); var gProperty = typeContext.ApplyTo(property);
bool isIndexer = property.IsIndexer();
foreach (var baseType in BaseTypes(property.DeclaringType)) foreach (var baseType in BaseTypes(property.DeclaringType))
foreach (var baseProperty in baseType.Item.Properties) foreach (var baseProperty in baseType.Item.Properties)
if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty) && IsVisbleFrom(baseProperty, property)) { if (MatchProperty(baseType.ApplyTo(baseProperty), gProperty)
&& IsVisibleFromDerived(baseProperty, property.DeclaringType)) {
if (isIndexer != baseProperty.IsIndexer())
continue;
yield return baseProperty; yield return baseProperty;
var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod; var anyPropertyAccessor = baseProperty.GetMethod ?? baseProperty.SetMethod;
if (!(anyPropertyAccessor.IsNewSlot ^ anyPropertyAccessor.IsVirtual)) 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; yield break;
} }
} }
private static bool IsVisbleFrom(MethodDefinition baseCandidate, MethodDefinition method) /// <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 (baseCandidate.IsPrivate) 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; return false;
if ((baseCandidate.IsAssembly || baseCandidate.IsFamilyAndAssembly) && baseCandidate.Module != method.Module) }
private static bool? IsVisibleFromDerived(IMemberDefinition member)
{
MethodAttributes attrs = GetAccessAttributes(member) & MethodAttributes.MemberAccessMask;
if (attrs == MethodAttributes.Private)
return false; return false;
if (attrs == MethodAttributes.Assembly || attrs == MethodAttributes.FamANDAssem)
return null;
return true; return true;
} }
private static bool IsVisbleFrom(PropertyDefinition baseCandidate, PropertyDefinition property) private static MethodAttributes GetAccessAttributes(IMemberDefinition member)
{ {
if (baseCandidate.GetMethod != null && property.GetMethod != null && IsVisbleFrom(baseCandidate.GetMethod, property.GetMethod)) var fld = member as FieldDefinition;
return true; if (fld != null)
if (baseCandidate.SetMethod != null && property.SetMethod != null && IsVisbleFrom(baseCandidate.SetMethod, property.SetMethod)) return (MethodAttributes)fld.Attributes;
return true;
return false; 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) private static bool MatchMethod(GenericContext<MethodDefinition> candidate, GenericContext<MethodDefinition> method)
@ -109,7 +247,7 @@ namespace ICSharpCode.Decompiler.Ast
if (mCandidate.HasOverrides) if (mCandidate.HasOverrides)
return false; return false;
if (!IsSameType(candidate.Resolve(mCandidate.ReturnType), method.Resolve(mMethod.ReturnType))) if (mCandidate.IsSpecialName != method.Item.IsSpecialName)
return false; return false;
if (mCandidate.HasGenericParameters || mMethod.HasGenericParameters) { if (mCandidate.HasGenericParameters || mMethod.HasGenericParameters) {
@ -130,6 +268,22 @@ namespace ICSharpCode.Decompiler.Ast
return true; 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) private static bool MatchProperty(GenericContext<PropertyDefinition> candidate, GenericContext<PropertyDefinition> property)
{ {
var mCandidate = candidate.Item; var mCandidate = candidate.Item;
@ -140,9 +294,6 @@ namespace ICSharpCode.Decompiler.Ast
if ((mCandidate.GetMethod ?? mCandidate.SetMethod).HasOverrides) if ((mCandidate.GetMethod ?? mCandidate.SetMethod).HasOverrides)
return false; return false;
if (!IsSameType(candidate.Resolve(mCandidate.PropertyType), property.Resolve(mProperty.PropertyType)))
return false;
if (mCandidate.HasParameters || mProperty.HasParameters) { if (mCandidate.HasParameters || mProperty.HasParameters) {
if (!mCandidate.HasParameters || !mProperty.HasParameters || mCandidate.Parameters.Count != mProperty.Parameters.Count) if (!mCandidate.HasParameters || !mProperty.HasParameters || mCandidate.Parameters.Count != mProperty.Parameters.Count)
return false; return false;
@ -156,10 +307,29 @@ namespace ICSharpCode.Decompiler.Ast
return true; 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) private static bool MatchParameters(GenericContext<ParameterDefinition> baseParameterType, GenericContext<ParameterDefinition> parameterType)
{ {
var baseParam = baseParameterType.Resolve(baseParameterType.Item.ParameterType); if (baseParameterType.Item.IsIn != parameterType.Item.IsIn ||
var param = parameterType.Resolve(parameterType.Item.ParameterType); 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); return IsSameType(baseParam, param);
} }
@ -170,8 +340,12 @@ namespace ICSharpCode.Decompiler.Ast
if (tr1 == null || tr2 == null) if (tr1 == null || tr2 == null)
return false; return false;
if (tr1.GetType() != tr2.GetType())
return false;
if (tr1.Name == tr2.Name && tr1.FullName == tr2.FullName) if (tr1.Name == tr2.Name && tr1.FullName == tr2.FullName)
return true; return true;
return false; return false;
} }
@ -186,10 +360,10 @@ namespace ICSharpCode.Decompiler.Ast
var baseType = type.Item.BaseType; var baseType = type.Item.BaseType;
var genericBaseType = baseType as GenericInstanceType; var genericBaseType = baseType as GenericInstanceType;
if (genericBaseType != null) { if (genericBaseType != null) {
type = new GenericContext<TypeDefinition>(genericBaseType.Resolve(), type = new GenericContext<TypeDefinition>(genericBaseType.ResolveOrThrow(),
genericBaseType.GenericArguments.Select(t => type.Resolve(t))); genericBaseType.GenericArguments.Select(t => type.ResolveWithContext(t)));
} else } else
type = new GenericContext<TypeDefinition>(baseType.Resolve()); type = new GenericContext<TypeDefinition>(baseType.ResolveOrThrow());
yield return type; yield return type;
} }
} }
@ -201,21 +375,31 @@ namespace ICSharpCode.Decompiler.Ast
: new GenericContext<TypeDefinition>(type); : new GenericContext<TypeDefinition>(type);
} }
struct GenericContext<T> struct GenericContext<T> where T : class
{ {
private static readonly ReadOnlyCollection<TypeReference> Empty = new ReadOnlyCollection<TypeReference>(new List<TypeReference>()); 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 T Item;
public readonly ReadOnlyCollection<TypeReference> TypeArguments; public readonly ReadOnlyCollection<TypeReference> TypeArguments;
public GenericContext(T item) public GenericContext(T item)
{ {
if (item == null)
throw new ArgumentNullException("item");
Item = item; Item = item;
TypeArguments = Empty; TypeArguments = Empty;
} }
public GenericContext(T item, IEnumerable<TypeReference> typeArguments) public GenericContext(T item, IEnumerable<TypeReference> typeArguments)
{ {
if (item == null)
throw new ArgumentNullException("item");
Item = item; Item = item;
var list = new List<TypeReference>(); var list = new List<TypeReference>();
foreach (var arg in typeArguments) { foreach (var arg in typeArguments) {
@ -231,31 +415,102 @@ namespace ICSharpCode.Decompiler.Ast
TypeArguments = typeArguments; TypeArguments = typeArguments;
} }
public TypeReference Resolve(TypeReference type) public TypeReference ResolveWithContext(TypeReference type)
{ {
var genericParameter = type as GenericParameter; var genericParameter = type as GenericParameter;
if (genericParameter != null && genericParameter.Owner.GenericParameterType == GenericParameterType.Type) { if (genericParameter != null)
if (genericParameter.Owner.GenericParameterType == GenericParameterType.Type)
return this.TypeArguments[genericParameter.Position]; 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();
} }
var arrayType = type as ArrayType;
private TypeReference ReplaceElementType(TypeSpecification ts, TypeReference newElementType)
{
var arrayType = ts as ArrayType;
if (arrayType != null) { if (arrayType != null) {
var resolvedElementType = Resolve(arrayType.ElementType); if (newElementType == arrayType.ElementType)
if (resolvedElementType == null)
return null;
if (resolvedElementType == arrayType.ElementType)
return arrayType; return arrayType;
var newArrayType = new ArrayType(resolvedElementType, arrayType.Rank); var newArrayType = new ArrayType(newElementType, arrayType.Rank);
for (int dimension = 0; dimension < arrayType.Rank; dimension++) for (int dimension = 0; dimension < arrayType.Rank; dimension++)
newArrayType.Dimensions[dimension] = arrayType.Dimensions[dimension]; newArrayType.Dimensions[dimension] = arrayType.Dimensions[dimension];
return newArrayType; return newArrayType;
} }
return type.Resolve(); 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) public GenericContext<T2> ApplyTo<T2>(T2 item) where T2 : class
{ {
return new GenericContext<T2>(item, this.TypeArguments); 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();
}
}
}
} }
} }
} }

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

@ -186,6 +186,14 @@ namespace ICSharpCode.Decompiler
return null; 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) public static bool IsCompilerGenerated(this ICustomAttributeProvider provider)
{ {
if (provider != null && provider.HasCustomAttributes) { if (provider != null && provider.HasCustomAttributes) {

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

@ -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; }
}
}

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Runtime.Serialization; using System.Runtime.Serialization;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.ComponentModel; using System.ComponentModel;
@ -164,6 +179,51 @@ namespace ICSharpCode.Decompiler
} }
} }
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; public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName) protected virtual void OnPropertyChanged(string propertyName)

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

@ -17,11 +17,32 @@
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
using System; using System;
using System.Collections.Generic;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
namespace ICSharpCode.Decompiler.Disassembler 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 class DisassemblerHelpers
{ {
public static void WriteOffsetReference(ITextOutput writer, Instruction instruction) public static void WriteOffsetReference(ITextOutput writer, Instruction instruction)
@ -35,6 +56,7 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteOffsetReference(writer, exceptionHandler.TryStart); WriteOffsetReference(writer, exceptionHandler.TryStart);
writer.Write('-'); writer.Write('-');
WriteOffsetReference(writer, exceptionHandler.TryEnd); WriteOffsetReference(writer, exceptionHandler.TryEnd);
writer.Write(' ');
writer.Write(exceptionHandler.HandlerType.ToString()); writer.Write(exceptionHandler.HandlerType.ToString());
if (exceptionHandler.FilterStart != null) { if (exceptionHandler.FilterStart != null) {
writer.Write(' '); writer.Write(' ');
@ -56,8 +78,14 @@ namespace ICSharpCode.Decompiler.Disassembler
writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction); writer.WriteDefinition(CecilExtensions.OffsetToString(instruction.Offset), instruction);
writer.Write(": "); writer.Write(": ");
writer.WriteReference(instruction.OpCode.Name, instruction.OpCode); writer.WriteReference(instruction.OpCode.Name, instruction.OpCode);
if(null != instruction.Operand) { if (instruction.Operand != null) {
writer.Write(' '); 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); WriteOperand(writer, instruction.Operand);
} }
} }
@ -82,93 +110,189 @@ namespace ICSharpCode.Decompiler.Disassembler
public static void WriteTo(this MethodReference method, ITextOutput writer) public static void WriteTo(this MethodReference method, ITextOutput writer)
{ {
if (method.HasThis) if (method.ExplicitThis) {
writer.Write("instance explicit ");
}
else if (method.HasThis) {
writer.Write("instance "); writer.Write("instance ");
method.ReturnType.WriteTo(writer); }
method.ReturnType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' '); writer.Write(' ');
if (method.DeclaringType != null) { if (method.DeclaringType != null) {
method.DeclaringType.WriteTo(writer, true); method.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::"); writer.Write("::");
} }
writer.WriteReference(method.Name, method); 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("("); writer.Write("(");
var parameters = method.Parameters; var parameters = method.Parameters;
for(int i = 0; i < parameters.Count; ++i) { for(int i = 0; i < parameters.Count; ++i) {
if (i > 0) writer.Write(", "); if (i > 0) writer.Write(", ");
parameters[i].ParameterType.WriteTo(writer); parameters[i].ParameterType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
} }
writer.Write(")"); writer.Write(")");
} }
static void WriteTo(this FieldReference field, ITextOutput writer) static void WriteTo(this FieldReference field, ITextOutput writer)
{ {
field.FieldType.WriteTo(writer); field.FieldType.WriteTo(writer, ILNameSyntax.SignatureNoNamedTypeParameters);
writer.Write(' '); writer.Write(' ');
field.DeclaringType.WriteTo(writer); field.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write("::"); writer.Write("::");
writer.WriteReference(field.Name, field); 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) public static string Escape(string identifier)
{ {
if (IsValidIdentifier(identifier) && !ilKeywords.Contains(identifier)) {
return 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, bool onlyName = false, bool shortName = false) 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) { if (type is PinnedType) {
writer.Write("pinned "); ((PinnedType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
((PinnedType)type).ElementType.WriteTo(writer, onlyName, shortName); writer.Write(" pinned");
} else if (type is ArrayType) { } else if (type is ArrayType) {
ArrayType at = (ArrayType)type; ArrayType at = (ArrayType)type;
at.ElementType.WriteTo(writer, onlyName, shortName); at.ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('['); writer.Write('[');
writer.Write(string.Join(", ", at.Dimensions)); writer.Write(string.Join(", ", at.Dimensions));
writer.Write(']'); writer.Write(']');
} else if (type is GenericParameter) { } else if (type is GenericParameter) {
writer.WriteReference(type.Name, type); 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) { } else if (type is ByReferenceType) {
((ByReferenceType)type).ElementType.WriteTo(writer, onlyName, shortName); ((ByReferenceType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('&'); writer.Write('&');
} else if (type is PointerType) { } else if (type is PointerType) {
((PointerType)type).ElementType.WriteTo(writer, onlyName, shortName); ((PointerType)type).ElementType.WriteTo(writer, syntaxForElementTypes);
writer.Write('*'); writer.Write('*');
} else if (type is GenericInstanceType) { } else if (type is GenericInstanceType) {
type.GetElementType().WriteTo(writer, onlyName, shortName); type.GetElementType().WriteTo(writer, syntaxForElementTypes);
writer.Write('<'); writer.Write('<');
var arguments = ((GenericInstanceType)type).GenericArguments; var arguments = ((GenericInstanceType)type).GenericArguments;
for (int i = 0; i < arguments.Count; i++) { for (int i = 0; i < arguments.Count; i++) {
if (i > 0) if (i > 0)
writer.Write(", "); writer.Write(", ");
arguments[i].WriteTo(writer, onlyName, shortName); arguments[i].WriteTo(writer, syntaxForElementTypes);
} }
writer.Write('>'); writer.Write('>');
} else if (type is OptionalModifierType) { } else if (type is OptionalModifierType) {
writer.Write("modopt("); ((OptionalModifierType)type).ElementType.WriteTo(writer, syntax);
((OptionalModifierType)type).ModifierType.WriteTo(writer, true, shortName); writer.Write(" modopt(");
((OptionalModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") "); writer.Write(") ");
((OptionalModifierType)type).ElementType.WriteTo(writer, onlyName, shortName);
} else if (type is RequiredModifierType) { } else if (type is RequiredModifierType) {
writer.Write("modreq("); ((RequiredModifierType)type).ElementType.WriteTo(writer, syntax);
((RequiredModifierType)type).ModifierType.WriteTo(writer, true, shortName); writer.Write(" modreq(");
((RequiredModifierType)type).ModifierType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write(") "); writer.Write(") ");
((RequiredModifierType)type).ElementType.WriteTo(writer, onlyName, shortName);
} else { } else {
string name = PrimitiveTypeName(type); string name = PrimitiveTypeName(type.FullName);
if (name != null) { 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); writer.Write(name);
} else { } else {
if (!onlyName) if (syntax == ILNameSyntax.Signature || syntax == ILNameSyntax.SignatureNoNamedTypeParameters)
writer.Write(type.IsValueType ? "valuetype " : "class "); writer.Write(type.IsValueType ? "valuetype " : "class ");
if (type.DeclaringType != null) { if (type.DeclaringType != null) {
type.DeclaringType.WriteTo(writer, true, shortName); type.DeclaringType.WriteTo(writer, ILNameSyntax.TypeName);
writer.Write('/'); writer.Write('/');
writer.WriteReference(Escape(type.Name), type); writer.WriteReference(Escape(type.Name), type);
} else { } else {
if (!type.IsDefinition && type.Scope != null && !shortName && !(type is TypeSpecification)) if (!type.IsDefinition && type.Scope != null && !(type is TypeSpecification))
writer.Write("[{0}]", Escape(type.Scope.Name)); writer.Write("[{0}]", Escape(type.Scope.Name));
writer.WriteReference(shortName ? type.Name : type.FullName, type); writer.WriteReference(Escape(type.FullName), type);
} }
} }
} }
@ -193,7 +317,19 @@ namespace ICSharpCode.Decompiler.Disassembler
VariableReference variableRef = operand as VariableReference; VariableReference variableRef = operand as VariableReference;
if (variableRef != null) { if (variableRef != null) {
if (string.IsNullOrEmpty(variableRef.Name))
writer.WriteReference(variableRef.Index.ToString(), variableRef); 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; return;
} }
@ -205,7 +341,7 @@ namespace ICSharpCode.Decompiler.Disassembler
TypeReference typeRef = operand as TypeReference; TypeReference typeRef = operand as TypeReference;
if (typeRef != null) { if (typeRef != null) {
typeRef.WriteTo(writer); typeRef.WriteTo(writer, ILNameSyntax.TypeName);
return; return;
} }
@ -217,17 +353,52 @@ namespace ICSharpCode.Decompiler.Disassembler
string s = operand as string; string s = operand as string;
if (s != null) { if (s != null) {
writer.Write("\"" + s.Replace("\\", "\\\\").Replace("\"", "\\\"") + "\""); writer.Write("\"" + NRefactory.CSharp.OutputVisitor.ConvertString(s) + "\"");
return; } 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); s = ToInvariantCultureString(operand);
writer.Write(s); writer.Write(s);
} }
}
public static string PrimitiveTypeName(this TypeReference type) public static string PrimitiveTypeName(string fullName)
{ {
switch (type.FullName) { switch (fullName) {
case "System.SByte": case "System.SByte":
return "int8"; return "int8";
case "System.Int16": case "System.Int16":
@ -258,6 +429,8 @@ namespace ICSharpCode.Decompiler.Disassembler
return "char"; return "char";
case "System.Object": case "System.Object":
return "object"; return "object";
case "System.IntPtr":
return "native int";
default: default:
return null; return null;
} }

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

@ -88,7 +88,9 @@ namespace ICSharpCode.Decompiler.Disassembler
: this(ILStructureType.Root, 0, body.CodeSize) : this(ILStructureType.Root, 0, body.CodeSize)
{ {
// Build the tree of exception structures: // Build the tree of exception structures:
foreach (ExceptionHandler eh in body.ExceptionHandlers) { 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)); AddNestedStructure(new ILStructure(ILStructureType.Try, eh.TryStart.Offset, eh.TryEnd.Offset, eh));
if (eh.HandlerType == ExceptionHandlerType.Filter) if (eh.HandlerType == ExceptionHandlerType.Filter)
AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh)); AddNestedStructure(new ILStructure(ILStructureType.Filter, eh.FilterStart.Offset, eh.HandlerStart.Offset, eh));

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

@ -20,8 +20,10 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
using ICSharpCode.Decompiler; using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.FlowAnalysis; using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.Decompiler.ILAst;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
@ -45,13 +47,11 @@ namespace ICSharpCode.Decompiler.Disassembler
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
} }
public void Disassemble(MethodBody body) public void Disassemble(MethodBody body, MemberMapping methodMapping)
{ {
// start writing IL code
MethodDefinition method = body.Method; MethodDefinition method = body.Method;
output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA); output.WriteLine("// Method begins at RVA 0x{0:x4}", method.RVA);
if (method.HasOverrides)
foreach (var methodOverride in method.Overrides)
output.WriteLine(".override {0}::{1}", methodOverride.DeclaringType.FullName, methodOverride.Name);
output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize); output.WriteLine("// Code size {0} (0x{0:x})", body.CodeSize);
output.WriteLine(".maxstack {0}", body.MaxStackSize); output.WriteLine(".maxstack {0}", body.MaxStackSize);
if (method.DeclaringType.Module.Assembly.EntryPoint == method) if (method.DeclaringType.Module.Assembly.EntryPoint == method)
@ -66,8 +66,12 @@ namespace ICSharpCode.Decompiler.Disassembler
foreach (var v in method.Body.Variables) { foreach (var v in method.Body.Variables) {
output.WriteDefinition("[" + v.Index + "] ", v); output.WriteDefinition("[" + v.Index + "] ", v);
v.VariableType.WriteTo(output); v.VariableType.WriteTo(output);
if (!string.IsNullOrEmpty(v.Name)) {
output.Write(' '); output.Write(' ');
output.Write(DisassemblerHelpers.Escape(v.Name)); output.Write(DisassemblerHelpers.Escape(v.Name));
}
if (v.Index + 1 < method.Body.Variables.Count)
output.Write(',');
output.WriteLine(); output.WriteLine();
} }
output.Unindent(); output.Unindent();
@ -77,12 +81,25 @@ namespace ICSharpCode.Decompiler.Disassembler
if (detectControlStructure && body.Instructions.Count > 0) { if (detectControlStructure && body.Instructions.Count > 0) {
Instruction inst = body.Instructions[0]; Instruction inst = body.Instructions[0];
WriteStructureBody(new ILStructure(body), ref inst); HashSet<int> branchTargets = GetBranchTargets(body.Instructions);
WriteStructureBody(new ILStructure(body), branchTargets, ref inst, methodMapping, method.Body.CodeSize);
} else { } else {
foreach (var inst in method.Body.Instructions) { foreach (var inst in method.Body.Instructions) {
inst.WriteTo(output); 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(); output.WriteLine();
} }
if (method.Body.HasExceptionHandlers) {
output.WriteLine(); output.WriteLine();
foreach (var eh in method.Body.ExceptionHandlers) { foreach (var eh in method.Body.ExceptionHandlers) {
eh.WriteTo(output); eh.WriteTo(output);
@ -90,6 +107,22 @@ namespace ICSharpCode.Decompiler.Disassembler
} }
} }
} }
}
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) void WriteStructureHeader(ILStructure s)
{ {
@ -104,7 +137,8 @@ namespace ICSharpCode.Decompiler.Disassembler
output.WriteLine(); output.WriteLine();
break; break;
case ILStructureType.Try: case ILStructureType.Try:
output.WriteLine(".try {"); output.WriteLine(".try");
output.WriteLine("{");
break; break;
case ILStructureType.Handler: case ILStructureType.Handler:
switch (s.ExceptionHandler.HandlerType) { switch (s.ExceptionHandler.HandlerType) {
@ -113,22 +147,24 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write("catch"); output.Write("catch");
if (s.ExceptionHandler.CatchType != null) { if (s.ExceptionHandler.CatchType != null) {
output.Write(' '); output.Write(' ');
s.ExceptionHandler.CatchType.WriteTo(output); s.ExceptionHandler.CatchType.WriteTo(output, ILNameSyntax.TypeName);
} }
output.WriteLine(" {"); output.WriteLine();
break; break;
case Mono.Cecil.Cil.ExceptionHandlerType.Finally: case Mono.Cecil.Cil.ExceptionHandlerType.Finally:
output.WriteLine("finally {"); output.WriteLine("finally");
break; break;
case Mono.Cecil.Cil.ExceptionHandlerType.Fault: case Mono.Cecil.Cil.ExceptionHandlerType.Fault:
output.WriteLine("fault {"); output.WriteLine("fault");
break; break;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
} }
output.WriteLine("{");
break; break;
case ILStructureType.Filter: case ILStructureType.Filter:
output.WriteLine("filter {"); output.WriteLine("filter");
output.WriteLine("{");
break; break;
default: default:
throw new NotSupportedException(); throw new NotSupportedException();
@ -136,21 +172,44 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Indent(); output.Indent();
} }
void WriteStructureBody(ILStructure s, ref Instruction inst) void WriteStructureBody(ILStructure s, HashSet<int> branchTargets, ref Instruction inst, MemberMapping currentMethodMapping, int codeSize)
{ {
bool isFirstInstructionInStructure = true;
bool prevInstructionWasBranch = false;
int childIndex = 0; int childIndex = 0;
while (inst != null && inst.Offset < s.EndOffset) { while (inst != null && inst.Offset < s.EndOffset) {
int offset = inst.Offset; int offset = inst.Offset;
if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) { if (childIndex < s.Children.Count && s.Children[childIndex].StartOffset <= offset && offset < s.Children[childIndex].EndOffset) {
ILStructure child = s.Children[childIndex++]; ILStructure child = s.Children[childIndex++];
WriteStructureHeader(child); WriteStructureHeader(child);
WriteStructureBody(child, ref inst); WriteStructureBody(child, branchTargets, ref inst, currentMethodMapping, codeSize);
WriteStructureFooter(child); WriteStructureFooter(child);
} else { } else {
if (!isFirstInstructionInStructure && (prevInstructionWasBranch || branchTargets.Contains(offset))) {
output.WriteLine(); // put an empty line after branches, and in front of branch targets
}
inst.WriteTo(output); 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(); 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; inst = inst.Next;
} }
isFirstInstructionInStructure = false;
} }
} }

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

@ -18,8 +18,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading; using System.Threading;
using Mono.Cecil; using Mono.Cecil;
using Mono.Collections.Generic; using Mono.Collections.Generic;
@ -28,13 +30,13 @@ namespace ICSharpCode.Decompiler.Disassembler
/// <summary> /// <summary>
/// Disassembles type and member definitions. /// Disassembles type and member definitions.
/// </summary> /// </summary>
public sealed class ReflectionDisassembler public sealed class ReflectionDisassembler : BaseCodeMappings
{ {
ITextOutput output; ITextOutput output;
CancellationToken cancellationToken; CancellationToken cancellationToken;
bool detectControlStructure;
bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings) bool isInType; // whether we are currently disassembling a whole type (-> defaultCollapsed for foldings)
MethodBodyDisassembler methodBodyDisassembler; MethodBodyDisassembler methodBodyDisassembler;
MemberReference currentMember;
public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken) public ReflectionDisassembler(ITextOutput output, bool detectControlStructure, CancellationToken cancellationToken)
{ {
@ -42,23 +44,27 @@ namespace ICSharpCode.Decompiler.Disassembler
throw new ArgumentNullException("output"); throw new ArgumentNullException("output");
this.output = output; this.output = output;
this.cancellationToken = cancellationToken; this.cancellationToken = cancellationToken;
this.detectControlStructure = detectControlStructure;
this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken); this.methodBodyDisassembler = new MethodBodyDisassembler(output, detectControlStructure, cancellationToken);
this.CodeMappings = new Dictionary<int, List<MemberMapping>>();
this.DecompiledMemberReferences = new Dictionary<int, MemberReference>();
} }
#region Disassemble Method #region Disassemble Method
EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() { EnumNameCollection<MethodAttributes> methodAttributeFlags = new EnumNameCollection<MethodAttributes>() {
{ MethodAttributes.Static, "static" },
{ MethodAttributes.Final, "final" }, { MethodAttributes.Final, "final" },
{ MethodAttributes.Virtual, "virtual" },
{ MethodAttributes.HideBySig, "hidebysig" }, { MethodAttributes.HideBySig, "hidebysig" },
{ MethodAttributes.Abstract, "abstract" },
{ MethodAttributes.SpecialName, "specialname" }, { MethodAttributes.SpecialName, "specialname" },
{ MethodAttributes.PInvokeImpl, "pinvokeimpl" }, { MethodAttributes.PInvokeImpl, null }, // handled separately
{ MethodAttributes.UnmanagedExport, "export" }, { MethodAttributes.UnmanagedExport, "export" },
{ MethodAttributes.RTSpecialName, "rtspecialname" }, { MethodAttributes.RTSpecialName, "rtspecialname" },
{ MethodAttributes.RequireSecObject, "requiresecobj" }, { MethodAttributes.RequireSecObject, "reqsecobj" },
{ MethodAttributes.NewSlot, "newslot" } { MethodAttributes.NewSlot, "newslot" },
{ MethodAttributes.CheckAccessOnOverride, "strict" },
{ MethodAttributes.Abstract, "abstract" },
{ MethodAttributes.Virtual, "virtual" },
{ MethodAttributes.Static, "static" },
{ MethodAttributes.HasSecurity, null }, // ?? also invisible in ILDasm
}; };
EnumNameCollection<MethodAttributes> methodVisibility = new EnumNameCollection<MethodAttributes>() { EnumNameCollection<MethodAttributes> methodVisibility = new EnumNameCollection<MethodAttributes>() {
@ -76,7 +82,7 @@ namespace ICSharpCode.Decompiler.Disassembler
{ MethodCallingConvention.ThisCall, "unmanaged thiscall" }, { MethodCallingConvention.ThisCall, "unmanaged thiscall" },
{ MethodCallingConvention.FastCall, "unmanaged fastcall" }, { MethodCallingConvention.FastCall, "unmanaged fastcall" },
{ MethodCallingConvention.VarArg, "vararg" }, { MethodCallingConvention.VarArg, "vararg" },
{ MethodCallingConvention.Generic, "generic" }, { MethodCallingConvention.Generic, null },
}; };
EnumNameCollection<MethodImplAttributes> methodCodeType = new EnumNameCollection<MethodImplAttributes>() { EnumNameCollection<MethodImplAttributes> methodCodeType = new EnumNameCollection<MethodImplAttributes>() {
@ -90,10 +96,16 @@ namespace ICSharpCode.Decompiler.Disassembler
{ MethodImplAttributes.Synchronized, "synchronized" }, { MethodImplAttributes.Synchronized, "synchronized" },
{ MethodImplAttributes.NoInlining, "noinlining" }, { MethodImplAttributes.NoInlining, "noinlining" },
{ MethodImplAttributes.NoOptimization, "nooptimization" }, { MethodImplAttributes.NoOptimization, "nooptimization" },
{ MethodImplAttributes.PreserveSig, "preservesig" },
{ MethodImplAttributes.InternalCall, "internalcall" },
{ MethodImplAttributes.ForwardRef, "forwardref" },
}; };
public void DisassembleMethod(MethodDefinition method) public void DisassembleMethod(MethodDefinition method)
{ {
// set current member
currentMember = method;
// write method header // write method header
output.WriteDefinition(".method ", method); output.WriteDefinition(".method ", method);
DisassembleMethodInternal(method); DisassembleMethodInternal(method);
@ -108,21 +120,71 @@ namespace ICSharpCode.Decompiler.Disassembler
//emit flags //emit flags
WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility); WriteEnum(method.Attributes & MethodAttributes.MemberAccessMask, methodVisibility);
WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags); WriteFlags(method.Attributes & ~MethodAttributes.MemberAccessMask, methodAttributeFlags);
if(method.IsCompilerControlled) output.Write("privatescope ");
if ((method.Attributes & MethodAttributes.PInvokeImpl) == MethodAttributes.PInvokeImpl) {
output.Write("pinvokeimpl");
if (method.HasPInvokeInfo && method.PInvokeInfo != null) {
PInvokeInfo info = method.PInvokeInfo;
output.Write("(\"" + NRefactory.CSharp.OutputVisitor.ConvertString(info.Module.Name) + "\"");
if (!string.IsNullOrEmpty(info.EntryPoint) && info.EntryPoint != method.Name)
output.Write(" as \"" + NRefactory.CSharp.OutputVisitor.ConvertString(info.EntryPoint) + "\"");
if (info.IsNoMangle)
output.Write(" nomangle");
if (info.IsCharSetAnsi)
output.Write(" ansi");
else if (info.IsCharSetAuto)
output.Write(" autochar");
else if (info.IsCharSetUnicode)
output.Write(" unicode");
if (info.SupportsLastError)
output.Write(" lasterr");
if (info.IsCallConvCdecl)
output.Write(" cdecl");
else if (info.IsCallConvFastcall)
output.Write(" fastcall");
else if (info.IsCallConvStdCall)
output.Write(" stdcall");
else if (info.IsCallConvThiscall)
output.Write(" thiscall");
else if (info.IsCallConvWinapi)
output.Write(" winapi");
output.Write(')');
}
output.Write(' ');
}
output.WriteLine(); output.WriteLine();
output.Indent(); output.Indent();
if (method.ExplicitThis) {
if (method.HasThis) output.Write("instance explicit ");
} else if (method.HasThis) {
output.Write("instance "); output.Write("instance ");
}
//call convention //call convention
WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention); WriteEnum(method.CallingConvention & (MethodCallingConvention)0x1f, callingConvention);
//return type //return type
method.ReturnType.WriteTo(output); method.ReturnType.WriteTo(output);
output.Write(' '); output.Write(' ');
if (method.MethodReturnType.HasMarshalInfo) {
WriteMarshalInfo(method.MethodReturnType.MarshalInfo);
}
if (method.IsCompilerControlled) {
output.Write(DisassemblerHelpers.Escape(method.Name + "$PST" + method.MetadataToken.ToInt32().ToString("X8")));
} else {
output.Write(DisassemblerHelpers.Escape(method.Name)); output.Write(DisassemblerHelpers.Escape(method.Name));
}
WriteTypeParameters(output, method);
//( params ) //( params )
output.Write(" ("); output.Write(" (");
@ -142,31 +204,455 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl); WriteFlags(method.ImplAttributes & ~(MethodImplAttributes.CodeTypeMask | MethodImplAttributes.ManagedMask), methodImpl);
output.Unindent(); output.Unindent();
if (method.HasBody || method.HasCustomAttributes) {
OpenBlock(defaultCollapsed: isInType); OpenBlock(defaultCollapsed: isInType);
WriteAttributes(method.CustomAttributes); WriteAttributes(method.CustomAttributes);
if (method.HasOverrides) {
foreach (var methodOverride in method.Overrides) {
output.Write(".override method ");
methodOverride.WriteTo(output);
output.WriteLine();
}
}
foreach (var p in method.Parameters) {
WriteParameterAttributes(p);
}
WriteSecurityDeclarations(method);
if (method.HasBody) if (method.HasBody) {
methodBodyDisassembler.Disassemble(method.Body); // create IL code mappings - used in debugger
CreateCodeMappings(method.MetadataToken.ToInt32(), currentMember);
MemberMapping methodMapping = method.CreateCodeMapping(this.CodeMappings[method.MetadataToken.ToInt32()], currentMember);
methodBodyDisassembler.Disassemble(method.Body, methodMapping);
}
CloseBlock("End of method " + method.DeclaringType.Name + "." + method.Name); CloseBlock("end of method " + DisassemblerHelpers.Escape(method.DeclaringType.Name) + "::" + DisassemblerHelpers.Escape(method.Name));
}
#region Write Security Declarations
void WriteSecurityDeclarations(ISecurityDeclarationProvider secDeclProvider)
{
if (!secDeclProvider.HasSecurityDeclarations)
return;
foreach (var secdecl in secDeclProvider.SecurityDeclarations) {
output.Write(".permissionset ");
switch (secdecl.Action) {
case SecurityAction.Request:
output.Write("request");
break;
case SecurityAction.Demand:
output.Write("demand");
break;
case SecurityAction.Assert:
output.Write("assert");
break;
case SecurityAction.Deny:
output.Write("deny");
break;
case SecurityAction.PermitOnly:
output.Write("permitonly");
break;
case SecurityAction.LinkDemand:
output.Write("linkcheck");
break;
case SecurityAction.InheritDemand:
output.Write("inheritcheck");
break;
case SecurityAction.RequestMinimum:
output.Write("reqmin");
break;
case SecurityAction.RequestOptional:
output.Write("reqopt");
break;
case SecurityAction.RequestRefuse:
output.Write("reqrefuse");
break;
case SecurityAction.PreJitGrant:
output.Write("prejitgrant");
break;
case SecurityAction.PreJitDeny:
output.Write("prejitdeny");
break;
case SecurityAction.NonCasDemand:
output.Write("noncasdemand");
break;
case SecurityAction.NonCasLinkDemand:
output.Write("noncaslinkdemand");
break;
case SecurityAction.NonCasInheritance:
output.Write("noncasinheritance");
break;
default:
output.Write(secdecl.Action.ToString());
break;
}
output.WriteLine(" = {");
output.Indent();
for (int i = 0; i < secdecl.SecurityAttributes.Count; i++) {
SecurityAttribute sa = secdecl.SecurityAttributes[i];
if (sa.AttributeType.Scope == sa.AttributeType.Module) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(sa.AttributeType)));
} else { } else {
sa.AttributeType.WriteTo(output, ILNameSyntax.TypeName);
}
output.Write(" = {");
if (sa.HasFields || sa.HasProperties) {
output.WriteLine();
output.Indent();
foreach (CustomAttributeNamedArgument na in sa.Fields) {
output.Write("field ");
WriteSecurityDeclarationArgument(na);
output.WriteLine(); output.WriteLine();
} }
foreach (CustomAttributeNamedArgument na in sa.Properties) {
output.Write("property ");
WriteSecurityDeclarationArgument(na);
output.WriteLine();
} }
output.Unindent();
}
output.Write('}');
if (i + 1< secdecl.SecurityAttributes.Count)
output.Write(',');
output.WriteLine();
}
output.Unindent();
output.WriteLine("}");
}
}
void WriteSecurityDeclarationArgument(CustomAttributeNamedArgument na)
{
TypeReference type = na.Argument.Type;
if (type.MetadataType == MetadataType.Class || type.MetadataType == MetadataType.ValueType) {
output.Write("enum ");
if (type.Scope != type.Module) {
output.Write("class ");
output.Write(DisassemblerHelpers.Escape(GetAssemblyQualifiedName(type)));
} else {
type.WriteTo(output, ILNameSyntax.TypeName);
}
} else {
type.WriteTo(output);
}
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(na.Name));
output.Write(" = ");
if (na.Argument.Value is string) {
// secdecls use special syntax for strings
output.Write("string('{0}')", NRefactory.CSharp.OutputVisitor.ConvertString((string)na.Argument.Value).Replace("'", "\'"));
} else {
WriteConstant(na.Argument.Value);
}
}
string GetAssemblyQualifiedName(TypeReference type)
{
AssemblyNameReference anr = type.Scope as AssemblyNameReference;
if (anr == null) {
ModuleDefinition md = type.Scope as ModuleDefinition;
if (md != null) {
anr = md.Assembly.Name;
}
}
if (anr != null) {
return type.FullName + ", " + anr.FullName;
} else {
return type.FullName;
}
}
#endregion
#region WriteMarshalInfo
void WriteMarshalInfo(MarshalInfo marshalInfo)
{
output.Write("marshal(");
WriteNativeType(marshalInfo.NativeType, marshalInfo);
output.Write(") ");
}
void WriteNativeType(NativeType nativeType, MarshalInfo marshalInfo = null)
{
switch (nativeType) {
case NativeType.None:
break;
case NativeType.Boolean:
output.Write("bool");
break;
case NativeType.I1:
output.Write("int8");
break;
case NativeType.U1:
output.Write("unsigned int8");
break;
case NativeType.I2:
output.Write("int16");
break;
case NativeType.U2:
output.Write("unsigned int16");
break;
case NativeType.I4:
output.Write("int32");
break;
case NativeType.U4:
output.Write("unsigned int32");
break;
case NativeType.I8:
output.Write("int64");
break;
case NativeType.U8:
output.Write("unsigned int64");
break;
case NativeType.R4:
output.Write("float32");
break;
case NativeType.R8:
output.Write("float64");
break;
case NativeType.LPStr:
output.Write("lpstr");
break;
case NativeType.Int:
output.Write("int");
break;
case NativeType.UInt:
output.Write("unsigned int");
break;
case NativeType.Func:
goto default; // ??
case NativeType.Array:
ArrayMarshalInfo ami = (ArrayMarshalInfo)marshalInfo;
if (ami == null)
goto default;
if (ami.ElementType != NativeType.Max)
WriteNativeType(ami.ElementType);
output.Write('[');
if (ami.SizeParameterMultiplier == 0) {
output.Write(ami.Size.ToString());
} else {
if (ami.Size >= 0)
output.Write(ami.Size.ToString());
output.Write(" + ");
output.Write(ami.SizeParameterIndex.ToString());
}
output.Write(']');
break;
case NativeType.Currency:
output.Write("currency");
break;
case NativeType.BStr:
output.Write("bstr");
break;
case NativeType.LPWStr:
output.Write("lpwstr");
break;
case NativeType.LPTStr:
output.Write("lptstr");
break;
case NativeType.FixedSysString:
output.Write("fixed sysstring[{0}]", ((FixedSysStringMarshalInfo)marshalInfo).Size);
break;
case NativeType.IUnknown:
output.Write("iunknown");
break;
case NativeType.IDispatch:
output.Write("idispatch");
break;
case NativeType.Struct:
output.Write("struct");
break;
case NativeType.IntF:
output.Write("interface");
break;
case NativeType.SafeArray:
output.Write("safearray ");
SafeArrayMarshalInfo sami = marshalInfo as SafeArrayMarshalInfo;
if (sami != null) {
switch (sami.ElementType) {
case VariantType.None:
break;
case VariantType.I2:
output.Write("int16");
break;
case VariantType.I4:
output.Write("int32");
break;
case VariantType.R4:
output.Write("float32");
break;
case VariantType.R8:
output.Write("float64");
break;
case VariantType.CY:
output.Write("currency");
break;
case VariantType.Date:
output.Write("date");
break;
case VariantType.BStr:
output.Write("bstr");
break;
case VariantType.Dispatch:
output.Write("idispatch");
break;
case VariantType.Error:
output.Write("error");
break;
case VariantType.Bool:
output.Write("bool");
break;
case VariantType.Variant:
output.Write("variant");
break;
case VariantType.Unknown:
output.Write("iunknown");
break;
case VariantType.Decimal:
output.Write("decimal");
break;
case VariantType.I1:
output.Write("int8");
break;
case VariantType.UI1:
output.Write("unsigned int8");
break;
case VariantType.UI2:
output.Write("unsigned int16");
break;
case VariantType.UI4:
output.Write("unsigned int32");
break;
case VariantType.Int:
output.Write("int");
break;
case VariantType.UInt:
output.Write("unsigned int");
break;
default:
output.Write(sami.ElementType.ToString());
break;
}
}
break;
case NativeType.FixedArray:
output.Write("fixed array");
FixedArrayMarshalInfo fami = marshalInfo as FixedArrayMarshalInfo;
if (fami != null) {
output.Write("[{0}]", fami.Size);
if (fami.ElementType != NativeType.None) {
output.Write(' ');
WriteNativeType(fami.ElementType);
}
}
break;
case NativeType.ByValStr:
output.Write("byvalstr");
break;
case NativeType.ANSIBStr:
output.Write("ansi bstr");
break;
case NativeType.TBStr:
output.Write("tbstr");
break;
case NativeType.VariantBool:
output.Write("variant bool");
break;
case NativeType.ASAny:
output.Write("as any");
break;
case NativeType.LPStruct:
output.Write("lpstruct");
break;
case NativeType.CustomMarshaler:
CustomMarshalInfo cmi = marshalInfo as CustomMarshalInfo;
if (cmi == null)
goto default;
output.Write("custom(\"{0}\", \"{1}\"",
NRefactory.CSharp.OutputVisitor.ConvertString(cmi.ManagedType.FullName),
NRefactory.CSharp.OutputVisitor.ConvertString(cmi.Cookie));
if (cmi.Guid != Guid.Empty || !string.IsNullOrEmpty(cmi.UnmanagedType)) {
output.Write(", \"{0}\", \"{1}\"", cmi.Guid.ToString(), NRefactory.CSharp.OutputVisitor.ConvertString(cmi.UnmanagedType));
}
output.Write(')');
break;
case NativeType.Error:
output.Write("error");
break;
default:
output.Write(nativeType.ToString());
break;
}
}
#endregion
void WriteParameters(Collection<ParameterDefinition> parameters) void WriteParameters(Collection<ParameterDefinition> parameters)
{ {
for (int i = 0; i < parameters.Count; i++) { for (int i = 0; i < parameters.Count; i++) {
var p = parameters[i]; var p = parameters[i];
if (p.IsIn)
output.Write("[in] ");
if (p.IsOut)
output.Write("[out] ");
if (p.IsOptional)
output.Write("[opt] ");
p.ParameterType.WriteTo(output); p.ParameterType.WriteTo(output);
output.Write(' '); output.Write(' ');
if (p.HasMarshalInfo) {
WriteMarshalInfo(p.MarshalInfo);
}
output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p); output.WriteDefinition(DisassemblerHelpers.Escape(p.Name), p);
if (i < parameters.Count - 1) if (i < parameters.Count - 1)
output.Write(','); output.Write(',');
output.WriteLine(); output.WriteLine();
} }
} }
bool HasParameterAttributes(ParameterDefinition p)
{
return p.HasConstant || p.HasCustomAttributes;
}
void WriteParameterAttributes(ParameterDefinition p)
{
if (!HasParameterAttributes(p))
return;
output.Write(".param [{0}]", p.Index + 1);
if (p.HasConstant) {
output.Write(" = ");
WriteConstant(p.Constant);
}
output.WriteLine();
WriteAttributes(p.CustomAttributes);
}
void WriteConstant(object constant)
{
if (constant == null) {
output.Write("nullref");
} else {
string typeName = DisassemblerHelpers.PrimitiveTypeName(constant.GetType().FullName);
if (typeName != null && typeName != "string") {
output.Write(typeName);
output.Write('(');
float? cf = constant as float?;
double? cd = constant as double?;
if (cf.HasValue && (float.IsNaN(cf.Value) || float.IsInfinity(cf.Value))) {
output.Write("0x{0:x8}", BitConverter.ToInt32(BitConverter.GetBytes(cf.Value), 0));
} else if (cd.HasValue && (double.IsNaN(cd.Value) || double.IsInfinity(cd.Value))) {
output.Write("0x{0:x16}", BitConverter.DoubleToInt64Bits(cd.Value));
} else {
DisassemblerHelpers.WriteOperand(output, constant);
}
output.Write(')');
} else {
DisassemblerHelpers.WriteOperand(output, constant);
}
}
}
#endregion #endregion
#region Disassemble Field #region Disassemble Field
@ -190,22 +676,31 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleField(FieldDefinition field) public void DisassembleField(FieldDefinition field)
{ {
// create mappings for decompiled fields only
this.DecompiledMemberReferences.Add(field.MetadataToken.ToInt32(), field);
output.WriteDefinition(".field ", field); output.WriteDefinition(".field ", field);
WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility); WriteEnum(field.Attributes & FieldAttributes.FieldAccessMask, fieldVisibility);
WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | FieldAttributes.HasDefault), fieldAttributes); const FieldAttributes hasXAttributes = FieldAttributes.HasDefault | FieldAttributes.HasFieldMarshal | FieldAttributes.HasFieldRVA;
WriteFlags(field.Attributes & ~(FieldAttributes.FieldAccessMask | hasXAttributes), fieldAttributes);
if (field.HasMarshalInfo) {
WriteMarshalInfo(field.MarshalInfo);
}
field.FieldType.WriteTo(output); field.FieldType.WriteTo(output);
output.Write(' '); output.Write(' ');
output.Write(DisassemblerHelpers.Escape(field.Name)); output.Write(DisassemblerHelpers.Escape(field.Name));
if ((field.Attributes & FieldAttributes.HasFieldRVA) == FieldAttributes.HasFieldRVA) {
output.Write(" at I_{0:x8}", field.RVA);
}
if (field.HasConstant) { if (field.HasConstant) {
output.Write(" = "); output.Write(" = ");
DisassemblerHelpers.WriteOperand(output, field.Constant); WriteConstant(field.Constant);
} }
output.WriteLine();
if (field.HasCustomAttributes) { if (field.HasCustomAttributes) {
OpenBlock(false); output.MarkFoldStart();
WriteAttributes(field.CustomAttributes); WriteAttributes(field.CustomAttributes);
CloseBlock(); output.MarkFoldEnd();
} else {
output.WriteLine();
} }
} }
#endregion #endregion
@ -219,17 +714,33 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleProperty(PropertyDefinition property) public void DisassembleProperty(PropertyDefinition property)
{ {
// set current member
currentMember = property;
output.WriteDefinition(".property ", property); output.WriteDefinition(".property ", property);
WriteFlags(property.Attributes, propertyAttributes); WriteFlags(property.Attributes, propertyAttributes);
if (property.HasThis)
output.Write("instance ");
property.PropertyType.WriteTo(output); property.PropertyType.WriteTo(output);
output.Write(' '); output.Write(' ');
output.Write(DisassemblerHelpers.Escape(property.Name)); output.Write(DisassemblerHelpers.Escape(property.Name));
output.Write("(");
if (property.HasParameters) {
output.WriteLine();
output.Indent();
WriteParameters(property.Parameters);
output.Unindent();
}
output.Write(")");
OpenBlock(false); OpenBlock(false);
WriteAttributes(property.CustomAttributes); WriteAttributes(property.CustomAttributes);
WriteNestedMethod(".get", property.GetMethod); WriteNestedMethod(".get", property.GetMethod);
WriteNestedMethod(".set", property.SetMethod); WriteNestedMethod(".set", property.SetMethod);
foreach (var method in property.OtherMethods) { foreach (var method in property.OtherMethods) {
WriteNestedMethod(".method", method); WriteNestedMethod(".other", method);
} }
CloseBlock(); CloseBlock();
} }
@ -238,17 +749,11 @@ namespace ICSharpCode.Decompiler.Disassembler
{ {
if (method == null) if (method == null)
return; return;
if (detectControlStructure) {
output.WriteDefinition(keyword, method);
output.Write(' ');
DisassembleMethodInternal(method);
} else {
output.Write(keyword); output.Write(keyword);
output.Write(' '); output.Write(' ');
method.WriteTo(output); method.WriteTo(output);
output.WriteLine(); output.WriteLine();
} }
}
#endregion #endregion
#region Disassemble Event #region Disassemble Event
@ -259,18 +764,21 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleEvent(EventDefinition ev) public void DisassembleEvent(EventDefinition ev)
{ {
// set current member
currentMember = ev;
output.WriteDefinition(".event ", ev); output.WriteDefinition(".event ", ev);
WriteFlags(ev.Attributes, eventAttributes); WriteFlags(ev.Attributes, eventAttributes);
ev.EventType.WriteTo(output); ev.EventType.WriteTo(output, ILNameSyntax.TypeName);
output.Write(' '); output.Write(' ');
output.Write(DisassemblerHelpers.Escape(ev.Name)); output.Write(DisassemblerHelpers.Escape(ev.Name));
OpenBlock(false); OpenBlock(false);
WriteAttributes(ev.CustomAttributes); WriteAttributes(ev.CustomAttributes);
WriteNestedMethod(".add", ev.AddMethod); WriteNestedMethod(".addon", ev.AddMethod);
WriteNestedMethod(".remove", ev.RemoveMethod); WriteNestedMethod(".removeon", ev.RemoveMethod);
WriteNestedMethod(".invoke", ev.InvokeMethod); WriteNestedMethod(".fire", ev.InvokeMethod);
foreach (var method in ev.OtherMethods) { foreach (var method in ev.OtherMethods) {
WriteNestedMethod(".method", method); WriteNestedMethod(".other", method);
} }
CloseBlock(); CloseBlock();
} }
@ -312,6 +820,7 @@ namespace ICSharpCode.Decompiler.Disassembler
public void DisassembleType(TypeDefinition type) public void DisassembleType(TypeDefinition type)
{ {
// start writing IL
output.WriteDefinition(".class ", type); output.WriteDefinition(".class ", type);
if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface) if ((type.Attributes & TypeAttributes.ClassSemanticMask) == TypeAttributes.Interface)
@ -322,14 +831,15 @@ namespace ICSharpCode.Decompiler.Disassembler
const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask; const TypeAttributes masks = TypeAttributes.ClassSemanticMask | TypeAttributes.VisibilityMask | TypeAttributes.LayoutMask | TypeAttributes.StringFormatMask;
WriteFlags(type.Attributes & ~masks, typeAttributes); WriteFlags(type.Attributes & ~masks, typeAttributes);
output.Write(DisassemblerHelpers.Escape(type.Name)); output.Write(DisassemblerHelpers.Escape(type.DeclaringType != null ? type.Name : type.FullName));
WriteTypeParameters(output, type);
output.MarkFoldStart(defaultCollapsed: isInType); output.MarkFoldStart(defaultCollapsed: isInType);
output.WriteLine(); output.WriteLine();
if (type.BaseType != null) { if (type.BaseType != null) {
output.Indent(); output.Indent();
output.Write("extends "); output.Write("extends ");
type.BaseType.WriteTo(output, true); type.BaseType.WriteTo(output, ILNameSyntax.TypeName);
output.WriteLine(); output.WriteLine();
output.Unindent(); output.Unindent();
} }
@ -342,9 +852,7 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write("implements "); output.Write("implements ");
else else
output.Write(" "); output.Write(" ");
if (type.Interfaces[index].Namespace != null) type.Interfaces[index].WriteTo(output, ILNameSyntax.TypeName);
output.Write("{0}.", type.Interfaces[index].Namespace);
output.Write(type.Interfaces[index].Name);
} }
output.WriteLine(); output.WriteLine();
output.Unindent(); output.Unindent();
@ -355,6 +863,7 @@ namespace ICSharpCode.Decompiler.Disassembler
bool oldIsInType = isInType; bool oldIsInType = isInType;
isInType = true; isInType = true;
WriteAttributes(type.CustomAttributes); WriteAttributes(type.CustomAttributes);
WriteSecurityDeclarations(type);
if (type.HasLayoutInfo) { if (type.HasLayoutInfo) {
output.WriteLine(".pack {0}", type.PackingSize); output.WriteLine(".pack {0}", type.PackingSize);
output.WriteLine(".size {0}", type.ClassSize); output.WriteLine(".size {0}", type.ClassSize);
@ -377,14 +886,14 @@ namespace ICSharpCode.Decompiler.Disassembler
} }
output.WriteLine(); output.WriteLine();
} }
if (type.HasProperties) { if (type.HasMethods) {
output.WriteLine("// Properties"); output.WriteLine("// Methods");
foreach (var prop in type.Properties) { foreach (var m in type.Methods) {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
DisassembleProperty(prop); DisassembleMethod(m);
}
output.WriteLine(); output.WriteLine();
} }
}
if (type.HasEvents) { if (type.HasEvents) {
output.WriteLine("// Events"); output.WriteLine("// Events");
foreach (var ev in type.Events) { foreach (var ev in type.Events) {
@ -394,19 +903,52 @@ namespace ICSharpCode.Decompiler.Disassembler
} }
output.WriteLine(); output.WriteLine();
} }
if (type.HasMethods) { if (type.HasProperties) {
output.WriteLine("// Methods"); output.WriteLine("// Properties");
var accessorMethods = type.GetAccessorMethods(); foreach (var prop in type.Properties) {
foreach (var m in type.Methods) {
cancellationToken.ThrowIfCancellationRequested(); cancellationToken.ThrowIfCancellationRequested();
if (!(detectControlStructure && accessorMethods.Contains(m))) { DisassembleProperty(prop);
DisassembleMethod(m); }
output.WriteLine(); output.WriteLine();
} }
CloseBlock("end of class " + (type.DeclaringType != null ? type.Name : type.FullName));
isInType = oldIsInType;
} }
void WriteTypeParameters(ITextOutput output, IGenericParameterProvider p)
{
if (p.HasGenericParameters) {
output.Write('<');
for (int i = 0; i < p.GenericParameters.Count; i++) {
if (i > 0)
output.Write(", ");
GenericParameter gp = p.GenericParameters[i];
if (gp.HasReferenceTypeConstraint) {
output.Write("class ");
} else if (gp.HasNotNullableValueTypeConstraint) {
output.Write("valuetype ");
}
if (gp.HasDefaultConstructorConstraint) {
output.Write(".ctor ");
}
if (gp.HasConstraints) {
output.Write('(');
for (int j = 0; j < gp.Constraints.Count; j++) {
if (j > 0)
output.Write(", ");
gp.Constraints[j].WriteTo(output, ILNameSyntax.TypeName);
}
output.Write(") ");
}
if (gp.IsContravariant) {
output.Write('-');
} else if (gp.IsCovariant) {
output.Write('+');
}
output.Write(DisassemblerHelpers.Escape(gp.Name));
}
output.Write('>');
} }
CloseBlock("End of class " + type.FullName);
isInType = oldIsInType;
} }
#endregion #endregion
@ -540,9 +1082,12 @@ namespace ICSharpCode.Decompiler.Disassembler
{ {
output.Write(".assembly " + DisassemblerHelpers.Escape(asm.Name.Name)); output.Write(".assembly " + DisassemblerHelpers.Escape(asm.Name.Name));
OpenBlock(false); OpenBlock(false);
Version v = asm.Name.Version; WriteAttributes(asm.CustomAttributes);
if (v != null) { WriteSecurityDeclarations(asm);
output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision); if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) {
output.Write(".publickey = ");
WriteBlob(asm.Name.PublicKey);
output.WriteLine();
} }
if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) { if (asm.Name.HashAlgorithm != AssemblyHashAlgorithm.None) {
output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm); output.Write(".hash algorithm 0x{0:x8}", (int)asm.Name.HashAlgorithm);
@ -550,13 +1095,64 @@ namespace ICSharpCode.Decompiler.Disassembler
output.Write(" // SHA1"); output.Write(" // SHA1");
output.WriteLine(); output.WriteLine();
} }
if (asm.Name.PublicKey != null && asm.Name.PublicKey.Length > 0) { Version v = asm.Name.Version;
output.Write(".publickey = "); if (v != null) {
WriteBlob(asm.Name.PublicKey); output.WriteLine(".ver {0}:{1}:{2}:{3}", v.Major, v.Minor, v.Build, v.Revision);
}
CloseBlock();
}
public void WriteAssemblyReferences(ModuleDefinition module)
{
foreach (var mref in module.ModuleReferences) {
output.WriteLine(".module extern {0}", DisassemblerHelpers.Escape(mref.Name));
}
foreach (var aref in module.AssemblyReferences) {
output.Write(".assembly extern {0}", DisassemblerHelpers.Escape(aref.Name));
OpenBlock(false);
if (aref.PublicKeyToken != null) {
output.Write(".publickeytoken = ");
WriteBlob(aref.PublicKeyToken);
output.WriteLine(); output.WriteLine();
} }
WriteAttributes(asm.CustomAttributes); if (aref.Version != null) {
output.WriteLine(".ver {0}:{1}:{2}:{3}", aref.Version.Major, aref.Version.Minor, aref.Version.Build, aref.Version.Revision);
}
CloseBlock(); CloseBlock();
} }
} }
public void WriteModuleHeader(ModuleDefinition module)
{
if (module.HasExportedTypes) {
foreach (ExportedType exportedType in module.ExportedTypes) {
output.Write(".class extern ");
if (exportedType.IsForwarder)
output.Write("forwarder ");
output.Write(exportedType.DeclaringType != null ? exportedType.Name : exportedType.FullName);
OpenBlock(false);
if (exportedType.DeclaringType != null)
output.WriteLine(".class extern {0}", DisassemblerHelpers.Escape(exportedType.DeclaringType.FullName));
else
output.WriteLine(".assembly extern {0}", DisassemblerHelpers.Escape(exportedType.Scope.Name));
CloseBlock();
}
}
output.WriteLine(".module {0}", module.Name);
output.WriteLine("// MVID: {0}", module.Mvid.ToString("B").ToUpperInvariant());
// TODO: imagebase, file alignment, stackreserve, subsystem
output.WriteLine(".corflags 0x{0:x} // {1}", module.Attributes, module.Attributes.ToString());
WriteAttributes(module.CustomAttributes);
}
public void WriteModuleContents(ModuleDefinition module)
{
foreach (TypeDefinition td in module.Types) {
DisassembleType(td);
output.WriteLine();
}
}
}
} }

4
src/Libraries/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -64,6 +64,7 @@
<Compile Include="Ast\Transforms\CombineQueryExpressions.cs" /> <Compile Include="Ast\Transforms\CombineQueryExpressions.cs" />
<Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" /> <Compile Include="Ast\Transforms\ContextTrackingVisitor.cs" />
<Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" /> <Compile Include="Ast\Transforms\ConvertConstructorCallIntoInitializer.cs" />
<Compile Include="Ast\Transforms\DecimalConstantTransform.cs" />
<Compile Include="Ast\Transforms\DeclareVariables.cs" /> <Compile Include="Ast\Transforms\DeclareVariables.cs" />
<Compile Include="Ast\Transforms\DelegateConstruction.cs" /> <Compile Include="Ast\Transforms\DelegateConstruction.cs" />
<Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" /> <Compile Include="Ast\Transforms\IntroduceExtensionMethods.cs" />
@ -76,6 +77,7 @@
<Compile Include="Ast\Transforms\PatternStatementTransform.cs" /> <Compile Include="Ast\Transforms\PatternStatementTransform.cs" />
<Compile Include="Ast\TypesHierarchyHelpers.cs" /> <Compile Include="Ast\TypesHierarchyHelpers.cs" />
<Compile Include="CecilExtensions.cs" /> <Compile Include="CecilExtensions.cs" />
<Compile Include="CodeMappings.cs" />
<Compile Include="DecompilerException.cs" /> <Compile Include="DecompilerException.cs" />
<Compile Include="DecompilerSettings.cs" /> <Compile Include="DecompilerSettings.cs" />
<Compile Include="Disassembler\DisassemblerHelpers.cs" /> <Compile Include="Disassembler\DisassemblerHelpers.cs" />
@ -112,6 +114,7 @@
<Compile Include="ILAst\YieldReturnDecompiler.cs" /> <Compile Include="ILAst\YieldReturnDecompiler.cs" />
<Compile Include="ITextOutput.cs" /> <Compile Include="ITextOutput.cs" />
<Compile Include="PlainTextOutput.cs" /> <Compile Include="PlainTextOutput.cs" />
<Compile Include="ReferenceResolvingException.cs" />
<Compile Include="TextOutputWriter.cs" /> <Compile Include="TextOutputWriter.cs" />
<None Include="Properties\AssemblyInfo.cs" /> <None Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </ItemGroup>
@ -119,7 +122,6 @@
<ProjectReference Include="..\Mono.Cecil\Mono.Cecil.csproj"> <ProjectReference Include="..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project> <Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name> <Name>Mono.Cecil</Name>
<Private>False</Private>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\NewNRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj"> <ProjectReference Include="..\NewNRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project> <Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>

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

@ -1,9 +1,20 @@
// <file> // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// <copyright see="prj:///doc/copyright.txt"/> //
// <license see="prj:///doc/license.txt"/> // Permission is hereby granted, free of charge, to any person obtaining a copy of this
// <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/> // software and associated documentation files (the "Software"), to deal in the Software
// <version>$Revision$</version> // without restriction, including without limitation the rights to use, copy, modify, merge,
// </file> // 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;
using System.Collections; using System.Collections;

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

@ -1,4 +1,22 @@
using System; // 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.Diagnostics;
using System.IO; using System.IO;
using System.Collections.Generic; using System.Collections.Generic;
@ -73,17 +91,34 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
var defaultCase = ilSwitch.CaseBlocks.Where(cb => cb.Values == null).SingleOrDefault(); var defaultCase = ilSwitch.CaseBlocks.SingleOrDefault(cb => cb.Values == null);
// If there is no default block, remove empty case blocks // If there is no default block, remove empty case blocks
if (defaultCase == null || (defaultCase.Body.Count == 1 && defaultCase.Body.Single().Match(ILCode.LoopOrSwitchBreak))) { 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)); ilSwitch.CaseBlocks.RemoveAll(b => b.Body.Count == 1 && b.Body.Single().Match(ILCode.LoopOrSwitchBreak));
} }
} }
// Remove redundant return // 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) { 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); 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) IEnumerable<ILNode> GetParents(ILNode node)
@ -122,14 +157,14 @@ namespace ICSharpCode.Decompiler.ILAst
return true; return true;
} }
ILNode breakBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop || n is ILSwitch).FirstOrDefault(); ILNode breakBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop || n is ILSwitch);
if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) { if (breakBlock != null && target == Exit(breakBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopOrSwitchBreak; gotoExpr.Code = ILCode.LoopOrSwitchBreak;
gotoExpr.Operand = null; gotoExpr.Operand = null;
return true; return true;
} }
ILNode continueBlock = GetParents(gotoExpr).Where(n => n is ILWhileLoop).FirstOrDefault(); ILNode continueBlock = GetParents(gotoExpr).FirstOrDefault(n => n is ILWhileLoop);
if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) { if (continueBlock != null && target == Enter(continueBlock, new HashSet<ILNode>() { gotoExpr })) {
gotoExpr.Code = ILCode.LoopContinue; gotoExpr.Code = ILCode.LoopContinue;
gotoExpr.Operand = null; gotoExpr.Operand = null;
@ -191,10 +226,10 @@ namespace ICSharpCode.Decompiler.ILAst
} else if (expr.Code == ILCode.Nop) { } else if (expr.Code == ILCode.Nop) {
return Exit(expr, visitedNodes); return Exit(expr, visitedNodes);
} else if (expr.Code == ILCode.LoopOrSwitchBreak) { } else if (expr.Code == ILCode.LoopOrSwitchBreak) {
ILNode breakBlock = GetParents(expr).Where(n => n is ILWhileLoop || n is ILSwitch).First(); ILNode breakBlock = GetParents(expr).First(n => n is ILWhileLoop || n is ILSwitch);
return Exit(breakBlock, new HashSet<ILNode>() { expr }); return Exit(breakBlock, new HashSet<ILNode>() { expr });
} else if (expr.Code == ILCode.LoopContinue) { } else if (expr.Code == ILCode.LoopContinue) {
ILNode continueBlock = GetParents(expr).Where(n => n is ILWhileLoop).First(); ILNode continueBlock = GetParents(expr).First(n => n is ILWhileLoop);
return Enter(continueBlock, new HashSet<ILNode>() { expr }); return Enter(continueBlock, new HashSet<ILNode>() { expr });
} else { } else {
return expr; return expr;

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

@ -1,7 +1,27 @@
// 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;
using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using Mono.Cecil; using Mono.Cecil;
using Mono.Cecil.Cil; using Mono.Cecil.Cil;
using Cecil = Mono.Cecil; using Cecil = Mono.Cecil;
@ -261,6 +281,8 @@ namespace ICSharpCode.Decompiler.ILAst
int varCount = methodDef.Body.Variables.Count; int varCount = methodDef.Body.Variables.Count;
var exceptionHandlerStarts = new HashSet<ByteCode>(methodDef.Body.ExceptionHandlers.Select(eh => instrToByteCode[eh.HandlerStart]));
// Add known states // Add known states
if(methodDef.Body.HasExceptionHandlers) { if(methodDef.Body.HasExceptionHandlers) {
foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) { foreach(ExceptionHandler ex in methodDef.Body.ExceptionHandlers) {
@ -327,8 +349,13 @@ namespace ICSharpCode.Decompiler.ILAst
// Find all successors // Find all successors
List<ByteCode> branchTargets = new List<ByteCode>(); List<ByteCode> branchTargets = new List<ByteCode>();
if (!byteCode.Code.IsUnconditionalControlFlow()) { 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); branchTargets.Add(byteCode.Next);
} }
}
if (byteCode.Operand is Instruction[]) { if (byteCode.Operand is Instruction[]) {
foreach(Instruction inst in (Instruction[])byteCode.Operand) { foreach(Instruction inst in (Instruction[])byteCode.Operand) {
ByteCode target = instrToByteCode[inst]; ByteCode target = instrToByteCode[inst];
@ -406,13 +433,12 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
// Occasionally the compiler generates unreachable code - it can be usually just ignored // Occasionally the compilers or obfuscators generate unreachable code (which migt be intentonally invalid)
var reachableBody = body.Where(b => b.StackBefore != null); // I belive it is safe to just remove it
var unreachableBody = body.Where(b => b.StackBefore == null); body.RemoveAll(b => b.StackBefore == null);
// Genertate temporary variables to replace stack // Genertate temporary variables to replace stack
// Unrachable code does not need temporary variables - the values are never pushed on the stack for consuption foreach(ByteCode byteCode in body) {
foreach(ByteCode byteCode in reachableBody) {
int argIdx = 0; int argIdx = 0;
int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count; int popCount = byteCode.PopCount ?? byteCode.StackBefore.Count;
for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) { for (int i = byteCode.StackBefore.Count - popCount; i < byteCode.StackBefore.Count; i++) {
@ -430,19 +456,18 @@ namespace ICSharpCode.Decompiler.ILAst
// Try to use single temporary variable insted of several if possilbe (especially useful for dup) // 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 // This has to be done after all temporary variables are assigned so we know about all loads
// Unrachable code will not have any StoreTo foreach(ByteCode byteCode in body) {
foreach(ByteCode byteCode in reachableBody) {
if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) { if (byteCode.StoreTo != null && byteCode.StoreTo.Count > 1) {
var locVars = byteCode.StoreTo; var locVars = byteCode.StoreTo;
// For each of the variables, find the location where it is loaded - there should be preciesly one // For each of the variables, find the location where it is loaded - there should be preciesly one
var loadedBy = locVars.Select(locVar => reachableBody.SelectMany(bc => bc.StackBefore).Where(s => s.LoadFrom == locVar).Single()).ToList(); 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, // We now know that all the variables have a single load,
// Let's make sure that they have also a single store - us // Let's make sure that they have also a single store - us
if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) { if (loadedBy.All(slot => slot.PushedBy.Length == 1 && slot.PushedBy[0] == byteCode)) {
// Great - we can reduce everything into single variable // Great - we can reduce everything into single variable
ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true }; ILVariable tmpVar = new ILVariable() { Name = string.Format("expr_{0:X2}", byteCode.Offset), IsGenerated = true };
byteCode.StoreTo = new List<ILVariable>() { tmpVar }; byteCode.StoreTo = new List<ILVariable>() { tmpVar };
foreach(ByteCode bc in reachableBody) { foreach(ByteCode bc in body) {
for (int i = 0; i < bc.StackBefore.Count; i++) { for (int i = 0; i < bc.StackBefore.Count; i++) {
// Is it one of the variable to be merged? // Is it one of the variable to be merged?
if (locVars.Contains(bc.StackBefore[i].LoadFrom)) { if (locVars.Contains(bc.StackBefore[i].LoadFrom)) {
@ -552,7 +577,7 @@ namespace ICSharpCode.Decompiler.ILAst
Loads = new List<ByteCode>() { load } Loads = new List<ByteCode>() { load }
}); });
} else if (storedBy.Length == 1) { } else if (storedBy.Length == 1) {
VariableInfo newVar = newVars.Where(v => v.Stores.Contains(storedBy[0])).Single(); VariableInfo newVar = newVars.Single(v => v.Stores.Contains(storedBy[0]));
newVar.Loads.Add(load); newVar.Loads.Add(load);
} else { } else {
List<VariableInfo> mergeVars = newVars.Where(v => v.Stores.Union(storedBy).Any()).ToList(); List<VariableInfo> mergeVars = newVars.Where(v => v.Stores.Union(storedBy).Any()).ToList();
@ -638,12 +663,14 @@ namespace ICSharpCode.Decompiler.ILAst
// Find the first and widest scope // Find the first and widest scope
int tryStart = ehs.Min(eh => eh.TryStart.Offset); int tryStart = ehs.Min(eh => eh.TryStart.Offset);
int tryEnd = ehs.Where(eh => eh.TryStart.Offset == tryStart).Max(eh => eh.TryEnd.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).ToList(); 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 // Cut all instructions up to the try block
{ {
int tryStartIdx; int tryStartIdx = 0;
for (tryStartIdx = 0; body[tryStartIdx].Offset != tryStart; tryStartIdx++); while (tryStartIdx < body.Count && body[tryStartIdx].Offset < tryStart) tryStartIdx++;
ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx))); ast.AddRange(ConvertToAst(body.CutRange(0, tryStartIdx)));
} }
@ -651,24 +678,22 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
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))); 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); ehs.ExceptWith(nestedEHs);
int tryEndIdx; int tryEndIdx = 0;
for (tryEndIdx = 0; tryEndIdx < body.Count && body[tryEndIdx].Offset != tryEnd; tryEndIdx++); while (tryEndIdx < body.Count && body[tryEndIdx].Offset < tryEnd) tryEndIdx++;
tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs)); tryCatchBlock.TryBlock = new ILBlock(ConvertToAst(body.CutRange(0, tryEndIdx), nestedEHs));
} }
// Cut all handlers // Cut all handlers
tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>(); tryCatchBlock.CatchBlocks = new List<ILTryCatchBlock.CatchBlock>();
foreach(ExceptionHandler eh in handlers) { foreach(ExceptionHandler eh in handlers) {
int startIndex; int handlerEndOffset = eh.HandlerEnd == null ? methodDef.Body.CodeSize : eh.HandlerEnd.Offset;
for (startIndex = 0; body[startIndex].Offset != eh.HandlerStart.Offset; startIndex++); int startIdx = 0;
int endInclusiveIndex; while (startIdx < body.Count && body[startIdx].Offset < eh.HandlerStart.Offset) startIdx++;
if (eh.HandlerEnd == null) endInclusiveIndex = body.Count - 1; int endIdx = 0;
// Note that the end(exclusive) instruction may not necessarly be in our body while (endIdx < body.Count && body[endIdx].Offset < handlerEndOffset) endIdx++;
else for (endInclusiveIndex = 0; body[endInclusiveIndex].Next.Offset != eh.HandlerEnd.Offset; endInclusiveIndex++); 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)));
int count = 1 + endInclusiveIndex - startIndex;
HashSet<ExceptionHandler> nestedEHs = new HashSet<ExceptionHandler>(ehs.Where(e => (eh.HandlerStart.Offset <= e.TryStart.Offset && e.TryEnd.Offset < eh.HandlerEnd.Offset) || (eh.HandlerStart.Offset < e.TryStart.Offset && e.TryEnd.Offset <= eh.HandlerEnd.Offset)));
ehs.ExceptWith(nestedEHs); ehs.ExceptWith(nestedEHs);
List<ILNode> handlerAst = ConvertToAst(body.CutRange(startIndex, count), nestedEHs); List<ILNode> handlerAst = ConvertToAst(body.CutRange(startIdx, endIdx - startIdx), nestedEHs);
if (eh.HandlerType == ExceptionHandlerType.Catch) { if (eh.HandlerType == ExceptionHandlerType.Catch) {
ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() { ILTryCatchBlock.CatchBlock catchBlock = new ILTryCatchBlock.CatchBlock() {
ExceptionType = eh.CatchType, ExceptionType = eh.CatchType,

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

@ -1,3 +1,21 @@
// 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -24,19 +42,24 @@ namespace ICSharpCode.Decompiler.ILAst
SimplifyTernaryOperator, SimplifyTernaryOperator,
SimplifyNullCoalescing, SimplifyNullCoalescing,
JoinBasicBlocks, JoinBasicBlocks,
SimplifyShiftOperators,
TransformDecimalCtorToConstant, TransformDecimalCtorToConstant,
SimplifyLdObjAndStObj, SimplifyLdObjAndStObj,
SimplifyCustomShortCircuit,
TransformArrayInitializers, TransformArrayInitializers,
TransformCollectionInitializers, TransformMultidimensionalArrayInitializers,
TransformObjectInitializers,
MakeAssignmentExpression, MakeAssignmentExpression,
IntroducePostIncrement, IntroducePostIncrement,
InlineVariables2, InlineVariables2,
FindLoops, FindLoops,
FindConditions, FindConditions,
FlattenNestedMovableBlocks, FlattenNestedMovableBlocks,
RemoveEndFinally,
RemoveRedundantCode2, RemoveRedundantCode2,
GotoRemoval, GotoRemoval,
DuplicateReturns, DuplicateReturns,
GotoRemoval2,
ReduceIfNesting, ReduceIfNesting,
InlineVariables3, InlineVariables3,
CachedDelegateInitialization, CachedDelegateInitialization,
@ -111,6 +134,9 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return; if (abortBeforeStep == ILAstOptimizationStep.JoinBasicBlocks) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks); modified |= block.RunOptimization(new SimpleControlFlow(context, method).JoinBasicBlocks);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyShiftOperators) return;
modified |= block.RunOptimization(SimplifyShiftOperators);
if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return; if (abortBeforeStep == ILAstOptimizationStep.TransformDecimalCtorToConstant) return;
modified |= block.RunOptimization(TransformDecimalCtorToConstant); modified |= block.RunOptimization(TransformDecimalCtorToConstant);
modified |= block.RunOptimization(SimplifyLdcI4ConvI8); modified |= block.RunOptimization(SimplifyLdcI4ConvI8);
@ -118,11 +144,17 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return; if (abortBeforeStep == ILAstOptimizationStep.SimplifyLdObjAndStObj) return;
modified |= block.RunOptimization(SimplifyLdObjAndStObj); modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.SimplifyCustomShortCircuit) return;
modified |= block.RunOptimization(new SimpleControlFlow(context, method).SimplifyCustomShortCircuit);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return; if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(Initializers.TransformArrayInitializers); modified |= block.RunOptimization(TransformArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformMultidimensionalArrayInitializers) return;
modified |= block.RunOptimization(TransformMultidimensionalArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformCollectionInitializers) return; if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) return;
modified |= block.RunOptimization(Initializers.TransformCollectionInitializers); modified |= block.RunOptimization(TransformObjectInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return; if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression); modified |= block.RunOptimization(MakeAssignmentExpression);
@ -151,6 +183,9 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return; if (abortBeforeStep == ILAstOptimizationStep.FlattenNestedMovableBlocks) return;
FlattenBasicBlocks(method); FlattenBasicBlocks(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveEndFinally) return;
RemoveEndFinally(method);
if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return; if (abortBeforeStep == ILAstOptimizationStep.RemoveRedundantCode2) return;
RemoveRedundantCode(method); RemoveRedundantCode(method);
@ -160,6 +195,9 @@ namespace ICSharpCode.Decompiler.ILAst
if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return; if (abortBeforeStep == ILAstOptimizationStep.DuplicateReturns) return;
DuplicateReturnStatements(method); DuplicateReturnStatements(method);
if (abortBeforeStep == ILAstOptimizationStep.GotoRemoval2) return;
new GotoRemoval().RemoveGotos(method);
if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return; if (abortBeforeStep == ILAstOptimizationStep.ReduceIfNesting) return;
ReduceIfNesting(method); ReduceIfNesting(method);
@ -290,10 +328,31 @@ namespace ICSharpCode.Decompiler.ILAst
/// Converts call and callvirt instructions that read/write properties into CallGetter/CallSetter instructions. /// 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)". /// 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> /// </summary>
void IntroducePropertyAccessInstructions(ILBlock method) 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)
{ {
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) {
if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) { if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
MethodReference cecilMethod = (MethodReference)expr.Operand; MethodReference cecilMethod = (MethodReference)expr.Operand;
if (cecilMethod.DeclaringType is ArrayType) { if (cecilMethod.DeclaringType is ArrayType) {
@ -305,8 +364,18 @@ namespace ICSharpCode.Decompiler.ILAst
expr.Code = ILCode.CallSetter; expr.Code = ILCode.CallSetter;
break; break;
case "Address": 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; expr.Code = ILCode.CallGetter;
expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress)); if (parentExpr != null) {
parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr);
}
break; break;
} }
} else { } else {
@ -318,6 +387,18 @@ namespace ICSharpCode.Decompiler.ILAst
expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter; 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();
} }
} }
} }
@ -352,7 +433,7 @@ namespace ICSharpCode.Decompiler.ILAst
lastNode.IsUnconditionalControlFlow()) lastNode.IsUnconditionalControlFlow())
{ {
// Try to reuse the label // Try to reuse the label
ILLabel label = currNode is ILLabel ? ((ILLabel)currNode) : new ILLabel() { Name = "Block_" + (nextLabelIndex++) }; ILLabel label = currNode as ILLabel ?? new ILLabel() { Name = "Block_" + (nextLabelIndex++).ToString() };
// Terminate the last block // Terminate the last block
if (!lastNode.IsUnconditionalControlFlow()) { if (!lastNode.IsUnconditionalControlFlow()) {
@ -461,6 +542,25 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
/// <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> /// <summary>
/// Reduce the nesting of conditions. /// Reduce the nesting of conditions.
/// It should be done on flat data that already had most gotos removed /// It should be done on flat data that already had most gotos removed
@ -510,16 +610,37 @@ namespace ICSharpCode.Decompiler.ILAst
// This ensures that a single IL variable is a single C# variable (gets assigned only one name) // 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. // 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>(); Dictionary<VariableDefinition, ILVariable> dict = new Dictionary<VariableDefinition, ILVariable>();
foreach (ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { ReplaceVariables(
ILVariable v = expr.Operand as ILVariable; method,
if (v != null && v.OriginalVariable != null) { delegate(ILVariable v) {
if (v.OriginalVariable == null)
return v;
ILVariable combinedVariable; ILVariable combinedVariable;
if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) { if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) {
dict.Add(v.OriginalVariable, v); dict.Add(v.OriginalVariable, v);
combinedVariable = v; combinedVariable = v;
} }
expr.Operand = combinedVariable; 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);
} }
} }
@ -648,15 +769,36 @@ namespace ICSharpCode.Decompiler.ILAst
// property getters can't be expression statements, but all other method calls can be // property getters can't be expression statements, but all other method calls can be
MethodReference mr = (MethodReference)expr.Operand; MethodReference mr = (MethodReference)expr.Operand;
return !mr.Name.StartsWith("get_", StringComparison.Ordinal); return !mr.Name.StartsWith("get_", StringComparison.Ordinal);
case ILCode.CallSetter:
case ILCode.CallvirtSetter:
case ILCode.Newobj: case ILCode.Newobj:
case ILCode.Newarr: case ILCode.Newarr:
case ILCode.Stloc: 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; return true;
default: default:
return false; 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) public static void RemoveTail(this List<ILNode> body, params ILCode[] codes)
{ {
for (int i = 0; i < codes.Length; i++) { for (int i = 0; i < codes.Length; i++) {

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

@ -1,3 +1,21 @@
// 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
@ -25,6 +43,7 @@ namespace ICSharpCode.Decompiler.ILAst
void AccumulateSelfAndChildrenRecursive<T>(List<T> list, Func<T, bool> predicate) where T:ILNode 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; T thisAsT = this as T;
if (thisAsT != null && (predicate == null || predicate(thisAsT))) if (thisAsT != null && (predicate == null || predicate(thisAsT)))
list.Add(thisAsT); list.Add(thisAsT);
@ -123,6 +142,10 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
output.Write("catch "); output.Write("catch ");
output.WriteReference(ExceptionType.FullName, ExceptionType); output.WriteReference(ExceptionType.FullName, ExceptionType);
if (ExceptionVariable != null) {
output.Write(' ');
output.Write(ExceptionVariable.Name);
}
output.WriteLine(" {"); output.WriteLine(" {");
output.Indent(); output.Indent();
base.WriteTo(output); base.WriteTo(output);
@ -210,7 +233,10 @@ namespace ICSharpCode.Decompiler.ILAst
public static List<ILRange> OrderAndJoint(IEnumerable<ILRange> input) public static List<ILRange> OrderAndJoint(IEnumerable<ILRange> input)
{ {
List<ILRange> ranges = input.OrderBy(r => r.From).ToList(); 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;) { for (int i = 0; i < ranges.Count - 1;) {
ILRange curr = ranges[i]; ILRange curr = ranges[i];
ILRange next = ranges[i + 1]; ILRange next = ranges[i + 1];
@ -227,6 +253,12 @@ namespace ICSharpCode.Decompiler.ILAst
public static IEnumerable<ILRange> Invert(IEnumerable<ILRange> input, int codeSize) 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); var ordered = OrderAndJoint(input);
if (ordered.Count == 0) { if (ordered.Count == 0) {
yield return new ILRange() { From = 0, To = codeSize }; yield return new ILRange() { From = 0, To = codeSize };
@ -351,10 +383,10 @@ namespace ICSharpCode.Decompiler.ILAst
output.Write(((ILVariable)Operand).Name); output.Write(((ILVariable)Operand).Name);
if (this.InferredType != null) { if (this.InferredType != null) {
output.Write(':'); output.Write(':');
this.InferredType.WriteTo(output, true, true); this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:"); output.Write("[exp:");
this.ExpectedType.WriteTo(output, true, true); this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']'); output.Write(']');
} }
} }
@ -372,15 +404,15 @@ namespace ICSharpCode.Decompiler.ILAst
output.Write(Code.GetName()); output.Write(Code.GetName());
if (this.InferredType != null) { if (this.InferredType != null) {
output.Write(':'); output.Write(':');
this.InferredType.WriteTo(output, true, true); this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) { if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:"); output.Write("[exp:");
this.ExpectedType.WriteTo(output, true, true); this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']'); output.Write(']');
} }
} else if (this.ExpectedType != null) { } else if (this.ExpectedType != null) {
output.Write("[exp:"); output.Write("[exp:");
this.ExpectedType.WriteTo(output, true, true); this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']'); output.Write(']');
} }
output.Write('('); output.Write('(');
@ -397,12 +429,14 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} else if (Operand is MethodReference) { } else if (Operand is MethodReference) {
MethodReference method = (MethodReference)Operand; MethodReference method = (MethodReference)Operand;
method.DeclaringType.WriteTo(output, true, true); if (method.DeclaringType != null) {
method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write("::"); output.Write("::");
}
output.WriteReference(method.Name, method); output.WriteReference(method.Name, method);
} else if (Operand is FieldReference) { } else if (Operand is FieldReference) {
FieldReference field = (FieldReference)Operand; FieldReference field = (FieldReference)Operand;
field.DeclaringType.WriteTo(output, true, true); field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write("::"); output.Write("::");
output.WriteReference(field.Name, field); output.WriteReference(field.Name, field);
} else { } else {

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using Mono.Cecil; using Mono.Cecil;
@ -238,8 +253,18 @@ namespace ICSharpCode.Decompiler.ILAst
LogicOr, LogicOr,
NullCoalescing, NullCoalescing,
InitArray, // Array Initializer InitArray, // Array Initializer
InitCollection, // Collection Initializer: first arg is newobj, remaining args are InitCollectionAddMethod method calls
InitCollectionAddMethod, // 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, // ?: TernaryOp, // ?:
LoopOrSwitchBreak, LoopOrSwitchBreak,
LoopContinue, LoopContinue,
@ -276,9 +301,12 @@ namespace ICSharpCode.Decompiler.ILAst
CallSetter, CallSetter,
/// <summary>Calls the setter of a instance property (or indexer)</summary> /// <summary>Calls the setter of a instance property (or indexer)</summary>
CallvirtSetter, CallvirtSetter,
/// <summary>Simulates getting the address of a property. Used as prefix on CallGetter or CallvirtGetter.</summary> /// <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</remarks> /// <remarks>
PropertyAddress /// 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 class ILCodeUtil

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -32,7 +47,13 @@ namespace ICSharpCode.Decompiler.ILAst
numLdloca.Clear(); numLdloca.Clear();
// Analyse the whole method // Analyse the whole method
foreach(ILExpression expr in method.GetSelfAndChildrenRecursive<ILExpression>()) { AnalyzeNode(method);
}
void AnalyzeNode(ILNode node)
{
ILExpression expr = node as ILExpression;
if (expr != null) {
ILVariable locVar = expr.Operand as ILVariable; ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) { if (locVar != null) {
if (expr.Code == ILCode.Stloc) { if (expr.Code == ILCode.Stloc) {
@ -45,6 +66,16 @@ namespace ICSharpCode.Decompiler.ILAst
throw new NotSupportedException(expr.Code.ToString()); 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);
} }
} }
@ -61,6 +92,20 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
bool modified = false; bool modified = false;
List<ILNode> body = block.Body; 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;) { for(int i = 0; i < body.Count - 1;) {
ILVariable locVar; ILVariable locVar;
ILExpression expr; ILExpression expr;
@ -169,7 +214,10 @@ namespace ICSharpCode.Decompiler.ILAst
bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive) bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive)
{ {
// ensure the variable is accessed only a single time // ensure the variable is accessed only a single time
if (!(numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1 && numLdloca.GetOrDefault(v) == 0)) if (numStloc.GetOrDefault(v) != 1)
return false;
int ldloc = numLdloc.GetOrDefault(v);
if (ldloc > 1 || ldloc + numLdloca.GetOrDefault(v) != 1)
return false; return false;
if (next is ILCondition) if (next is ILCondition)
@ -180,35 +228,135 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression parent; ILExpression parent;
int pos; int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) { 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)) if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
return false; return false;
}
// Assign the ranges of the ldloc instruction: // Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges); 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; parent.Arguments[pos] = inlinedExpression;
}
return true; return true;
} }
return false; return false;
} }
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression) /// <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) { switch (inlinedExpression.Code) {
case ILCode.InitArray: case ILCode.Ldloc:
case ILCode.InitCollection: case ILCode.Stloc:
case ILCode.DefaultValue: 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 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) { switch (next.Code) {
case ILCode.Ret: case ILCode.Ret:
return parent.Code == ILCode.Ret;
case ILCode.Brtrue: case ILCode.Brtrue:
return parent.Code == ILCode.Brtrue; return parent == next;
case ILCode.Switch: case ILCode.Switch:
return parent.Code == ILCode.Switch || parent.Code == ILCode.Sub; return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]);
default: default:
return false; return false;
} }
@ -237,12 +385,12 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < expr.Arguments.Count; i++) { for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated. // 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. // 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)) if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing))
return false; return false;
ILExpression arg = expr.Arguments[i]; ILExpression arg = expr.Arguments[i];
if (arg.Code == ILCode.Ldloc && arg.Operand == v) { if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) {
parent = expr; parent = expr;
pos = i; pos = i;
return true; return true;

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

@ -1,10 +1,25 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using Mono.Cecil; using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst namespace ICSharpCode.Decompiler.ILAst
@ -12,41 +27,31 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary> /// <summary>
/// IL AST transformation that introduces array, object and collection initializers. /// IL AST transformation that introduces array, object and collection initializers.
/// </summary> /// </summary>
public class Initializers partial class ILAstOptimizer
{ {
public static bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos) #region Array Initializers
bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
{ {
ILVariable v, v2, v3; ILVariable v, v3;
ILExpression newarrExpr; ILExpression newarrExpr;
TypeReference arrayType; TypeReference elementType;
ILExpression lengthExpr; ILExpression lengthExpr;
int arrayLength; int arrayLength;
if (expr.Match(ILCode.Stloc, out v, out newarrExpr) && if (expr.Match(ILCode.Stloc, out v, out newarrExpr) &&
newarrExpr.Match(ILCode.Newarr, out arrayType, out lengthExpr) && newarrExpr.Match(ILCode.Newarr, out elementType, out lengthExpr) &&
lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) && lengthExpr.Match(ILCode.Ldc_I4, out arrayLength) &&
arrayLength > 0) arrayLength > 0) {
{ ILExpression[] newArr;
MethodReference methodRef; int initArrayPos;
ILExpression methodArg1; if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out newArr, out initArrayPos)) {
ILExpression methodArg2; var arrayType = new ArrayType(elementType, 1);
FieldDefinition field; arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength);
if (body.ElementAtOrDefault(pos + 1).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) &&
v == v2 &&
methodArg2.Match(ILCode.Ldtoken, out field) &&
field != null && field.InitialValue != null)
{
ILExpression[] newArr = new ILExpression[arrayLength];
if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType), field.InitialValue, newArr)) {
body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr)); body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
body.RemoveAt(pos + 1); body.RemoveAt(initArrayPos);
return true;
} }
} // 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 = 10; const int maxConsecutiveDefaultValueExpressions = 300;
List<ILExpression> operands = new List<ILExpression>(); List<ILExpression> operands = new List<ILExpression>();
int numberOfInstructionsToRemove = 0; int numberOfInstructionsToRemove = 0;
for (int j = pos + 1; j < body.Count; j++) { for (int j = pos + 1; j < body.Count; j++) {
@ -58,10 +63,9 @@ namespace ICSharpCode.Decompiler.ILAst
v == v3 && v == v3 &&
nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) && nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
arrayPos >= operands.Count && arrayPos >= operands.Count &&
arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions) arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions) {
{
while (operands.Count < arrayPos) while (operands.Count < arrayPos)
operands.Add(new ILExpression(ILCode.DefaultValue, arrayType)); operands.Add(new ILExpression(ILCode.DefaultValue, elementType));
operands.Add(nextExpr.Arguments[2]); operands.Add(nextExpr.Arguments[2]);
numberOfInstructionsToRemove++; numberOfInstructionsToRemove++;
} else { } else {
@ -69,11 +73,79 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
if (operands.Count == arrayLength) { 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); expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
body.RemoveRange(pos + 1, numberOfInstructionsToRemove); 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; return true;
} }
} }
}
values = null;
foundPos = -1;
return false; return false;
} }
@ -81,7 +153,6 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
switch (elementType) { switch (elementType) {
case TypeCode.Boolean: case TypeCode.Boolean:
case TypeCode.SByte:
case TypeCode.Byte: case TypeCode.Byte:
if (initialValue.Length == output.Length) { if (initialValue.Length == output.Length) {
for (int j = 0; j < output.Length; j++) { for (int j = 0; j < output.Length; j++) {
@ -90,9 +161,15 @@ namespace ICSharpCode.Decompiler.ILAst
return true; return true;
} }
return false; return false;
case TypeCode.Char: 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: case TypeCode.Int16:
case TypeCode.UInt16:
if (initialValue.Length == output.Length * 2) { if (initialValue.Length == output.Length * 2) {
for (int j = 0; j < output.Length; j++) { for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToInt16(initialValue, j * 2)); output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToInt16(initialValue, j * 2));
@ -100,6 +177,15 @@ namespace ICSharpCode.Decompiler.ILAst
return true; return true;
} }
return false; 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.Int32:
case TypeCode.UInt32: case TypeCode.UInt32:
if (initialValue.Length == output.Length * 4) { if (initialValue.Length == output.Length * 4) {
@ -138,50 +224,300 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
} }
#endregion
public static bool TransformCollectionInitializers(List<ILNode> body, ILExpression expr, int pos) /// <summary>
/// Handles both object and collection initializers.
/// </summary>
bool TransformObjectInitializers(List<ILNode> body, ILExpression expr, int pos)
{ {
ILVariable v, v2; if (!context.Settings.ObjectOrCollectionInitializers)
return false;
Debug.Assert(body[pos] == expr); // should be called for top-level expressions only
ILVariable v;
ILExpression newObjExpr; ILExpression newObjExpr;
TypeReference newObjType;
bool isValueType;
MethodReference ctor; MethodReference ctor;
List<ILExpression> ctorArgs; List<ILExpression> ctorArgs;
if (expr.Match(ILCode.Stloc, out v, out newObjExpr) && if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) {
newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) 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)
{ {
TypeDefinition td = ctor.DeclaringType.Resolve(); if (tr == null)
if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections")) 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; return false;
}
// This is a collection: we can convert Add() calls into a collection initializer /// <summary>
ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr); /// Gets whether 'expr' represents a setter in an object initializer.
bool anyAdded = false; /// ('CallvirtSetter(Property, v, value)')
while(pos + 1 < body.Count) { /// </summary>
ILExpression nextExpr = body[pos + 1] as ILExpression; 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; MethodReference addMethod;
List<ILExpression> args; List<ILExpression> args;
if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) && if (expr.Match(ILCode.Callvirt, out addMethod, out args)) {
addMethod.Name == "Add" && if (addMethod.Name == "Add" && addMethod.HasThis) {
addMethod.HasThis && return args.Count >= 2;
args.Count >= 2 && }
args[0].Match(ILCode.Ldloc, out v2) && }
v == v2) 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)
{ {
nextExpr.Code = ILCode.InitCollectionAddMethod; // Take care not to modify any existing ILExpressions in here.
nextExpr.Arguments.RemoveAt(0); // We just construct new ones around the old ones, any modifications must wait until the whole
collectionInitializer.Arguments.Add(nextExpr); // object/collection initializer was analyzed.
body.RemoveAt(pos + 1); ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr);
anyAdded = true; 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 { } else {
// can't match any more initializers: end of object initializer
break; break;
} }
} }
// ensure we added at least one additional arg to the collection initializer: return objectInitializer;
if (anyAdded) {
expr.Arguments[0] = collectionInitializer;
return true;
} }
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; 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);
}
}
} }
} }
} }

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -16,7 +31,7 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>(); Dictionary<ILLabel, ControlFlowNode> labelToCfNode = new Dictionary<ILLabel, ControlFlowNode>();
DecompilerContext context; readonly DecompilerContext context;
uint nextLabelIndex = 0; uint nextLabelIndex = 0;
@ -271,7 +286,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILLabel condLabel = caseLabels[i]; ILLabel condLabel = caseLabels[i];
// Find or create new case block // Find or create new case block
ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.Where(b => b.EntryGoto.Operand == condLabel).FirstOrDefault(); ILSwitch.CaseBlock caseBlock = ilSwitch.CaseBlocks.FirstOrDefault(b => b.EntryGoto.Operand == condLabel);
if (caseBlock == null) { if (caseBlock == null) {
caseBlock = new ILSwitch.CaseBlock() { caseBlock = new ILSwitch.CaseBlock() {
Values = new List<int>(), Values = new List<int>(),

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -75,6 +90,10 @@ namespace ICSharpCode.Decompiler.ILAst
expr.Code = ILCode.Stobj; expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand)); expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
modified = true; 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; ILExpression arg, arg2;
TypeReference type; TypeReference type;
@ -425,6 +444,7 @@ namespace ICSharpCode.Decompiler.ILAst
#endregion #endregion
#region IntroducePostIncrement #region IntroducePostIncrement
bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos) bool IntroducePostIncrement(List<ILNode> body, ILExpression expr, int pos)
{ {
bool modified = IntroducePostIncrementForVariables(body, expr, pos); bool modified = IntroducePostIncrementForVariables(body, expr, pos);
@ -450,19 +470,50 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression exprInit; ILExpression exprInit;
if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated)) if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
return false; return false;
if (!(exprInit.Code == ILCode.Ldloc || exprInit.Code == ILCode.Ldsfld || (exprInit.Code == ILCode.CallGetter && exprInit.Arguments.Count == 0)))
return false;
//The next expression
ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression; ILExpression nextExpr = body.ElementAtOrDefault(pos + 1) as ILExpression;
if (nextExpr == null) if (nextExpr == null)
return false; return false;
if (exprInit.Code == ILCode.CallGetter) {
if (!(nextExpr.Code == ILCode.CallSetter && IsGetterSetterPair(exprInit.Operand, nextExpr.Operand))) 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; return false;
} else { }
if (!(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.Operand)) 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; return false;
} }
ILExpression addExpr = nextExpr.Arguments[0]; ILExpression addExpr = nextExpr.Arguments[0];
int incrementAmount; int incrementAmount;
@ -470,12 +521,23 @@ namespace ICSharpCode.Decompiler.ILAst
if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar))) if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar)))
return false; return false;
if (exprInit.Code == ILCode.Ldloc) 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; exprInit.Code = ILCode.Ldloca;
else if (exprInit.Code == ILCode.CallGetter) break;
exprInit.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress)); case ILCode.Ldsfld:
else
exprInit.Code = ILCode.Ldsflda; exprInit.Code = ILCode.Ldsflda;
break;
case ILCode.CallGetter:
exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
break;
}
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit); expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
body.RemoveAt(pos + 1); // TODO ILRanges body.RemoveAt(pos + 1); // TODO ILRanges
return true; return true;
@ -567,8 +629,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.Stobj) { if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]); stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) { } 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); stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
} else { } else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue); stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema); initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);
@ -806,5 +868,40 @@ namespace ICSharpCode.Decompiler.ILAst
return false; return false;
} }
#endregion #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
} }
} }

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -219,6 +234,107 @@ namespace ICSharpCode.Decompiler.ILAst
return false; 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) ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right)
{ {
// Assuming that the inputs are already left associative // Assuming that the inputs are already left associative

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -252,6 +267,10 @@ namespace ICSharpCode.Decompiler.ILAst
return typeSystem.Boolean; return typeSystem.Boolean;
case ILCode.LogicAnd: case ILCode.LogicAnd:
case ILCode.LogicOr: case ILCode.LogicOr:
// if Operand is set the logic and/or expression is a custom operator
// we can deal with it the same as a normal invocation.
if (expr.Operand != null)
goto case ILCode.Call;
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean); InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean); InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean);
@ -304,13 +323,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren) { if (forceInferChildren) {
for (int i = 0; i < expr.Arguments.Count; i++) { for (int i = 0; i < expr.Arguments.Count; i++) {
if (i == 0 && method.HasThis) { if (i == 0 && method.HasThis) {
ILExpressionPrefix constraint = expr.GetPrefix(ILCode.Constrained); InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(method.DeclaringType, expr.GetPrefix(ILCode.Constrained)));
if (constraint != null)
InferTypeForExpression(expr.Arguments[i], new ByReferenceType((TypeReference)constraint.Operand));
else if (method.DeclaringType.IsValueType)
InferTypeForExpression(expr.Arguments[i], new ByReferenceType(method.DeclaringType));
else
InferTypeForExpression(expr.Arguments[i], method.DeclaringType);
} else { } else {
InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(method.Parameters[method.HasThis ? i - 1 : i].ParameterType, method)); InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(method.Parameters[method.HasThis ? i - 1 : i].ParameterType, method));
} }
@ -319,10 +332,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) { if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method); return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method);
} else { } else {
TypeReference type = SubstituteTypeArgs(method.ReturnType, method); return SubstituteTypeArgs(method.ReturnType, method);
if (expr.GetPrefix(ILCode.PropertyAddress) != null && !(type is ByReferenceType))
type = new ByReferenceType(type);
return type;
} }
} }
case ILCode.Newobj: case ILCode.Newobj:
@ -335,32 +345,32 @@ namespace ICSharpCode.Decompiler.ILAst
} }
return ctor.DeclaringType; return ctor.DeclaringType;
} }
case ILCode.InitObject:
case ILCode.InitCollection: case ILCode.InitCollection:
return InferTypeForExpression(expr.Arguments[0], expectedType); return InferTypeForExpression(expr.Arguments[0], expectedType);
case ILCode.InitCollectionAddMethod: case ILCode.InitializedObject:
{ // expectedType should always be known due to the parent method call / property setter
MethodReference addMethod = (MethodReference)expr.Operand; Debug.Assert(expectedType != null);
if (forceInferChildren) { return expectedType;
for (int i = 0; i < addMethod.Parameters.Count; i++) {
InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(addMethod.Parameters[i].ParameterType, addMethod));
}
}
return addMethod.DeclaringType;
}
#endregion #endregion
#region Load/Store Fields #region Load/Store Fields
case ILCode.Ldfld: case ILCode.Ldfld:
if (forceInferChildren) if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType); InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained)));
}
return GetFieldType((FieldReference)expr.Operand); return GetFieldType((FieldReference)expr.Operand);
case ILCode.Ldsfld: case ILCode.Ldsfld:
return GetFieldType((FieldReference)expr.Operand); return GetFieldType((FieldReference)expr.Operand);
case ILCode.Ldflda: case ILCode.Ldflda:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained)));
}
return new ByReferenceType(GetFieldType((FieldReference)expr.Operand));
case ILCode.Ldsflda: case ILCode.Ldsflda:
return new ByReferenceType(GetFieldType((FieldReference)expr.Operand)); return new ByReferenceType(GetFieldType((FieldReference)expr.Operand));
case ILCode.Stfld: case ILCode.Stfld:
if (forceInferChildren) { if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType); InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained)));
InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand)); InferTypeForExpression(expr.Arguments[1], GetFieldType((FieldReference)expr.Operand));
} }
return GetFieldType((FieldReference)expr.Operand); return GetFieldType((FieldReference)expr.Operand);
@ -458,6 +468,21 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments[0], (TypeReference)expr.Operand); InferTypeForExpression(expr.Arguments[0], (TypeReference)expr.Operand);
} }
return typeSystem.TypedReference; return typeSystem.TypedReference;
case ILCode.Refanytype:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
}
return new TypeReference("System", "RuntimeTypeHandle", module, module, true);
case ILCode.Refanyval:
if (forceInferChildren) {
InferTypeForExpression(expr.Arguments[0], typeSystem.TypedReference);
}
return new ByReferenceType((TypeReference)expr.Operand);
case ILCode.AddressOf:
{
TypeReference t = InferTypeForExpression(expr.Arguments[0], UnpackPointer(expectedType));
return t != null ? new ByReferenceType(t) : null;
}
#endregion #endregion
#region Arithmetic instructions #region Arithmetic instructions
case ILCode.Not: // bitwise complement case ILCode.Not: // bitwise complement
@ -489,14 +514,47 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Rem_Un: case ILCode.Rem_Un:
return InferArgumentsInBinaryOperator(expr, false, expectedType); return InferArgumentsInBinaryOperator(expr, false, expectedType);
case ILCode.Shl: case ILCode.Shl:
case ILCode.Shr:
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return InferTypeForExpression(expr.Arguments[0], typeSystem.Int32); if (expectedType != null && (
expectedType.MetadataType == MetadataType.Int32 || expectedType.MetadataType == MetadataType.UInt32 ||
expectedType.MetadataType == MetadataType.Int64 || expectedType.MetadataType == MetadataType.UInt64)
)
return NumericPromotion(InferTypeForExpression(expr.Arguments[0], expectedType));
else
return NumericPromotion(InferTypeForExpression(expr.Arguments[0], null));
case ILCode.Shr:
case ILCode.Shr_Un: case ILCode.Shr_Un:
{
if (forceInferChildren) if (forceInferChildren)
InferTypeForExpression(expr.Arguments[1], typeSystem.Int32); InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32); TypeReference type = NumericPromotion(InferTypeForExpression(expr.Arguments[0], null));
TypeReference expectedInputType = null;
switch (type.MetadataType) {
case MetadataType.Int32:
if (expr.Code == ILCode.Shr_Un)
expectedInputType = typeSystem.UInt32;
break;
case MetadataType.UInt32:
if (expr.Code == ILCode.Shr)
expectedInputType = typeSystem.Int32;
break;
case MetadataType.Int64:
if (expr.Code == ILCode.Shr_Un)
expectedInputType = typeSystem.UInt64;
break;
case MetadataType.UInt64:
if (expr.Code == ILCode.Shr)
expectedInputType = typeSystem.UInt64;
break;
}
if (expectedInputType != null) {
InferTypeForExpression(expr.Arguments[0], expectedInputType);
return expectedInputType;
} else {
return type;
}
}
case ILCode.CompoundAssignment: case ILCode.CompoundAssignment:
{ {
TypeReference varType = InferTypeForExpression(expr.Arguments[0].Arguments[0], null); TypeReference varType = InferTypeForExpression(expr.Arguments[0].Arguments[0], null);
@ -517,9 +575,19 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldc_I4: case ILCode.Ldc_I4:
if (IsBoolean(expectedType) && ((int)expr.Operand == 0 || (int)expr.Operand == 1)) if (IsBoolean(expectedType) && ((int)expr.Operand == 0 || (int)expr.Operand == 1))
return typeSystem.Boolean; return typeSystem.Boolean;
return (IsIntegerOrEnum(expectedType) || expectedType is PointerType) ? expectedType : typeSystem.Int32; if (expectedType is PointerType && (int)expr.Operand == 0)
return expectedType;
if (IsIntegerOrEnum(expectedType) && OperandFitsInType(expectedType, (int)expr.Operand))
return expectedType;
else
return typeSystem.Int32;
case ILCode.Ldc_I8: case ILCode.Ldc_I8:
return (IsIntegerOrEnum(expectedType) || expectedType is PointerType) ? expectedType : typeSystem.Int64; if (expectedType is PointerType && (long)expr.Operand == 0)
return expectedType;
if (IsIntegerOrEnum(expectedType) && GetInformationAmount(expectedType) >= NativeInt)
return expectedType;
else
return typeSystem.Int64;
case ILCode.Ldc_R4: case ILCode.Ldc_R4:
return typeSystem.Single; return typeSystem.Single;
case ILCode.Ldc_R8: case ILCode.Ldc_R8:
@ -542,11 +610,13 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32); InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32);
return new ArrayType((TypeReference)expr.Operand); return new ArrayType((TypeReference)expr.Operand);
case ILCode.InitArray: case ILCode.InitArray:
if (forceInferChildren) { var operandAsArrayType = (ArrayType)expr.Operand;
if (forceInferChildren)
{
foreach (ILExpression arg in expr.Arguments) foreach (ILExpression arg in expr.Arguments)
InferTypeForExpression(arg, (TypeReference)expr.Operand); InferTypeForExpression(arg, operandAsArrayType.ElementType);
} }
return new ArrayType((TypeReference)expr.Operand); return operandAsArrayType;
case ILCode.Ldlen: case ILCode.Ldlen:
return typeSystem.Int32; return typeSystem.Int32;
case ILCode.Ldelem_U1: case ILCode.Ldelem_U1:
@ -655,6 +725,8 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Castclass: case ILCode.Castclass:
case ILCode.Unbox_Any: case ILCode.Unbox_Any:
return (TypeReference)expr.Operand; return (TypeReference)expr.Operand;
case ILCode.Unbox:
return new ByReferenceType((TypeReference)expr.Operand);
case ILCode.Isinst: case ILCode.Isinst:
{ {
// isinst performs the equivalent of a cast only for reference types; // isinst performs the equivalent of a cast only for reference types;
@ -723,6 +795,41 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
/// <summary>
/// Wraps 'type' in a ByReferenceType if it is a value type. If a constrained prefix is specified,
/// returns the constrained type wrapped in a ByReferenceType.
/// </summary>
TypeReference MakeRefIfValueType(TypeReference type, ILExpressionPrefix constrainedPrefix)
{
if (constrainedPrefix != null)
return new ByReferenceType((TypeReference)constrainedPrefix.Operand);
if (type.IsValueType)
return new ByReferenceType(type);
else
return type;
}
/// <summary>
/// Promotes primitive types smaller than int32 to int32.
/// </summary>
/// <remarks>
/// Always promotes to signed int32.
/// </remarks>
TypeReference NumericPromotion(TypeReference type)
{
if (type == null)
return null;
switch (type.MetadataType) {
case MetadataType.SByte:
case MetadataType.Int16:
case MetadataType.Byte:
case MetadataType.UInt16:
return typeSystem.Int32;
default:
return type;
}
}
TypeReference HandleConversion(int targetBitSize, bool targetSigned, ILExpression arg, TypeReference expectedType, TypeReference targetType) TypeReference HandleConversion(int targetBitSize, bool targetSigned, ILExpression arg, TypeReference expectedType, TypeReference targetType)
{ {
if (targetBitSize >= NativeInt && expectedType is PointerType) { if (targetBitSize >= NativeInt && expectedType is PointerType) {
@ -743,12 +850,12 @@ namespace ICSharpCode.Decompiler.ILAst
return resultType; return resultType;
} }
static TypeReference GetFieldType(FieldReference fieldReference) public static TypeReference GetFieldType(FieldReference fieldReference)
{ {
return SubstituteTypeArgs(UnpackModifiers(fieldReference.FieldType), fieldReference); return SubstituteTypeArgs(UnpackModifiers(fieldReference.FieldType), fieldReference);
} }
static TypeReference SubstituteTypeArgs(TypeReference type, MemberReference member) public static TypeReference SubstituteTypeArgs(TypeReference type, MemberReference member)
{ {
if (type is TypeSpecification) { if (type is TypeSpecification) {
ArrayType arrayType = type as ArrayType; ArrayType arrayType = type as ArrayType;
@ -821,7 +928,7 @@ namespace ICSharpCode.Decompiler.ILAst
return null; return null;
} }
static TypeReference UnpackModifiers(TypeReference type) internal static TypeReference UnpackModifiers(TypeReference type)
{ {
while (type is OptionalModifierType || type is RequiredModifierType) while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType; type = ((TypeSpecification)type).ElementType;
@ -927,7 +1034,7 @@ namespace ICSharpCode.Decompiler.ILAst
{ {
if (type == null) if (type == null)
return 0; return 0;
if (type.IsValueType) { if (type.IsValueType && !IsArrayPointerOrReference(type)) {
// value type might be an enum // value type might be an enum
TypeDefinition typeDef = type.Resolve() as TypeDefinition; TypeDefinition typeDef = type.Resolve() as TypeDefinition;
if (typeDef != null && typeDef.IsEnum) { if (typeDef != null && typeDef.IsEnum) {
@ -975,7 +1082,9 @@ namespace ICSharpCode.Decompiler.ILAst
public static bool IsEnum(TypeReference type) public static bool IsEnum(TypeReference type)
{ {
if (type == null) // Arrays/Pointers/ByReference resolve to their element type, but we don't want to consider those to be enums
// However, GenericInstanceTypes, ModOpts etc. should be considered enums.
if (type == null || IsArrayPointerOrReference(type))
return false; return false;
// unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec) // unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec)
TypeDefinition typeDef = type.Resolve() as TypeDefinition; TypeDefinition typeDef = type.Resolve() as TypeDefinition;
@ -984,7 +1093,7 @@ namespace ICSharpCode.Decompiler.ILAst
static bool? IsSigned(TypeReference type) static bool? IsSigned(TypeReference type)
{ {
if (type == null) if (type == null || IsArrayPointerOrReference(type))
return null; return null;
// unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec) // unfortunately we cannot rely on type.IsValueType here - it's not set when the instruction operand is a typeref (as opposed to a typespec)
TypeDefinition typeDef = type.Resolve() as TypeDefinition; TypeDefinition typeDef = type.Resolve() as TypeDefinition;
@ -1011,6 +1120,39 @@ namespace ICSharpCode.Decompiler.ILAst
} }
} }
static bool OperandFitsInType(TypeReference type, int num)
{
TypeDefinition typeDef = type.Resolve() as TypeDefinition;
if (typeDef != null && typeDef.IsEnum) {
type = typeDef.Fields.Single(f => f.IsRuntimeSpecialName && !f.IsStatic).FieldType;
}
switch (type.MetadataType) {
case MetadataType.SByte:
return sbyte.MinValue <= num && num <= sbyte.MaxValue;
case MetadataType.Int16:
return short.MinValue <= num && num <= short.MaxValue;
case MetadataType.Byte:
return byte.MinValue <= num && num <= byte.MaxValue;
case MetadataType.Char:
return char.MinValue <= num && num <= char.MaxValue;
case MetadataType.UInt16:
return ushort.MinValue <= num && num <= ushort.MaxValue;
default:
return true;
}
}
static bool IsArrayPointerOrReference(TypeReference type)
{
TypeSpecification typeSpec = type as TypeSpecification;
while (typeSpec != null) {
if (typeSpec is ArrayType || typeSpec is PointerType || typeSpec is ByReferenceType)
return true;
typeSpec = typeSpec.ElementType as TypeSpecification;
}
return false;
}
public static TypeCode GetTypeCode(TypeReference type) public static TypeCode GetTypeCode(TypeReference type)
{ {
if (type == null) if (type == null)

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

@ -1,5 +1,20 @@
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
// This code is distributed under MIT X11 license (for details please see \doc\license.txt) //
// 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;
using System.Collections.Generic; using System.Collections.Generic;
@ -164,7 +179,7 @@ namespace ICSharpCode.Decompiler.ILAst
public static bool IsCompilerGeneratorEnumerator(TypeDefinition type) public static bool IsCompilerGeneratorEnumerator(TypeDefinition type)
{ {
if (!(type.Name.StartsWith("<", StringComparison.Ordinal) && type.IsCompilerGenerated())) if (!(type.DeclaringType != null && type.IsCompilerGenerated()))
return false; return false;
foreach (TypeReference i in type.Interfaces) { foreach (TypeReference i in type.Interfaces) {
if (i.Namespace == "System.Collections" && i.Name == "IEnumerator") if (i.Namespace == "System.Collections" && i.Name == "IEnumerator")

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

@ -23,6 +23,9 @@ namespace ICSharpCode.Decompiler
{ {
public interface ITextOutput public interface ITextOutput
{ {
int CurrentLine { get; }
int CurrentColumn { get; }
void Indent(); void Indent();
void Unindent(); void Unindent();
void Write(char ch); void Write(char ch);

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

@ -23,9 +23,13 @@ namespace ICSharpCode.Decompiler
{ {
public sealed class PlainTextOutput : ITextOutput public sealed class PlainTextOutput : ITextOutput
{ {
const int TAB_SIZE = 4;
readonly TextWriter writer; readonly TextWriter writer;
int indent; int indent;
bool needsIndent; bool needsIndent;
int lineNumber = 1;
int columnNumber = 1;
public PlainTextOutput(TextWriter writer) public PlainTextOutput(TextWriter writer)
{ {
@ -39,6 +43,14 @@ namespace ICSharpCode.Decompiler
this.writer = new StringWriter(); this.writer = new StringWriter();
} }
public int CurrentLine {
get { return lineNumber; }
}
public int CurrentColumn {
get { return columnNumber; }
}
public override string ToString() public override string ToString()
{ {
return writer.ToString(); return writer.ToString();
@ -60,6 +72,7 @@ namespace ICSharpCode.Decompiler
needsIndent = false; needsIndent = false;
for (int i = 0; i < indent; i++) { for (int i = 0; i < indent; i++) {
writer.Write('\t'); writer.Write('\t');
columnNumber += TAB_SIZE - 1;
} }
} }
} }
@ -68,18 +81,22 @@ namespace ICSharpCode.Decompiler
{ {
WriteIndent(); WriteIndent();
writer.Write(ch); writer.Write(ch);
columnNumber++;
} }
public void Write(string text) public void Write(string text)
{ {
WriteIndent(); WriteIndent();
writer.Write(text); writer.Write(text);
columnNumber += text.Length;
} }
public void WriteLine() public void WriteLine()
{ {
lineNumber++;
writer.WriteLine(); writer.WriteLine();
needsIndent = true; needsIndent = true;
columnNumber = TAB_SIZE * indent;
} }
public void WriteDefinition(string text, object definition) public void WriteDefinition(string text, object definition)

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

@ -19,6 +19,8 @@ using System.Runtime.InteropServices;
// If you need to expose a type to COM, use [ComVisible(true)] on that type. // If you need to expose a type to COM, use [ComVisible(true)] on that type.
[assembly: ComVisible(false)] [assembly: ComVisible(false)]
[assembly: AssemblyVersion("2.0.0.1221")]
[assembly: AssemblyInformationalVersion("2.0.0.1221-26633dc2")]
[assembly: NeutralResourcesLanguage("en-US")] [assembly: NeutralResourcesLanguage("en-US")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly", [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly",

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

@ -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("$INSERTVERSION$")]
[assembly: AssemblyInformationalVersion("$INSERTVERSION$$INSERTBRANCHPOSTFIX$$INSERTVERSIONNAMEPOSTFIX$-$INSERTSHORTCOMMITHASH$")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly",
Justification = "AssemblyInformationalVersion does not need to be a parsable version")]

68
src/Libraries/ICSharpCode.Decompiler/ReferenceResolvingException.cs

@ -0,0 +1,68 @@
// 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.Text;
namespace ICSharpCode.Decompiler
{
/// <summary>
/// Represents an error while resolving a reference to a type or a member.
/// </summary>
[Serializable]
public class ReferenceResolvingException : Exception
{
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
public ReferenceResolvingException()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
/// <param name="message">A <see cref="T:System.String"/> that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture.</param>
public ReferenceResolvingException(string message)
: base(message)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
/// <param name="message">A <see cref="T:System.String"/> that describes the error. The content of message is intended to be understood by humans. The caller of this constructor is required to ensure that this string has been localized for the current system culture.</param>
/// <param name="inner">The exception that is the cause of the current exception. If the innerException parameter is not a null reference, the current exception is raised in a catch block that handles the inner exception.</param>
public ReferenceResolvingException(string message, Exception inner)
: base(message, inner)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="T:ResolveException"/> class
/// </summary>
/// <param name="info">The object that holds the serialized object data.</param>
/// <param name="context">The contextual information about the source or destination.</param>
protected ReferenceResolvingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
: base(info, context)
{
}
}
}

59
src/Libraries/ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il

@ -0,0 +1,59 @@
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 4:0:0:0
}
.assembly BooleanConsumedAsInteger
{
.hash algorithm 0x00008004
.ver 1:0:0:0
}
.module BooleanConsumedAsInteger.exe
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000003 // ILONLY 32BITREQUIRED
.class private auto ansi beforefieldinit BooleanConsumedAsInteger.Program extends [mscorlib]System.Object
{
.method public hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 8
ret
}
.method public hidebysig static int32 ReturnBoolAsInt() cil managed
{
ldnull
ldnull
call bool [mscorlib] System.Object::Equals(object, object)
ret
}
.method public hidebysig static int32 BitwiseOperationOnBool() cil managed
{
ldnull
ldnull
call bool [mscorlib] System.Object::Equals(object, object)
ldc.i4 255
and
ret
}
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
// Code size 7 (0x7)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: ret
} // end of method Program::.ctor
} // end of class StackTests.Program
// =============================================================

52
src/Libraries/ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs

@ -0,0 +1,52 @@
// Copyright (c) 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;
public class CallOverloadedMethod
{
public void OverloadedMethod(object a)
{
}
public void OverloadedMethod(int? a)
{
}
public void OverloadedMethod(string a)
{
}
public void Call()
{
this.OverloadedMethod("(string)");
this.OverloadedMethod((object)"(object)");
this.OverloadedMethod(5);
this.OverloadedMethod((object)5);
this.OverloadedMethod(5L);
this.OverloadedMethod((object)null);
this.OverloadedMethod((string)null);
this.OverloadedMethod((int?)null);
}
public void CallMethodUsingInterface(List<int> list)
{
((ICollection<int>)list).Clear();
}
}

66
src/Libraries/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs

@ -0,0 +1,66 @@
// Copyright (c) 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.
public class CheckedUnchecked
{
public int Operators(int a, int b)
{
int num = checked(a + b);
int num2 = a + b;
int num3 = checked(a - b);
int num4 = a - b;
int num5 = checked(a * b);
int num6 = a * b;
int num7 = a / b;
int num8 = a % b;
// The division operators / and % only exist in one form (checked vs. unchecked doesn't matter for them)
return num * num2 * num3 * num4 * num5 * num6 * num7 * num8;
}
public int Cast(int a)
{
short num = checked((short)a);
short num2 = (short)a;
byte b = checked((byte)a);
byte b2 = (byte)a;
return num * num2 * b * b2;
}
public void ForWithCheckedIteratorAndUncheckedBody(int n)
{
checked
{
for (int i = n + 1; i < n + 1; i++)
{
n = unchecked(i * i);
}
}
}
public void ForWithCheckedInitializerAndUncheckedIterator(int n)
{
checked
{
int i = n;
for (i -= 10; i < n; i = unchecked(i + 1))
{
n--;
}
}
}
}

132
src/Libraries/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs

@ -0,0 +1,132 @@
// Copyright (c) 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.Text;
using System.IO;
namespace ICSharpCode.Decompiler.Tests
{
static class CodeSampleFileParser
{
public static IEnumerable<string> ListSections(string s)
{
var query = from line in ToLines(s)
let sectionName = ReadSectionName(line)
where sectionName != null
select sectionName;
return query;
}
public static string GetSection(string sectionName, string s)
{
var lines = ToLines(s);
bool sectionFound = false;
var sectionText = new StringBuilder();
Action<string> parser = null;
Action<string> commonSectionReader = line =>
{
if (IsCommonSectionEnd(line))
parser = null;
else
sectionText.AppendLine(line);
};
Action<string> namedSectionReader = line =>
{
string name = ReadSectionName(line);
if (name == null)
sectionText.AppendLine(line);
else if (name != sectionName)
parser = null;
};
Action<string> defaultReader = line =>
{
if (IsCommonSectionStart(line))
parser = commonSectionReader;
else if (ReadSectionName(line) == sectionName)
{
parser = namedSectionReader;
sectionFound = true;
}
};
foreach(var line in lines)
{
(parser ?? defaultReader)(line);
}
if (sectionFound)
return sectionText.ToString();
else
return "";
}
public static bool IsCommentOrBlank(string s)
{
if(String.IsNullOrWhiteSpace(s))
return true;
return s.Trim().StartsWith("//");
}
public static string ConcatLines(IEnumerable<string> lines)
{
var buffer = new StringBuilder();
foreach (var line in lines)
{
buffer.AppendLine(line);
}
return buffer.ToString();
}
static string ReadSectionName(string line)
{
line = line.TrimStart();
if (line.StartsWith("//$$"))
return line.Substring(4).Trim();
else
return null;
}
static bool IsCommonSectionStart(string line)
{
return line.Trim() == "//$CS";
}
static bool IsCommonSectionEnd(string line)
{
return line.Trim() == "//$CE";
}
static IEnumerable<string> ToLines(string s)
{
var reader = new StringReader(s);
string line;
while ((line = reader.ReadLine()) != null)
{
yield return line;
}
}
}
}

41
src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs

@ -0,0 +1,41 @@
using System;
namespace aa
{
public static class CustomAtributes
{
[Flags]
public enum EnumWithFlag
{
All = 15,
None = 0,
Item1 = 1,
Item2 = 2,
Item3 = 4,
Item4 = 8
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
public MyAttribute(CustomAtributes.EnumWithFlag en)
{
}
}
[CustomAtributes.MyAttribute(CustomAtributes.EnumWithFlag.Item1 | CustomAtributes.EnumWithFlag.Item2)]
private static int field;
[CustomAtributes.MyAttribute(CustomAtributes.EnumWithFlag.All)]
public static string Property
{
get
{
return "aa";
}
}
[Obsolete("some message")]
public static void ObsoletedMethod()
{
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field);
AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field;
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets);
}
}
}

30
src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs

@ -0,0 +1,30 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests.CustomAttributes
{
[TestFixture]
public class CustomAttributeTests : DecompilerTestBase
{
[Test]
public void CustomAttributeSamples()
{
ValidateFileRoundtrip(@"CustomAttributes\S_CustomAttributeSamples.cs");
}
[Test]
public void CustomAttributesMultiTest()
{
ValidateFileRoundtrip(@"CustomAttributes\S_CustomAttributes.cs");
}
[Test]
public void AssemblyCustomAttributesMultiTest()
{
ValidateFileRoundtrip(@"CustomAttributes\S_AssemblyCustomAttribute.cs");
}
}
}

6
src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs

@ -0,0 +1,6 @@
// 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;
[assembly: CLSCompliant(false)]

480
src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs

@ -0,0 +1,480 @@
// 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)
//$CS
using System;
//$CE
//$$ TargetModule (ignored)
//[module: CLSCompliantAttribute(false)]
//$$ ParameterlessAttributeUsage
namespace ParameterLessAttributeUsage
{
[Flags]
public enum EnumWithFlagsAttribute
{
None = 0
}
}
//$$ AttributeWithEnumArgument
namespace AttributeWithEnumArgument
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
}
//$$ AttributeWithEnumExpressionArgument
namespace AttributeWithEnumExpressionArgument
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Interface)]
public class MyAttributeAttribute : Attribute
{
}
}
//$$ AttributeWithStringExpressionArgument
namespace AttributeWithStringExpressionArgument
{
[Obsolete("message")]
public class ObsoletedClass
{
}
}
//$$ AttributeWithTypeArgument
namespace AttributeWithTypeArgument
{
[AttributeUsage(AttributeTargets.All)]
public class MyTypeAttribute : Attribute
{
public MyTypeAttribute(Type t)
{
}
}
[MyType(typeof(Attribute))]
public class SomeClass
{
}
}
//$$ AppliedToEvent
namespace AppliedToEvent
{
[AttributeUsage(AttributeTargets.Event)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
[MyAttribute]
public event EventHandler MyEvent;
}
}
//$$ AppliedToField
namespace AppliedToField
{
[AttributeUsage(AttributeTargets.Field)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
[MyAttribute]
public int Field;
}
}
//$$ AppliedToProperty
namespace AppliedToProperty
{
public class TestClass
{
[Obsolete("reason")]
public int Property
{
get
{
return 0;
}
}
}
}
//$$ AppliedToPropertyGet
namespace AppliedToPropertyGet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
public int Property
{
[MyAttribute]
get
{
return 0;
}
}
}
}
//$$ AppliedToPropertySet
namespace AppliedToPropertySet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
public int Property
{
get
{
return 3;
}
[MyAttribute]
set
{
}
}
}
}
//$$ AppliedToIndexer
namespace AppliedToIndexer
{
public class TestClass
{
[Obsolete("reason")]
public int this[int i]
{
get
{
return 0;
}
}
}
}
//$$ AppliedToDelegate
[Obsolete("reason")]
public delegate int AppliedToDelegate();
//$$ AppliedToMethod
namespace AppliedToMethod
{
[AttributeUsage(AttributeTargets.Method)]
public class MyAttributeAttribute : Attribute
{
}
public class TestClass
{
[MyAttribute]
public void Method()
{
}
}
}
//$$ AppliedToInterface
[Obsolete("reason")]
public interface AppliedToInterface
{
}
//$$ AppliedToStruct
[Obsolete("reason")]
public struct AppliedToStruct
{
public int Field;
}
//$$ AppliedToParameter
namespace AppliedToParameter
{
[AttributeUsage(AttributeTargets.Parameter)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public void Method([MyAttribute] int val)
{
}
}
}
//$$ NamedInitializerProperty
namespace NamedInitializerProperty
{
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class MyAttributeAttribute : Attribute
{
}
}
//$$ NamedInitializerPropertyString
namespace NamedInitializerPropertyString
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public string Prop
{
get
{
return "";
}
set
{
}
}
}
[MyAttribute(Prop = "value")]
public class MyClass
{
}
}
//$$ NamedInitializerPropertyType
namespace NamedInitializerPropertyType
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public Type Prop
{
get
{
return null;
}
set
{
}
}
}
[MyAttribute(Prop = typeof(Enum))]
public class MyClass
{
}
}
//$$ NamedInitializerPropertyEnum
namespace NamedInitializerPropertyEnum
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public AttributeTargets Prop
{
get
{
return AttributeTargets.All;
}
set
{
}
}
}
[MyAttribute(Prop = (AttributeTargets.Class | AttributeTargets.Method))]
public class MyClass
{
}
}
//$$ NamedInitializerFieldEnum
namespace NamedInitializerFieldEnum
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public AttributeTargets Field;
}
[MyAttribute(Field = (AttributeTargets.Class | AttributeTargets.Method))]
public class MyClass
{
}
}
//$$ TargetReturn
namespace TargetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
[return: MyAttribute]
public int MyMethod()
{
return 5;
}
}
}
//$$ TargetPropertyGetReturn
namespace TargetPropertyGetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int Prop
{
[return: MyAttribute]
get
{
return 3;
}
}
}
}
//$$ TargetPropertySetParam
namespace TargetPropertySetParam
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int Prop
{
[param: MyAttribute]
set
{
}
}
}
}
//$$ TargetPropertySetReturn
namespace TargetPropertySetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int Prop
{
get
{
return 3;
}
[return: MyAttribute]
set
{
}
}
}
}
//$$ TargetPropertyIndexGetReturn
namespace TargetPropertyIndexGetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int this[string s]
{
[return: MyAttribute]
get
{
return 3;
}
}
}
}
//$$ TargetPropertyIndexParamOnlySet
namespace TargetPropertyIndexParamOnlySet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int this[[MyAttribute] string s]
{
set
{
}
}
}
}
//$$ TargetPropertyIndexParamOnlyGet
namespace TargetPropertyIndexParamOnlyGet
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public int this[[MyAttribute] string s]
{
get
{
return 3;
}
}
}
}
//$$ TargetPropertyIndexSetReturn
namespace TargetPropertyIndexSetReturn
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass
{
public string this[int index]
{
get
{
return "";
}
[return: MyAttribute]
set
{
}
}
}
}
//$$ TargetPropertyIndexSetMultiParam
namespace TargetPropertyIndexSetMultiParam
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
public int Field;
}
public class MyClass
{
public string this[[MyAttribute(Field = 2)] int index1, [MyAttribute(Field = 3)] int index2]
{
get
{
return "";
}
[param: MyAttribute]
set
{
}
}
}
}
//$$ ClassAttributeOnTypeParameter
namespace ClassAttributeOnTypeParameter
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
public class MyClass<[MyAttribute] T>
{
}
}
//$$ AttributeOnReturnTypeOfDelegate
namespace AttributeOnReturnTypeOfDelegate
{
[AttributeUsage(AttributeTargets.All)]
public class MyAttributeAttribute : Attribute
{
}
[return: MyAttribute]
public delegate void Test();
}

64
src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs

@ -0,0 +1,64 @@
// 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;
namespace aa
{
public static class CustomAttributes
{
[Flags]
public enum EnumWithFlag
{
All = 15,
None = 0,
Item1 = 1,
Item2 = 2,
Item3 = 4,
Item4 = 8
}
[AttributeUsage(AttributeTargets.All)]
public class MyAttribute : Attribute
{
public MyAttribute(object val)
{
}
}
[CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.Item1 | CustomAttributes.EnumWithFlag.Item2)]
private static int field;
[CustomAttributes.MyAttribute(CustomAttributes.EnumWithFlag.All)]
public static string Property
{
get
{
return "aa";
}
}
[Obsolete("some message")]
public static void ObsoletedMethod()
{
//Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, (AttributeTargets)(AttributeTargets.Property | AttributeTargets.Field));
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, AttributeTargets.Property | AttributeTargets.Field);
AttributeTargets attributeTargets = AttributeTargets.Property | AttributeTargets.Field;
Console.WriteLine("{0} $$$ {1}", AttributeTargets.Interface, attributeTargets);
}
// No Boxing
[CustomAttributes.MyAttribute(new StringComparison[]
{
StringComparison.Ordinal,
StringComparison.CurrentCulture
})]
public static void ArrayAsAttribute1()
{
}
// Boxing of each array element
[CustomAttributes.MyAttribute(new object[]
{
StringComparison.Ordinal,
StringComparison.CurrentCulture
})]
public static void ArrayAsAttribute2()
{
}
}
}

90
src/Libraries/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs

@ -0,0 +1,90 @@
// Copyright (c) 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;
public static class CustomShortCircuitOperators
{
private class B
{
public static bool operator true(CustomShortCircuitOperators.B x)
{
return true;
}
public static bool operator false(CustomShortCircuitOperators.B x)
{
return false;
}
}
private class C : CustomShortCircuitOperators.B
{
public static CustomShortCircuitOperators.C operator &(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y)
{
return null;
}
public static CustomShortCircuitOperators.C operator |(CustomShortCircuitOperators.C x, CustomShortCircuitOperators.C y)
{
return null;
}
public static bool operator !(CustomShortCircuitOperators.C x)
{
return false;
}
private static void Main()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
CustomShortCircuitOperators.C c2 = new CustomShortCircuitOperators.C();
CustomShortCircuitOperators.C c3 = c && c2;
CustomShortCircuitOperators.C c4 = c || c2;
Console.WriteLine(c3.ToString());
Console.WriteLine(c4.ToString());
}
private static void Test2()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
if (c && c)
{
Console.WriteLine(c.ToString());
}
if (!(c && c))
{
Console.WriteLine(c.ToString());
}
}
private static void Test3()
{
CustomShortCircuitOperators.C c = new CustomShortCircuitOperators.C();
if (c)
{
Console.WriteLine(c.ToString());
}
if (!c)
{
Console.WriteLine(c.ToString());
}
}
}
}

93
src/Libraries/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs

@ -0,0 +1,93 @@
// Copyright (c) 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.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.Decompiler.Tests.Helpers;
using Microsoft.CSharp;
using Mono.Cecil;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests
{
public abstract class DecompilerTestBase
{
protected static void ValidateFileRoundtrip(string samplesFileName)
{
var lines = File.ReadAllLines(Path.Combine(@"..\..\Tests", samplesFileName));
var testCode = RemoveIgnorableLines(lines);
var decompiledTestCode = RoundtripCode(testCode);
CodeAssert.AreEqual(testCode, decompiledTestCode);
}
static string RemoveIgnorableLines(IEnumerable<string> lines)
{
return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l)));
}
/// <summary>
/// Compiles and decompiles a source code.
/// </summary>
/// <param name="code">The source code to copile.</param>
/// <returns>The decompilation result of compiled source code.</returns>
static string RoundtripCode(string code)
{
DecompilerSettings settings = new DecompilerSettings();
settings.FullyQualifyAmbiguousTypeNames = false;
AssemblyDefinition assembly = Compile(code);
AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule) { Settings = settings });
decompiler.AddAssembly(assembly);
new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit);
StringWriter output = new StringWriter();
decompiler.GenerateCode(new PlainTextOutput(output));
return output.ToString();
}
static AssemblyDefinition Compile(string code)
{
CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary<string, string> { { "CompilerVersion", "v4.0" } });
CompilerParameters options = new CompilerParameters();
options.ReferencedAssemblies.Add("System.Core.dll");
CompilerResults results = provider.CompileAssemblyFromSource(options, code);
try
{
if (results.Errors.Count > 0)
{
StringBuilder b = new StringBuilder("Compiler error:");
foreach (var error in results.Errors)
{
b.AppendLine(error.ToString());
}
throw new Exception(b.ToString());
}
return AssemblyDefinition.ReadAssembly(results.PathToAssembly);
}
finally
{
File.Delete(results.PathToAssembly);
results.TempFiles.Delete();
}
}
}
}

189
src/Libraries/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs

@ -0,0 +1,189 @@
// Copyright (c) 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;
public static class DelegateConstruction
{
class InstanceTests
{
public Action CaptureOfThis()
{
return delegate {
CaptureOfThis();
};
}
public Action CaptureOfThisAndParameter(int a)
{
return delegate {
CaptureOfThisAndParameter(a);
};
}
public Action CaptureOfThisAndParameterInForEach(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a);
};
}
}
return null;
}
public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a)
{
foreach (int item in Enumerable.Empty<int>()) {
int copyOfItem = item;
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a + copyOfItem);
};
}
}
return null;
}
}
public static void Test(this string a)
{
}
public static Action<string> ExtensionMethodUnbound()
{
return new Action<string>(DelegateConstruction.Test);
}
public static Action ExtensionMethodBound()
{
return new Action("abc".Test);
}
public static Action ExtensionMethodBoundOnNull()
{
return new Action(((string)null).Test);
}
public static object StaticMethod()
{
return new Func<Action>(DelegateConstruction.ExtensionMethodBound);
}
public static object InstanceMethod()
{
return new Func<string>("hello".ToUpper);
}
public static object InstanceMethodOnNull()
{
return new Func<string>(((string)null).ToUpper);
}
public static List<Action<int>> AnonymousMethodStoreWithinLoop()
{
List<Action<int>> list = new List<Action<int>>();
for (int i = 0; i < 10; i++)
{
int counter;
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
public static List<Action<int>> AnonymousMethodStoreOutsideLoop()
{
List<Action<int>> list = new List<Action<int>>();
int counter;
for (int i = 0; i < 10; i++)
{
list.Add(delegate(int x)
{
counter = x;
}
);
}
return list;
}
public static Action StaticAnonymousMethodNoClosure()
{
return delegate
{
Console.WriteLine();
};
}
public static void NameConflict()
{
// i is captured variable,
// j is parameter in anonymous method
// k is local in anonymous method,
// l is local in main method
// Ensure that the decompiler doesn't introduce name conflicts
List<Action<int>> list = new List<Action<int>>();
for (int l = 0; l < 10; l++) {
int i;
for (i = 0; i < 10; i++) {
list.Add(
delegate (int j) {
for (int k = 0; k < i; k += j) {
Console.WriteLine();
}
});
}
}
}
public static void NameConflict2(int j)
{
List<Action<int>> list = new List<Action<int>>();
for (int k = 0; k < 10; k++) {
list.Add(
delegate(int i) {
Console.WriteLine(i);
});
}
}
public static Action<int> NameConflict3(int i)
{
return delegate(int j) {
for (int k = 0; k < j; k++) {
Console.WriteLine(k);
}
};
}
public static Func<int, Func<int, int>> CurriedAddition(int a)
{
return b => c => a + b + c;
}
public static Func<int, Func<int, Func<int, int>>> CurriedAddition2(int a)
{
return b => c => d => a + b + c + d;
}
}

128
src/Libraries/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs

@ -0,0 +1,128 @@
// Copyright (c) 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;
public class ExceptionHandling
{
public void MethodEndingWithEndFinally()
{
try
{
throw null;
}
finally
{
Console.WriteLine();
}
}
public void MethodEndingWithRethrow()
{
try
{
throw null;
}
catch
{
throw;
}
}
public void TryCatchFinally()
{
try
{
Console.WriteLine("Try");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
Console.WriteLine("Finally");
}
}
public void TryCatchMultipleHandlers()
{
try
{
Console.WriteLine("Try");
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.Message);
}
catch (Exception ex2)
{
Console.WriteLine(ex2.Message);
}
catch
{
Console.WriteLine("other");
}
}
public void NoUsingStatementBecauseTheVariableIsAssignedTo()
{
CancellationTokenSource cancellationTokenSource = null;
try
{
cancellationTokenSource = new CancellationTokenSource();
}
finally
{
if (cancellationTokenSource != null)
{
cancellationTokenSource.Dispose();
}
}
}
public void UsingStatementThatChangesTheVariable()
{
CancellationTokenSource cancellationTokenSource = null;
using (cancellationTokenSource)
{
cancellationTokenSource = new CancellationTokenSource();
}
}
public void TwoCatchBlocksWithSameVariable()
{
try
{
Console.WriteLine("Try1");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
try
{
Console.WriteLine("Try2");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}

105
src/Libraries/ICSharpCode.Decompiler/Tests/Generics.cs

@ -0,0 +1,105 @@
// Copyright (c) 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;
public static class Generics
{
public class MyArray<T>
{
public class NestedClass<Y>
{
public T Item1;
public Y Item2;
}
public enum NestedEnum
{
A,
B
}
private T[] arr;
public MyArray(int capacity)
{
this.arr = new T[capacity];
}
public void Size(int capacity)
{
Array.Resize<T>(ref this.arr, capacity);
}
public void Grow(int capacity)
{
if (capacity >= this.arr.Length)
{
this.Size(capacity);
}
}
}
public interface IInterface
{
void Method1<T>() where T : class;
void Method2<T>() where T : class;
}
public abstract class Base : Generics.IInterface
{
// constraints must be repeated on implicit interface implementation
public abstract void Method1<T>() where T : class;
// constraints must not be specified on explicit interface implementation
void Generics.IInterface.Method2<T>()
{
}
}
public class Derived : Generics.Base
{
// constraints are inherited automatically and must not be specified
public override void Method1<T>()
{
}
}
private const Generics.MyArray<string>.NestedEnum enumVal = Generics.MyArray<string>.NestedEnum.A;
private static Type type1 = typeof(List<>);
private static Type type2 = typeof(Generics.MyArray<>);
private static Type type3 = typeof(List<>.Enumerator);
private static Type type4 = typeof(Generics.MyArray<>.NestedClass<>);
private static Type type5 = typeof(List<int>[]);
private static Type type6 = typeof(Generics.MyArray<>.NestedEnum);
public static void MethodWithConstraint<T, S>() where T : class, S where S : ICloneable, new()
{
}
public static void MethodWithStructConstraint<T>() where T : struct
{
}
public static Dictionary<string, string>.KeyCollection.Enumerator GetEnumerator(Dictionary<string, string> d, Generics.MyArray<string>.NestedClass<int> nc)
{
// Tests references to inner classes in generic classes
return d.Keys.GetEnumerator();
}
}

110
src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs

@ -0,0 +1,110 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using DiffLib;
using NUnit.Framework;
namespace ICSharpCode.Decompiler.Tests.Helpers
{
public class CodeAssert
{
public static void AreEqual(string input1, string input2)
{
var diff = new StringWriter();
if (!Compare(input1, input2, diff)) {
Assert.Fail(diff.ToString());
}
}
static bool Compare(string input1, string input2, StringWriter diff)
{
var differ = new AlignedDiff<string>(
NormalizeAndSplitCode(input1),
NormalizeAndSplitCode(input2),
new CodeLineEqualityComparer(),
new StringSimilarityComparer(),
new StringAlignmentFilter());
bool result = true, ignoreChange;
int line1 = 0, line2 = 0;
foreach (var change in differ.Generate()) {
switch (change.Change) {
case ChangeType.Same:
diff.Write("{0,4} {1,4} ", ++line1, ++line2);
diff.Write(" ");
diff.WriteLine(change.Element1);
break;
case ChangeType.Added:
diff.Write(" {1,4} ", line1, ++line2);
result &= ignoreChange = ShouldIgnoreChange(change.Element2);
diff.Write(ignoreChange ? " " : " + ");
diff.WriteLine(change.Element2);
break;
case ChangeType.Deleted:
diff.Write("{0,4} ", ++line1, line2);
result &= ignoreChange = ShouldIgnoreChange(change.Element1);
diff.Write(ignoreChange ? " " : " - ");
diff.WriteLine(change.Element1);
break;
case ChangeType.Changed:
diff.Write("{0,4} ", ++line1, line2);
result = false;
diff.Write("(-) ");
diff.WriteLine(change.Element1);
diff.Write(" {1,4} ", line1, ++line2);
diff.Write("(+) ");
diff.WriteLine(change.Element2);
break;
}
}
return result;
}
class CodeLineEqualityComparer : IEqualityComparer<string>
{
private IEqualityComparer<string> baseComparer = EqualityComparer<string>.Default;
public bool Equals(string x, string y)
{
return baseComparer.Equals(
NormalizeLine(x),
NormalizeLine(y)
);
}
public int GetHashCode(string obj)
{
return baseComparer.GetHashCode(NormalizeLine(obj));
}
}
private static string NormalizeLine(string line)
{
line = line.Trim();
var index = line.IndexOf("//");
if (index >= 0) {
return line.Substring(0, index);
} else if (line.StartsWith("#")) {
return string.Empty;
} else {
return line;
}
}
private static bool ShouldIgnoreChange(string line)
{
// for the result, we should ignore blank lines and added comments
return NormalizeLine(line) == string.Empty;
}
private static IEnumerable<string> NormalizeAndSplitCode(string input)
{
return input.Split(new[] { "\r\n", "\n\r", "\n", "\r" }, StringSplitOptions.None);
}
}
}

38
src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.Decompiler.Ast.Transforms;
using ICSharpCode.NRefactory.CSharp;
namespace ICSharpCode.Decompiler.Tests.Helpers
{
class RemoveCompilerAttribute : DepthFirstAstVisitor<object, object>, IAstTransform
{
public override object VisitAttribute(NRefactory.CSharp.Attribute attribute, object data)
{
var section = (AttributeSection)attribute.Parent;
SimpleType type = attribute.Type as SimpleType;
if (section.AttributeTarget == "assembly" &&
(type.Identifier == "CompilationRelaxations" || type.Identifier == "RuntimeCompatibility" || type.Identifier == "SecurityPermission" || type.Identifier == "AssemblyVersion"))
{
attribute.Remove();
if (section.Attributes.Count == 0)
section.Remove();
}
if (section.AttributeTarget == "module" && type.Identifier == "UnverifiableCode")
{
attribute.Remove();
if (section.Attributes.Count == 0)
section.Remove();
}
return null;
}
public void Run(AstNode node)
{
node.AcceptVisitor(this, null);
}
}
}

113
src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj

@ -0,0 +1,113 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
<PropertyGroup>
<ProjectGuid>{FEC0DA52-C4A6-4710-BE36-B484A20C5E22}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<OutputType>Library</OutputType>
<RootNamespace>ICSharpCode.Decompiler.Tests</RootNamespace>
<AssemblyName>ICSharpCode.Decompiler.Tests</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<AppDesignerFolder>Properties</AppDesignerFolder>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
<NoStdLib>False</NoStdLib>
<WarningLevel>4</WarningLevel>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<CheckForOverflowUnderflow>False</CheckForOverflowUnderflow>
<NoWarn>67,169,1058,728</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Platform)' == 'x86' ">
<PlatformTarget>x86</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>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<OutputPath>..\bin\Release\</OutputPath>
<DebugSymbols>false</DebugSymbols>
<DebugType>None</DebugType>
<Optimize>True</Optimize>
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Reference Include="DiffLib">
<HintPath>..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll</HintPath>
</Reference>
<Reference Include="nunit.framework">
<SpecificVersion>False</SpecificVersion>
<HintPath>.\nunit.framework.dll</HintPath>
</Reference>
<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="CallOverloadedMethod.cs" />
<Compile Include="CheckedUnchecked.cs" />
<Compile Include="CustomShortCircuitOperators.cs" />
<Compile Include="Helpers\CodeAssert.cs" />
<Compile Include="IncrementDecrement.cs" />
<Compile Include="PInvoke.cs" />
<Compile Include="QueryExpressions.cs" />
<Compile Include="Switch.cs" />
<Compile Include="TypeAnalysisTests.cs" />
<Compile Include="UndocumentedExpressions.cs" />
<Compile Include="UnsafeCode.cs" />
<Compile Include="Types\S_TypeDeclarations.cs" />
<Compile Include="YieldReturn.cs" />
<None Include="packages.config" />
<None Include="Types\S_EnumSamples.cs" />
<None Include="CustomAttributes\S_AssemblyCustomAttribute.cs" />
<Compile Include="Helpers\RemoveCompilerAttribute.cs" />
<Compile Include="Types\S_TypeMemberDeclarations.cs" />
<Compile Include="Types\EnumTests.cs" />
<Compile Include="Types\TypeTests.cs" />
<Compile Include="DelegateConstruction.cs" />
<None Include="CustomAttributes\S_CustomAttributes.cs" />
<Compile Include="Loops.cs" />
<Compile Include="PropertiesAndEvents.cs" />
<None Include="CustomAttributes\S_CustomAttributeSamples.cs" />
<Compile Include="CodeSampleFileParser.cs" />
<Compile Include="CustomAttributes\CustomAttributeTests.cs" />
<Compile Include="DecompilerTestBase.cs" />
<Compile Include="InitializerTests.cs" />
<Compile Include="ExceptionHandling.cs" />
<Compile Include="Generics.cs" />
<Compile Include="MultidimensionalArray.cs" />
<Compile Include="TestRunner.cs" />
<Compile Include="ValueTypes.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Mono.Cecil\Mono.Cecil.csproj">
<Project>{D68133BD-1E63-496E-9EDE-4FBDBF77B486}</Project>
<Name>Mono.Cecil</Name>
</ProjectReference>
<ProjectReference Include="..\..\NRefactory\ICSharpCode.NRefactory\ICSharpCode.NRefactory.csproj">
<Project>{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}</Project>
<Name>ICSharpCode.NRefactory</Name>
</ProjectReference>
<ProjectReference Include="..\ICSharpCode.Decompiler.csproj">
<Project>{984CC812-9470-4A13-AFF9-CC44068D666C}</Project>
<Name>ICSharpCode.Decompiler</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<None Include="BooleanConsumedAsInteger.il" />
<None Include="StackTests\StackTests.il" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.Targets" />
</Project>

254
src/Libraries/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs

@ -0,0 +1,254 @@
// Copyright (c) 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;
public class IncrementDecrement
{
[Flags]
private enum MyEnum
{
None = 0,
One = 1,
Two = 2,
Four = 4
}
public class MutableClass
{
public int Field;
public int Property
{
get;
set;
}
public uint this[string name]
{
get
{
return 0u;
}
set
{
}
}
}
private IncrementDecrement.MyEnum enumField;
public static int StaticField;
public static int StaticProperty
{
get;
set;
}
private IncrementDecrement.MutableClass M()
{
return new IncrementDecrement.MutableClass();
}
private int[,] Array()
{
return null;
}
private unsafe int* GetPointer()
{
return null;
}
public int PreIncrementInAddition(int i, int j)
{
return i + ++j;
}
public int PreIncrementArrayElement(int[] array, int pos)
{
return --array[pos];
}
public int PreIncrementInstanceField()
{
return ++this.M().Field;
}
public int PreIncrementInstanceField2(IncrementDecrement.MutableClass m)
{
return ++m.Field;
}
public int PreIncrementInstanceProperty()
{
return ++this.M().Property;
}
public int PreIncrementStaticField()
{
return ++IncrementDecrement.StaticField;
}
public int PreIncrementStaticProperty()
{
return ++IncrementDecrement.StaticProperty;
}
// public uint PreIncrementIndexer(string name)
// {
// return ++this.M()[name];
// }
public int PreIncrementByRef(ref int i)
{
return ++i;
}
public unsafe int PreIncrementByPointer()
{
return ++(*this.GetPointer());
}
public int PreIncrement2DArray()
{
return ++this.Array()[1, 2];
}
public int CompoundAssignInstanceField()
{
return this.M().Field *= 10;
}
public int CompoundAssignInstanceProperty()
{
return this.M().Property *= 10;
}
public int CompoundAssignStaticField()
{
return IncrementDecrement.StaticField ^= 100;
}
public int CompoundAssignStaticProperty()
{
return IncrementDecrement.StaticProperty &= 10;
}
public int CompoundAssignArrayElement1(int[] array, int pos)
{
return array[pos] *= 10;
}
public int CompoundAssignArrayElement2(int[] array)
{
return array[Environment.TickCount] *= 10;
}
// public uint CompoundAssignIndexer(string name)
// {
// return this.M()[name] -= 2;
// }
public int CompoundAssignIncrement2DArray()
{
return this.Array()[1, 2] %= 10;
}
public int CompoundAssignByRef(ref int i)
{
return i <<= 2;
}
public unsafe double CompoundAssignByPointer(double* ptr)
{
return *ptr /= 1.5;
}
public void CompoundAssignEnum()
{
this.enumField |= IncrementDecrement.MyEnum.Two;
this.enumField &= ~IncrementDecrement.MyEnum.Four;
}
public int PostIncrementInAddition(int i, int j)
{
return i++ + j;
}
public void PostIncrementInlineLocalVariable(Func<int, int> f)
{
int num = 0;
f(num++);
}
public int PostIncrementArrayElement(int[] array, int pos)
{
return array[pos]--;
}
public int PostIncrementStaticField()
{
return IncrementDecrement.StaticField++;
}
public int PostIncrementStaticProperty()
{
return IncrementDecrement.StaticProperty++;
}
public int PostIncrementInstanceField(IncrementDecrement.MutableClass m)
{
return m.Field++;
}
// public uint PostIncrementIndexer(string name)
// {
// return this.M()[name]++;
// }
// public unsafe int PostIncrementOfPointer(int* ptr)
// {
// return *(ptr++);
// }
public int PostIncrementInstanceField()
{
return this.M().Field--;
}
public int PostIncrementInstanceProperty()
{
return this.M().Property--;
}
public int PostIncrement2DArray()
{
return this.Array()[IncrementDecrement.StaticField, IncrementDecrement.StaticProperty]++;
}
public int PostIncrementByRef(ref int i)
{
return i++;
}
public unsafe int PostIncrementByPointer()
{
return (*this.GetPointer())++;
}
}

869
src/Libraries/ICSharpCode.Decompiler/Tests/InitializerTests.cs

@ -0,0 +1,869 @@
// Copyright (c) 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;
public class InitializerTests
{
private enum MyEnum
{
a,
b
}
private enum MyEnum2
{
c,
d
}
private class Data
{
public List<InitializerTests.MyEnum2> FieldList = new List<InitializerTests.MyEnum2>();
public InitializerTests.MyEnum a
{
get;
set;
}
public List<InitializerTests.MyEnum2> PropertyList
{
get;
set;
}
public InitializerTests.Data MoreData
{
get;
set;
}
public InitializerTests.StructData NestedStruct
{
get;
set;
}
}
private struct StructData
{
public int Field;
public int Property
{
get;
set;
}
public InitializerTests.Data MoreData
{
get;
set;
}
public StructData(int initialValue)
{
this = default(InitializerTests.StructData);
this.Field = initialValue;
this.Property = initialValue;
}
}
// Helper methods used to ensure initializers used within expressions work correctly
private static void X(object a, object b)
{
}
private static object Y()
{
return null;
}
#region Array Initializers
public static void Array1()
{
InitializerTests.X(InitializerTests.Y(), new int[]
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
});
}
public static void Array2(int a, int b, int c)
{
InitializerTests.X(InitializerTests.Y(), new int[]
{
a,
0,
b,
0,
c
});
}
public static void NestedArray(int a, int b, int c)
{
InitializerTests.X(InitializerTests.Y(), new int[][]
{
new int[]
{
1,
2,
3,
4,
5,
6,
7,
8,
9,
10
},
new int[]
{
a,
b,
c
},
new int[]
{
1,
2,
3,
4,
5,
6
}
});
}
public static void ArrayBoolean()
{
InitializerTests.X(InitializerTests.Y(), new bool[]
{
true,
false,
true,
false,
false,
false,
true,
true
});
}
public static void ArrayByte()
{
InitializerTests.X(InitializerTests.Y(), new byte[]
{
1,
2,
3,
4,
5,
6,
7,
8,
254,
255
});
}
public static void ArraySByte()
{
InitializerTests.X(InitializerTests.Y(), new sbyte[]
{
-128,
-127,
0,
1,
2,
3,
4,
127
});
}
public static void ArrayShort()
{
InitializerTests.X(InitializerTests.Y(), new short[]
{
-32768,
-1,
0,
1,
32767
});
}
public static void ArrayUShort()
{
InitializerTests.X(InitializerTests.Y(), new ushort[]
{
0,
1,
32767,
32768,
65534,
65535
});
}
public static void ArrayInt()
{
InitializerTests.X(InitializerTests.Y(), new int[]
{
1,
-2,
2000000000,
4,
5,
-6,
7,
8,
9,
10
});
}
public static void ArrayUInt()
{
InitializerTests.X(InitializerTests.Y(), new uint[]
{
1u,
2000000000u,
3000000000u,
4u,
5u,
6u,
7u,
8u,
9u,
10u
});
}
public static void ArrayLong()
{
InitializerTests.X(InitializerTests.Y(), new long[]
{
-4999999999999999999L,
-1L,
0L,
1L,
4999999999999999999L
});
}
public static void ArrayULong()
{
InitializerTests.X(InitializerTests.Y(), new ulong[]
{
1uL,
2000000000uL,
3000000000uL,
4uL,
5uL,
6uL,
7uL,
8uL,
4999999999999999999uL,
9999999999999999999uL
});
}
public static void ArrayFloat()
{
InitializerTests.X(InitializerTests.Y(), new float[]
{
-1.5f,
0f,
1.5f,
float.NegativeInfinity,
float.PositiveInfinity,
float.NaN
});
}
public static void ArrayDouble()
{
InitializerTests.X(InitializerTests.Y(), new double[]
{
-1.5,
0.0,
1.5,
double.NegativeInfinity,
double.PositiveInfinity,
double.NaN
});
}
public static void ArrayDecimal()
{
InitializerTests.X(InitializerTests.Y(), new decimal[]
{
-100m,
0m,
100m,
-79228162514264337593543950335m,
79228162514264337593543950335m,
0.0000001m
});
}
public static void ArrayString()
{
InitializerTests.X(InitializerTests.Y(), new string[]
{
"",
null,
"Hello",
"World"
});
}
public static void ArrayEnum()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.MyEnum[]
{
InitializerTests.MyEnum.a,
InitializerTests.MyEnum.b,
InitializerTests.MyEnum.a,
InitializerTests.MyEnum.b
});
}
#endregion
public static void CollectionInitializerList()
{
InitializerTests.X(InitializerTests.Y(), new List<int>
{
1,
2,
3
});
}
public static void CollectionInitializerDictionary()
{
InitializerTests.X(InitializerTests.Y(), new Dictionary<string, int>
{
{
"First",
1
},
{
"Second",
2
},
{
"Third",
3
}
});
}
public static void CollectionInitializerDictionaryWithEnumTypes()
{
InitializerTests.X(InitializerTests.Y(), new Dictionary<InitializerTests.MyEnum, InitializerTests.MyEnum2>
{
{
InitializerTests.MyEnum.a,
InitializerTests.MyEnum2.c
},
{
InitializerTests.MyEnum.b,
InitializerTests.MyEnum2.d
}
});
}
public static void NotACollectionInitializer()
{
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
InitializerTests.X(InitializerTests.Y(), list);
}
public static void ObjectInitializer()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a
});
}
public static void NotAObjectInitializer()
{
InitializerTests.Data data = new InitializerTests.Data();
data.a = InitializerTests.MyEnum.a;
InitializerTests.X(InitializerTests.Y(), data);
}
public static void ObjectInitializerAssignCollectionToField()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
FieldList = new List<InitializerTests.MyEnum2>
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerAddToCollectionInField()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
FieldList =
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerAssignCollectionToProperty()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
PropertyList = new List<InitializerTests.MyEnum2>
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerAddToCollectionInProperty()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
PropertyList =
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerWithInitializationOfNestedObjects()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data
{
MoreData =
{
a = InitializerTests.MyEnum.a
}
});
}
public static void StructInitializer_DefaultConstructor()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData
{
Field = 1,
Property = 2
});
}
public static void StructInitializer_ExplicitConstructor()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData(0)
{
Field = 1,
Property = 2
});
}
public static void StructInitializerWithInitializationOfNestedObjects()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.StructData
{
MoreData =
{
a = InitializerTests.MyEnum.a,
FieldList =
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
}
});
}
public static void StructInitializerWithinObjectInitializer()
{
InitializerTests.X(InitializerTests.Y(), new InitializerTests.Data
{
NestedStruct = new InitializerTests.StructData(2)
{
Field = 1,
Property = 2
}
});
}
public void MultidimensionalInit()
{
int[,] expr_09 = new int[, ]
{
{
0,
0,
0,
0
},
{
1,
1,
1,
1
},
{
0,
0,
0,
0
},
{
0,
0,
0,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
0,
0
},
{
1,
1,
1,
1
},
{
0,
0,
0,
0
},
{
0,
0,
0,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
}
};
}
public void MultidimensionalInit2()
{
int[][,] array = new int[][,]
{
new int[, ]
{
{
0,
0,
0,
0
},
{
1,
1,
1,
1
},
{
0,
0,
0,
0
},
{
0,
0,
0,
0
}
},
new int[, ]
{
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
}
},
new int[, ]
{
{
0,
0,
0,
0
},
{
1,
1,
1,
1
},
{
0,
0,
0,
0
},
{
0,
0,
0,
0
}
},
new int[, ]
{
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
},
{
0,
0,
1,
0
}
}
};
}
public void ArrayOfArrayOfArrayInit()
{
int[][,,] array = new int[][,,]
{
new int[, , ]
{
{
{
1,
2,
3
},
{
4,
5,
6
},
{
7,
8,
9
}
},
{
{
11,
12,
13
},
{
14,
15,
16
},
{
17,
18,
19
}
}
},
new int[, , ]
{
{
{
21,
22,
23
},
{
24,
25,
26
},
{
27,
28,
29
}
},
{
{
31,
32,
33
},
{
34,
35,
36
},
{
37,
38,
39
}
}
}
};
}
}

74
src/Libraries/ICSharpCode.Decompiler/Tests/Loops.cs

@ -0,0 +1,74 @@
// Copyright (c) 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;
public class Loops
{
public void ForEach(IEnumerable<string> enumerable)
{
foreach (string current in enumerable)
{
current.ToLower();
}
}
public void ForEachOverList(List<string> list)
{
// List has a struct as enumerator, so produces quite different IL than foreach over the IEnumerable interface
foreach (string current in list)
{
current.ToLower();
}
}
public void ForEachOverNonGenericEnumerable(IEnumerable enumerable)
{
foreach (object current in enumerable)
{
current.ToString();
}
}
public void ForEachOverNonGenericEnumerableWithAutomaticCast(IEnumerable enumerable)
{
foreach (int num in enumerable)
{
num.ToString();
}
}
// public void ForEachOverArray(string[] array)
// {
// foreach (string text in array)
// {
// text.ToLower();
// }
// }
public void ForOverArray(string[] array)
{
for (int i = 0; i < array.Length; i++)
{
array[i].ToLower();
}
}
}

58
src/Libraries/ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs

@ -0,0 +1,58 @@
// Copyright (c) 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;
public class MultidimensionalArray
{
internal class Generic<T, S> where T : new()
{
private T[,] a = new T[20, 20];
private S[,][] b = new S[20, 20][];
public T this[int i, int j]
{
get
{
return this.a[i, j];
}
set
{
this.a[i, j] = value;
}
}
public void TestB(S x, ref S y)
{
this.b[5, 3] = new S[10];
this.b[5, 3][0] = default(S);
this.b[5, 3][1] = x;
this.b[5, 3][2] = y;
}
public void PassByReference(ref T arr)
{
this.PassByReference(ref this.a[10, 10]);
}
}
public int[][,] MakeArray()
{
return new int[10][,];
}
}

87
src/Libraries/ICSharpCode.Decompiler/Tests/PInvoke.cs

@ -0,0 +1,87 @@
// 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.InteropServices;
// P/Invoke and marshalling attribute tests
public class PInvoke
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode, Pack = 2)]
public struct MarshalAsTest
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)]
public uint[] FixedArray;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4, ArraySubType = UnmanagedType.Bool)]
public int[] FixedBoolArray;
[MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
public string[] SafeBStrArray;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 8)]
public string FixedString;
}
[StructLayout(LayoutKind.Explicit)]
public struct Rect
{
[FieldOffset(0)]
public int left;
[FieldOffset(4)]
public int top;
[FieldOffset(8)]
public int right;
[FieldOffset(12)]
public int bottom;
}
public static decimal MarshalAttributesOnPropertyAccessors
{
[return: MarshalAs(UnmanagedType.Currency)]
get
{
return 0m;
}
[param: MarshalAs(UnmanagedType.Currency)]
set
{
}
}
[DllImport("xyz.dll", CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool Method([MarshalAs(UnmanagedType.LPStr)] string input);
[DllImport("xyz.dll")]
private static extern void New1(int ElemCnt, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] ar);
[DllImport("xyz.dll")]
private static extern void New2([MarshalAs(UnmanagedType.LPArray, SizeConst = 128)] int[] ar);
[DllImport("xyz.dll")]
private static extern void New3([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.Bool, SizeConst = 64, SizeParamIndex = 1)] int[] ar);
public void CustomMarshal1([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler")] object o)
{
}
public void CustomMarshal2([MarshalAs(UnmanagedType.CustomMarshaler, MarshalType = "MyCompany.MyMarshaler", MarshalCookie = "Cookie")] object o)
{
}
}

81
src/Libraries/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs

@ -0,0 +1,81 @@
// Copyright (c) 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.Text;
public class PropertiesAndEvents
{
public event EventHandler AutomaticEvent;
[field: NonSerialized]
public event EventHandler AutomaticEventWithInitializer = delegate
{
}
;
public event EventHandler CustomEvent
{
add
{
this.AutomaticEvent += value;
}
remove
{
this.AutomaticEvent -= value;
}
}
public int AutomaticProperty
{
get;
set;
}
public int CustomProperty
{
get
{
return this.AutomaticProperty;
}
set
{
this.AutomaticProperty = value;
}
}
public int Getter(StringBuilder b)
{
return b.Length;
}
public void Setter(StringBuilder b)
{
b.Capacity = 100;
}
public char IndexerGetter(StringBuilder b)
{
return b[50];
}
public void IndexerSetter(StringBuilder b)
{
b[42] = 'b';
}
}

164
src/Libraries/ICSharpCode.Decompiler/Tests/QueryExpressions.cs

@ -0,0 +1,164 @@
// Copyright (c) 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;
public class QueryExpressions
{
public class Customer
{
public int CustomerID;
public IEnumerable<QueryExpressions.Order> Orders;
public string Name;
public string Country;
public string City;
}
public class Order
{
public int OrderID;
public DateTime OrderDate;
public Customer Customer;
public int CustomerID;
public decimal Total;
public IEnumerable<QueryExpressions.OrderDetail> Details;
}
public class OrderDetail
{
public decimal UnitPrice;
public int Quantity;
}
public IEnumerable<QueryExpressions.Customer> customers;
public IEnumerable<QueryExpressions.Order> orders;
public object MultipleWhere()
{
return
from c in this.customers
where c.Orders.Count() > 10
where c.Country == "DE"
select c;
}
public object SelectManyFollowedBySelect()
{
return
from c in this.customers
from o in c.Orders
select new { c.Name, o.OrderID, o.Total };
}
public object SelectManyFollowedByOrderBy()
{
return
from c in this.customers
from o in c.Orders
orderby o.Total descending
select new { c.Name, o.OrderID, o.Total };
}
public object MultipleSelectManyFollowedBySelect()
{
return
from c in this.customers
from o in c.Orders
from d in o.Details
select new { c.Name, o.OrderID, d.Quantity };
}
public object MultipleSelectManyFollowedByLet()
{
return
from c in this.customers
from o in c.Orders
from d in o.Details
let x = d.Quantity * d.UnitPrice
select new { c.Name, o.OrderID, x };
}
public object FromLetWhereSelect()
{
return
from o in this.orders
let t = o.Details.Sum(d => d.UnitPrice * d.Quantity)
where t >= 1000
select new { o.OrderID, Total = t };
}
public object MultipleLet()
{
return
from a in this.customers
let b = a.Country
let c = a.Name
select b + c;
}
public object Join()
{
return
from c in customers
join o in orders on c.CustomerID equals o.CustomerID
select new { c.Name, o.OrderDate, o.Total };
}
public object JoinInto()
{
return
from c in customers
join o in orders on c.CustomerID equals o.CustomerID into co
let n = co.Count()
where n >= 10
select new { c.Name, OrderCount = n };
}
public object OrderBy()
{
return
from o in orders
orderby o.Customer.Name, o.Total descending
select o;
}
public object GroupBy()
{
return
from c in customers
group c.Name by c.Country;
}
public object ExplicitType()
{
return
from Customer c in customers
where c.City == "London"
select c;
}
public object QueryContinuation()
{
return
from c in customers
group c by c.Country into g
select new { Country = g.Key, CustCount = g.Count() };
}
}

BIN
src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe

Binary file not shown.

132
src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.il

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

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

Loading…
Cancel
Save