diff --git a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
index ddced0ecf8..292319a323 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
+++ b/src/AddIns/Debugger/Debugger.AddIn/Debugger.AddIn.csproj
@@ -360,6 +360,14 @@
ICSharpCode.AvalonEditFalse
+
+ {984CC812-9470-4A13-AFF9-CC44068D666C}
+ ICSharpCode.Decompiler
+
+
+ {D68133BD-1E63-496E-9EDE-4FBDBF77B486}
+ Mono.Cecil
+ {2748AD25-9C63-4E12-877B-4DCE96FBED54}ICSharpCode.SharpDevelop
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs b/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
index 1d0aec5605..4a72a07811 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Service/WindowsDebugger.cs
+++ b/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)
using System;
+using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
@@ -15,8 +16,12 @@ using Debugger;
using Debugger.AddIn.Tooltips;
using Debugger.AddIn.TreeModel;
using Debugger.Interop.CorPublish;
+using Debugger.MetaData;
using ICSharpCode.Core;
+using ICSharpCode.Core.Services;
using ICSharpCode.Core.WinForms;
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.NRefactory.Visitors;
@@ -25,6 +30,7 @@ using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Gui;
using ICSharpCode.SharpDevelop.Gui.OptionPanels;
using ICSharpCode.SharpDevelop.Project;
+using Mono.Cecil;
using Process = Debugger.Process;
namespace ICSharpCode.SharpDevelop.Services
@@ -81,6 +87,18 @@ namespace ICSharpCode.SharpDevelop.Services
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)
{
if (ProcessSelected != null) {
@@ -210,6 +228,8 @@ namespace ICSharpCode.SharpDevelop.Services
DebugStarting(this, EventArgs.Empty);
try {
+ // set the JIT flag for evaluating optimized code
+ Process.DebugMode = DebugModeFlag.Debug;
Process process = debugger.Start(processStartInfo.FileName,
processStartInfo.WorkingDirectory,
processStartInfo.Arguments);
@@ -271,6 +291,8 @@ namespace ICSharpCode.SharpDevelop.Services
DebugStarting(this, EventArgs.Empty);
try {
+ // set the JIT flag for evaluating optimized code
+ Process.DebugMode = DebugModeFlag.Debug;
Process process = debugger.Attach(existingProcess);
attached = true;
SelectProcess(process);
@@ -444,16 +466,56 @@ namespace ICSharpCode.SharpDevelop.Services
// 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)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()
{
if (!IsDebugging) {
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepInto}");
return;
}
- if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
+
+ var frame = debuggedProcess.SelectedStackFrame ?? debuggedProcess.SelectedThread.MostRecentStackFrame;
+ if (frame == null || debuggedProcess.IsRunning) {
MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}");
} else {
- debuggedProcess.SelectedStackFrame.AsyncStepInto();
+ if (IsInExternalCode) {
+ // get frame info from external code mappings
+ frame = GetStackFrame();
+ }
+
+ frame.AsyncStepInto();
}
}
@@ -463,10 +525,17 @@ namespace ICSharpCode.SharpDevelop.Services
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOver}");
return;
}
- if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
+
+ var frame = debuggedProcess.SelectedStackFrame ?? debuggedProcess.SelectedThread.MostRecentStackFrame;
+ if (frame == null || debuggedProcess.IsRunning) {
MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepOver}");
} else {
- debuggedProcess.SelectedStackFrame.AsyncStepOver();
+ if (IsInExternalCode) {
+ // get frame info from external code mappings
+ frame = GetStackFrame();
+ }
+
+ frame.AsyncStepOver();
}
}
@@ -476,10 +545,17 @@ namespace ICSharpCode.SharpDevelop.Services
MessageService.ShowMessage(errorNotDebugging, "${res:XML.MainMenu.DebugMenu.StepOut}");
return;
}
- if (debuggedProcess.SelectedStackFrame == null || debuggedProcess.IsRunning) {
- MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepOut}");
+
+ var frame = debuggedProcess.SelectedStackFrame ?? debuggedProcess.SelectedThread.MostRecentStackFrame;
+ if (frame == null || debuggedProcess.IsRunning) {
+ MessageService.ShowMessage(errorCannotStepNoActiveFunction, "${res:XML.MainMenu.DebugMenu.StepInto}");
} else {
- debuggedProcess.SelectedStackFrame.AsyncStepOut();
+ if (IsInExternalCode) {
+ // get frame info from external code mappings
+ frame = GetStackFrame();
+ }
+
+ frame.AsyncStepOut();
}
}
@@ -505,9 +581,19 @@ namespace ICSharpCode.SharpDevelop.Services
if (!CanEvaluate) {
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;
+ }
}
-
+
///
/// Gets Expression for given variable. Can throw GetValueException.
/// Thrown when getting expression fails. Exception message explains reason.
@@ -550,7 +636,8 @@ namespace ICSharpCode.SharpDevelop.Services
bool CanEvaluate
{
get {
- return debuggedProcess != null && !debuggedProcess.IsRunning && debuggedProcess.SelectedStackFrame != null;
+ return debuggedProcess != null && !debuggedProcess.IsRunning &&
+ (debuggedProcess.SelectedStackFrame != null || debuggedProcess.SelectedThread.MostRecentStackFrame != null);
}
}
@@ -566,7 +653,7 @@ namespace ICSharpCode.SharpDevelop.Services
var image = ExpressionNode.GetImageForLocalVariable(out imageName);
ExpressionNode expressionNode = new ExpressionNode(image, variableName, tooltipExpression);
expressionNode.ImageName = imageName;
- return new DebuggerTooltipControl(logicalPosition, expressionNode);
+ return new DebuggerTooltipControl(logicalPosition, expressionNode) { ShowPins = !IsInExternalCode };
} catch (GetValueException) {
return null;
}
@@ -885,15 +972,64 @@ namespace ICSharpCode.SharpDevelop.Services
public void JumpToCurrentLine()
{
+ if (debuggedProcess == null || debuggedProcess.SelectedThread == null)
+ return;
+
WorkbenchSingleton.MainWindow.Activate();
DebuggerService.RemoveCurrentLineMarker();
- if (debuggedProcess != null) {
+
+ if (!IsInExternalCode) {
SourcecodeSegment nextStatement = debuggedProcess.NextStatement;
if (nextStatement != null) {
DebuggerService.JumpToCurrentLine(nextStatement.Filename, nextStatement.StartLine, nextStatement.StartColumn, nextStatement.EndLine, nextStatement.EndColumn);
}
+ } else {
+ 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()) {
+ 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()
{
@@ -912,5 +1048,31 @@ namespace ICSharpCode.SharpDevelop.Services
.Where(p => e.Item.Name.IndexOf(p.Name) >= 0)
.ForEach(p => e.Item.LoadSymbolsFromDisk(new []{ Path.GetDirectoryName(p.OutputAssemblyFullPath) }));
}
+
+ public static object GetLocalVariableIndex(Debugger.StackFrame stackFrame, string variableName)
+ {
+ // get the target name
+ int 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 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;
+ }
}
}
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs
index 87529f37e1..f6132f6ee1 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerPopup.cs
+++ b/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)
{
- this.contentControl = new DebuggerTooltipControl(parentControl, logicalPosition, showPins);
+ this.contentControl = new DebuggerTooltipControl(parentControl, logicalPosition) { ShowPins = showPins };
this.contentControl.containingPopup = this;
this.Child = this.contentControl;
this.IsLeaf = false;
diff --git a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
index f5463752d3..85646c668c 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/Tooltips/DebuggerTooltipControl.xaml.cs
@@ -58,11 +58,10 @@ namespace Debugger.AddIn.Tooltips
this.itemsSource = nodes;
}
- public DebuggerTooltipControl(DebuggerTooltipControl parentControl, Location logicalPosition, bool showPins = true)
+ public DebuggerTooltipControl(DebuggerTooltipControl parentControl, Location logicalPosition)
: this(logicalPosition)
{
this.parentControl = parentControl;
- this.showPins = showPins;
}
private void OnLoaded(object sender, RoutedEventArgs e)
@@ -82,6 +81,11 @@ namespace Debugger.AddIn.Tooltips
}
}
+ public bool ShowPins {
+ get { return showPins; }
+ set { showPins = value; }
+ }
+
public IEnumerable ItemsSource {
get { return this.itemsSource; }
}
@@ -95,8 +99,16 @@ namespace Debugger.AddIn.Tooltips
this.itemsSource.ForEach(item => observable.Add(item));
// verify if at the line of the root there's a pin bookmark
- ITextEditorProvider provider = WorkbenchSingleton.Workbench.ActiveViewContent as ITextEditorProvider;
- var editor = provider.TextEditor;
+ ITextEditor editor;
+ var viewContent = WorkbenchSingleton.Workbench.ActiveViewContent;
+ ITextEditorProvider provider = viewContent as ITextEditorProvider;
+ if (provider != null) {
+ editor = provider.TextEditor;
+ } else {
+ dynamic codeView = viewContent.Control;
+ editor = codeView.TextEditor as ITextEditor;
+ }
+
if (editor != null) {
var pin = BookmarkManager.Bookmarks.Find(
b => b is PinBookmark &&
@@ -208,7 +220,7 @@ namespace Debugger.AddIn.Tooltips
// open child Popup
if (this.childPopup == null) {
- this.childPopup = new DebuggerPopup(this, logicalPosition);
+ this.childPopup = new DebuggerPopup(this, logicalPosition, showPins);
this.childPopup.Placement = PlacementMode.Absolute;
}
if (this.containingPopup != null) {
diff --git a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs
index 813135904c..deb6752d7f 100644
--- a/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs
+++ b/src/AddIns/Debugger/Debugger.AddIn/TreeModel/ExpressionNode.cs
@@ -5,13 +5,17 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
+using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
+
using Debugger.AddIn.Visualizers;
using Debugger.MetaData;
using ICSharpCode.Core;
+using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.Ast;
using ICSharpCode.SharpDevelop;
using ICSharpCode.SharpDevelop.Debugging;
@@ -154,6 +158,15 @@ namespace Debugger.AddIn.TreeModel
Value val;
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);
} catch (GetValueException e) {
error = e;
diff --git a/src/AddIns/Debugger/Debugger.Core/Interop/CorDebug.cs b/src/AddIns/Debugger/Debugger.Core/Interop/CorDebug.cs
index 88ccf6d91d..fba895667f 100644
--- a/src/AddIns/Debugger/Debugger.Core/Interop/CorDebug.cs
+++ b/src/AddIns/Debugger/Debugger.Core/Interop/CorDebug.cs
@@ -62,6 +62,14 @@ namespace Debugger.Interop.CorDebug
CHAIN_SECURITY = 4,
CHAIN_THREAD_START = 0x40
}
+
+ [Flags]
+ public enum CorDebugJITCompilerFlags
+ {
+ CORDEBUG_JIT_DEFAULT = 0x1,
+ CORDEBUG_JIT_DISABLE_OPTIMIZATION = 0x3,
+ CORDEBUG_JIT_ENABLE_ENC = 0x7
+ }
[ComImport, TypeLibType((short) 2), Guid("6FEF44D0-39E7-4C77-BE8E-C9F8CF988630"), ClassInterface((short) 0)]
public class CorDebugClass : ICorDebug, CorDebug
diff --git a/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs b/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
index 5a0b7d92a5..56517541dc 100644
--- a/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
+++ b/src/AddIns/Debugger/Debugger.Core/ManagedCallback.cs
@@ -320,6 +320,17 @@ namespace Debugger
EnterCallback(PausedReason.Other, "CreateProcess", pProcess);
// Process is added in NDebugger.Start
+ // disable NGen
+ if (!this.process.Options.EnableJustMyCode && !this.process.Options.StepOverNoSymbols) {
+ ICorDebugProcess2 pProcess2 = pProcess as ICorDebugProcess2;
+ if (pProcess2 != null && Process.DebugMode == DebugModeFlag.Debug) {
+ try {
+ pProcess2.SetDesiredNGENCompilerFlags((uint)CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION);
+ } catch (COMException) {
+ // we cannot set the NGEN flag => no evaluation for optimized code.
+ }
+ }
+ }
ExitCallback();
}
diff --git a/src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs b/src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
index f1bc327d70..35bb848459 100644
--- a/src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
+++ b/src/AddIns/Debugger/Debugger.Core/MetaData/DebugMethodInfo.cs
@@ -68,7 +68,7 @@ namespace Debugger.MetaData
sb.Append(this.ReturnType.Name);
sb.Append(" ");
} else {
- sb.Append("void ");
+ sb.Append("System.Void ");
}
sb.Append(this.DeclaringType.FullName);
@@ -80,7 +80,7 @@ namespace Debugger.MetaData
if (!first)
sb.Append(", ");
first = false;
- sb.Append(p.ParameterType.Name);
+ sb.Append(p.ParameterType.FullName);
sb.Append(" ");
sb.Append(p.Name);
}
@@ -89,6 +89,37 @@ namespace Debugger.MetaData
}
}
+ /// Name including the declaring type, return type without parameters names
+ 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();
+ }
+ }
+
///
public override string Name {
get { return methodProps.Name; }
@@ -343,12 +374,12 @@ namespace Debugger.MetaData
uint token = 0;
bool success =
- (Read(code, 0x00) || true) && // nop || nothing
+ (Read(code, 0x00) || true) && // nop || nothing
(Read(code, 0x02, 0x7B) || Read(code, 0x7E)) && // ldarg.0; ldfld || ldsfld
ReadToken(code, ref token) && //
(Read(code, 0x0A, 0x2B, 0x00, 0x06) || true) && // stloc.0; br.s; offset+00; ldloc.0 || nothing
Read(code, 0x2A); // ret
-
+
if (!success) return;
if (this.Process.Options.Verbose) {
@@ -427,7 +458,7 @@ namespace Debugger.MetaData
// Look on the method
DebugType.IsDefined(
this,
- false,
+ false,
typeof(System.Diagnostics.DebuggerStepThroughAttribute),
typeof(System.Diagnostics.DebuggerNonUserCodeAttribute),
typeof(System.Diagnostics.DebuggerHiddenAttribute))
@@ -435,7 +466,7 @@ namespace Debugger.MetaData
// Look on the type
DebugType.IsDefined(
declaringType,
- false,
+ false,
typeof(System.Diagnostics.DebuggerStepThroughAttribute),
typeof(System.Diagnostics.DebuggerNonUserCodeAttribute),
typeof(System.Diagnostics.DebuggerHiddenAttribute));
@@ -464,6 +495,19 @@ namespace Debugger.MetaData
}
}
+ public static Value GetLocalVariableValue(StackFrame context, int varIndex)
+ {
+ ICorDebugValue corVal;
+ try {
+ corVal = context.CorILFrame.GetLocalVariable((uint)varIndex);
+ } catch (COMException e) {
+ if ((uint)e.ErrorCode == 0x80131304) throw new GetValueException("Unavailable in optimized code");
+ // show the message in case of bad index
+ throw new GetValueException(e.Message);
+ }
+ return new Value(context.AppDomain, corVal);
+ }
+
public DebugLocalVariableInfo GetLocalVariable(int offset, string name)
{
foreach(DebugLocalVariableInfo loc in GetLocalVariables(offset)) {
diff --git a/src/AddIns/Debugger/Debugger.Core/Module.cs b/src/AddIns/Debugger/Debugger.Core/Module.cs
index cea0eeac7d..dafb26bbb9 100644
--- a/src/AddIns/Debugger/Debugger.Core/Module.cs
+++ b/src/AddIns/Debugger/Debugger.Core/Module.cs
@@ -93,22 +93,22 @@ namespace Debugger
}
[Debugger.Tests.Ignore]
- public ulong BaseAdress {
+ public ulong BaseAdress {
get {
return this.CorModule.GetBaseAddress();
- }
+ }
}
- public bool IsDynamic {
+ public bool IsDynamic {
get {
return this.CorModule.IsDynamic() == 1;
- }
+ }
}
- public bool IsInMemory {
+ public bool IsInMemory {
get {
return this.CorModule.IsInMemory() == 1;
- }
+ }
}
internal uint AppDomainID {
@@ -124,19 +124,19 @@ namespace Debugger
}
[Debugger.Tests.Ignore]
- public string FullPath {
+ public string FullPath {
get {
return fullPath;
- }
+ }
}
- public bool HasSymbols {
+ public bool HasSymbols {
get {
return symReader != null;
- }
+ }
}
- public int OrderOfLoading {
+ public int OrderOfLoading {
get {
return orderOfLoading;
}
@@ -145,6 +145,22 @@ namespace Debugger
}
}
+ [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);
+ }
+ }
+
/// Returns all non-generic types defined in the module
/// Generic types can not be returned, because we do not know how to instanciate them
public List GetDefinedTypes()
@@ -183,6 +199,8 @@ namespace Debugger
name = System.IO.Path.GetFileName(FullPath);
}
+ SetJITCompilerFlags();
+
LoadSymbolsFromDisk(process.Options.SymbolsSearchPaths);
ResetJustMyCodeStatus();
}
@@ -268,7 +286,7 @@ namespace Debugger
}
}
- void SetBreakpoints()
+ void SetBreakpoints()
{
if (this.HasSymbols) {
// This is in case that the client modifies the collection as a response to set breakpoint
@@ -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));
+ }
+ }
+ }
+
/// Sets all code as being 'my code'. The code will be gradually
/// set to not-user-code as encountered acording to stepping options
public void ResetJustMyCodeStatus()
@@ -319,6 +365,29 @@ namespace Debugger
{
return string.Format("{0}", this.Name);
}
+
+ public static CorDebugJITCompilerFlags MapDebugModeToJITCompilerFlags(DebugModeFlag debugMode)
+ {
+ CorDebugJITCompilerFlags jcf;
+ switch (debugMode)
+ {
+ case DebugModeFlag.Optimized:
+ jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DEFAULT; // DEFAULT really means force optimized.
+ break;
+ case DebugModeFlag.Debug:
+ jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION;
+ break;
+ case DebugModeFlag.Enc:
+ jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_ENABLE_ENC;
+ break;
+ default:
+ // we don't have mapping from default to "default",
+ // therefore we'll use DISABLE_OPTIMIZATION.
+ jcf = CorDebugJITCompilerFlags.CORDEBUG_JIT_DISABLE_OPTIMIZATION;
+ break;
+ }
+ return jcf;
+ }
}
[Serializable]
diff --git a/src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs b/src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs
index 063d1a5298..3002d6f916 100644
--- a/src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs
+++ b/src/AddIns/Debugger/Debugger.Core/NRefactory/Visitors/ExpressionEvaluator.cs
@@ -71,29 +71,33 @@ namespace ICSharpCode.NRefactory.Visitors
/// Evaluate given expression. If you have expression tree already, use overloads of this method.
/// Returned value or null for statements
- public static Value Evaluate(string code, SupportedLanguage language, StackFrame context)
+ public static Value Evaluate(string code, SupportedLanguage language, StackFrame context, object data = null)
{
- return Evaluate(Parse(code, language), context);
+ return Evaluate(Parse(code, language), context, data);
}
public static Value Evaluate(INode code, Process context)
{
- if (context.SelectedStackFrame != null) {
- return Evaluate(code, context.SelectedStackFrame);
- } else if (context.SelectedThread.MostRecentStackFrame != null ) {
- return Evaluate(code, context.SelectedThread.MostRecentStackFrame);
- } else {
+ StackFrame stackFrame;
+ if (context.SelectedStackFrame == null && context.SelectedThread.MostRecentStackFrame == null)
// This can happen when needed 'dll' is missing. This causes an exception dialog to be shown even before the applicaiton starts
throw new GetValueException("Can not evaluate because the process has no managed stack frames");
- }
+
+ if (context.SelectedStackFrame.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.IsInvalid) throw new DebuggerException("The context is no longer valid");
- TypedValue val = new ExpressionEvaluator(context).Evaluate(code, false);
+ TypedValue val = new ExpressionEvaluator(context).Evaluate(code, false, data);
if (val == null)
return null;
return val.Value;
@@ -164,7 +168,7 @@ namespace ICSharpCode.NRefactory.Visitors
return Evaluate(expression, true);
}
- TypedValue Evaluate(INode expression, bool permRef)
+ TypedValue Evaluate(INode expression, bool permRef, object data = null)
{
// Try to get the value from cache
// (the cache is cleared when the process is resumed)
@@ -177,7 +181,7 @@ namespace ICSharpCode.NRefactory.Visitors
System.Diagnostics.Stopwatch watch = new System.Diagnostics.Stopwatch();
watch.Start();
try {
- val = (TypedValue)expression.AcceptVisitor(this, null);
+ val = (TypedValue)expression.AcceptVisitor(this, data);
if (val != null && permRef)
val = new TypedValue(val.Value.GetPermanentReference(), val.Type);
} catch (GetValueException e) {
@@ -277,21 +281,21 @@ namespace ICSharpCode.NRefactory.Visitors
{
BinaryOperatorType op;
switch (assignmentExpression.Op) {
- case AssignmentOperatorType.Assign: op = BinaryOperatorType.None; break;
- case AssignmentOperatorType.Add: op = BinaryOperatorType.Add; break;
- case AssignmentOperatorType.ConcatString: op = BinaryOperatorType.Concat; break;
- case AssignmentOperatorType.Subtract: op = BinaryOperatorType.Subtract; break;
- case AssignmentOperatorType.Multiply: op = BinaryOperatorType.Multiply; break;
- case AssignmentOperatorType.Divide: op = BinaryOperatorType.Divide; break;
- case AssignmentOperatorType.DivideInteger: op = BinaryOperatorType.DivideInteger; break;
- case AssignmentOperatorType.ShiftLeft: op = BinaryOperatorType.ShiftLeft; break;
- case AssignmentOperatorType.ShiftRight: op = BinaryOperatorType.ShiftRight; break;
- case AssignmentOperatorType.ExclusiveOr: op = BinaryOperatorType.ExclusiveOr; break;
- case AssignmentOperatorType.Modulus: op = BinaryOperatorType.Modulus; break;
- case AssignmentOperatorType.BitwiseAnd: op = BinaryOperatorType.BitwiseAnd; break;
- case AssignmentOperatorType.BitwiseOr: op = BinaryOperatorType.BitwiseOr; break;
- case AssignmentOperatorType.Power: op = BinaryOperatorType.Power; break;
- default: throw new GetValueException("Unknown operator " + assignmentExpression.Op);
+ case AssignmentOperatorType.Assign: op = BinaryOperatorType.None; break;
+ case AssignmentOperatorType.Add: op = BinaryOperatorType.Add; break;
+ case AssignmentOperatorType.ConcatString: op = BinaryOperatorType.Concat; break;
+ case AssignmentOperatorType.Subtract: op = BinaryOperatorType.Subtract; break;
+ case AssignmentOperatorType.Multiply: op = BinaryOperatorType.Multiply; break;
+ case AssignmentOperatorType.Divide: op = BinaryOperatorType.Divide; break;
+ case AssignmentOperatorType.DivideInteger: op = BinaryOperatorType.DivideInteger; break;
+ case AssignmentOperatorType.ShiftLeft: op = BinaryOperatorType.ShiftLeft; break;
+ case AssignmentOperatorType.ShiftRight: op = BinaryOperatorType.ShiftRight; break;
+ case AssignmentOperatorType.ExclusiveOr: op = BinaryOperatorType.ExclusiveOr; break;
+ case AssignmentOperatorType.Modulus: op = BinaryOperatorType.Modulus; break;
+ case AssignmentOperatorType.BitwiseAnd: op = BinaryOperatorType.BitwiseAnd; break;
+ case AssignmentOperatorType.BitwiseOr: op = BinaryOperatorType.BitwiseOr; break;
+ case AssignmentOperatorType.Power: op = BinaryOperatorType.Power; break;
+ default: throw new GetValueException("Unknown operator " + assignmentExpression.Op);
}
TypedValue right;
@@ -382,6 +386,14 @@ namespace ICSharpCode.NRefactory.Visitors
if (loc != null)
return new TypedValue(loc.GetValue(context), (DebugType)loc.LocalType);
+ // try get local var from external information - data or UserData
+ int[] localIndex = (data ?? identifierExpression.UserData) as int[];
+
+ if (localIndex != null) {
+ Value localValue = DebugMethodInfo.GetLocalVariableValue(context, localIndex[0]);
+ return new TypedValue(localValue, localValue.Type);
+ }
+
// Instance class members
// Note that the method might be generated instance method that represents anonymous method
TypedValue thisValue = GetThisValue();
@@ -554,7 +566,14 @@ namespace ICSharpCode.NRefactory.Visitors
DebugLocalVariableInfo thisVar = context.MethodInfo.GetLocalVariableThis();
if (thisVar != null)
return new TypedValue(thisVar.GetValue(context), (DebugType)thisVar.LocalType);
- return null;
+
+ // when symbols are not present
+ try {
+ return new TypedValue(context.GetThisValue(), (DebugType)context.MethodInfo.DeclaringType);
+ } catch (GetValueException) {
+ // static method
+ return null;
+ }
}
public override object VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, object data)
@@ -640,7 +659,7 @@ namespace ICSharpCode.NRefactory.Visitors
}
}
}
-
+
throw new EvaluateException(unaryOperatorExpression, "Can not use the unary operator {0} on type {1}", op.ToString(), value.Type.FullName);
}
@@ -654,57 +673,57 @@ namespace ICSharpCode.NRefactory.Visitors
if (argType == typeof(bool)) {
bool a = (bool)val;
switch (op) {
- case UnaryOperatorType.Not: return !a;
+ case UnaryOperatorType.Not: return !a;
}
}
if (argType == typeof(float)) {
float a = (float)val;
switch (op) {
- case UnaryOperatorType.Minus: return -a;
- case UnaryOperatorType.Plus: return +a;
+ case UnaryOperatorType.Minus: return -a;
+ case UnaryOperatorType.Plus: return +a;
}
}
if (argType == typeof(double)) {
double a = (double)val;
switch (op) {
- case UnaryOperatorType.Minus: return -a;
- case UnaryOperatorType.Plus: return +a;
+ case UnaryOperatorType.Minus: return -a;
+ case UnaryOperatorType.Plus: return +a;
}
}
-
+
if (argType == typeof(int)) {
int a = (int)val;
switch (op) {
- case UnaryOperatorType.Minus: return -a;
- case UnaryOperatorType.Plus: return +a;
- case UnaryOperatorType.BitNot: return ~a;
+ case UnaryOperatorType.Minus: return -a;
+ case UnaryOperatorType.Plus: return +a;
+ case UnaryOperatorType.BitNot: return ~a;
}
}
if (argType == typeof(uint)) {
uint a = (uint)val;
switch (op) {
- case UnaryOperatorType.Plus: return +a;
- case UnaryOperatorType.BitNot: return ~a;
+ case UnaryOperatorType.Plus: return +a;
+ case UnaryOperatorType.BitNot: return ~a;
}
}
if (argType == typeof(long)) {
long a = (long)val;
switch (op) {
- case UnaryOperatorType.Minus: return -a;
- case UnaryOperatorType.Plus: return +a;
- case UnaryOperatorType.BitNot: return ~a;
+ case UnaryOperatorType.Minus: return -a;
+ case UnaryOperatorType.Plus: return +a;
+ case UnaryOperatorType.BitNot: return ~a;
}
}
if (argType == typeof(ulong)) {
ulong a = (ulong)val;
switch (op) {
- case UnaryOperatorType.Plus: return +a;
- case UnaryOperatorType.BitNot: return ~a;
+ case UnaryOperatorType.Plus: return +a;
+ case UnaryOperatorType.BitNot: return ~a;
}
}
}
@@ -789,8 +808,8 @@ namespace ICSharpCode.NRefactory.Visitors
// void operator +(ulong x, long y);
// float operator +(float x, float y);
// double operator +(double x, double y);
- //
- // &, |, ^, &&, ||
+ //
+ // &, |, ^, &&, ||
// ==, !=
// bool operator &(bool x, bool y);
@@ -834,9 +853,9 @@ namespace ICSharpCode.NRefactory.Visitors
string a = (string)left;
string b = (string)right;
switch (op) {
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.Add: return a + b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.Add: return a + b;
}
}
@@ -844,13 +863,13 @@ namespace ICSharpCode.NRefactory.Visitors
bool a = (bool)left;
bool b = (bool)right;
switch (op) {
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.ExclusiveOr: return a ^ b;
- case BinaryOperatorType.BitwiseAnd: return a & b;
- case BinaryOperatorType.BitwiseOr: return a | b;
- case BinaryOperatorType.LogicalAnd: return a && b;
- case BinaryOperatorType.LogicalOr: return a || b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.ExclusiveOr: return a ^ b;
+ case BinaryOperatorType.BitwiseAnd: return a & b;
+ case BinaryOperatorType.BitwiseOr: return a | b;
+ case BinaryOperatorType.LogicalAnd: return a && b;
+ case BinaryOperatorType.LogicalOr: return a || b;
}
}
@@ -858,19 +877,19 @@ namespace ICSharpCode.NRefactory.Visitors
float a = (float)left;
float b = (float)right;
switch (op) {
- case BinaryOperatorType.GreaterThan: return a > b;
- case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.LessThan: return a < b;
- case BinaryOperatorType.LessThanOrEqual: return a <= b;
-
- case BinaryOperatorType.Add: return a + b;
- case BinaryOperatorType.Subtract: return a - b;
- case BinaryOperatorType.Multiply: return a * b;
- case BinaryOperatorType.Divide: return a / b;
- case BinaryOperatorType.Modulus: return a % b;
- case BinaryOperatorType.Concat: return a + b;
+ case BinaryOperatorType.GreaterThan: return a > b;
+ case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.LessThan: return a < b;
+ case BinaryOperatorType.LessThanOrEqual: return a <= b;
+
+ case BinaryOperatorType.Add: return a + b;
+ case BinaryOperatorType.Subtract: return a - b;
+ case BinaryOperatorType.Multiply: return a * b;
+ case BinaryOperatorType.Divide: return a / b;
+ case BinaryOperatorType.Modulus: return a % b;
+ case BinaryOperatorType.Concat: return a + b;
}
}
@@ -878,134 +897,134 @@ namespace ICSharpCode.NRefactory.Visitors
double a = (double)left;
double b = (double)right;
switch (op) {
- case BinaryOperatorType.GreaterThan: return a > b;
- case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.LessThan: return a < b;
- case BinaryOperatorType.LessThanOrEqual: return a <= b;
-
- case BinaryOperatorType.Add: return a + b;
- case BinaryOperatorType.Subtract: return a - b;
- case BinaryOperatorType.Multiply: return a * b;
- case BinaryOperatorType.Divide: return a / b;
- case BinaryOperatorType.Modulus: return a % b;
- case BinaryOperatorType.Concat: return a + b;
+ case BinaryOperatorType.GreaterThan: return a > b;
+ case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.LessThan: return a < b;
+ case BinaryOperatorType.LessThanOrEqual: return a <= b;
+
+ case BinaryOperatorType.Add: return a + b;
+ case BinaryOperatorType.Subtract: return a - b;
+ case BinaryOperatorType.Multiply: return a * b;
+ case BinaryOperatorType.Divide: return a / b;
+ case BinaryOperatorType.Modulus: return a % b;
+ case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(int)) {
switch (op) {
- case BinaryOperatorType.ShiftLeft: return (int)left << (int)right;
- case BinaryOperatorType.ShiftRight: return (int)left >> (int)right;
+ case BinaryOperatorType.ShiftLeft: return (int)left << (int)right;
+ case BinaryOperatorType.ShiftRight: return (int)left >> (int)right;
}
int a = (int)left;
int b = (int)right;
switch (op) {
- case BinaryOperatorType.BitwiseAnd: return a & b;
- case BinaryOperatorType.BitwiseOr: return a | b;
- case BinaryOperatorType.ExclusiveOr: return a ^ b;
-
- case BinaryOperatorType.GreaterThan: return a > b;
- case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.LessThan: return a < b;
- case BinaryOperatorType.LessThanOrEqual: return a <= b;
-
- case BinaryOperatorType.Add: return a + b;
- case BinaryOperatorType.Subtract: return a - b;
- case BinaryOperatorType.Multiply: return a * b;
- case BinaryOperatorType.Divide: return a / b;
- case BinaryOperatorType.Modulus: return a % b;
- case BinaryOperatorType.Concat: return a + b;
+ case BinaryOperatorType.BitwiseAnd: return a & b;
+ case BinaryOperatorType.BitwiseOr: return a | b;
+ case BinaryOperatorType.ExclusiveOr: return a ^ b;
+
+ case BinaryOperatorType.GreaterThan: return a > b;
+ case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.LessThan: return a < b;
+ case BinaryOperatorType.LessThanOrEqual: return a <= b;
+
+ case BinaryOperatorType.Add: return a + b;
+ case BinaryOperatorType.Subtract: return a - b;
+ case BinaryOperatorType.Multiply: return a * b;
+ case BinaryOperatorType.Divide: return a / b;
+ case BinaryOperatorType.Modulus: return a % b;
+ case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(uint)) {
switch (op) {
- case BinaryOperatorType.ShiftLeft: return (uint)left << (int)right;
- case BinaryOperatorType.ShiftRight: return (uint)left >> (int)right;
+ case BinaryOperatorType.ShiftLeft: return (uint)left << (int)right;
+ case BinaryOperatorType.ShiftRight: return (uint)left >> (int)right;
}
uint a = (uint)left;
uint b = (uint)right;
switch (op) {
- case BinaryOperatorType.BitwiseAnd: return a & b;
- case BinaryOperatorType.BitwiseOr: return a | b;
- case BinaryOperatorType.ExclusiveOr: return a ^ b;
-
- case BinaryOperatorType.GreaterThan: return a > b;
- case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.LessThan: return a < b;
- case BinaryOperatorType.LessThanOrEqual: return a <= b;
-
- case BinaryOperatorType.Add: return a + b;
- case BinaryOperatorType.Subtract: return a - b;
- case BinaryOperatorType.Multiply: return a * b;
- case BinaryOperatorType.Divide: return a / b;
- case BinaryOperatorType.Modulus: return a % b;
- case BinaryOperatorType.Concat: return a + b;
+ case BinaryOperatorType.BitwiseAnd: return a & b;
+ case BinaryOperatorType.BitwiseOr: return a | b;
+ case BinaryOperatorType.ExclusiveOr: return a ^ b;
+
+ case BinaryOperatorType.GreaterThan: return a > b;
+ case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.LessThan: return a < b;
+ case BinaryOperatorType.LessThanOrEqual: return a <= b;
+
+ case BinaryOperatorType.Add: return a + b;
+ case BinaryOperatorType.Subtract: return a - b;
+ case BinaryOperatorType.Multiply: return a * b;
+ case BinaryOperatorType.Divide: return a / b;
+ case BinaryOperatorType.Modulus: return a % b;
+ case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(long)) {
switch (op) {
- case BinaryOperatorType.ShiftLeft: return (long)left << (int)right;
- case BinaryOperatorType.ShiftRight: return (long)left >> (int)right;
+ case BinaryOperatorType.ShiftLeft: return (long)left << (int)right;
+ case BinaryOperatorType.ShiftRight: return (long)left >> (int)right;
}
long a = (long)left;
long b = (long)right;
switch (op) {
- case BinaryOperatorType.BitwiseAnd: return a & b;
- case BinaryOperatorType.BitwiseOr: return a | b;
- case BinaryOperatorType.ExclusiveOr: return a ^ b;
-
- case BinaryOperatorType.GreaterThan: return a > b;
- case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.LessThan: return a < b;
- case BinaryOperatorType.LessThanOrEqual: return a <= b;
-
- case BinaryOperatorType.Add: return a + b;
- case BinaryOperatorType.Subtract: return a - b;
- case BinaryOperatorType.Multiply: return a * b;
- case BinaryOperatorType.Divide: return a / b;
- case BinaryOperatorType.Modulus: return a % b;
- case BinaryOperatorType.Concat: return a + b;
+ case BinaryOperatorType.BitwiseAnd: return a & b;
+ case BinaryOperatorType.BitwiseOr: return a | b;
+ case BinaryOperatorType.ExclusiveOr: return a ^ b;
+
+ case BinaryOperatorType.GreaterThan: return a > b;
+ case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.LessThan: return a < b;
+ case BinaryOperatorType.LessThanOrEqual: return a <= b;
+
+ case BinaryOperatorType.Add: return a + b;
+ case BinaryOperatorType.Subtract: return a - b;
+ case BinaryOperatorType.Multiply: return a * b;
+ case BinaryOperatorType.Divide: return a / b;
+ case BinaryOperatorType.Modulus: return a % b;
+ case BinaryOperatorType.Concat: return a + b;
}
}
if (argTypes == typeof(ulong)) {
switch (op) {
- case BinaryOperatorType.ShiftLeft: return (ulong)left << (int)right;
- case BinaryOperatorType.ShiftRight: return (ulong)left >> (int)right;
+ case BinaryOperatorType.ShiftLeft: return (ulong)left << (int)right;
+ case BinaryOperatorType.ShiftRight: return (ulong)left >> (int)right;
}
ulong a = (ulong)left;
ulong b = (ulong)right;
switch (op) {
- case BinaryOperatorType.BitwiseAnd: return a & b;
- case BinaryOperatorType.BitwiseOr: return a | b;
- case BinaryOperatorType.ExclusiveOr: return a ^ b;
-
- case BinaryOperatorType.GreaterThan: return a > b;
- case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
- case BinaryOperatorType.Equality: return a == b;
- case BinaryOperatorType.InEquality: return a != b;
- case BinaryOperatorType.LessThan: return a < b;
- case BinaryOperatorType.LessThanOrEqual: return a <= b;
-
- case BinaryOperatorType.Add: return a + b;
- case BinaryOperatorType.Subtract: return a - b;
- case BinaryOperatorType.Multiply: return a * b;
- case BinaryOperatorType.Divide: return a / b;
- case BinaryOperatorType.Modulus: return a % b;
- case BinaryOperatorType.Concat: return a + b;
+ case BinaryOperatorType.BitwiseAnd: return a & b;
+ case BinaryOperatorType.BitwiseOr: return a | b;
+ case BinaryOperatorType.ExclusiveOr: return a ^ b;
+
+ case BinaryOperatorType.GreaterThan: return a > b;
+ case BinaryOperatorType.GreaterThanOrEqual: return a >= b;
+ case BinaryOperatorType.Equality: return a == b;
+ case BinaryOperatorType.InEquality: return a != b;
+ case BinaryOperatorType.LessThan: return a < b;
+ case BinaryOperatorType.LessThanOrEqual: return a <= b;
+
+ case BinaryOperatorType.Add: return a + b;
+ case BinaryOperatorType.Subtract: return a - b;
+ case BinaryOperatorType.Multiply: return a * b;
+ case BinaryOperatorType.Divide: return a / b;
+ case BinaryOperatorType.Modulus: return a % b;
+ case BinaryOperatorType.Concat: return a + b;
}
}
-
+
return null;
}
}
diff --git a/src/AddIns/Debugger/Debugger.Core/Options.cs b/src/AddIns/Debugger/Debugger.Core/Options.cs
index 72a84ae3e7..43d6f64b31 100644
--- a/src/AddIns/Debugger/Debugger.Core/Options.cs
+++ b/src/AddIns/Debugger/Debugger.Core/Options.cs
@@ -5,14 +5,15 @@ namespace Debugger
{
public class Options
{
- public bool EnableJustMyCode = true;
- public bool StepOverNoSymbols = true;
+ public bool EnableJustMyCode = false;
+ public bool StepOverNoSymbols = false;
public bool StepOverDebuggerAttributes = true;
public bool StepOverAllProperties = false;
public bool StepOverSingleLineProperties = false;
public bool StepOverFieldAccessProperties = true;
public bool Verbose = false;
public string[] SymbolsSearchPaths = new string[0];
- public bool SuspendOtherThreads = true;
+ public bool SuspendOtherThreads = false;
+ public bool EnableEditAndContinue = false;
}
}
diff --git a/src/AddIns/Debugger/Debugger.Core/Process.cs b/src/AddIns/Debugger/Debugger.Core/Process.cs
index b788876e84..bd47e6f2b5 100644
--- a/src/AddIns/Debugger/Debugger.Core/Process.cs
+++ b/src/AddIns/Debugger/Debugger.Core/Process.cs
@@ -14,6 +14,29 @@ namespace Debugger
{
internal enum DebuggeeStateAction { Keep, Clear }
+ ///
+ /// Debug Mode Flags.
+ ///
+ public enum DebugModeFlag
+ {
+ ///
+ /// Run in the same mode as without debugger.
+ ///
+ Default,
+ ///
+ /// Run in forced optimized mode.
+ ///
+ Optimized,
+ ///
+ /// Run in debug mode (easy inspection) but slower.
+ ///
+ Debug,
+ ///
+ /// Run in ENC mode (ENC possible) but even slower than debug
+ ///
+ Enc
+ }
+
public class Process: DebuggerObject
{
NDebugger debugger;
@@ -28,6 +51,7 @@ namespace Debugger
string workingDirectory;
+
public NDebugger Debugger {
get { return debugger; }
}
@@ -108,6 +132,8 @@ namespace Debugger
get { return workingDirectory; }
}
+ public static DebugModeFlag DebugMode { get; set; }
+
internal Process(NDebugger debugger, ICorDebugProcess corProcess, string workingDirectory)
{
this.debugger = debugger;
@@ -660,13 +686,18 @@ namespace Debugger
breakpoint.Remove();
breakpoint = null;
};
- } catch {
+ } catch {
// the app does not have an entry point - COM exception
}
BreakAtBeginning = false;
}
+
+ if (ModulesAdded != null)
+ ModulesAdded(this, new ModuleEventArgs(e.Item));
}
#endregion
+
+ public event EventHandler ModulesAdded;
}
}
diff --git a/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs b/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs
index d9150899e8..459d34451e 100644
--- a/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs
+++ b/src/AddIns/Debugger/Debugger.Core/SourcecodeSegment.cs
@@ -16,6 +16,7 @@ namespace Debugger
Module module;
string filename;
+ string typename;
byte[] checkSum;
int startLine;
int startColumn;
@@ -35,6 +36,10 @@ namespace Debugger
get { return filename; }
}
+ public string Typename {
+ get { return typename; }
+ }
+
public byte[] CheckSum {
get { return checkSum; }
}
@@ -99,7 +104,7 @@ namespace Debugger
yield return symUrl;
yield break;
}
-
+
if (Path.IsPathRooted(symUrl)) {
Dictionary returned = new Dictionary();
@@ -207,7 +212,7 @@ namespace Debugger
segment.corFunction = module.CorModule.GetFunctionFromToken(symMethod.GetToken());
segment.ilStart = (int)sqPoint.Offset;
segment.ilEnd = (int)sqPoint.Offset;
- segment.stepRanges = null;
+ segment.stepRanges = null;
return segment;
}
}
@@ -336,7 +341,57 @@ namespace Debugger
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
}
}
diff --git a/src/AddIns/Debugger/Debugger.Core/StackFrame.cs b/src/AddIns/Debugger/Debugger.Core/StackFrame.cs
index 2f0858c23f..05f6b00579 100644
--- a/src/AddIns/Debugger/Debugger.Core/StackFrame.cs
+++ b/src/AddIns/Debugger/Debugger.Core/StackFrame.cs
@@ -16,7 +16,7 @@ namespace Debugger
/// Use to obtain arguments or local variables.
///
public class StackFrame: DebuggerObject
- {
+ {
Thread thread;
AppDomain appDomain;
Process process;
@@ -59,7 +59,7 @@ namespace Debugger
}
- /// True if the stack frame has symbols defined.
+ /// True if the stack frame has symbols defined.
/// (That is has accesss to the .pdb file)
public bool HasSymbols {
get {
@@ -139,8 +139,14 @@ namespace Debugger
}
}
+ public int[] ILRanges { get; set; }
+
+ public int SourceCodeLine { get; set; }
+
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);
}
@@ -187,17 +193,23 @@ namespace Debugger
void AsyncStep(bool stepIn)
{
- if (this.MethodInfo.DebugModule.HasSymbols == false) {
+ if (this.MethodInfo.DebugModule.HasSymbols == false && process.Options.StepOverNoSymbols) {
throw new DebuggerException("Unable to step. No symbols loaded.");
}
- SourcecodeSegment nextSt = NextStatement;
- if (nextSt == null) {
- throw new DebuggerException("Unable to step. Next statement not aviable");
+ int[] stepRanges;
+ if (ILRanges == null) {
+ SourcecodeSegment nextSt = NextStatement;
+ if (nextSt == null) {
+ throw new DebuggerException("Unable to step. Next statement not aviable");
+ }
+ stepRanges = nextSt.StepRanges;
+ } else {
+ stepRanges = ILRanges;
}
if (stepIn) {
- Stepper stepInStepper = Stepper.StepIn(this, nextSt.StepRanges, "normal");
+ Stepper stepInStepper = Stepper.StepIn(this, stepRanges, "normal");
this.Thread.CurrentStepIn = stepInStepper;
Stepper clearCurrentStepIn = Stepper.StepOut(this, "clear current step in");
clearCurrentStepIn.StepComplete += delegate {
@@ -207,7 +219,7 @@ namespace Debugger
};
clearCurrentStepIn.Ignore = true;
} else {
- Stepper.StepOver(this, nextSt.StepRanges, "normal");
+ Stepper.StepOver(this, stepRanges, "normal");
}
AsyncContinue();
@@ -276,7 +288,7 @@ namespace Debugger
return null;
}
- ///
+ ///
/// Gets the instance of the class asociated with the current frame.
/// That is, 'this' in C#.
/// Note that for delegates and enumerators this returns the instance of the display class.
@@ -397,8 +409,8 @@ namespace Debugger
{
int hashCode = 0;
unchecked {
- if (thread != null) hashCode += 1000000009 * thread.GetHashCode();
- if (methodInfo != null) hashCode += 1000000093 * methodInfo.GetHashCode();
+ if (thread != null) hashCode += 1000000009 * thread.GetHashCode();
+ if (methodInfo != null) hashCode += 1000000093 * methodInfo.GetHashCode();
hashCode += 1000000097 * chainIndex.GetHashCode();
hashCode += 1000000103 * frameIndex.GetHashCode();
}
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
index 180bc7e13e..a0a634c413 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/CodeEditor.cs
@@ -31,11 +31,20 @@ using ICSharpCode.SharpDevelop.Editor.CodeCompletion;
namespace ICSharpCode.AvalonEdit.AddIn
{
+ public interface ICodeEditor
+ {
+ TextDocument Document { get; }
+
+ void Redraw(ISegment segment, DispatcherPriority priority);
+
+ event EventHandler DocumentChanged;
+ }
+
///
/// Integrates AvalonEdit with SharpDevelop.
/// Also provides support for Split-View (showing two AvalonEdit instances using the same TextDocument)
///
- public class CodeEditor : Grid, IDisposable
+ public class CodeEditor : Grid, IDisposable, ICodeEditor
{
const string contextMenuPath = "/SharpDevelop/ViewContent/AvalonEdit/ContextMenu";
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarManager.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarManager.cs
index 86eccd0125..9ec975baa9 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarManager.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarManager.cs
@@ -40,7 +40,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
public event EventHandler RedrawRequested;
- internal void UpdateClassMemberBookmarks(ParseInformation parseInfo)
+ public void UpdateClassMemberBookmarks(ParseInformation parseInfo)
{
for (int i = bookmarks.Count - 1; i >= 0; i--) {
if (IsClassMemberBookmark(bookmarks[i]))
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs
index aa17b024ec..20cd1bf9f3 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/IconBarMargin.cs
@@ -20,9 +20,9 @@ namespace ICSharpCode.AvalonEdit.AddIn
///
public class IconBarMargin : AbstractMargin, IDisposable
{
- readonly IconBarManager manager;
+ readonly IBookmarkMargin manager;
- public IconBarMargin(IconBarManager manager)
+ public IconBarMargin(IBookmarkMargin manager)
{
if (manager == null)
throw new ArgumentNullException("manager");
diff --git a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
index 7fb7e03ef2..83a5ce5208 100644
--- a/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
+++ b/src/AddIns/DisplayBindings/AvalonEdit.AddIn/Src/TextMarkerService.cs
@@ -18,12 +18,12 @@ namespace ICSharpCode.AvalonEdit.AddIn
///
/// Handles the text markers for a code editor.
///
- sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService
+ public sealed class TextMarkerService : DocumentColorizingTransformer, IBackgroundRenderer, ITextMarkerService
{
- readonly CodeEditor codeEditor;
+ readonly ICodeEditor codeEditor;
TextSegmentCollection markers;
- public TextMarkerService(CodeEditor codeEditor)
+ public TextMarkerService(ICodeEditor codeEditor)
{
if (codeEditor == null)
throw new ArgumentNullException("codeEditor");
@@ -200,7 +200,7 @@ namespace ICSharpCode.AvalonEdit.AddIn
#endregion
}
- sealed class TextMarker : TextSegment, ITextMarker
+ public sealed class TextMarker : TextSegment, ITextMarker
{
readonly TextMarkerService service;
diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj
index e2d4ae0e46..415cbd3c4f 100644
--- a/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj
+++ b/src/AddIns/DisplayBindings/ILSpyAddIn/ILSpyAddIn.csproj
@@ -99,6 +99,10 @@
{3B2A5653-EC97-4001-BB9B-D90F1AF2C371}ICSharpCode.NRefactory
+
+ {3A9AE6AA-BC07-4A2F-972C-581E3AE2F195}
+ NRefactory
+ {2748AD25-9C63-4E12-877B-4DCE96FBED54}ICSharpCode.SharpDevelop
diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs
index e4a0f88884..4304c15231 100644
--- a/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs
+++ b/src/AddIns/DisplayBindings/ILSpyAddIn/NavigateToDecompiledEntityService.cs
@@ -4,15 +4,18 @@
using System;
using System.IO;
using System.Linq;
+
+using ICSharpCode.Core.Services;
using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Debugging;
using ICSharpCode.SharpDevelop.Dom;
using ICSharpCode.SharpDevelop.Gui;
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)
throw new ArgumentNullException("entity");
@@ -41,6 +44,12 @@ namespace ICSharpCode.ILSpyAddIn
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()) {
if (string.Equals(vc.AssemblyFile, assemblyFile, StringComparison.OrdinalIgnoreCase) && typeName == vc.FullTypeName) {
vc.WorkbenchWindow.SelectWindow();
@@ -50,5 +59,18 @@ namespace ICSharpCode.ILSpyAddIn
}
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()) {
+ 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;
+ }
}
}
diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs
index 1ff758072a..4e8dcb56ad 100644
--- a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/CodeView.cs
+++ b/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)
using System;
+using System.Diagnostics;
+using System.Windows;
using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Input;
+
+using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.AddIn;
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.AvalonEdit.Highlighting;
+using ICSharpCode.AvalonEdit.Rendering;
+using ICSharpCode.SharpDevelop;
+using ICSharpCode.SharpDevelop.Editor;
+using ICSharpCode.SharpDevelop.Editor.AvalonEdit;
namespace ICSharpCode.ILSpyAddIn.ViewContent
{
///
/// Equivalent to AE.AddIn CodeEditor, but without editing capabilities.
///
- 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()
{
- this.Children.Add(textEditor);
- textEditor.SyntaxHighlighting = HighlightingManager.Instance.GetDefinition("C#");
+ this.Children.Add(adapter.TextEditor);
+ 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;
+ }
+
+ /// Returns Popup position based on mouse position, in device independent units
+ Point GetPopupPosition(MouseEventArgs mouseArgs)
+ {
+ Point mousePos = mouseArgs.GetPosition(this);
+ Point positionInPixels;
+ // align Popup with line bottom
+ TextViewPosition? logicalPos = adapter.TextEditor.GetPositionFromPoint(mousePos);
+ if (logicalPos.HasValue) {
+ var textView = adapter.TextEditor.TextArea.TextView;
+ positionInPixels =
+ textView.PointToScreen(
+ textView.GetVisualPosition(logicalPos.Value, VisualYPosition.LineBottom) - textView.ScrollOffset);
+ positionInPixels.X -= 4;
+ } else {
+ positionInPixels = PointToScreen(mousePos + new Vector(-4, 6));
+ }
+ // use device independent units, because Popup Left/Top are in independent units
+ return positionInPixels.TransformFromDevice(this);
+ }
+
+ Popup CreatePopup()
+ {
+ popup = new Popup();
+ popup.Closed += (s, e) => popup = null;
+ popup.AllowsTransparency = true;
+ popup.PlacementTarget = this; // required for property inheritance
+ popup.Placement = PlacementMode.Absolute;
+ popup.StaysOpen = true;
+ return popup;
}
+ void TextEditorMouseHoverStopped(object sender, MouseEventArgs e)
+ {
+ if (toolTip != null) {
+ toolTip.IsOpen = false;
+ e.Handled = true;
+ }
+
+ TextEditorMouseLeave(sender, e);
+ }
+
+ void TextEditorMouseLeave(object sender, MouseEventArgs e)
+ {
+ if (popup != null && !popup.IsMouseOver) {
+ // do not close popup if mouse moved from editor to popup
+ TryCloseExistingPopup(false);
+ }
+ }
+
+ #endregion
+
public TextDocument Document {
- get { return textEditor.Document; }
- set { textEditor.Document = value; }
+ get { return adapter.TextEditor.Document; }
+ set {
+ adapter.TextEditor.Document = value;
+ if (DocumentChanged != null) {
+ DocumentChanged(value, EventArgs.Empty);
+ }
+ }
+ }
+
+ public ITextEditor TextEditor {
+ get {
+ return adapter;
+ }
+ }
+
+ public IconBarManager IconBarManager {
+ get { return iconBarManager; }
}
public 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);
+ }
}
}
diff --git a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs b/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs
index 304bffa655..8c83ea14a1 100644
--- a/src/AddIns/DisplayBindings/ILSpyAddIn/ViewContent/DecompiledViewContent.cs
+++ b/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)
using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Threading;
+
using ICSharpCode.AvalonEdit.Document;
using ICSharpCode.Core;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast;
+using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.ILSpyAddIn.ViewContent;
+using ICSharpCode.NRefactory.CSharp;
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 Mono.Cecil;
@@ -46,7 +57,9 @@ namespace ICSharpCode.ILSpyAddIn
Thread thread = new Thread(DecompilationThread);
thread.Name = "Decompiler (" + shortTypeName + ")";
thread.Start();
+ BookmarkManager.Removed += BookmarkManager_Removed;
}
+
#endregion
#region Properties
@@ -61,6 +74,10 @@ namespace ICSharpCode.ILSpyAddIn
public override object Control {
get { return codeView; }
}
+
+ public override bool IsReadOnly {
+ get { return true; }
+ }
#endregion
#region Dispose
@@ -68,6 +85,7 @@ namespace ICSharpCode.ILSpyAddIn
{
cancellation.Cancel();
codeView.Dispose();
+ BookmarkManager.Removed -= BookmarkManager_Removed;
base.Dispose();
}
#endregion
@@ -139,6 +157,17 @@ namespace ICSharpCode.ILSpyAddIn
AstBuilder astBuilder = new AstBuilder(context);
astBuilder.AddType(typeDefinition);
astBuilder.GenerateCode(textOutput);
+
+ // save decompilation data
+ var nodes = TreeTraversal
+ .PreOrder((AstNode)astBuilder.CompilationUnit, n => n.Children)
+ .Where(n => n is AttributedNode && n.Annotation>() != null);
+ DebuggerService.ExternalDebugInformation = new DecompileInformation {
+ CodeMappings = astBuilder.CodeMappings,
+ LocalVariables = astBuilder.LocalVariables,
+ DecompiledMemberReferences = astBuilder.DecompiledMemberReferences,
+ AstNodes = nodes
+ };
}
void OnDecompilationFinished(StringWriter output)
@@ -150,6 +179,53 @@ namespace ICSharpCode.ILSpyAddIn
this.decompilationFinished = true;
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
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
index a163213ddf..0927ec865c 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/AstBuilder.cs
+++ b/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.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
@@ -7,6 +26,7 @@ using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;
+
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Ast.Transforms;
using ICSharpCode.Decompiler.ILAst;
@@ -26,10 +46,11 @@ namespace ICSharpCode.Decompiler.Ast
{
None = 0,
IncludeNamespace = 1,
- IncludeTypeParameterDefinitions = 2
+ IncludeTypeParameterDefinitions = 2,
+ DoNotUsePrimitiveTypeNames = 4
}
- public class AstBuilder
+ public class AstBuilder : BaseCodeMappings
{
DecompilerContext context;
CompilationUnit astCompileUnit = new CompilationUnit();
@@ -42,6 +63,10 @@ namespace ICSharpCode.Decompiler.Ast
throw new ArgumentNullException("context");
this.context = context;
this.DecompileMethodBodies = true;
+
+ this.LocalVariables = new ConcurrentDictionary>();
+ this.CodeMappings = new Dictionary>();
+ this.DecompiledMemberReferences = new Dictionary();
}
public static bool MemberIsHidden(MemberReference member, DecompilerSettings settings)
@@ -121,8 +146,28 @@ namespace ICSharpCode.Decompiler.Ast
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");
+ ConvertSecurityAttributes(astCompileUnit, assemblyDefinition, "assembly");
ConvertCustomAttributes(astCompileUnit, assemblyDefinition.MainModule, "module");
+ AddTypeForwarderAttributes(astCompileUnit, assemblyDefinition.MainModule, "assembly");
if (!onlyAssemblyLevel) {
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)
{
if (string.IsNullOrEmpty(name)) {
@@ -192,6 +261,7 @@ namespace ICSharpCode.Decompiler.Ast
/// TypeDeclaration or DelegateDeclaration.
public AttributedNode CreateType(TypeDefinition typeDef)
{
+ // create type
TypeDefinition oldCurrentType = context.CurrentType;
context.CurrentType = typeDef;
TypeDeclaration astType = new TypeDeclaration();
@@ -219,13 +289,6 @@ namespace ICSharpCode.Decompiler.Ast
astType.TypeParameters.AddRange(MakeTypeParameters(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;
if (typeDef.IsEnum) {
long expectedEnumMemberValue = 0;
@@ -238,6 +301,7 @@ namespace ICSharpCode.Decompiler.Ast
}
} else {
EnumMemberDeclaration enumMember = new EnumMemberDeclaration();
+ enumMember.AddAnnotation(field);
enumMember.Name = CleanName(field.Name);
long memberValue = (long)CSharpPrimitiveCast.Cast(TypeCode.Int64, field.Constant, false);
if (forcePrintingInitializers || memberValue != expectedEnumMemberValue) {
@@ -259,6 +323,7 @@ namespace ICSharpCode.Decompiler.Ast
if (m.Name == "Invoke") {
dd.ReturnType = ConvertType(m.ReturnType, m.MethodReturnType);
dd.Parameters.AddRange(MakeParameters(m));
+ ConvertAttributes(dd, m.MethodReturnType, m.Module);
}
}
result = dd;
@@ -302,6 +367,45 @@ namespace ICSharpCode.Decompiler.Ast
return name;
}
+ #region Create TypeOf Expression
+ ///
+ /// Creates a typeof-expression for the specified type.
+ ///
+ public static TypeOfExpression CreateTypeOfExpression(TypeReference type)
+ {
+ return new TypeOfExpression(AddEmptyTypeArgumentsForUnboundGenerics(ConvertType(type)));
+ }
+
+ static AstType AddEmptyTypeArgumentsForUnboundGenerics(AstType type)
+ {
+ TypeReference typeRef = type.Annotation();
+ 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
///
/// Converts a type reference.
@@ -376,39 +480,42 @@ namespace ICSharpCode.Decompiler.Ast
return new PrimitiveType("dynamic");
} else {
if (ns == "System") {
- switch (name) {
- case "SByte":
- return new PrimitiveType("sbyte");
- case "Int16":
- return new PrimitiveType("short");
- case "Int32":
- return new PrimitiveType("int");
- case "Int64":
- return new PrimitiveType("long");
- case "Byte":
- return new PrimitiveType("byte");
- case "UInt16":
- return new PrimitiveType("ushort");
- case "UInt32":
- return new PrimitiveType("uint");
- case "UInt64":
- return new PrimitiveType("ulong");
- case "String":
- return new PrimitiveType("string");
- case "Single":
- return new PrimitiveType("float");
- case "Double":
- return new PrimitiveType("double");
- case "Decimal":
- return new PrimitiveType("decimal");
- case "Char":
- return new PrimitiveType("char");
- case "Boolean":
- return new PrimitiveType("bool");
- case "Void":
- return new PrimitiveType("void");
- case "Object":
- return new PrimitiveType("object");
+ if ((options & ConvertTypeOptions.DoNotUsePrimitiveTypeNames)
+ != ConvertTypeOptions.DoNotUsePrimitiveTypeNames) {
+ switch (name) {
+ case "SByte":
+ return new PrimitiveType("sbyte");
+ case "Int16":
+ return new PrimitiveType("short");
+ case "Int32":
+ return new PrimitiveType("int");
+ case "Int64":
+ return new PrimitiveType("long");
+ case "Byte":
+ return new PrimitiveType("byte");
+ case "UInt16":
+ return new PrimitiveType("ushort");
+ case "UInt32":
+ return new PrimitiveType("uint");
+ case "UInt64":
+ return new PrimitiveType("ulong");
+ case "String":
+ return new PrimitiveType("string");
+ case "Single":
+ return new PrimitiveType("float");
+ case "Double":
+ return new PrimitiveType("double");
+ case "Decimal":
+ return new PrimitiveType("decimal");
+ case "Char":
+ return new PrimitiveType("char");
+ case "Boolean":
+ return new PrimitiveType("bool");
+ case "Void":
+ return new PrimitiveType("void");
+ case "Object":
+ return new PrimitiveType("object");
+ }
}
}
@@ -585,6 +692,15 @@ namespace ICSharpCode.Decompiler.Ast
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
foreach(FieldDefinition fieldDef in typeDef.Fields) {
if (MemberIsHidden(fieldDef, context.Settings)) continue;
@@ -614,22 +730,26 @@ namespace ICSharpCode.Decompiler.Ast
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.ReturnType = ConvertType(methodDef.ReturnType, methodDef.MethodReturnType);
astMethod.Name = CleanName(methodDef.Name);
astMethod.TypeParameters.AddRange(MakeTypeParameters(methodDef.GenericParameters));
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.HasOverrides) {
astMethod.Modifiers = ConvertModifiers(methodDef);
- if (methodDef.IsVirtual ^ !methodDef.IsNewSlot) {
- if (TypesHierarchyHelpers.FindBaseMethods(methodDef).Any())
- astMethod.Modifiers |= Modifiers.New;
- }
- } else
+ if (methodDef.IsVirtual == methodDef.IsNewSlot)
+ SetNewModifier(astMethod);
+ } else {
astMethod.PrivateImplementationType = ConvertType(methodDef.Overrides.First().DeclaringType);
+ }
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
}
ConvertAttributes(astMethod, methodDef);
@@ -699,6 +819,10 @@ namespace ICSharpCode.Decompiler.Ast
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();
astMethod.AddAnnotation(methodDef);
astMethod.Modifiers = ConvertModifiers(methodDef);
@@ -710,6 +834,10 @@ namespace ICSharpCode.Decompiler.Ast
astMethod.Parameters.AddRange(MakeParameters(methodDef));
astMethod.Body = CreateMethodBody(methodDef, astMethod.Parameters);
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;
}
@@ -739,49 +867,71 @@ namespace ICSharpCode.Decompiler.Ast
getterModifiers = ConvertModifiers(propDef.GetMethod);
setterModifiers = ConvertModifiers(propDef.SetMethod);
astProp.Modifiers = FixUpVisibility(getterModifiers | setterModifiers);
- if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) {
- foreach (var basePropDef in TypesHierarchyHelpers.FindBaseProperties(propDef)) {
- if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) {
- var propVisibilityModifiers = ConvertModifiers(basePropDef.GetMethod) | ConvertModifiers(basePropDef.SetMethod);
- astProp.Modifiers = FixUpVisibility((astProp.Modifiers & ~Modifiers.VisibilityMask) | (propVisibilityModifiers & Modifiers.VisibilityMask));
- break;
- } else if ((basePropDef.GetMethod ?? basePropDef.SetMethod).IsNewSlot) {
- break;
+ try {
+ if (accessor.IsVirtual && !accessor.IsNewSlot && (propDef.GetMethod == null || propDef.SetMethod == null)) {
+ foreach (var basePropDef in TypesHierarchyHelpers.FindBaseProperties(propDef)) {
+ if (basePropDef.GetMethod != null && basePropDef.SetMethod != null) {
+ var propVisibilityModifiers = ConvertModifiers(basePropDef.GetMethod) | ConvertModifiers(basePropDef.SetMethod);
+ astProp.Modifiers = FixUpVisibility((astProp.Modifiers & ~Modifiers.VisibilityMask) | (propVisibilityModifiers & Modifiers.VisibilityMask));
+ break;
+ } else if ((basePropDef.GetMethod ?? basePropDef.SetMethod).IsNewSlot) {
+ break;
+ }
}
}
- }
- if (accessor.IsVirtual ^ !accessor.IsNewSlot) {
- if (TypesHierarchyHelpers.FindBaseProperties(propDef).Any())
- astProp.Modifiers |= Modifiers.New;
+ } catch (ReferenceResolvingException) {
+ // TODO: add some kind of notification (a comment?) about possible problems with decompiled code due to unresolved references.
}
}
astProp.Name = CleanName(propDef.Name);
astProp.ReturnType = ConvertType(propDef.PropertyType, propDef);
+
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.Body = CreateMethodBody(propDef.GetMethod);
- astProp.AddAnnotation(propDef.GetMethod);
+ astProp.Getter.AddAnnotation(propDef.GetMethod);
ConvertAttributes(astProp.Getter, propDef.GetMethod);
if ((getterModifiers & Modifiers.VisibilityMask) != (astProp.Modifiers & Modifiers.VisibilityMask))
astProp.Getter.Modifiers = getterModifiers & Modifiers.VisibilityMask;
+
+ astProp.Getter.WithAnnotation(methodMapping);
}
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.Body = CreateMethodBody(propDef.SetMethod);
astProp.Setter.AddAnnotation(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))
astProp.Setter.Modifiers = setterModifiers & Modifiers.VisibilityMask;
+
+ astProp.Setter.WithAnnotation(methodMapping);
}
ConvertCustomAttributes(astProp, propDef);
-
+
+ MemberDeclaration member = astProp;
if(propDef.IsIndexer())
- return ConvertPropertyToIndexer(astProp, propDef);
- else
- return astProp;
+ member = ConvertPropertyToIndexer(astProp, propDef);
+ if(!accessor.HasOverrides && !accessor.DeclaringType.IsInterface)
+ if (accessor.IsVirtual == accessor.IsNewSlot)
+ SetNewModifier(member);
+ return member;
}
IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef)
@@ -821,17 +971,34 @@ namespace ICSharpCode.Decompiler.Ast
astEvent.Modifiers = ConvertModifiers(eventDef.AddMethod);
else
astEvent.PrivateImplementationType = ConvertType(eventDef.AddMethod.Overrides.First().DeclaringType);
+
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 {
Body = CreateMethodBody(eventDef.AddMethod)
}.WithAnnotation(eventDef.AddMethod);
ConvertAttributes(astEvent.AddAccessor, eventDef.AddMethod);
+
+ astEvent.AddAccessor.WithAnnotation(methodMapping);
}
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 {
Body = CreateMethodBody(eventDef.RemoveMethod)
}.WithAnnotation(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;
}
@@ -842,13 +1009,15 @@ namespace ICSharpCode.Decompiler.Ast
BlockStatement CreateMethodBody(MethodDefinition method, IEnumerable parameters = null)
{
if (DecompileMethodBodies)
- return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters);
+ return AstMethodBodyBuilder.CreateMethodBody(method, context, parameters, LocalVariables);
else
return null;
}
FieldDeclaration CreateField(FieldDefinition fieldDef)
{
+ this.DecompiledMemberReferences.Add(fieldDef.MetadataToken.ToInt32(), fieldDef);
+
FieldDeclaration astField = new FieldDeclaration();
astField.AddAnnotation(fieldDef);
VariableInitializer initializer = new VariableInitializer(CleanName(fieldDef.Name));
@@ -856,21 +1025,30 @@ namespace ICSharpCode.Decompiler.Ast
astField.ReturnType = ConvertType(fieldDef.FieldType, fieldDef);
astField.Modifiers = ConvertModifiers(fieldDef);
if (fieldDef.HasConstant) {
- if (fieldDef.Constant == null) {
- initializer.Initializer = new NullReferenceExpression();
- } else {
- TypeCode c = Type.GetTypeCode(fieldDef.Constant.GetType());
- if (c >= TypeCode.SByte && c <= TypeCode.UInt64 && !fieldDef.DeclaringType.IsEnum) {
- initializer.Initializer = MakePrimitive((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, fieldDef.Constant, false), fieldDef.FieldType);
- } else {
- initializer.Initializer = new PrimitiveExpression(fieldDef.Constant);
- }
- }
+ initializer.Initializer = CreateExpressionForConstant(fieldDef.Constant, fieldDef.FieldType, fieldDef.DeclaringType.IsEnum);
}
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 {
+ TypeCode c = Type.GetTypeCode(constant.GetType());
+ if (c >= TypeCode.SByte && c <= TypeCode.UInt64 && !isEnumMemberDeclaration) {
+ return MakePrimitive((long)CSharpPrimitiveCast.Cast(TypeCode.Int64, constant, false), type);
+ } else {
+ return new PrimitiveExpression(constant);
+ }
+ }
+ }
+
public static IEnumerable MakeParameters(MethodDefinition method, bool isLambda = false)
{
var parameters = MakeParameters(method.Parameters, isLambda);
@@ -896,12 +1074,16 @@ namespace ICSharpCode.Decompiler.Ast
if (ct != null && ct.PointerRank > 0)
ct.PointerRank--;
}
+
if (paramDef.HasCustomAttributes) {
foreach (CustomAttribute ca in paramDef.CustomAttributes) {
if (ca.AttributeType.Name == "ParamArrayAttribute" && ca.AttributeType.Namespace == "System")
astParam.ParameterModifier = ParameterModifier.Params;
}
}
+ if (paramDef.IsOptional) {
+ astParam.DefaultExpression = CreateExpressionForConstant(paramDef.Constant, paramDef.ParameterType);
+ }
ConvertCustomAttributes(astParam, paramDef);
ModuleDefinition module = ((MethodDefinition)paramDef.Method).Module;
@@ -922,6 +1104,7 @@ namespace ICSharpCode.Decompiler.Ast
void ConvertAttributes(AttributedNode attributedNode, TypeDefinition typeDefinition)
{
ConvertCustomAttributes(attributedNode, typeDefinition);
+ ConvertSecurityAttributes(attributedNode, typeDefinition);
// Handle the non-custom attributes:
#region SerializableAttribute
@@ -972,11 +1155,12 @@ namespace ICSharpCode.Decompiler.Ast
void ConvertAttributes(AttributedNode attributedNode, MethodDefinition methodDefinition)
{
ConvertCustomAttributes(attributedNode, methodDefinition);
+ ConvertSecurityAttributes(attributedNode, methodDefinition);
MethodImplAttributes implAttributes = methodDefinition.ImplAttributes & ~MethodImplAttributes.CodeTypeMask;
#region DllImportAttribute
- if (methodDefinition.HasPInvokeInfo) {
+ if (methodDefinition.HasPInvokeInfo && methodDefinition.PInvokeInfo != null) {
PInvokeInfo info = methodDefinition.PInvokeInfo;
Ast.Attribute dllImport = CreateNonCustomAttribute(typeof(DllImportAttribute));
dllImport.Arguments.Add(new PrimitiveExpression(info.Module.Name));
@@ -1065,9 +1249,14 @@ namespace ICSharpCode.Decompiler.Ast
}
#endregion
- ConvertCustomAttributes(attributedNode, methodDefinition.MethodReturnType, "return");
- if (methodDefinition.MethodReturnType.HasMarshalInfo) {
- var marshalInfo = ConvertMarshalInfo(methodDefinition.MethodReturnType, methodDefinition.Module);
+ ConvertAttributes(attributedNode, 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" });
}
}
@@ -1103,6 +1292,37 @@ namespace ICSharpCode.Decompiler.Ast
Ast.Attribute attr = CreateNonCustomAttribute(typeof(MarshalAsAttribute), module);
var unmanagedType = new TypeReference("System.Runtime.InteropServices", "UnmanagedType", module, module.TypeSystem.Corlib);
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;
}
#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();
+ 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)
{
if (argument.Value is CustomAttributeArgument[]) {
@@ -1212,9 +1491,7 @@ namespace ICSharpCode.Decompiler.Ast
if (type != null && type.IsEnum) {
return MakePrimitive(Convert.ToInt64(argument.Value), type);
} else if (argument.Value is TypeReference) {
- return new TypeOfExpression() {
- Type = ConvertType((TypeReference)argument.Value),
- };
+ return CreateTypeOfExpression((TypeReference)argument.Value);
} else {
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)
TypeDefinition enumDefinition = type.Resolve();
if (enumDefinition != null && enumDefinition.IsEnum) {
+ TypeCode enumBaseTypeCode = TypeCode.Int32;
foreach (FieldDefinition field in enumDefinition.Fields) {
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)
- 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)) {
long enumValue = val;
Expression expr = null;
@@ -1266,7 +1543,7 @@ namespace ICSharpCode.Decompiler.Ast
continue; // skip None enum value
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)
expr = fieldExpression;
else
@@ -1275,7 +1552,7 @@ namespace ICSharpCode.Decompiler.Ast
enumValue &= ~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)
negatedExpr = fieldExpression;
else
@@ -1293,7 +1570,7 @@ namespace ICSharpCode.Decompiler.Ast
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);
@@ -1309,5 +1586,88 @@ namespace ICSharpCode.Decompiler.Ast
return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute");
}
+
+ ///
+ /// Sets new modifier if the member hides some other member from a base type.
+ ///
+ /// The node of the member which new modifier state should be determined.
+ static void SetNewModifier(AttributedNode member)
+ {
+ try {
+ bool addNewModifier = false;
+ if (member is IndexerDeclaration) {
+ var propertyDef = member.Annotation();
+ 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();
+ 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;
+ }
+
+ ///
+ /// Determines whether any base class member has the same name as the given member.
+ ///
+ /// The derived type's member.
+ /// true if names of methods declared in base types should also be checked.
+ /// true if any base member has the same name as given member, otherwise false.
+ 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(IEnumerable members, IMemberDefinition derived, Predicate condition = null)
+ where T : IMemberDefinition
+ {
+ return members.Any(m => m.Name == derived.Name
+ && (condition == null || condition(m))
+ && TypesHierarchyHelpers.IsVisibleFromDerived(m, derived.DeclaringType));
+ }
+
+ ///
+ /// Gets the local variables for the current decompiled type, method, etc.
+ /// The key is the metadata token.
+ ///
+ public ConcurrentDictionary> LocalVariables { get; private set; }
}
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
index 368dadefd8..cab334432f 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
+++ b/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.Collections.Concurrent;
using System.Collections.Generic;
@@ -32,9 +50,16 @@ namespace ICSharpCode.Decompiler.Ast
/// Decompilation context.
/// Parameter declarations of the method being decompiled.
/// These are used to update the parameter names when the decompiler generates names for the parameters.
+ /// Local variables storage that will be filled/updated with the local variables.
/// Block for the method body
- public static BlockStatement CreateMethodBody(MethodDefinition methodDef, DecompilerContext context, IEnumerable parameters = null)
+ public static BlockStatement CreateMethodBody(MethodDefinition methodDef,
+ DecompilerContext context,
+ IEnumerable parameters = null,
+ ConcurrentDictionary> localVariables = null)
{
+ if (localVariables == null)
+ localVariables = new ConcurrentDictionary>();
+
MethodDefinition oldCurrentMethod = context.CurrentMethod;
Debug.Assert(oldCurrentMethod == null || oldCurrentMethod == methodDef);
context.CurrentMethod = methodDef;
@@ -44,10 +69,10 @@ namespace ICSharpCode.Decompiler.Ast
builder.context = context;
builder.typeSystem = methodDef.Module.TypeSystem;
if (Debugger.IsAttached) {
- return builder.CreateMethodBody(parameters);
+ return builder.CreateMethodBody(parameters, localVariables);
} else {
try {
- return builder.CreateMethodBody(parameters);
+ return builder.CreateMethodBody(parameters, localVariables);
} catch (OperationCanceledException) {
throw;
} catch (Exception ex) {
@@ -59,10 +84,14 @@ namespace ICSharpCode.Decompiler.Ast
}
}
- public BlockStatement CreateMethodBody(IEnumerable parameters)
+ public BlockStatement CreateMethodBody(IEnumerable parameters,
+ ConcurrentDictionary> localVariables)
{
if (methodDef.Body == null) return null;
+ if (localVariables == null)
+ throw new ArgumentException("localVariables must be instantiated");
+
context.CancellationToken.ThrowIfCancellationRequested();
ILBlock ilMethod = new ILBlock();
ILAstBuilder astBuilder = new ILAstBuilder();
@@ -102,6 +131,10 @@ namespace ICSharpCode.Decompiler.Ast
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;
}
@@ -215,10 +248,20 @@ namespace ICSharpCode.Decompiler.Ast
{
AstNode node = TransformByteCode(expr);
Expression astExpr = node as Expression;
+
+ // get IL ranges - used in debugger
+ List ilRanges = ILRange.OrderAndJoint(expr.GetSelfAndChildrenRecursive().SelectMany(e => e.ILRanges));
+ AstNode result;
+
if (astExpr != null)
- return Convert(astExpr, expr.InferredType, expr.ExpectedType);
+ result = Convert(astExpr, expr.InferredType, expr.ExpectedType);
else
- return node;
+ result = node;
+
+ if (result != null)
+ return result.WithAnnotation(ilRanges);
+
+ return result;
}
AstNode TransformByteCode(ILExpression byteCode)
@@ -306,8 +349,7 @@ namespace ICSharpCode.Decompiler.Ast
}
#endregion
#region Arrays
- case ILCode.Newarr:
- case ILCode.InitArray: {
+ case ILCode.Newarr: {
var ace = new Ast.ArrayCreateExpression();
ace.Type = operandAsTypeRef;
ComposedType ct = operandAsTypeRef as ComposedType;
@@ -323,7 +365,39 @@ namespace ICSharpCode.Decompiler.Ast
}
return ace;
}
- case ILCode.Ldlen: return arg1.Member("Length");
+ 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();
+ 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.Ldelem_I:
case ILCode.Ldelem_I1:
case ILCode.Ldelem_I2:
@@ -460,9 +534,12 @@ namespace ICSharpCode.Decompiler.Ast
return arg1;
else
return arg1.CastTo(operandAsTypeRef);
- case ILCode.Isinst: return arg1.CastAs(operandAsTypeRef);
- case ILCode.Box: return arg1;
- case ILCode.Unbox: return InlineAssembly(byteCode, args);
+ case ILCode.Isinst:
+ return arg1.CastAs(operandAsTypeRef);
+ case ILCode.Box:
+ return arg1;
+ case ILCode.Unbox:
+ return MakeRef(arg1.CastTo(operandAsTypeRef));
#endregion
#region Indirect
case ILCode.Ldind_Ref:
@@ -514,11 +591,7 @@ namespace ICSharpCode.Decompiler.Ast
case ILCode.Endfilter: return InlineAssembly(byteCode, args);
case ILCode.Endfinally: return null;
case ILCode.Initblk: return InlineAssembly(byteCode, args);
- case ILCode.Initobj:
- if (args[0] is DirectionExpression)
- return new AssignmentExpression(((DirectionExpression)args[0]).Expression.Detach(), MakeDefaultValue((TypeReference)operand));
- else
- return InlineAssembly(byteCode, args);
+ case ILCode.Initobj: return InlineAssembly(byteCode, args);
case ILCode.DefaultValue:
return MakeDefaultValue((TypeReference)operand);
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.Ldtoken:
if (operand is Cecil.TypeReference) {
- return new Ast.TypeOfExpression { Type = operandAsTypeRef }.Member("TypeHandle");
+ return AstBuilder.CreateTypeOfExpression((TypeReference)operand).Member("TypeHandle");
} else {
return InlineAssembly(byteCode, args);
}
@@ -678,24 +751,81 @@ namespace ICSharpCode.Decompiler.Ast
return new Ast.YieldBreakStatement();
case ILCode.YieldReturn:
return new Ast.YieldStatement { Expression = arg1 };
- case ILCode.InitCollection: {
- ObjectCreateExpression oce = (ObjectCreateExpression)arg1;
- oce.Initializer = new ArrayInitializerExpression();
+ case ILCode.InitObject:
+ case ILCode.InitCollection:
+ {
+ ArrayInitializerExpression initializer = new ArrayInitializerExpression();
for (int i = 1; i < args.Count; i++) {
- ArrayInitializerExpression aie = args[i] as ArrayInitializerExpression;
- if (aie != null && aie.Elements.Count == 1)
- oce.Initializer.Elements.Add(aie.Elements.Single().Detach());
- else
- oce.Initializer.Elements.Add(args[i]);
+ Match m = objectInitializerPattern.Match(args[i]);
+ if (m.Success) {
+ MemberReferenceExpression mre = m.Get("left").Single();
+ initializer.Elements.Add(
+ new NamedArgumentExpression {
+ Identifier = mre.MemberName,
+ Expression = m.Get("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("arg").Single().Detach());
+ } else {
+ ArrayInitializerExpression argList = new ArrayInitializerExpression();
+ foreach (var expr in m.Get("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;
+ } else if (dve != null) {
+ oce = new ObjectCreateExpression(dve.Type.Detach());
+ oce.CopyAnnotationsFrom(dve);
+ oce.Initializer = initializer;
+ return oce;
+ } else {
+ return new AssignmentExpression(arg1, initializer);
}
- return oce;
- }
- 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) {
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) {
return new AssignmentExpression(target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)), methodArgs.Last());
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs
index 0a5348bf47..2e10dfda14 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/CecilTypeResolveContext.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
+using ICSharpCode.NRefactory;
using ICSharpCode.NRefactory.TypeSystem;
using Mono.Cecil;
@@ -12,7 +28,7 @@ namespace ICSharpCode.Decompiler.Ast
///
/// ITypeResolveContext implementation that lazily loads types from Cecil.
///
- public class CecilTypeResolveContext : ISynchronizedTypeResolveContext, IProjectContent
+ public class CecilTypeResolveContext : AbstractAnnotatable, ISynchronizedTypeResolveContext, IProjectContent
{
readonly ModuleDefinition module;
readonly string[] namespaces;
@@ -76,7 +92,7 @@ namespace ICSharpCode.Decompiler.Ast
public IList 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)
name = name + "`" + typeParameterCount.ToString();
@@ -98,14 +114,14 @@ namespace ICSharpCode.Decompiler.Ast
return null;
}
- public IEnumerable GetClasses()
+ public IEnumerable GetTypes()
{
foreach (TypeDefinition cecilType in module.Types) {
yield return GetClass(cecilType);
}
}
- public IEnumerable GetClasses(string nameSpace, StringComparer nameComparer)
+ public IEnumerable GetTypes(string nameSpace, StringComparer nameComparer)
{
foreach (TypeDefinition cecilType in module.Types) {
if (nameComparer.Equals(nameSpace, cecilType.Namespace))
@@ -144,5 +160,14 @@ namespace ICSharpCode.Decompiler.Ast
{
// exit from Synchronize() block
}
+
+ IEnumerable IProjectContent.Files {
+ get { return new IParsedFile[0]; }
+ }
+
+ IParsedFile IProjectContent.GetFile(string fileName)
+ {
+ return null;
+ }
}
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/CommentStatement.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/CommentStatement.cs
index 8bbc6deda5..0940c6c5c7 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/CommentStatement.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
index cc47456176..4a3f277827 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
index 0c2a60b863..2edd30b591 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/NRefactoryExtensions.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/NameVariables.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/NameVariables.cs
index 22e32d8f05..f0a862f44c 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/NameVariables.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
@@ -42,8 +57,23 @@ namespace ICSharpCode.Decompiler.Ast
foreach (var p in parameters)
nv.AddExistingName(p.Name);
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);
+ } else if (v.OriginalVariable != null && context.Settings.UseDebugSymbols) {
+ string varName = v.OriginalVariable.Name;
+ if (string.IsNullOrEmpty(varName) || varName.StartsWith("V_", StringComparison.Ordinal) || !IsValidName(varName))
+ {
+ // don't use the name from the debug symbols if it looks like a generated name
+ v.Name = null;
+ } else {
+ // use the name from the debug symbols
+ // (but ensure we don't use the same name for two variables)
+ v.Name = nv.GetAlternativeName(varName);
+ }
+ } else {
+ v.Name = null;
+ }
}
// Now generate names:
foreach (ILVariable p in parameters) {
@@ -51,12 +81,24 @@ namespace ICSharpCode.Decompiler.Ast
p.Name = nv.GenerateNameForVariable(p, methodBody);
}
foreach (ILVariable varDef in variables) {
- if (!varDef.IsGenerated) {
+ if (string.IsNullOrEmpty(varDef.Name))
varDef.Name = nv.GenerateNameForVariable(varDef, methodBody);
- }
}
}
+ static bool IsValidName(string varName)
+ {
+ if (string.IsNullOrEmpty(varName))
+ return false;
+ if (!(char.IsLetter(varName[0]) || varName[0] == '_'))
+ return false;
+ for (int i = 1; i < varName.Length; i++) {
+ if (!(char.IsLetterOrDigit(varName[i]) || varName[i] == '_'))
+ return false;
+ }
+ return true;
+ }
+
DecompilerContext context;
List fieldNamesInCurrentType;
Dictionary typeNames = new Dictionary();
@@ -107,10 +149,10 @@ namespace ICSharpCode.Decompiler.Ast
string nameWithoutDigits = SplitName(oldVariableName, out number);
if (!typeNames.ContainsKey(nameWithoutDigits)) {
- typeNames.Add(nameWithoutDigits, 0);
+ typeNames.Add(nameWithoutDigits, number - 1);
}
int count = ++typeNames[nameWithoutDigits];
- if (count > 1) {
+ if (count != 1) {
return nameWithoutDigits + count.ToString();
} else {
return nameWithoutDigits;
@@ -255,6 +297,8 @@ namespace ICSharpCode.Decompiler.Ast
string GetNameByType(TypeReference type)
{
+ type = TypeAnalysis.UnpackModifiers(type);
+
GenericInstanceType git = type as GenericInstanceType;
if (git != null && git.ElementType.FullName == "System.Nullable`1" && git.GenericArguments.Count == 1) {
type = ((GenericInstanceType)type).GenericArguments[0];
@@ -265,6 +309,8 @@ namespace ICSharpCode.Decompiler.Ast
name = "array";
} else if (type.IsPointer || type.IsByReference) {
name = "ptr";
+ } else if (type.Name.EndsWith("Exception", StringComparison.Ordinal)) {
+ name = "ex";
} else if (!typeNameToVariableNameDict.TryGetValue(type.FullName, out name)) {
name = type.Name;
// remove the 'I' for interfaces
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
index 1013715352..7c8ca1230d 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.Linq;
+
using ICSharpCode.Decompiler;
+using ICSharpCode.Decompiler.ILAst;
using ICSharpCode.NRefactory.CSharp;
using Mono.Cecil;
@@ -15,6 +32,7 @@ namespace ICSharpCode.Decompiler.Ast
readonly ITextOutput output;
readonly Stack nodeStack = new Stack();
int braceLevelWithinType = -1;
+ bool inDocumentationComment = false;
public TextOutputFormatter(ITextOutput output)
{
@@ -113,14 +131,56 @@ namespace ICSharpCode.Decompiler.Ast
output.Write("*/");
break;
case CommentType.Documentation:
+ if (!inDocumentationComment)
+ output.MarkFoldStart("///" + content, true);
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;
}
}
public void StartNode(AstNode node)
{
+ // code mappings
+ var ranges = node.Annotation>();
+ 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() != null);
+ if (n != null) {
+ MemberMapping mapping = n.Annotation();
+
+ // 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 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);
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
index b8d25a0463..21638c0b40 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/AddCheckedBlocks.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs
index d614d55fb6..24a14b9215 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/CombineQueryExpressions.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
using System.Linq;
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs
index 236b95711d..1d1f3d0817 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ContextTrackingVisitor.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
index 9b83394797..054d3066e3 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/ConvertConstructorCallIntoInitializer.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
+using System.Collections.Generic;
using System.Linq;
using ICSharpCode.NRefactory.CSharp;
using ICSharpCode.NRefactory.PatternMatching;
@@ -54,9 +70,29 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
public override object VisitTypeDeclaration(TypeDeclaration typeDeclaration, object data)
{
- var instanceCtors = typeDeclaration.Members.OfType().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 members)
+ {
+ var instanceCtors = members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).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();
+ if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsValueType)
+ return;
+
// Recognize field initializers:
// Convert first statement in all ctors (if all ctors have the same statement) into a field initializer.
bool allSame;
@@ -68,7 +104,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = m.Get("fieldAccess").Single().Annotation().ResolveWithinSameModule();
if (fieldDef == null)
break;
- AttributedNode fieldOrEventDecl = typeDeclaration.Members.FirstOrDefault(f => f.Annotation() == fieldDef);
+ AstNode fieldOrEventDecl = members.FirstOrDefault(f => f.Annotation() == fieldDef);
if (fieldOrEventDecl == null)
break;
@@ -84,11 +120,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
}
} while (allSame);
}
-
- // Now convert base constructor calls to initializers:
- base.VisitTypeDeclaration(typeDeclaration, data);
-
- // Remove single empty constructor:
+ }
+
+ void RemoveSingleEmptyConstructor(TypeDeclaration typeDeclaration)
+ {
+ var instanceCtors = typeDeclaration.Members.OfType().Where(c => (c.Modifiers & Modifiers.Static) == 0).ToArray();
if (instanceCtors.Length == 1) {
ConstructorDeclaration emptyCtor = new ConstructorDeclaration();
emptyCtor.Modifiers = ((typeDeclaration.Modifiers & Modifiers.Abstract) == Modifiers.Abstract ? Modifiers.Protected : Modifiers.Public);
@@ -96,12 +132,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (emptyCtor.IsMatch(instanceCtors[0]))
instanceCtors[0].Remove();
}
-
+ }
+
+ void HandleStaticFieldInitializers(IEnumerable members)
+ {
// Convert static constructor into field initializers if the class is BeforeFieldInit
- var staticCtor = typeDeclaration.Members.OfType().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
+ var staticCtor = members.OfType().FirstOrDefault(c => (c.Modifiers & Modifiers.Static) == Modifiers.Static);
if (staticCtor != null) {
- TypeDefinition typeDef = typeDeclaration.Annotation();
- if (typeDef != null && typeDef.IsBeforeFieldInit) {
+ MethodDefinition ctorMethodDef = staticCtor.Annotation();
+ if (ctorMethodDef != null && ctorMethodDef.DeclaringType.IsBeforeFieldInit) {
while (true) {
ExpressionStatement es = staticCtor.Body.Statements.FirstOrDefault() as ExpressionStatement;
if (es == null)
@@ -112,7 +151,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
FieldDefinition fieldDef = assignment.Left.Annotation().ResolveWithinSameModule();
if (fieldDef == null || !fieldDef.IsStatic)
break;
- FieldDeclaration fieldDecl = typeDeclaration.Members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef);
+ FieldDeclaration fieldDecl = members.OfType().FirstOrDefault(f => f.Annotation() == fieldDef);
if (fieldDecl == null)
break;
fieldDecl.Variables.Single().Initializer = assignment.Right.Detach();
@@ -122,11 +161,15 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
staticCtor.Remove();
}
}
- return null;
}
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);
}
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs b/src/Libraries/ICSharpCode.Decompiler/Ast/Transforms/DecimalConstantTransform.cs
new file mode 100644
index 0000000000..298682afb6
--- /dev/null
+++ b/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
+{
+ ///
+ /// Transforms decimal constant fields.
+ ///
+ public class DecimalConstantTransform : DepthFirstAstVisitor
- void IntroducePropertyAccessInstructions(ILBlock method)
+ void IntroducePropertyAccessInstructions(ILNode node)
{
- foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) {
- if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
- MethodReference cecilMethod = (MethodReference)expr.Operand;
- if (cecilMethod.DeclaringType is ArrayType) {
- switch (cecilMethod.Name) {
- case "Get":
- expr.Code = ILCode.CallGetter;
- break;
- case "Set":
- expr.Code = ILCode.CallSetter;
- break;
- case "Address":
- expr.Code = ILCode.CallGetter;
- expr.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
- break;
- }
- } else {
- MethodDefinition cecilMethodDef = cecilMethod.Resolve();
- if (cecilMethodDef != null) {
- if (cecilMethodDef.IsGetter)
- expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
- else if (cecilMethodDef.IsSetter)
- expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
- }
+ ILExpression parentExpr = node as ILExpression;
+ if (parentExpr != null) {
+ for (int i = 0; i < parentExpr.Arguments.Count; i++) {
+ ILExpression expr = parentExpr.Arguments[i];
+ IntroducePropertyAccessInstructions(expr);
+ IntroducePropertyAccessInstructions(expr, parentExpr, i);
+ }
+ } else {
+ foreach (ILNode child in node.GetChildren()) {
+ IntroducePropertyAccessInstructions(child);
+ ILExpression expr = child as ILExpression;
+ if (expr != null) {
+ IntroducePropertyAccessInstructions(expr, null, -1);
}
}
}
}
+ void IntroducePropertyAccessInstructions(ILExpression expr, ILExpression parentExpr, int posInParent)
+ {
+ if (expr.Code == ILCode.Call || expr.Code == ILCode.Callvirt) {
+ MethodReference cecilMethod = (MethodReference)expr.Operand;
+ if (cecilMethod.DeclaringType is ArrayType) {
+ switch (cecilMethod.Name) {
+ case "Get":
+ expr.Code = ILCode.CallGetter;
+ break;
+ case "Set":
+ expr.Code = ILCode.CallSetter;
+ break;
+ case "Address":
+ ByReferenceType brt = cecilMethod.ReturnType as ByReferenceType;
+ if (brt != null) {
+ MethodReference getMethod = new MethodReference("Get", brt.ElementType, cecilMethod.DeclaringType);
+ foreach (var p in cecilMethod.Parameters)
+ getMethod.Parameters.Add(p);
+ getMethod.HasThis = cecilMethod.HasThis;
+ expr.Operand = getMethod;
+ }
+ expr.Code = ILCode.CallGetter;
+ if (parentExpr != null) {
+ parentExpr.Arguments[posInParent] = new ILExpression(ILCode.AddressOf, null, expr);
+ }
+ break;
+ }
+ } else {
+ MethodDefinition cecilMethodDef = cecilMethod.Resolve();
+ if (cecilMethodDef != null) {
+ if (cecilMethodDef.IsGetter)
+ expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallGetter : ILCode.CallvirtGetter;
+ else if (cecilMethodDef.IsSetter)
+ expr.Code = (expr.Code == ILCode.Call) ? ILCode.CallSetter : ILCode.CallvirtSetter;
+ }
+ }
+ } else if (expr.Code == ILCode.Newobj && expr.Arguments.Count == 2) {
+ // Might be 'newobj(SomeDelegate, target, ldvirtftn(F, target))'.
+ ILVariable target;
+ if (expr.Arguments[0].Match(ILCode.Ldloc, out target)
+ && expr.Arguments[1].Code == ILCode.Ldvirtftn
+ && expr.Arguments[1].Arguments.Count == 1
+ && expr.Arguments[1].Arguments[0].MatchLdloc(target))
+ {
+ // Remove the 'target' argument from the ldvirtftn instruction.
+ // It's not needed in the translation to C#, and needs to be eliminated so that the target expression
+ // can be inlined.
+ expr.Arguments[1].Arguments.Clear();
+ }
+ }
+ }
+
///
/// Group input into a set of blocks that can be later arbitraliby schufled.
/// The method adds necessary branches to make control flow between blocks
@@ -352,7 +433,7 @@ namespace ICSharpCode.Decompiler.ILAst
lastNode.IsUnconditionalControlFlow())
{
// 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
if (!lastNode.IsUnconditionalControlFlow()) {
@@ -461,6 +542,25 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
+ ///
+ /// Replace endfinally with jump to the end of the finally block
+ ///
+ void RemoveEndFinally(ILBlock method)
+ {
+ // Go thought the list in reverse so that we do the nested blocks first
+ foreach(var tryCatch in method.GetSelfAndChildrenRecursive(tc => tc.FinallyBlock != null).Reverse()) {
+ ILLabel label = new ILLabel() { Name = "EndFinally_" + nextLabelIndex++ };
+ tryCatch.FinallyBlock.Body.Add(label);
+ foreach(var block in tryCatch.FinallyBlock.GetSelfAndChildrenRecursive()) {
+ 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);
+ }
+ }
+ }
+ }
+ }
+
///
/// Reduce the nesting of conditions.
/// 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)
// The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes.
Dictionary dict = new Dictionary();
- foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) {
- ILVariable v = expr.Operand as ILVariable;
- if (v != null && v.OriginalVariable != null) {
+ ReplaceVariables(
+ method,
+ delegate(ILVariable v) {
+ if (v.OriginalVariable == null)
+ return v;
ILVariable combinedVariable;
if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) {
dict.Add(v.OriginalVariable, v);
combinedVariable = v;
}
- expr.Operand = combinedVariable;
+ return combinedVariable;
+ });
+ }
+
+ public static void ReplaceVariables(ILNode node, Func 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
MethodReference mr = (MethodReference)expr.Operand;
return !mr.Name.StartsWith("get_", StringComparison.Ordinal);
+ case ILCode.CallSetter:
+ case ILCode.CallvirtSetter:
case ILCode.Newobj:
case ILCode.Newarr:
case ILCode.Stloc:
+ case ILCode.Stobj:
+ case ILCode.Stsfld:
+ case ILCode.Stfld:
+ case ILCode.Stind_Ref:
+ case ILCode.Stelem_Any:
+ case ILCode.Stelem_I:
+ case ILCode.Stelem_I1:
+ case ILCode.Stelem_I2:
+ case ILCode.Stelem_I4:
+ case ILCode.Stelem_I8:
+ case ILCode.Stelem_R4:
+ case ILCode.Stelem_R8:
+ case ILCode.Stelem_Ref:
return true;
default:
return false;
}
}
+ public static ILExpression WithILRanges(this ILExpression expr, IEnumerable ilranges)
+ {
+ expr.ILRanges.AddRange(ilranges);
+ return expr;
+ }
+
public static void RemoveTail(this List body, params ILCode[] codes)
{
for (int i = 0; i < codes.Length; i++) {
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
index 721458348f..4f3930fcdd 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILAstTypes.cs
+++ b/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.Collections.Generic;
using System.Diagnostics;
@@ -25,6 +43,7 @@ namespace ICSharpCode.Decompiler.ILAst
void AccumulateSelfAndChildrenRecursive(List list, Func predicate) where T:ILNode
{
+ // Note: RemoveEndFinally depends on self coming before children
T thisAsT = this as T;
if (thisAsT != null && (predicate == null || predicate(thisAsT)))
list.Add(thisAsT);
@@ -123,6 +142,10 @@ namespace ICSharpCode.Decompiler.ILAst
{
output.Write("catch ");
output.WriteReference(ExceptionType.FullName, ExceptionType);
+ if (ExceptionVariable != null) {
+ output.Write(' ');
+ output.Write(ExceptionVariable.Name);
+ }
output.WriteLine(" {");
output.Indent();
base.WriteTo(output);
@@ -210,7 +233,10 @@ namespace ICSharpCode.Decompiler.ILAst
public static List OrderAndJoint(IEnumerable input)
{
- List ranges = input.OrderBy(r => r.From).ToList();
+ if (input == null)
+ throw new ArgumentNullException("Input is null!");
+
+ List ranges = input.Where(r => r != null).OrderBy(r => r.From).ToList();
for (int i = 0; i < ranges.Count - 1;) {
ILRange curr = ranges[i];
ILRange next = ranges[i + 1];
@@ -227,6 +253,12 @@ namespace ICSharpCode.Decompiler.ILAst
public static IEnumerable Invert(IEnumerable input, int codeSize)
{
+ if (input == null)
+ throw new ArgumentNullException("Input is null!");
+
+ if (codeSize <= 0)
+ throw new ArgumentException("Code size must be grater than 0");
+
var ordered = OrderAndJoint(input);
if (ordered.Count == 0) {
yield return new ILRange() { From = 0, To = codeSize };
@@ -351,10 +383,10 @@ namespace ICSharpCode.Decompiler.ILAst
output.Write(((ILVariable)Operand).Name);
if (this.InferredType != null) {
output.Write(':');
- this.InferredType.WriteTo(output, true, true);
+ this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:");
- this.ExpectedType.WriteTo(output, true, true);
+ this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
}
@@ -372,15 +404,15 @@ namespace ICSharpCode.Decompiler.ILAst
output.Write(Code.GetName());
if (this.InferredType != null) {
output.Write(':');
- this.InferredType.WriteTo(output, true, true);
+ this.InferredType.WriteTo(output, ILNameSyntax.ShortTypeName);
if (this.ExpectedType != null && this.ExpectedType.FullName != this.InferredType.FullName) {
output.Write("[exp:");
- this.ExpectedType.WriteTo(output, true, true);
+ this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
} else if (this.ExpectedType != null) {
output.Write("[exp:");
- this.ExpectedType.WriteTo(output, true, true);
+ this.ExpectedType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write(']');
}
output.Write('(');
@@ -397,12 +429,14 @@ namespace ICSharpCode.Decompiler.ILAst
}
} else if (Operand is MethodReference) {
MethodReference method = (MethodReference)Operand;
- method.DeclaringType.WriteTo(output, true, true);
- output.Write("::");
+ if (method.DeclaringType != null) {
+ method.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
+ output.Write("::");
+ }
output.WriteReference(method.Name, method);
} else if (Operand is FieldReference) {
FieldReference field = (FieldReference)Operand;
- field.DeclaringType.WriteTo(output, true, true);
+ field.DeclaringType.WriteTo(output, ILNameSyntax.ShortTypeName);
output.Write("::");
output.WriteReference(field.Name, field);
} else {
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs
index e26c8d2655..e7322a770b 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILCodes.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
using Mono.Cecil;
@@ -238,8 +253,18 @@ namespace ICSharpCode.Decompiler.ILAst
LogicOr,
NullCoalescing,
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, // ?:
LoopOrSwitchBreak,
LoopContinue,
@@ -276,9 +301,12 @@ namespace ICSharpCode.Decompiler.ILAst
CallSetter,
/// Calls the setter of a instance property (or indexer)
CallvirtSetter,
- /// Simulates getting the address of a property. Used as prefix on CallGetter or CallvirtGetter.
- /// Used for postincrement for properties, and to represent the Address() method on multi-dimensional arrays
- PropertyAddress
+ /// Simulates getting the address of the argument instruction.
+ ///
+ /// 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(...))"
+ ///
+ AddressOf
}
public static class ILCodeUtil
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs
index 647f035a7f..be749a6586 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/ILInlining.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
@@ -32,7 +47,13 @@ namespace ICSharpCode.Decompiler.ILAst
numLdloca.Clear();
// Analyse the whole method
- foreach(ILExpression expr in method.GetSelfAndChildrenRecursive()) {
+ AnalyzeNode(method);
+ }
+
+ void AnalyzeNode(ILNode node)
+ {
+ ILExpression expr = node as ILExpression;
+ if (expr != null) {
ILVariable locVar = expr.Operand as ILVariable;
if (locVar != null) {
if (expr.Code == ILCode.Stloc) {
@@ -45,6 +66,16 @@ namespace ICSharpCode.Decompiler.ILAst
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;
List body = block.Body;
+ if (block is ILTryCatchBlock.CatchBlock && body.Count > 1) {
+ ILVariable v = ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable;
+ if (v != null && v.IsGenerated) {
+ if (numLdloca.GetOrDefault(v) == 0 && numStloc.GetOrDefault(v) == 1 && numLdloc.GetOrDefault(v) == 1) {
+ ILVariable v2;
+ ILExpression ldException;
+ if (body[0].Match(ILCode.Stloc, out v2, out ldException) && ldException.MatchLdloc(v)) {
+ body.RemoveAt(0);
+ ((ILTryCatchBlock.CatchBlock)block).ExceptionVariable = v2;
+ modified = true;
+ }
+ }
+ }
+ }
for(int i = 0; i < body.Count - 1;) {
ILVariable locVar;
ILExpression expr;
@@ -73,7 +118,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
foreach(ILBasicBlock bb in body.OfType()) {
modified |= InlineAllInBasicBlock(bb);
- }
+ }
return modified;
}
@@ -169,7 +214,10 @@ namespace ICSharpCode.Decompiler.ILAst
bool InlineIfPossible(ILVariable v, ILExpression inlinedExpression, ILNode next, bool aggressive)
{
// 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;
if (next is ILCondition)
@@ -180,35 +228,135 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression parent;
int pos;
if (FindLoadInNext(next as ILExpression, v, inlinedExpression, out parent, out pos) == true) {
- if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
- return false;
+ if (ldloc == 0) {
+ if (!IsGeneratedValueTypeTemporary((ILExpression)next, parent, pos, v, inlinedExpression))
+ return false;
+ } else {
+ if (!aggressive && !v.IsGenerated && !NonAggressiveInlineInto((ILExpression)next, parent, inlinedExpression))
+ return false;
+ }
// Assign the ranges of the ldloc instruction:
inlinedExpression.ILRanges.AddRange(parent.Arguments[pos].ILRanges);
- parent.Arguments[pos] = inlinedExpression;
-
+ if (ldloc == 0) {
+ // it was an ldloca instruction, so we need to use the pseudo-opcode 'addressof' so that the types
+ // comes out correctly
+ parent.Arguments[pos] = new ILExpression(ILCode.AddressOf, null, inlinedExpression);
+ } else {
+ parent.Arguments[pos] = inlinedExpression;
+ }
return true;
}
return false;
}
+
+ ///
+ /// Is this a temporary variable generated by the C# compiler for instance method calls on value type values
+ ///
+ /// The next top-level expression
+ /// The direct parent of the load within 'next'
+ /// Index of the load within 'parent'
+ /// The variable being inlined.
+ /// The expression being inlined
+ bool IsGeneratedValueTypeTemporary(ILExpression next, ILExpression parent, int pos, ILVariable v, ILExpression inlinedExpression)
+ {
+ if (pos == 0 && v.Type != null && v.Type.IsValueType) {
+ // Inlining a value type variable is allowed only if the resulting code will maintain the semantics
+ // that the method is operating on a copy.
+ // Thus, we have to disallow inlining of other locals, fields, array elements, dereferenced pointers
+ switch (inlinedExpression.Code) {
+ case ILCode.Ldloc:
+ case ILCode.Stloc:
+ case ILCode.CompoundAssignment:
+ case ILCode.Ldelem_Any:
+ case ILCode.Ldelem_I:
+ case ILCode.Ldelem_I1:
+ case ILCode.Ldelem_I2:
+ case ILCode.Ldelem_I4:
+ case ILCode.Ldelem_I8:
+ case ILCode.Ldelem_R4:
+ case ILCode.Ldelem_R8:
+ case ILCode.Ldelem_Ref:
+ case ILCode.Ldelem_U1:
+ case ILCode.Ldelem_U2:
+ case ILCode.Ldelem_U4:
+ case ILCode.Ldobj:
+ case ILCode.Ldind_Ref:
+ return false;
+ case ILCode.Ldfld:
+ case ILCode.Stfld:
+ case ILCode.Ldsfld:
+ case ILCode.Stsfld:
+ // allow inlining field access only if it's a readonly field
+ FieldDefinition f = ((FieldReference)inlinedExpression.Operand).Resolve();
+ if (!(f != null && f.IsInitOnly))
+ return false;
+ break;
+ case ILCode.Call:
+ case ILCode.CallGetter:
+ // inlining runs both before and after IntroducePropertyAccessInstructions,
+ // so we have to handle both 'call' and 'callgetter'
+ MethodReference mr = (MethodReference)inlinedExpression.Operand;
+ // ensure that it's not an multi-dimensional array getter
+ if (mr.DeclaringType is ArrayType)
+ return false;
+ goto case ILCode.Callvirt;
+ case ILCode.Callvirt:
+ case ILCode.CallvirtGetter:
+ // don't inline foreach loop variables:
+ mr = (MethodReference)inlinedExpression.Operand;
+ if (mr.Name == "get_Current" && mr.HasThis)
+ return false;
+ break;
+ case ILCode.Castclass:
+ case ILCode.Unbox_Any:
+ // These are valid, but might occur as part of a foreach loop variable.
+ ILExpression arg = inlinedExpression.Arguments[0];
+ if (arg.Code == ILCode.CallGetter || arg.Code == ILCode.CallvirtGetter || arg.Code == ILCode.Call || arg.Code == ILCode.Callvirt) {
+ mr = (MethodReference)arg.Operand;
+ if (mr.Name == "get_Current" && mr.HasThis)
+ return false; // looks like a foreach loop variable, so don't inline it
+ }
+ break;
+ }
+
+ // inline the compiler-generated variable that are used when accessing a member on a value type:
+ switch (parent.Code) {
+ case ILCode.Call:
+ case ILCode.CallGetter:
+ case ILCode.CallSetter:
+ case ILCode.Callvirt:
+ case ILCode.CallvirtGetter:
+ case ILCode.CallvirtSetter:
+ MethodReference mr = (MethodReference)parent.Operand;
+ return mr.HasThis;
+ case ILCode.Stfld:
+ case ILCode.Ldfld:
+ case ILCode.Ldflda:
+ return true;
+ }
+ }
+ return false;
+ }
+ ///
+ /// Determines whether a variable should be inlined in non-aggressive mode, even though it is not a generated variable.
+ ///
+ /// The next top-level expression
+ /// The direct parent of the load within 'next'
+ /// The expression being inlined
bool NonAggressiveInlineInto(ILExpression next, ILExpression parent, ILExpression inlinedExpression)
{
- switch (inlinedExpression.Code) {
- case ILCode.InitArray:
- case ILCode.InitCollection:
- case ILCode.DefaultValue:
- return true;
- }
+ if (inlinedExpression.Code == ILCode.DefaultValue)
+ return true;
switch (next.Code) {
case ILCode.Ret:
- return parent.Code == ILCode.Ret;
case ILCode.Brtrue:
- return parent.Code == ILCode.Brtrue;
+ return parent == next;
case ILCode.Switch:
- return parent.Code == ILCode.Switch || parent.Code == ILCode.Sub;
+ return parent == next || (parent.Code == ILCode.Sub && parent == next.Arguments[0]);
default:
return false;
}
@@ -237,12 +385,12 @@ namespace ICSharpCode.Decompiler.ILAst
for (int i = 0; i < expr.Arguments.Count; i++) {
// Stop when seeing an opcode that does not guarantee that its operands will be evaluated.
// Inlining in that case might result in the inlined expresion not being evaluted.
- if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp))
+ if (i == 1 && (expr.Code == ILCode.LogicAnd || expr.Code == ILCode.LogicOr || expr.Code == ILCode.TernaryOp || expr.Code == ILCode.NullCoalescing))
return false;
ILExpression arg = expr.Arguments[i];
- if (arg.Code == ILCode.Ldloc && arg.Operand == v) {
+ if ((arg.Code == ILCode.Ldloc || arg.Code == ILCode.Ldloca) && arg.Operand == v) {
parent = expr;
pos = i;
return true;
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
index 29d8695265..5b194b7389 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this
+// software and associated documentation files (the "Software"), to deal in the Software
+// without restriction, including without limitation the rights to use, copy, modify, merge,
+// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
+// to whom the Software is furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
+// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
+// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
+// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
-
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
@@ -12,41 +27,31 @@ namespace ICSharpCode.Decompiler.ILAst
///
/// IL AST transformation that introduces array, object and collection initializers.
///
- public class Initializers
+ partial class ILAstOptimizer
{
- public static bool TransformArrayInitializers(List body, ILExpression expr, int pos)
+ #region Array Initializers
+ bool TransformArrayInitializers(List body, ILExpression expr, int pos)
{
- ILVariable v, v2, v3;
+ ILVariable v, v3;
ILExpression newarrExpr;
- TypeReference arrayType;
+ TypeReference elementType;
ILExpression lengthExpr;
int arrayLength;
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) &&
- arrayLength > 0)
- {
- MethodReference methodRef;
- ILExpression methodArg1;
- ILExpression methodArg2;
- FieldDefinition field;
- 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.RemoveAt(pos + 1);
- return true;
- }
+ arrayLength > 0) {
+ ILExpression[] newArr;
+ int initArrayPos;
+ if (ForwardScanInitializeArrayRuntimeHelper(body, pos + 1, v, elementType, arrayLength, out newArr, out initArrayPos)) {
+ var arrayType = new ArrayType(elementType, 1);
+ arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength);
+ body[pos] = new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.InitArray, arrayType, newArr));
+ body.RemoveAt(initArrayPos);
}
-
- const int maxConsecutiveDefaultValueExpressions = 10;
+ // Put in a limit so that we don't consume too much memory if the code allocates a huge array
+ // and populates it extremely sparsly. However, 255 "null" elements in a row actually occur in the Mono C# compiler!
+ const int maxConsecutiveDefaultValueExpressions = 300;
List operands = new List();
int numberOfInstructionsToRemove = 0;
for (int j = pos + 1; j < body.Count; j++) {
@@ -58,10 +63,9 @@ namespace ICSharpCode.Decompiler.ILAst
v == v3 &&
nextExpr.Arguments[1].Match(ILCode.Ldc_I4, out arrayPos) &&
arrayPos >= operands.Count &&
- arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions)
- {
+ arrayPos <= operands.Count + maxConsecutiveDefaultValueExpressions) {
while (operands.Count < arrayPos)
- operands.Add(new ILExpression(ILCode.DefaultValue, arrayType));
+ operands.Add(new ILExpression(ILCode.DefaultValue, elementType));
operands.Add(nextExpr.Arguments[2]);
numberOfInstructionsToRemove++;
} else {
@@ -69,19 +73,86 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
if (operands.Count == arrayLength) {
+ var arrayType = new ArrayType(elementType, 1);
+ arrayType.Dimensions[0] = new ArrayDimension(0, arrayLength);
expr.Arguments[0] = new ILExpression(ILCode.InitArray, arrayType, operands);
body.RemoveRange(pos + 1, numberOfInstructionsToRemove);
+
+ new ILInlining(method).InlineIfPossible(body, ref pos);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool TransformMultidimensionalArrayInitializers(List body, ILExpression expr, int pos)
+ {
+ ILVariable v;
+ ILExpression newarrExpr;
+ MethodReference ctor;
+ List 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 body, int pos, ILVariable array, TypeReference arrayType, int arrayLength, out ILExpression[] values, out int foundPos)
+ {
+ ILVariable v2;
+ MethodReference methodRef;
+ ILExpression methodArg1;
+ ILExpression methodArg2;
+ FieldReference fieldRef;
+ if (body.ElementAtOrDefault(pos).Match(ILCode.Call, out methodRef, out methodArg1, out methodArg2) &&
+ methodRef.DeclaringType.FullName == "System.Runtime.CompilerServices.RuntimeHelpers" &&
+ methodRef.Name == "InitializeArray" &&
+ methodArg1.Match(ILCode.Ldloc, out v2) &&
+ array == v2 &&
+ methodArg2.Match(ILCode.Ldtoken, out fieldRef))
+ {
+ FieldDefinition fieldDef = fieldRef.ResolveWithinSameModule();
+ if (fieldDef != null && fieldDef.InitialValue != null) {
+ ILExpression[] newArr = new ILExpression[arrayLength];
+ if (DecodeArrayInitializer(TypeAnalysis.GetTypeCode(arrayType.GetElementType()),
+ fieldDef.InitialValue, newArr))
+ {
+ values = newArr;
+ foundPos = pos;
+ return true;
+ }
+ }
+ }
+ values = null;
+ foundPos = -1;
+ return false;
+ }
+
static bool DecodeArrayInitializer(TypeCode elementType, byte[] initialValue, ILExpression[] output)
{
switch (elementType) {
case TypeCode.Boolean:
- case TypeCode.SByte:
case TypeCode.Byte:
if (initialValue.Length == output.Length) {
for (int j = 0; j < output.Length; j++) {
@@ -90,9 +161,15 @@ namespace ICSharpCode.Decompiler.ILAst
return true;
}
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.UInt16:
if (initialValue.Length == output.Length * 2) {
for (int j = 0; j < output.Length; j++) {
output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToInt16(initialValue, j * 2));
@@ -100,6 +177,15 @@ namespace ICSharpCode.Decompiler.ILAst
return true;
}
return false;
+ case TypeCode.Char:
+ case TypeCode.UInt16:
+ if (initialValue.Length == output.Length * 2) {
+ for (int j = 0; j < output.Length; j++) {
+ output[j] = new ILExpression(ILCode.Ldc_I4, (int)BitConverter.ToUInt16(initialValue, j * 2));
+ }
+ return true;
+ }
+ return false;
case TypeCode.Int32:
case TypeCode.UInt32:
if (initialValue.Length == output.Length * 4) {
@@ -138,50 +224,300 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
}
-
- public static bool TransformCollectionInitializers(List body, ILExpression expr, int pos)
+ #endregion
+
+ ///
+ /// Handles both object and collection initializers.
+ ///
+ bool TransformObjectInitializers(List 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;
+ TypeReference newObjType;
+ bool isValueType;
MethodReference ctor;
List ctorArgs;
- if (expr.Match(ILCode.Stloc, out v, out newObjExpr) &&
- newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs))
- {
- TypeDefinition td = ctor.DeclaringType.Resolve();
- if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
- return false;
-
- // This is a collection: we can convert Add() calls into a collection initializer
- ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr);
- bool anyAdded = false;
- while(pos + 1 < body.Count) {
- ILExpression nextExpr = body[pos + 1] as ILExpression;
- MethodReference addMethod;
- List args;
- if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) &&
- addMethod.Name == "Add" &&
- addMethod.HasThis &&
- args.Count >= 2 &&
- args[0].Match(ILCode.Ldloc, out v2) &&
- v == v2)
- {
- nextExpr.Code = ILCode.InitCollectionAddMethod;
- nextExpr.Arguments.RemoveAt(0);
- collectionInitializer.Arguments.Add(nextExpr);
- body.RemoveAt(pos + 1);
- anyAdded = true;
- } else {
+ if (expr.Match(ILCode.Stloc, out v, out newObjExpr)) {
+ if (newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)) {
+ // v = newObj(ctor, ctorArgs)
+ newObjType = ctor.DeclaringType;
+ isValueType = false;
+ } else if (newObjExpr.Match(ILCode.DefaultValue, out newObjType)) {
+ // v = defaultvalue(type)
+ isValueType = true;
+ } else {
+ return false;
+ }
+ } else if (expr.Match(ILCode.Call, out ctor, out ctorArgs)) {
+ // call(SomeStruct::.ctor, ldloca(v), remainingArgs)
+ if (ctorArgs.Count > 0 && ctorArgs[0].Match(ILCode.Ldloca, out v)) {
+ isValueType = true;
+ newObjType = ctor.DeclaringType;
+ ctorArgs = new List(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;
+ }
+
+ ///
+ /// Gets whether the type supports collection initializers.
+ ///
+ static bool IsCollectionType(TypeReference tr)
+ {
+ if (tr == null)
+ return false;
+ TypeDefinition td = tr.Resolve();
+ while (td != null) {
+ if (td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
+ return true;
+ td = td.BaseType != null ? td.BaseType.Resolve() : null;
+ }
+ return false;
+ }
+
+ ///
+ /// Gets whether 'expr' represents a setter in an object initializer.
+ /// ('CallvirtSetter(Property, v, value)')
+ ///
+ 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;
+ }
+
+ ///
+ /// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer.
+ ///
+ static bool IsAddMethodCall(ILExpression expr)
+ {
+ MethodReference addMethod;
+ List args;
+ if (expr.Match(ILCode.Callvirt, out addMethod, out args)) {
+ if (addMethod.Name == "Add" && addMethod.HasThis) {
+ return args.Count >= 2;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Parses an object initializer.
+ ///
+ /// ILAst block
+ ///
+ /// Input: position of the instruction assigning to 'v'.
+ /// Output: first position after the object initializer
+ ///
+ /// The variable that holds the object being initialized
+ /// The newobj instruction
+ /// InitObject instruction
+ ILExpression ParseObjectInitializer(List body, ref int pos, ILVariable v, ILExpression newObjExpr, bool isCollection, bool isValueType)
+ {
+ // Take care not to modify any existing ILExpressions in here.
+ // We just construct new ones around the old ones, any modifications must wait until the whole
+ // object/collection initializer was analyzed.
+ ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr);
+ List initializerStack = new List();
+ initializerStack.Add(objectInitializer);
+ while (++pos < body.Count) {
+ ILExpression nextExpr = body[pos] as ILExpression;
+ if (IsSetterInObjectInitializer(nextExpr)) {
+ if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false, isValueType)) {
+ CleanupInitializerStackAfterFailedAdjustment(initializerStack);
+ break;
+ }
+ initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
+ } else if (IsAddMethodCall(nextExpr)) {
+ if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true, isValueType)) {
+ CleanupInitializerStackAfterFailedAdjustment(initializerStack);
break;
}
+ initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
+ } else {
+ // can't match any more initializers: end of object initializer
+ break;
+ }
+ }
+ return objectInitializer;
+ }
+
+ static bool AdjustInitializerStack(List 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 getters = new List();
+ while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.CallGetter || argument.Code == ILCode.Ldfld) {
+ getters.Add(argument);
+ if (argument.Arguments.Count != 1)
+ return false;
+ argument = argument.Arguments[0];
+ }
+ // Ensure that the final argument is 'v'
+ if (isValueType) {
+ ILVariable loadedVar;
+ if (!(argument.Match(ILCode.Ldloca, out loadedVar) && loadedVar == v))
+ return false;
+ } else {
+ if (!argument.MatchLdloc(v))
+ return false;
+ }
+ // Now compare the getters with those that are currently active on the initializer stack:
+ int i;
+ for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++) {
+ ILExpression g1 = initializerStack[i].Arguments[0]; // getter stored in initializer
+ ILExpression g2 = getters[getters.Count - i]; // matching getter from argument
+ if (g1.Operand != g2.Operand) {
+ // operands differ, so we abort the comparison
+ break;
+ }
+ }
+ // Remove all initializers from the stack that were not matched with one from the argument:
+ initializerStack.RemoveRange(i, initializerStack.Count - i);
+ // Now create new initializers for the remaining arguments:
+ for (; i <= getters.Count; i++) {
+ ILExpression g = getters[getters.Count - i];
+ MemberReference mr = (MemberReference)g.Operand;
+ TypeReference returnType;
+ if (mr is FieldReference)
+ returnType = TypeAnalysis.GetFieldType((FieldReference)mr);
+ else
+ returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, mr);
+
+ ILExpression nestedInitializer = new ILExpression(
+ IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject,
+ null, g);
+ // add new initializer to its parent:
+ ILExpression parentInitializer = initializerStack[initializerStack.Count - 1];
+ if (parentInitializer.Code == ILCode.InitCollection) {
+ // can't add children to collection initializer
+ if (parentInitializer.Arguments.Count == 1) {
+ // convert empty collection initializer to object initializer
+ parentInitializer.Code = ILCode.InitObject;
+ } else {
+ return false;
+ }
}
- // ensure we added at least one additional arg to the collection initializer:
- if (anyAdded) {
- expr.Arguments[0] = collectionInitializer;
+ 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;
}
}
- return false;
+ }
+
+ static void CleanupInitializerStackAfterFailedAdjustment(List 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);
+ }
+ }
}
}
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
index 964b1f84e2..e5409a2e58 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/LoopsAndConditions.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
@@ -16,7 +31,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
Dictionary labelToCfNode = new Dictionary();
- DecompilerContext context;
+ readonly DecompilerContext context;
uint nextLabelIndex = 0;
@@ -271,7 +286,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILLabel condLabel = caseLabels[i];
// 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) {
caseBlock = new ILSwitch.CaseBlock() {
Values = new List(),
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
index d2a6fd48ef..cc4e5ee5f5 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/PatternMatching.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
index 67368c1fac..b8110651b9 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
@@ -75,6 +90,10 @@ namespace ICSharpCode.Decompiler.ILAst
expr.Code = ILCode.Stobj;
expr.Arguments.Add(new ILExpression(ILCode.DefaultValue, expr.Operand));
modified = true;
+ } else if (expr.Code == ILCode.Cpobj) {
+ expr.Code = ILCode.Stobj;
+ expr.Arguments[1] = new ILExpression(ILCode.Ldobj, expr.Operand, expr.Arguments[1]);
+ modified = true;
}
ILExpression arg, arg2;
TypeReference type;
@@ -425,6 +444,7 @@ namespace ICSharpCode.Decompiler.ILAst
#endregion
#region IntroducePostIncrement
+
bool IntroducePostIncrement(List body, ILExpression expr, int pos)
{
bool modified = IntroducePostIncrementForVariables(body, expr, pos);
@@ -437,7 +457,7 @@ namespace ICSharpCode.Decompiler.ILAst
}
return modified;
}
-
+
bool IntroducePostIncrementForVariables(List body, ILExpression expr, int pos)
{
// Works for variables and static fields/properties
@@ -450,19 +470,50 @@ namespace ICSharpCode.Decompiler.ILAst
ILExpression exprInit;
if (!(expr.Match(ILCode.Stloc, out exprVar, out exprInit) && exprVar.IsGenerated))
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;
if (nextExpr == null)
return false;
- if (exprInit.Code == ILCode.CallGetter) {
- if (!(nextExpr.Code == ILCode.CallSetter && IsGetterSetterPair(exprInit.Operand, nextExpr.Operand)))
- return false;
- } else {
- if (!(nextExpr.Code == (exprInit.Code == ILCode.Ldloc ? ILCode.Stloc : ILCode.Stsfld) && nextExpr.Operand == exprInit.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;
+ }
+ break;
+ case ILCode.Ldsfld:
+ if (storeInstruction != ILCode.Stsfld)
+ return false;
+ if (exprInit.Operand != nextExpr.Operand)
+ return false;
+ break;
+ case ILCode.CallGetter:
+ // non-static getters would have the 'this' argument
+ if (exprInit.Arguments.Count != 0)
+ return false;
+ if (storeInstruction != ILCode.CallSetter)
+ return false;
+ if (!IsGetterSetterPair(exprInit.Operand, nextExpr.Operand))
+ return false;
+ break;
+ default:
return false;
}
+
ILExpression addExpr = nextExpr.Arguments[0];
int incrementAmount;
@@ -470,12 +521,23 @@ namespace ICSharpCode.Decompiler.ILAst
if (!(incrementAmount != 0 && addExpr.Arguments[0].MatchLdloc(exprVar)))
return false;
- if (exprInit.Code == ILCode.Ldloc)
- exprInit.Code = ILCode.Ldloca;
- else if (exprInit.Code == ILCode.CallGetter)
- exprInit.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
- else
- exprInit.Code = ILCode.Ldsflda;
+ if (recombineVariable) {
+ // Split local variable, unsplit these two instances
+ // replace nextExpr.Operand with exprInit.Operand
+ ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (ILVariable)exprInit.Operand : oldVar);
+ }
+
+ switch (loadInstruction) {
+ case ILCode.Ldloc:
+ exprInit.Code = ILCode.Ldloca;
+ break;
+ case ILCode.Ldsfld:
+ exprInit.Code = ILCode.Ldsflda;
+ break;
+ case ILCode.CallGetter:
+ exprInit = new ILExpression(ILCode.AddressOf, null, exprInit);
+ break;
+ }
expr.Arguments[0] = new ILExpression(incrementCode, incrementAmount, exprInit);
body.RemoveAt(pos + 1); // TODO ILRanges
return true;
@@ -567,8 +629,8 @@ namespace ICSharpCode.Decompiler.ILAst
if (expr.Code == ILCode.Stobj) {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue.Arguments[0]);
} else if (expr.Code == ILCode.CallSetter || expr.Code == ILCode.CallvirtSetter) {
+ initialValue = new ILExpression(ILCode.AddressOf, null, initialValue);
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
- initialValue.AddPrefix(new ILExpressionPrefix(ILCode.PropertyAddress));
} else {
stloc.Arguments[0] = new ILExpression(ILCode.PostIncrement, incrementAmount, initialValue);
initialValue.Code = (expr.Code == ILCode.Stfld ? ILCode.Ldflda : ILCode.Ldelema);
@@ -806,5 +868,40 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
#endregion
+
+ #region SimplifyShiftOperators
+ static bool SimplifyShiftOperators(List 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
}
}
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
index 0d71a5907d..f7f2cf8a3f 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/SimpleControlFlow.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
@@ -100,7 +115,7 @@ namespace ICSharpCode.Decompiler.ILAst
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr);
}
} else {
- // Ternary operator tends to create long complicated return statements
+ // Ternary operator tends to create long complicated return statements
if (opCode == ILCode.Ret)
return false;
@@ -141,7 +156,7 @@ namespace ICSharpCode.Decompiler.ILAst
// ...
// v = NullCoalescing(ldloc(leftVar), rightExpr)
// br(endBBLabel)
-
+
ILVariable v, v2;
ILExpression leftExpr, leftExpr2;
ILVariable leftVar;
@@ -150,7 +165,7 @@ namespace ICSharpCode.Decompiler.ILAst
ILBasicBlock rightBB;
ILExpression rightExpr;
if (head.Body.Count >= 3 &&
- head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) &&
+ head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) &&
leftExpr.Match(ILCode.Ldloc, out leftVar) &&
head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) &&
leftExpr2.MatchLdloc(leftVar) &&
@@ -219,6 +234,107 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
+ public bool SimplifyCustomShortCircuit(List body, ILBasicBlock head, int pos)
+ {
+ Debug.Assert(body.Contains(head));
+
+ // --- looking for the following pattern ---
+ // stloc(targetVar, leftVar)
+ // brtrue(exitLabel, call(op_False, leftVar)
+ // br(followingBlock)
+ //
+ // FollowingBlock:
+ // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
+ // br(exitLabel)
+ // ---
+
+ if (head.Body.Count < 3)
+ return false;
+
+ // looking for:
+ // stloc(targetVar, leftVar)
+ ILVariable targetVar;
+ ILExpression targetVarInitExpr;
+ if (!head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out targetVar, out targetVarInitExpr))
+ return false;
+
+ ILVariable leftVar;
+ if (!targetVarInitExpr.Match(ILCode.Ldloc, out leftVar))
+ return false;
+
+ // looking for:
+ // brtrue(exitLabel, call(op_False, leftVar)
+ // br(followingBlock)
+ ILExpression callExpr;
+ ILLabel exitLabel;
+ ILLabel followingBlock;
+ if(!head.MatchLastAndBr(ILCode.Brtrue, out exitLabel, out callExpr, out followingBlock))
+ return false;
+
+ if (labelGlobalRefCount[followingBlock] > 1)
+ return false;
+
+ MethodReference opFalse;
+ ILExpression opFalseArg;
+ if (!callExpr.Match(ILCode.Call, out opFalse, out opFalseArg))
+ return false;
+
+ // ignore operators other than op_False and op_True
+ if (opFalse.Name != "op_False" && opFalse.Name != "op_True")
+ return false;
+
+ if (!opFalseArg.MatchLdloc(leftVar))
+ return false;
+
+ ILBasicBlock followingBasicBlock = labelToBasicBlock[followingBlock];
+
+ // FollowingBlock:
+ // stloc(targetVar, call(op_BitwiseAnd, leftVar, rightExpression))
+ // br(exitLabel)
+ ILVariable _targetVar;
+ ILExpression opBitwiseCallExpr;
+ ILLabel _exitLabel;
+ if (!followingBasicBlock.MatchSingleAndBr(ILCode.Stloc, out _targetVar, out opBitwiseCallExpr, out _exitLabel))
+ return false;
+
+ if (_targetVar != targetVar || exitLabel != _exitLabel)
+ return false;
+
+ MethodReference opBitwise;
+ ILExpression leftVarExpression;
+ ILExpression rightExpression;
+ if (!opBitwiseCallExpr.Match(ILCode.Call, out opBitwise, out leftVarExpression, out rightExpression))
+ return false;
+
+ if (!leftVarExpression.MatchLdloc(leftVar))
+ return false;
+
+ // ignore operators other than op_BitwiseAnd and op_BitwiseOr
+ if (opBitwise.Name != "op_BitwiseAnd" && opBitwise.Name != "op_BitwiseOr")
+ return false;
+
+ // insert:
+ // stloc(targetVar, LogicAnd(C::op_BitwiseAnd, leftVar, rightExpression)
+ // br(exitLabel)
+ ILCode op = opBitwise.Name == "op_BitwiseAnd" ? ILCode.LogicAnd : ILCode.LogicOr;
+
+ if (op == ILCode.LogicAnd && opFalse.Name != "op_False")
+ return false;
+
+ if (op == ILCode.LogicOr && opFalse.Name != "op_True")
+ return false;
+
+ ILExpression shortCircuitExpr = MakeLeftAssociativeShortCircuit(op, opFalseArg, rightExpression);
+ shortCircuitExpr.Operand = opBitwise;
+
+ head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br);
+ head.Body.Add(new ILExpression(ILCode.Stloc, targetVar, shortCircuitExpr));
+ head.Body.Add(new ILExpression(ILCode.Br, exitLabel));
+ body.Remove(followingBasicBlock);
+
+ return true;
+ }
+
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right)
{
// Assuming that the inputs are already left associative
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
index be93bdccae..0891f82f14 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
@@ -252,6 +267,10 @@ namespace ICSharpCode.Decompiler.ILAst
return typeSystem.Boolean;
case ILCode.LogicAnd:
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) {
InferTypeForExpression(expr.Arguments[0], typeSystem.Boolean);
InferTypeForExpression(expr.Arguments[1], typeSystem.Boolean);
@@ -304,13 +323,7 @@ namespace ICSharpCode.Decompiler.ILAst
if (forceInferChildren) {
for (int i = 0; i < expr.Arguments.Count; i++) {
if (i == 0 && method.HasThis) {
- ILExpressionPrefix constraint = 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);
+ InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(method.DeclaringType, expr.GetPrefix(ILCode.Constrained)));
} else {
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) {
return SubstituteTypeArgs(method.Parameters.Last().ParameterType, method);
} else {
- TypeReference type = SubstituteTypeArgs(method.ReturnType, method);
- if (expr.GetPrefix(ILCode.PropertyAddress) != null && !(type is ByReferenceType))
- type = new ByReferenceType(type);
- return type;
+ return SubstituteTypeArgs(method.ReturnType, method);
}
}
case ILCode.Newobj:
@@ -335,32 +345,32 @@ namespace ICSharpCode.Decompiler.ILAst
}
return ctor.DeclaringType;
}
+ case ILCode.InitObject:
case ILCode.InitCollection:
return InferTypeForExpression(expr.Arguments[0], expectedType);
- case ILCode.InitCollectionAddMethod:
- {
- MethodReference addMethod = (MethodReference)expr.Operand;
- if (forceInferChildren) {
- for (int i = 0; i < addMethod.Parameters.Count; i++) {
- InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(addMethod.Parameters[i].ParameterType, addMethod));
- }
- }
- return addMethod.DeclaringType;
- }
+ case ILCode.InitializedObject:
+ // expectedType should always be known due to the parent method call / property setter
+ Debug.Assert(expectedType != null);
+ return expectedType;
#endregion
#region Load/Store Fields
case ILCode.Ldfld:
- if (forceInferChildren)
- InferTypeForExpression(expr.Arguments[0], ((FieldReference)expr.Operand).DeclaringType);
+ if (forceInferChildren) {
+ InferTypeForExpression(expr.Arguments[0], MakeRefIfValueType(((FieldReference)expr.Operand).DeclaringType, expr.GetPrefix(ILCode.Constrained)));
+ }
return GetFieldType((FieldReference)expr.Operand);
case ILCode.Ldsfld:
return GetFieldType((FieldReference)expr.Operand);
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:
return new ByReferenceType(GetFieldType((FieldReference)expr.Operand));
case ILCode.Stfld:
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));
}
return GetFieldType((FieldReference)expr.Operand);
@@ -458,6 +468,21 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments[0], (TypeReference)expr.Operand);
}
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
#region Arithmetic instructions
case ILCode.Not: // bitwise complement
@@ -489,14 +514,47 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Rem_Un:
return InferArgumentsInBinaryOperator(expr, false, expectedType);
case ILCode.Shl:
- case ILCode.Shr:
if (forceInferChildren)
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:
- if (forceInferChildren)
- InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
- return InferTypeForExpression(expr.Arguments[0], typeSystem.UInt32);
+ {
+ if (forceInferChildren)
+ InferTypeForExpression(expr.Arguments[1], typeSystem.Int32);
+ 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:
{
TypeReference varType = InferTypeForExpression(expr.Arguments[0].Arguments[0], null);
@@ -517,9 +575,19 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Ldc_I4:
if (IsBoolean(expectedType) && ((int)expr.Operand == 0 || (int)expr.Operand == 1))
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:
- 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:
return typeSystem.Single;
case ILCode.Ldc_R8:
@@ -542,11 +610,13 @@ namespace ICSharpCode.Decompiler.ILAst
InferTypeForExpression(expr.Arguments.Single(), typeSystem.Int32);
return new ArrayType((TypeReference)expr.Operand);
case ILCode.InitArray:
- if (forceInferChildren) {
+ var operandAsArrayType = (ArrayType)expr.Operand;
+ if (forceInferChildren)
+ {
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:
return typeSystem.Int32;
case ILCode.Ldelem_U1:
@@ -655,6 +725,8 @@ namespace ICSharpCode.Decompiler.ILAst
case ILCode.Castclass:
case ILCode.Unbox_Any:
return (TypeReference)expr.Operand;
+ case ILCode.Unbox:
+ return new ByReferenceType((TypeReference)expr.Operand);
case ILCode.Isinst:
{
// isinst performs the equivalent of a cast only for reference types;
@@ -723,6 +795,41 @@ namespace ICSharpCode.Decompiler.ILAst
}
}
+ ///
+ /// 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.
+ ///
+ 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;
+ }
+
+ ///
+ /// Promotes primitive types smaller than int32 to int32.
+ ///
+ ///
+ /// Always promotes to signed int32.
+ ///
+ 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)
{
if (targetBitSize >= NativeInt && expectedType is PointerType) {
@@ -743,12 +850,12 @@ namespace ICSharpCode.Decompiler.ILAst
return resultType;
}
- static TypeReference GetFieldType(FieldReference fieldReference)
+ public static TypeReference GetFieldType(FieldReference 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) {
ArrayType arrayType = type as ArrayType;
@@ -821,7 +928,7 @@ namespace ICSharpCode.Decompiler.ILAst
return null;
}
- static TypeReference UnpackModifiers(TypeReference type)
+ internal static TypeReference UnpackModifiers(TypeReference type)
{
while (type is OptionalModifierType || type is RequiredModifierType)
type = ((TypeSpecification)type).ElementType;
@@ -927,7 +1034,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
if (type == null)
return 0;
- if (type.IsValueType) {
+ if (type.IsValueType && !IsArrayPointerOrReference(type)) {
// value type might be an enum
TypeDefinition typeDef = type.Resolve() as TypeDefinition;
if (typeDef != null && typeDef.IsEnum) {
@@ -975,7 +1082,9 @@ namespace ICSharpCode.Decompiler.ILAst
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;
// 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;
@@ -984,7 +1093,7 @@ namespace ICSharpCode.Decompiler.ILAst
static bool? IsSigned(TypeReference type)
{
- if (type == null)
+ if (type == null || IsArrayPointerOrReference(type))
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)
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)
{
if (type == null)
diff --git a/src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
index 3e5825c564..71971273c1 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
+++ b/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)
-// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+// 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;
@@ -164,7 +179,7 @@ namespace ICSharpCode.Decompiler.ILAst
public static bool IsCompilerGeneratorEnumerator(TypeDefinition type)
{
- if (!(type.Name.StartsWith("<", StringComparison.Ordinal) && type.IsCompilerGenerated()))
+ if (!(type.DeclaringType != null && type.IsCompilerGenerated()))
return false;
foreach (TypeReference i in type.Interfaces) {
if (i.Namespace == "System.Collections" && i.Name == "IEnumerator")
diff --git a/src/Libraries/ICSharpCode.Decompiler/ITextOutput.cs b/src/Libraries/ICSharpCode.Decompiler/ITextOutput.cs
index b4374d50b6..608a45c64b 100644
--- a/src/Libraries/ICSharpCode.Decompiler/ITextOutput.cs
+++ b/src/Libraries/ICSharpCode.Decompiler/ITextOutput.cs
@@ -23,6 +23,9 @@ namespace ICSharpCode.Decompiler
{
public interface ITextOutput
{
+ int CurrentLine { get; }
+ int CurrentColumn { get; }
+
void Indent();
void Unindent();
void Write(char ch);
diff --git a/src/Libraries/ICSharpCode.Decompiler/PlainTextOutput.cs b/src/Libraries/ICSharpCode.Decompiler/PlainTextOutput.cs
index 76721e6664..86a28a5c8c 100644
--- a/src/Libraries/ICSharpCode.Decompiler/PlainTextOutput.cs
+++ b/src/Libraries/ICSharpCode.Decompiler/PlainTextOutput.cs
@@ -23,9 +23,13 @@ namespace ICSharpCode.Decompiler
{
public sealed class PlainTextOutput : ITextOutput
{
+ const int TAB_SIZE = 4;
+
readonly TextWriter writer;
int indent;
bool needsIndent;
+ int lineNumber = 1;
+ int columnNumber = 1;
public PlainTextOutput(TextWriter writer)
{
@@ -39,6 +43,14 @@ namespace ICSharpCode.Decompiler
this.writer = new StringWriter();
}
+ public int CurrentLine {
+ get { return lineNumber; }
+ }
+
+ public int CurrentColumn {
+ get { return columnNumber; }
+ }
+
public override string ToString()
{
return writer.ToString();
@@ -60,6 +72,7 @@ namespace ICSharpCode.Decompiler
needsIndent = false;
for (int i = 0; i < indent; i++) {
writer.Write('\t');
+ columnNumber += TAB_SIZE - 1;
}
}
}
@@ -68,18 +81,22 @@ namespace ICSharpCode.Decompiler
{
WriteIndent();
writer.Write(ch);
+ columnNumber++;
}
public void Write(string text)
{
WriteIndent();
writer.Write(text);
+ columnNumber += text.Length;
}
public void WriteLine()
{
+ lineNumber++;
writer.WriteLine();
needsIndent = true;
+ columnNumber = TAB_SIZE * indent;
}
public void WriteDefinition(string text, object definition)
diff --git a/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs b/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs
index c0577c5fcc..a5eb7d728a 100644
--- a/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.cs
+++ b/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.
[assembly: ComVisible(false)]
+[assembly: AssemblyVersion("2.0.0.1221")]
+[assembly: AssemblyInformationalVersion("2.0.0.1221-26633dc2")]
[assembly: NeutralResourcesLanguage("en-US")]
[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2243:AttributeStringLiteralsShouldParseCorrectly",
diff --git a/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs b/src/Libraries/ICSharpCode.Decompiler/Properties/AssemblyInfo.template.cs
new file mode 100644
index 0000000000..bec57df5fd
--- /dev/null
+++ b/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")]
diff --git a/src/Libraries/ICSharpCode.Decompiler/ReferenceResolvingException.cs b/src/Libraries/ICSharpCode.Decompiler/ReferenceResolvingException.cs
new file mode 100644
index 0000000000..dbceb44b3a
--- /dev/null
+++ b/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
+{
+ ///
+ /// Represents an error while resolving a reference to a type or a member.
+ ///
+ [Serializable]
+ public class ReferenceResolvingException : Exception
+ {
+ ///
+ /// Initializes a new instance of the class
+ ///
+ public ReferenceResolvingException()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// A 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.
+ public ReferenceResolvingException(string message)
+ : base(message)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// A 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.
+ /// 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.
+ public ReferenceResolvingException(string message, Exception inner)
+ : base(message, inner)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class
+ ///
+ /// The object that holds the serialized object data.
+ /// The contextual information about the source or destination.
+ protected ReferenceResolvingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)
+ : base(info, context)
+ {
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il b/src/Libraries/ICSharpCode.Decompiler/Tests/BooleanConsumedAsInteger.il
new file mode 100644
index 0000000000..9e30002790
--- /dev/null
+++ b/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
+
+
+// =============================================================
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CallOverloadedMethod.cs
new file mode 100644
index 0000000000..4f493ef20f
--- /dev/null
+++ b/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 list)
+ {
+ ((ICollection)list).Clear();
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CheckedUnchecked.cs
new file mode 100644
index 0000000000..08c5b39a3f
--- /dev/null
+++ b/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--;
+ }
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CodeSampleFileParser.cs
new file mode 100644
index 0000000000..552d092ee5
--- /dev/null
+++ b/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 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 parser = null;
+
+ Action commonSectionReader = line =>
+ {
+ if (IsCommonSectionEnd(line))
+ parser = null;
+ else
+ sectionText.AppendLine(line);
+ };
+
+ Action namedSectionReader = line =>
+ {
+ string name = ReadSectionName(line);
+ if (name == null)
+ sectionText.AppendLine(line);
+ else if (name != sectionName)
+ parser = null;
+ };
+
+ Action 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 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 ToLines(string s)
+ {
+ var reader = new StringReader(s);
+ string line;
+ while ((line = reader.ReadLine()) != null)
+ {
+ yield return line;
+ }
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes.code.cs
new file mode 100644
index 0000000000..4130b0b0f7
--- /dev/null
+++ b/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);
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/CustomAttributeTests.cs
new file mode 100644
index 0000000000..892f33bf95
--- /dev/null
+++ b/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");
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_AssemblyCustomAttribute.cs
new file mode 100644
index 0000000000..9d95a4861d
--- /dev/null
+++ b/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)]
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributeSamples.cs
new file mode 100644
index 0000000000..df395b446f
--- /dev/null
+++ b/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();
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CustomAttributes/S_CustomAttributes.cs
new file mode 100644
index 0000000000..b75e4e2b04
--- /dev/null
+++ b/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()
+ {
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/CustomShortCircuitOperators.cs
new file mode 100644
index 0000000000..f51908659b
--- /dev/null
+++ b/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());
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/DecompilerTestBase.cs
new file mode 100644
index 0000000000..5eb7012284
--- /dev/null
+++ b/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 lines)
+ {
+ return CodeSampleFileParser.ConcatLines(lines.Where(l => !CodeSampleFileParser.IsCommentOrBlank(l)));
+ }
+
+ ///
+ /// Compiles and decompiles a source code.
+ ///
+ /// The source code to copile.
+ /// The decompilation result of compiled source code.
+ 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 { { "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();
+ }
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/DelegateConstruction.cs
new file mode 100644
index 0000000000..5d5071f676
--- /dev/null
+++ b/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()) {
+ if (item > 0) {
+ return delegate {
+ CaptureOfThisAndParameter(item + a);
+ };
+ }
+ }
+ return null;
+ }
+
+ public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a)
+ {
+ foreach (int item in Enumerable.Empty()) {
+ int copyOfItem = item;
+ if (item > 0) {
+ return delegate {
+ CaptureOfThisAndParameter(item + a + copyOfItem);
+ };
+ }
+ }
+ return null;
+ }
+ }
+
+ public static void Test(this string a)
+ {
+ }
+
+ public static Action ExtensionMethodUnbound()
+ {
+ return new Action(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(DelegateConstruction.ExtensionMethodBound);
+ }
+
+ public static object InstanceMethod()
+ {
+ return new Func("hello".ToUpper);
+ }
+
+ public static object InstanceMethodOnNull()
+ {
+ return new Func(((string)null).ToUpper);
+ }
+
+ public static List> AnonymousMethodStoreWithinLoop()
+ {
+ List> list = new List>();
+ for (int i = 0; i < 10; i++)
+ {
+ int counter;
+ list.Add(delegate(int x)
+ {
+ counter = x;
+ }
+ );
+ }
+ return list;
+ }
+
+ public static List> AnonymousMethodStoreOutsideLoop()
+ {
+ List> list = new List>();
+ 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> list = new List>();
+ 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> list = new List>();
+ for (int k = 0; k < 10; k++) {
+ list.Add(
+ delegate(int i) {
+ Console.WriteLine(i);
+ });
+ }
+ }
+
+ public static Action NameConflict3(int i)
+ {
+ return delegate(int j) {
+ for (int k = 0; k < j; k++) {
+ Console.WriteLine(k);
+ }
+ };
+ }
+
+ public static Func> CurriedAddition(int a)
+ {
+ return b => c => a + b + c;
+ }
+
+ public static Func>> CurriedAddition2(int a)
+ {
+ return b => c => d => a + b + c + d;
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs
new file mode 100644
index 0000000000..0db35e3573
--- /dev/null
+++ b/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);
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Generics.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Generics.cs
new file mode 100644
index 0000000000..9b7bd3feaf
--- /dev/null
+++ b/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
+ {
+ public class NestedClass
+ {
+ 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(ref this.arr, capacity);
+ }
+
+ public void Grow(int capacity)
+ {
+ if (capacity >= this.arr.Length)
+ {
+ this.Size(capacity);
+ }
+ }
+ }
+
+ public interface IInterface
+ {
+ void Method1() where T : class;
+ void Method2() where T : class;
+ }
+
+ public abstract class Base : Generics.IInterface
+ {
+ // constraints must be repeated on implicit interface implementation
+ public abstract void Method1() where T : class;
+
+ // constraints must not be specified on explicit interface implementation
+ void Generics.IInterface.Method2()
+ {
+ }
+ }
+
+ public class Derived : Generics.Base
+ {
+ // constraints are inherited automatically and must not be specified
+ public override void Method1()
+ {
+ }
+ }
+
+ private const Generics.MyArray.NestedEnum enumVal = Generics.MyArray.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[]);
+ private static Type type6 = typeof(Generics.MyArray<>.NestedEnum);
+
+ public static void MethodWithConstraint() where T : class, S where S : ICloneable, new()
+ {
+ }
+
+ public static void MethodWithStructConstraint() where T : struct
+ {
+ }
+
+ public static Dictionary.KeyCollection.Enumerator GetEnumerator(Dictionary d, Generics.MyArray.NestedClass nc)
+ {
+ // Tests references to inner classes in generic classes
+ return d.Keys.GetEnumerator();
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/CodeAssert.cs
new file mode 100644
index 0000000000..0a82fb82ac
--- /dev/null
+++ b/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(
+ 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
+ {
+ private IEqualityComparer baseComparer = EqualityComparer.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 NormalizeAndSplitCode(string input)
+ {
+ return input.Split(new[] { "\r\n", "\n\r", "\n", "\r" }, StringSplitOptions.None);
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Helpers/RemoveCompilerAttribute.cs
new file mode 100644
index 0000000000..2519165578
--- /dev/null
+++ b/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, 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);
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
new file mode 100644
index 0000000000..a3bcddf9ea
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -0,0 +1,113 @@
+
+
+
+ {FEC0DA52-C4A6-4710-BE36-B484A20C5E22}
+ Debug
+ x86
+ Library
+ ICSharpCode.Decompiler.Tests
+ ICSharpCode.Decompiler.Tests
+ v4.0
+ Properties
+ True
+ False
+ 4
+ false
+ False
+ 67,169,1058,728
+
+
+ x86
+ False
+ Auto
+ 4194304
+ 4096
+
+
+ ..\bin\Debug\
+ true
+ Full
+ False
+ DEBUG;TRACE
+
+
+ ..\bin\Release\
+ false
+ None
+ True
+ TRACE
+
+
+
+ ..\..\packages\DiffLib.1.0.0.55\lib\net35-Client\DiffLib.dll
+
+
+ False
+ .\nunit.framework.dll
+
+
+
+ 3.5
+
+
+
+ 3.5
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {D68133BD-1E63-496E-9EDE-4FBDBF77B486}
+ Mono.Cecil
+
+
+ {3B2A5653-EC97-4001-BB9B-D90F1AF2C371}
+ ICSharpCode.NRefactory
+
+
+ {984CC812-9470-4A13-AFF9-CC44068D666C}
+ ICSharpCode.Decompiler
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/IncrementDecrement.cs
new file mode 100644
index 0000000000..e06b680d68
--- /dev/null
+++ b/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 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())++;
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/InitializerTests.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/InitializerTests.cs
new file mode 100644
index 0000000000..a4ab57fbf8
--- /dev/null
+++ b/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 FieldList = new List();
+ public InitializerTests.MyEnum a
+ {
+ get;
+ set;
+ }
+ public List 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
+ {
+ 1,
+ 2,
+ 3
+ });
+ }
+
+ public static void CollectionInitializerDictionary()
+ {
+ InitializerTests.X(InitializerTests.Y(), new Dictionary
+ {
+ {
+ "First",
+ 1
+ },
+ {
+ "Second",
+ 2
+ },
+ {
+ "Third",
+ 3
+ }
+ });
+ }
+
+ public static void CollectionInitializerDictionaryWithEnumTypes()
+ {
+ InitializerTests.X(InitializerTests.Y(), new Dictionary
+ {
+ {
+ InitializerTests.MyEnum.a,
+ InitializerTests.MyEnum2.c
+ },
+ {
+ InitializerTests.MyEnum.b,
+ InitializerTests.MyEnum2.d
+ }
+ });
+ }
+
+ public static void NotACollectionInitializer()
+ {
+ List list = new List();
+ 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.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.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
+ }
+ }
+ }
+ };
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Loops.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Loops.cs
new file mode 100644
index 0000000000..03427a8bdc
--- /dev/null
+++ b/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 enumerable)
+ {
+ foreach (string current in enumerable)
+ {
+ current.ToLower();
+ }
+ }
+
+ public void ForEachOverList(List 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();
+ }
+ }
+}
+
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/MultidimensionalArray.cs
new file mode 100644
index 0000000000..c3557cee44
--- /dev/null
+++ b/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 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][,];
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/PInvoke.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/PInvoke.cs
new file mode 100644
index 0000000000..fe9b9b5bcc
--- /dev/null
+++ b/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)
+ {
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/PropertiesAndEvents.cs
new file mode 100644
index 0000000000..4bacc06e3e
--- /dev/null
+++ b/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';
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/QueryExpressions.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/QueryExpressions.cs
new file mode 100644
index 0000000000..143f3d30ca
--- /dev/null
+++ b/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 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 Details;
+ }
+
+ public class OrderDetail
+ {
+ public decimal UnitPrice;
+ public int Quantity;
+ }
+
+ public IEnumerable customers;
+ public IEnumerable 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() };
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe b/src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe
new file mode 100644
index 0000000000..db6194ad68
Binary files /dev/null and b/src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.exe differ
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.il b/src/Libraries/ICSharpCode.Decompiler/Tests/StackTests/StackTests.il
new file mode 100644
index 0000000000..51cee37549
--- /dev/null
+++ b/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
+
+
+// =============================================================
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Switch.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Switch.cs
new file mode 100644
index 0000000000..108b86cf40
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/Switch.cs
@@ -0,0 +1,77 @@
+// 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 Switch
+{
+ public static string ShortSwitchOverString(string text)
+ {
+ switch (text) {
+ case "First case":
+ return "Text";
+ default:
+ return "Default";
+ }
+ }
+
+ public static string SwitchOverString1(string text)
+ {
+ switch (text)
+ {
+ case "First case":
+ return "Text1";
+ case "Second case":
+ case "2nd case":
+ return "Text2";
+ case "Third case":
+ return "Text3";
+ case "Fourth case":
+ return "Text4";
+ case "Fifth case":
+ return "Text5";
+ case "Sixth case":
+ return "Text6";
+ case null:
+ return null;
+ default:
+ return "Default";
+ }
+ }
+
+ public static string SwitchOverString2()
+ {
+ switch (Environment.UserName)
+ {
+ case "First case":
+ return "Text1";
+ case "Second case":
+ return "Text2";
+ case "Third case":
+ return "Text3";
+ case "Fourth case":
+ return "Text4";
+ case "Fifth case":
+ return "Text5";
+ case "Sixth case":
+ return "Text6";
+ default:
+ return "Default";
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs
new file mode 100644
index 0000000000..f6d21fde1e
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/TestRunner.cs
@@ -0,0 +1,185 @@
+// 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 DiffLib;
+using ICSharpCode.Decompiler.Ast;
+using ICSharpCode.Decompiler.Tests.Helpers;
+using Microsoft.CSharp;
+using Mono.Cecil;
+using NUnit.Framework;
+
+namespace ICSharpCode.Decompiler.Tests
+{
+ [TestFixture]
+ public class TestRunner
+ {
+ [Test, Ignore("disambiguating overloads is not yet implemented")]
+ public void CallOverloadedMethod()
+ {
+ TestFile(@"..\..\Tests\CallOverloadedMethod.cs");
+ }
+
+ [Test, Ignore("unncessary primitive casts")]
+ public void CheckedUnchecked()
+ {
+ TestFile(@"..\..\Tests\CheckedUnchecked.cs");
+ }
+
+ [Test, Ignore("Missing cast on null")]
+ public void DelegateConstruction()
+ {
+ TestFile(@"..\..\Tests\DelegateConstruction.cs");
+ }
+
+ [Test]
+ public void ExceptionHandling()
+ {
+ TestFile(@"..\..\Tests\ExceptionHandling.cs");
+ }
+
+ [Test]
+ public void Generics()
+ {
+ TestFile(@"..\..\Tests\Generics.cs");
+ }
+
+ [Test]
+ public void CustomShortCircuitOperators()
+ {
+ TestFile(@"..\..\Tests\CustomShortCircuitOperators.cs");
+ }
+
+ [Test]
+ public void IncrementDecrement()
+ {
+ TestFile(@"..\..\Tests\IncrementDecrement.cs");
+ }
+
+ [Test]
+ public void InitializerTests()
+ {
+ TestFile(@"..\..\Tests\InitializerTests.cs");
+ }
+
+ [Test]
+ public void Loops()
+ {
+ TestFile(@"..\..\Tests\Loops.cs");
+ }
+
+ [Test]
+ public void MultidimensionalArray()
+ {
+ TestFile(@"..\..\Tests\MultidimensionalArray.cs");
+ }
+
+ [Test]
+ public void PInvoke()
+ {
+ TestFile(@"..\..\Tests\PInvoke.cs");
+ }
+
+ [Test]
+ public void PropertiesAndEvents()
+ {
+ TestFile(@"..\..\Tests\PropertiesAndEvents.cs");
+ }
+
+ [Test, Ignore("Formatting differences in anonymous method create expressions")]
+ public void QueryExpressions()
+ {
+ TestFile(@"..\..\Tests\QueryExpressions.cs");
+ }
+
+ [Test, Ignore("switch transform doesn't recreate the exact original switch")]
+ public void Switch()
+ {
+ TestFile(@"..\..\Tests\Switch.cs");
+ }
+
+ [Test]
+ public void UndocumentedExpressions()
+ {
+ TestFile(@"..\..\Tests\UndocumentedExpressions.cs");
+ }
+
+ [Test, Ignore("has incorrect casts to IntPtr")]
+ public void UnsafeCode()
+ {
+ TestFile(@"..\..\Tests\UnsafeCode.cs");
+ }
+
+ [Test]
+ public void ValueTypes()
+ {
+ TestFile(@"..\..\Tests\ValueTypes.cs");
+ }
+
+ [Test, Ignore("Redundant yield break; not removed")]
+ public void YieldReturn()
+ {
+ TestFile(@"..\..\Tests\YieldReturn.cs");
+ }
+
+ [Test]
+ public void TypeAnalysis()
+ {
+ TestFile(@"..\..\Tests\TypeAnalysisTests.cs");
+ }
+
+ static void TestFile(string fileName)
+ {
+ string code = File.ReadAllText(fileName);
+ AssemblyDefinition assembly = Compile(code);
+ AstBuilder decompiler = new AstBuilder(new DecompilerContext(assembly.MainModule));
+ decompiler.AddAssembly(assembly);
+ new Helpers.RemoveCompilerAttribute().Run(decompiler.CompilationUnit);
+ StringWriter output = new StringWriter();
+ decompiler.GenerateCode(new PlainTextOutput(output));
+ CodeAssert.AreEqual(code, output.ToString());
+ }
+
+ static AssemblyDefinition Compile(string code)
+ {
+ CSharpCodeProvider provider = new CSharpCodeProvider(new Dictionary { { "CompilerVersion", "v4.0" } });
+ CompilerParameters options = new CompilerParameters();
+ options.CompilerOptions = "/unsafe /o-";
+ 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();
+ }
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs
new file mode 100644
index 0000000000..3224debdde
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/TypeAnalysisTests.cs
@@ -0,0 +1,109 @@
+// 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 TypeAnalysisTests
+{
+ public byte SubtractFrom256(byte b)
+ {
+ return (byte)(256 - (int)b);
+ }
+
+ #region Shift
+ public int LShiftInteger(int num1, int num2)
+ {
+ return num1 << num2;
+ }
+
+ public uint LShiftUnsignedInteger(uint num1, uint num2)
+ {
+ return num1 << (int)num2;
+ }
+
+ public long LShiftLong(long num1, long num2)
+ {
+ return num1 << (int)num2;
+ }
+
+ public ulong LShiftUnsignedLong(ulong num1, ulong num2)
+ {
+ return num1 << (int)num2;
+ }
+
+ public int RShiftInteger(int num1, int num2)
+ {
+ return num1 >> num2;
+ }
+
+ public uint RShiftUnsignedInteger(uint num1, int num2)
+ {
+ return num1 >> num2;
+ }
+
+ public long RShiftLong(long num1, long num2)
+ {
+ return num1 >> (int)num2;
+ }
+
+ public ulong RShiftUnsignedLong(ulong num1, ulong num2)
+ {
+ return num1 >> (int)num2;
+ }
+
+ public int ShiftByte(byte num)
+ {
+ return (int)num << 8;
+ }
+
+ public int RShiftByte(byte num)
+ {
+ return num >> 8;
+ }
+
+ public uint RShiftByteWithZeroExtension(byte num)
+ {
+ return (uint)num >> 8;
+ }
+
+ public int RShiftByteAsSByte(byte num)
+ {
+ return (sbyte)num >> 8;
+ }
+
+ public int RShiftSByte(sbyte num)
+ {
+ return num >> 8;
+ }
+
+ public uint RShiftSByteWithZeroExtension(sbyte num)
+ {
+ return (uint)num >> 8;
+ }
+
+ public int RShiftSByteAsByte(sbyte num)
+ {
+ return (byte)num >> 8;
+ }
+ #endregion
+
+ public int GetHashCode(long num)
+ {
+ return (int)num ^ (int)(num >> 32);
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Types/EnumTests.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/EnumTests.cs
new file mode 100644
index 0000000000..fb48d016ed
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/EnumTests.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace ICSharpCode.Decompiler.Tests.Types
+{
+ [TestFixture]
+ public class EnumTests : DecompilerTestBase
+ {
+ [Test]
+ public void EnumSamples()
+ {
+ ValidateFileRoundtrip(@"Types\S_EnumSamples.cs");
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_EnumSamples.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_EnumSamples.cs
new file mode 100644
index 0000000000..67d81fa30e
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_EnumSamples.cs
@@ -0,0 +1,114 @@
+// 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
+
+//$$ SingleValue
+public class TS_SingleValue
+{
+ public AttributeTargets Method()
+ {
+ return AttributeTargets.Class;
+ }
+}
+//$$ TwoValuesOr
+public class TS_TwoValuesOr
+{
+ public AttributeTargets Method()
+ {
+ return AttributeTargets.Class | AttributeTargets.Method;
+ }
+}
+//$$ ThreeValuesOr
+public class TS_ThreeValuesOr
+{
+ public AttributeTargets Method()
+ {
+ return AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Parameter;
+ }
+}
+//$$ UnknownNumericValue
+public class TS_UnknownNumericValue
+{
+ public AttributeTargets Method()
+ {
+ return (AttributeTargets)1000000;
+ }
+}
+//$$ AllValue
+public class TS_AllValue
+{
+ public AttributeTargets Method()
+ {
+ return AttributeTargets.All;
+ }
+}
+//$$ ZeroValue
+public class TS_ZeroValue
+{
+ public AttributeTargets Method()
+ {
+ return (AttributeTargets)0;
+ }
+}
+//$$ PreservingTypeWhenBoxed
+public class TS_PreservingTypeWhenBoxed
+{
+ public object Method()
+ {
+ return AttributeTargets.Delegate;
+ }
+}
+//$$ PreservingTypeWhenBoxedTwoEnum
+public class TS_PreservingTypeWhenBoxedTwoEnum
+{
+ public object Method()
+ {
+ return AttributeTargets.Class | AttributeTargets.Delegate;
+ }
+}
+//$$ DeclarationSimpleEnum
+public enum TS_DeclarationSimpleEnum
+{
+ Item1,
+ Item2
+}
+//$$ DeclarationLongBasedEnum
+public enum TS_DeclarationLongBasedEnum : long
+{
+ Item1,
+ Item2
+}
+//$$ DeclarationLongWithInitializers
+public enum TS_DeclarationLongWithInitializers : long
+{
+ Item1,
+ Item2 = 20L,
+ Item3
+}
+//$$ DeclarationShortWithInitializers
+public enum TS_DeclarationShortWithInitializers : short
+{
+ Item1,
+ Item2 = 20,
+ Item3
+}
+//$$ DeclarationByteWithInitializers
+public enum TS_DeclarationByteWithInitializers : byte
+{
+ Item1,
+ Item2 = 20,
+ Item3
+}
+//$$ DeclarationFlags
+[Flags]
+public enum TS_DeclarationFlags
+{
+ None = 0,
+ Item1 = 1,
+ Item2 = 2,
+ Item3 = 4,
+ All = 7
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs
new file mode 100644
index 0000000000..99bf4279e3
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_TypeDeclarations.cs
@@ -0,0 +1,17 @@
+using System;
+
+namespace ClassMultiInterface
+{
+ public interface IA
+ {
+ }
+ public interface IA2 : IA
+ {
+ }
+ public interface IB
+ {
+ }
+ public class C : IA2, IB
+ {
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs
new file mode 100644
index 0000000000..22458bcfce
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs
@@ -0,0 +1,1121 @@
+// 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
+
+//$$ IndexerWithGetOnly
+namespace IndexerWithGetOnly
+{
+ public class MyClass
+ {
+ public int this[int i]
+ {
+ get
+ {
+ return i;
+ }
+ }
+ }
+}
+//$$ IndexerWithSetOnly
+namespace IndexerWithSetOnly
+{
+ public class MyClass
+ {
+ public int this[int i]
+ {
+ set
+ {
+ }
+ }
+ }
+}
+//$$ IndexerWithMoreParameters
+namespace IndexerWithMoreParameters
+{
+ public class MyClass
+ {
+ public int this[int i, string s, Type t]
+ {
+ get
+ {
+ return 0;
+ }
+ }
+ }
+}
+//$$ IndexerInGenericClass
+namespace IndexerInGenericClass
+{
+ public class MyClass
+ {
+ public int this[T t]
+ {
+ get
+ {
+ return 0;
+ }
+ }
+ }
+}
+//$$ OverloadedIndexer
+namespace OverloadedIndexer
+{
+ public class MyClass
+ {
+ public int this[int t]
+ {
+ get
+ {
+ return 0;
+ }
+ }
+ public int this[string s]
+ {
+ get
+ {
+ return 0;
+ }
+ set
+ {
+ Console.WriteLine(value + " " + s);
+ }
+ }
+ }
+}
+//$$ IndexerInInterface
+namespace IndexerInInterface
+{
+ public interface IInterface
+ {
+ int this[string s, string s2]
+ {
+ set;
+ }
+ }
+}
+//$$ IndexerInterfaceExplicitImplementation
+namespace IndexerInterfaceExplicitImplementation
+{
+ public interface IMyInterface
+ {
+ int this[string s]
+ {
+ get;
+ }
+ }
+ public class MyClass : IMyInterface
+ {
+ int IMyInterface.this[string s]
+ {
+ get
+ {
+ return 3;
+ }
+ }
+ }
+}
+//$$ IndexerInterfaceImplementation
+namespace IndexerInterfaceImplementation
+{
+ public interface IMyInterface
+ {
+ int this[string s]
+ {
+ get;
+ }
+ }
+ public class MyClass : IMyInterface
+ {
+ public int this[string s]
+ {
+ get
+ {
+ return 3;
+ }
+ }
+ }
+}
+//$$ IndexerAbstract
+namespace IndexerAbstract
+{
+ public abstract class MyClass
+ {
+ public abstract int this[string s, string s2]
+ {
+ set;
+ }
+ protected abstract string this[int index]
+ {
+ get;
+ }
+ }
+}
+//$$ MethodExplicit
+namespace MethodExplicit
+{
+ public interface IMyInterface
+ {
+ void MyMethod();
+ }
+ public class MyClass : IMyInterface
+ {
+ void IMyInterface.MyMethod()
+ {
+ }
+ }
+}
+//$$ MethodFromInterfaceVirtual
+namespace MethodFromInterfaceVirtual
+{
+ public interface IMyInterface
+ {
+ void MyMethod();
+ }
+ public class MyClass : IMyInterface
+ {
+ public virtual void MyMethod()
+ {
+ }
+ }
+}
+//$$ MethodFromInterface
+namespace MethodFromInterface
+{
+ public interface IMyInterface
+ {
+ void MyMethod();
+ }
+ public class MyClass : IMyInterface
+ {
+ public void MyMethod()
+ {
+ }
+ }
+}
+//$$ MethodFromInterfaceAbstract
+namespace MethodFromInterfaceAbstract
+{
+ public interface IMyInterface
+ {
+ void MyMethod();
+ }
+ public abstract class MyClass : IMyInterface
+ {
+ public abstract void MyMethod();
+ }
+}
+//$$ PropertyInterface
+namespace PropertyInterface
+{
+ public interface IMyInterface
+ {
+ int MyProperty
+ {
+ get;
+ set;
+ }
+ }
+}
+//$$ PropertyInterfaceExplicitImplementation
+namespace PropertyInterfaceExplicitImplementation
+{
+ public interface IMyInterface
+ {
+ int MyProperty
+ {
+ get;
+ set;
+ }
+ }
+ public class MyClass : IMyInterface
+ {
+ int IMyInterface.MyProperty
+ {
+ get
+ {
+ return 0;
+ }
+ set
+ {
+ }
+ }
+ }
+}
+//$$ PropertyInterfaceImplementation
+namespace PropertyInterfaceImplementation
+{
+ public interface IMyInterface
+ {
+ int MyProperty
+ {
+ get;
+ set;
+ }
+ }
+ public class MyClass : IMyInterface
+ {
+ public int MyProperty
+ {
+ get
+ {
+ return 0;
+ }
+ set
+ {
+ }
+ }
+ }
+}
+//$$ PropertyPrivateGetPublicSet
+namespace PropertyPrivateGetPublicSet
+{
+ public class MyClass
+ {
+ public int MyProperty
+ {
+ private get
+ {
+ return 3;
+ }
+ set
+ {
+ }
+ }
+ }
+}
+//$$ PropertyPublicGetProtectedSet
+namespace PropertyPublicGetProtectedSet
+{
+ public class MyClass
+ {
+ public int MyProperty
+ {
+ get
+ {
+ return 3;
+ }
+ protected set
+ {
+ }
+ }
+ }
+}
+//$$ PropertyOverrideDefaultAccessorOnly
+namespace PropertyOverrideDefaultAccessorOnly
+{
+ public class MyClass
+ {
+ public virtual int MyProperty
+ {
+ get
+ {
+ return 3;
+ }
+ protected set
+ {
+ }
+ }
+ }
+ public class Derived : MyClass
+ {
+ public override int MyProperty
+ {
+ get
+ {
+ return 4;
+ }
+ }
+ }
+}
+//$$ PropertyOverrideRestrictedAccessorOnly
+namespace PropertyOverrideRestrictedAccessorOnly
+{
+ public class MyClass
+ {
+ public virtual int MyProperty
+ {
+ get
+ {
+ return 3;
+ }
+ protected set
+ {
+ }
+ }
+ }
+ public class Derived : MyClass
+ {
+ public override int MyProperty
+ {
+ protected set
+ {
+ }
+ }
+ }
+}
+//$$ PropertyOverrideOneAccessor
+namespace PropertyOverrideOneAccessor
+{
+ public class MyClass
+ {
+ protected internal virtual int MyProperty
+ {
+ get
+ {
+ return 3;
+ }
+ protected set
+ {
+ }
+ }
+ }
+ public class DerivedNew : MyClass
+ {
+ public new virtual int MyProperty
+ {
+ set
+ {
+ }
+ }
+ }
+ public class DerivedOverride : DerivedNew
+ {
+ public override int MyProperty
+ {
+ set
+ {
+ }
+ }
+ }
+}
+//$$ IndexerOverrideRestrictedAccessorOnly
+namespace IndexerOverrideRestrictedAccessorOnly
+{
+ public class MyClass
+ {
+ public virtual int this[string s]
+ {
+ get
+ {
+ return 3;
+ }
+ protected set
+ {
+ }
+ }
+ protected internal virtual int this[int i]
+ {
+ protected get
+ {
+ return 2;
+ }
+ set
+ {
+ }
+ }
+ }
+ public class Derived : MyClass
+ {
+ protected internal override int this[int i]
+ {
+ protected get
+ {
+ return 4;
+ }
+ }
+ }
+}
+//$$ HideProperty
+namespace HideProperty
+{
+ public class A
+ {
+ public virtual int P
+ {
+ get
+ {
+ return 0;
+ }
+ set
+ {
+ }
+ }
+ }
+ public class B : A
+ {
+ private new int P
+ {
+ get
+ {
+ return 0;
+ }
+ set
+ {
+ }
+ }
+ }
+ public class C : B
+ {
+ public override int P
+ {
+ set
+ {
+ }
+ }
+ }
+}
+//$$ HideMembers
+namespace HideMembers
+{
+ public class A
+ {
+ public int F;
+ public int Prop
+ {
+ get
+ {
+ return 3;
+ }
+ }
+ public int G
+ {
+ get
+ {
+ return 3;
+ }
+ }
+ }
+ public class B : A
+ {
+ public new int F
+ {
+ get
+ {
+ return 3;
+ }
+ }
+ public new string Prop
+ {
+ get
+ {
+ return "a";
+ }
+ }
+ }
+ public class C : A
+ {
+ public new int G;
+ }
+ public class D : A
+ {
+ public new void F()
+ {
+ }
+ }
+ public class D1 : D
+ {
+ public new int F;
+ }
+ public class E : A
+ {
+ private new class F
+ {
+ }
+ }
+}
+//$$ HideMembers2
+namespace HideMembers2
+{
+ public class G
+ {
+ public int Item
+ {
+ get
+ {
+ return 1;
+ }
+ }
+ }
+ public class G2 : G
+ {
+ public int this[int i]
+ {
+ get
+ {
+ return 2;
+ }
+ }
+ }
+ public class G3 : G2
+ {
+ public new int Item
+ {
+ get
+ {
+ return 4;
+ }
+ }
+ }
+ public class H
+ {
+ public int this[int j]
+ {
+ get
+ {
+ return 0;
+ }
+ }
+ }
+ public class H2 : H
+ {
+ public int Item
+ {
+ get
+ {
+ return 2;
+ }
+ }
+ }
+ public class H3 : H2
+ {
+ public new string this[int j]
+ {
+ get
+ {
+ return null;
+ }
+ }
+ }
+}
+//$$ HideMembers2a
+namespace HideMembers2a
+{
+ public interface IA
+ {
+ int this[int i]
+ {
+ get;
+ }
+ }
+ public class A : IA
+ {
+ int IA.this[int i]
+ {
+ get
+ {
+ throw new NotImplementedException();
+ }
+ }
+ }
+ public class A1 : A
+ {
+ public int this[int i]
+ {
+ get
+ {
+ return 3;
+ }
+ }
+ }
+}
+//$$ HideMembers3
+namespace HideMembers3
+{
+ public class G
+ {
+ public void M1(T p)
+ {
+ }
+ public int M2(int t)
+ {
+ return 3;
+ }
+ }
+ public class G1 : G
+ {
+ public new int M1(int i)
+ {
+ return 0;
+ }
+ public int M2(T i)
+ {
+ return 2;
+ }
+ }
+ public class G2 : G
+ {
+ public int M1(T p)
+ {
+ return 4;
+ }
+ }
+ public class J
+ {
+ public int P
+ {
+ get
+ {
+ return 2;
+ }
+ }
+ }
+ public class J2 : J
+ {
+ public int get_P;
+ }
+}
+//$$ HideMembers4
+namespace HideMembers4
+{
+ public class A
+ {
+ public void M(T t)
+ {
+ }
+ }
+ public class A1 : A
+ {
+ public new void M(K t)
+ {
+ }
+ public void M(int t)
+ {
+ }
+ }
+ public class B
+ {
+ public void M()
+ {
+ }
+ public void M1()
+ {
+ }
+ public void M2(T t)
+ {
+ }
+ }
+ public class B1 : B
+ {
+ public void M()
+ {
+ }
+ public new void M1()
+ {
+ }
+ public new void M2(R r)
+ {
+ }
+ }
+ public class C
+ {
+ public void M(T t)
+ {
+ }
+ }
+ public class C1 : C
+ {
+ public void M(TT t)
+ {
+ }
+ }
+}
+//$$ HideMembers5
+namespace HideMembers5
+{
+ public class A
+ {
+ public void M(int t)
+ {
+ }
+ }
+ public class A1 : A
+ {
+ public void M(ref int t)
+ {
+ }
+ }
+ public class B
+ {
+ public void M(ref int l)
+ {
+ }
+ }
+ public class B1 : B
+ {
+ public void M(out int l)
+ {
+ l = 2;
+ }
+ public void M(ref long l)
+ {
+ }
+ }
+}
+//$$ HideMemberSkipNotVisible
+namespace HideMemberSkipNotVisible
+{
+ public class A
+ {
+ protected int F;
+ protected string P
+ {
+ get
+ {
+ return null;
+ }
+ }
+ }
+ public class B : A
+ {
+ private new string F;
+ private new int P
+ {
+ set
+ {
+ }
+ }
+ }
+}
+//$$ HideNestedClass
+namespace HideNestedClass
+{
+ public class A
+ {
+ public class N1
+ {
+ }
+ protected class N2
+ {
+ }
+ private class N3
+ {
+ }
+ internal class N4
+ {
+ }
+ protected internal class N5
+ {
+ }
+ }
+ public class B : A
+ {
+ public new int N1;
+ public new int N2;
+ public int N3;
+ public new int N4;
+ public new int N5;
+ }
+}
+//$$ HidePropertyReservedMethod
+namespace HidePropertyReservedMethod
+{
+ public class A
+ {
+ public int P
+ {
+ get
+ {
+ return 1;
+ }
+ }
+ }
+ public class B : A
+ {
+ public int get_P()
+ {
+ return 2;
+ }
+ public void set_P(int value)
+ {
+ }
+ }
+}
+//$$ HideIndexerDiffAccessor
+namespace HideIndexerDiffAccessor
+{
+ public class A
+ {
+ public int this[int i]
+ {
+ get
+ {
+ return 2;
+ }
+ }
+ }
+ public class B : A
+ {
+ public new int this[int j]
+ {
+ set
+ {
+ }
+ }
+ }
+}
+//$$ HideIndexerGeneric
+namespace HideIndexerGeneric
+{
+ public class A
+ {
+ public virtual int this[T r]
+ {
+ get
+ {
+ return 0;
+ }
+ set
+ {
+ }
+ }
+ }
+ public class B : A
+ {
+ private new int this[int k]
+ {
+ get
+ {
+ return 0;
+ }
+ set
+ {
+ }
+ }
+ }
+ public class C : A
+ {
+ public override int this[T s]
+ {
+ set
+ {
+ }
+ }
+ }
+ public class D : C
+ {
+ public new virtual int this[T s]
+ {
+ set
+ {
+ }
+ }
+ }
+}
+//$$ HideMethod
+namespace HideMethod
+{
+ public class A
+ {
+ public virtual void F()
+ {
+ }
+ }
+ public class B : A
+ {
+ private new void F()
+ {
+ base.F();
+ }
+ }
+ public class C : B
+ {
+ public override void F()
+ {
+ base.F();
+ }
+ }
+}
+//$$ HideMethodGeneric
+namespace HideMethodGeneric
+{
+ public class A
+ {
+ public virtual void F(T s)
+ {
+ }
+ public new static bool Equals(object o1, object o2)
+ {
+ return true;
+ }
+ }
+ public class B : A
+ {
+ private new void F(string k)
+ {
+ }
+ public void F(int i)
+ {
+ }
+ }
+ public class C : A
+ {
+ public override void F(T r)
+ {
+ }
+ public void G(T t)
+ {
+ }
+ }
+ public class D : C
+ {
+ public new virtual void F(T1 k)
+ {
+ }
+ public virtual void F(T2 k)
+ {
+ }
+ public virtual void G(T2 t)
+ {
+ }
+ }
+}
+//$$ HideMethodGenericSkipPrivate
+namespace HideMethodGenericSkipPrivate
+{
+ public class A
+ {
+ public virtual void F(T t)
+ {
+ }
+ }
+ public class B : A
+ {
+ private new void F(T t)
+ {
+ }
+ private void K()
+ {
+ }
+ }
+ public class C : B
+ {
+ public override void F(T tt)
+ {
+ }
+ public void K()
+ {
+ }
+ }
+ public class D : B
+ {
+ public override void F(int t)
+ {
+ }
+ }
+}
+//$$ HideMethodGeneric2
+namespace HideMethodGeneric2
+{
+ public class A
+ {
+ public virtual void F(int i)
+ {
+ }
+ public void K()
+ {
+ }
+ }
+ public class B : A
+ {
+ protected virtual void F(T t)
+ {
+ }
+ public void K()
+ {
+ }
+ }
+ public class C : B
+ {
+ protected override void F(int k)
+ {
+ }
+ public new void K()
+ {
+ }
+ }
+ public class D : B
+ {
+ public override void F(int k)
+ {
+ }
+ public void L()
+ {
+ }
+ }
+ public class E
+ {
+ public void M(T t, T2 t2)
+ {
+ }
+ }
+ public class F : E
+ {
+ public void M(T t1, T t2)
+ {
+ }
+ }
+}
+//$$ HideMethodDiffSignatures
+namespace HideMethodDiffSignatures
+{
+ public class C1
+ {
+ public virtual void M(T arg)
+ {
+ }
+ }
+ public class C2 : C1
+ {
+ public new virtual void M(T2 arg)
+ {
+ }
+ }
+ public class C3 : C2
+ {
+ public new virtual void M(bool arg)
+ {
+ }
+ }
+}
+//$$ HideMethodStatic
+namespace HideMethodStatic
+{
+ public class A
+ {
+ public int N
+ {
+ get
+ {
+ return 0;
+ }
+ }
+ }
+ public class B
+ {
+ public int N()
+ {
+ return 0;
+ }
+ }
+}
+//$$ HideEvent
+namespace HideEvent
+{
+ public class A
+ {
+ public virtual event EventHandler E;
+ public event EventHandler F;
+ }
+ public class B : A
+ {
+ public new virtual event EventHandler E;
+ public new event EventHandler F;
+ }
+ public class C : B
+ {
+ public override event EventHandler E;
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs
new file mode 100644
index 0000000000..3bb80ed84b
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/Types/TypeTests.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using NUnit.Framework;
+
+namespace ICSharpCode.Decompiler.Tests.Types
+{
+ [TestFixture]
+ public class TypeTests : DecompilerTestBase
+ {
+ [Test]
+ public void TypeMemberDeclarations()
+ {
+ ValidateFileRoundtrip(@"Types\S_TypeMemberDeclarations.cs");
+ }
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs
new file mode 100644
index 0000000000..80f5b0371e
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/UndocumentedExpressions.cs
@@ -0,0 +1,41 @@
+// 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 UndocumentedExpressions
+{
+ public static int GetArgCount(__arglist)
+ {
+ ArgIterator argIterator = new ArgIterator(__arglist);
+ return argIterator.GetRemainingCount();
+ }
+
+ public static void MakeTypedRef(object o)
+ {
+ TypedReference tr = __makeref(o);
+ UndocumentedExpressions.AcceptTypedRef(tr);
+ }
+
+ private static void AcceptTypedRef(TypedReference tr)
+ {
+ Console.WriteLine("Value is: " + __refvalue(tr, object).ToString());
+ Console.WriteLine("Type is: " + __reftype(tr).Name);
+ __refvalue(tr, object) = 1;
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/UnsafeCode.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/UnsafeCode.cs
new file mode 100644
index 0000000000..198f0fc978
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/UnsafeCode.cs
@@ -0,0 +1,114 @@
+// 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 UnsafeCode
+{
+ public unsafe int* NullPointer
+ {
+ get
+ {
+ return null;
+ }
+ }
+
+ public unsafe long ConvertDoubleToLong(double d)
+ {
+ return *(long*)(&d);
+ }
+
+ public unsafe void PassRefParameterAsPointer(ref int p)
+ {
+ fixed (int* ptr = &p)
+ {
+ this.PassPointerAsRefParameter(ptr);
+ }
+ }
+
+ public unsafe void PassPointerAsRefParameter(int* p)
+ {
+ this.PassRefParameterAsPointer(ref *p);
+ }
+
+ public unsafe void AddressInMultiDimensionalArray(double[,] matrix)
+ {
+ fixed (double* ptr = &matrix[1, 2]) {
+ this.PointerReferenceExpression(ptr);
+ }
+ }
+
+ public unsafe void FixedStringAccess(string text)
+ {
+ fixed (char* ptr = text)
+ {
+ char* ptr2 = ptr;
+ while (*ptr2 != 0)
+ {
+ *ptr2 = 'A';
+ ptr2++;
+ }
+ }
+ }
+
+ public unsafe void PutDoubleIntoLongArray1(long[] array, int index, double val)
+ {
+ fixed (long* ptr = array)
+ {
+ ((double*)ptr)[index] = val;
+ }
+ }
+
+ public unsafe void PutDoubleIntoLongArray2(long[] array, int index, double val)
+ {
+ fixed (long* ptr = &array[index])
+ {
+ *(double*)ptr = val;
+ }
+ }
+
+ public unsafe string PointerReferenceExpression(double* d)
+ {
+ return d->ToString();
+ }
+
+ public unsafe void FixMultipleStrings(string text)
+ {
+ fixed (char* ptr = text, userName = Environment.UserName, ptr2 = text)
+ {
+ *ptr = 'c';
+ *userName = 'd';
+ *ptr2 = 'e';
+ }
+ }
+
+ public unsafe string StackAlloc(int count)
+ {
+ char* ptr = stackalloc char[count];
+ for (int i = 0; i < count; i++)
+ {
+ ptr[i] = (char)i;
+ }
+ return this.PointerReferenceExpression((double*)ptr);
+ }
+
+ unsafe ~UnsafeCode()
+ {
+ this.PassPointerAsRefParameter(this.NullPointer);
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/ValueTypes.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/ValueTypes.cs
new file mode 100644
index 0000000000..1d7ec437d8
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/ValueTypes.cs
@@ -0,0 +1,162 @@
+// 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 ValueTypes
+{
+ public struct S
+ {
+ public int Field;
+
+ public S(int field)
+ {
+ this.Field = field;
+ }
+
+ public void SetField()
+ {
+ this.Field = 5;
+ }
+
+ public void MethodCalls()
+ {
+ this.SetField();
+ ValueTypes.S.Test(this);
+ ValueTypes.S.Test(ref this);
+ }
+
+ private static void Test(ValueTypes.S byVal)
+ {
+ }
+
+ private static void Test(ref ValueTypes.S byRef)
+ {
+ }
+ }
+
+ private static readonly ValueTypes.S ReadOnlyS = default(ValueTypes.S);
+ private static ValueTypes.S MutableS = default(ValueTypes.S);
+
+ public static void CallMethodViaField()
+ {
+ ValueTypes.ReadOnlyS.SetField();
+ ValueTypes.MutableS.SetField();
+ ValueTypes.S mutableS = ValueTypes.MutableS;
+ mutableS.SetField();
+ }
+
+ public static ValueTypes.S InitObj1()
+ {
+ ValueTypes.S result = default(ValueTypes.S);
+ ValueTypes.MakeArray();
+ return result;
+ }
+
+ public static ValueTypes.S InitObj2()
+ {
+ return default(ValueTypes.S);
+ }
+
+ public static void InitObj3(out ValueTypes.S p)
+ {
+ p = default(ValueTypes.S);
+ }
+
+ public static ValueTypes.S CallValueTypeCtor1()
+ {
+ return new ValueTypes.S(10);
+ }
+
+ public static ValueTypes.S CallValueTypeCtor2()
+ {
+ ValueTypes.S result = new ValueTypes.S(10);
+ return result;
+ }
+
+ public static ValueTypes.S Copy1(ValueTypes.S p)
+ {
+ return p;
+ }
+
+ public static ValueTypes.S Copy2(ref ValueTypes.S p)
+ {
+ return p;
+ }
+
+ public static void Copy3(ValueTypes.S p, out ValueTypes.S o)
+ {
+ o = p;
+ }
+
+ public static void Copy4(ref ValueTypes.S p, out ValueTypes.S o)
+ {
+ o = p;
+ }
+
+ public static void Copy4b(ref ValueTypes.S p, out ValueTypes.S o)
+ {
+ // test passing through by-ref arguments
+ ValueTypes.Copy4(ref p, out o);
+ }
+
+ public static void Issue56(int i, out string str)
+ {
+ str = "qq";
+ str += i.ToString();
+ }
+
+ public static void CopyAroundAndModifyField(ValueTypes.S s)
+ {
+ ValueTypes.S s2 = s;
+ s2.Field += 10;
+ s = s2;
+ }
+
+ private static int[] MakeArray()
+ {
+ return null;
+ }
+
+ public static void IncrementArrayLocation()
+ {
+ ValueTypes.MakeArray()[Environment.TickCount]++;
+ }
+
+ public static bool Is(object obj)
+ {
+ return obj is ValueTypes.S;
+ }
+
+ public static bool IsNullable(object obj)
+ {
+ return obj is ValueTypes.S?;
+ }
+
+ public static ValueTypes.S? As(object obj)
+ {
+ return obj as ValueTypes.S?;
+ }
+
+ public static ValueTypes.S OnlyChangeTheCopy(ValueTypes.S p)
+ {
+ ValueTypes.S s = p;
+ s.SetField();
+ return p;
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/YieldReturn.cs b/src/Libraries/ICSharpCode.Decompiler/Tests/YieldReturn.cs
new file mode 100644
index 0000000000..7546b194f6
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/YieldReturn.cs
@@ -0,0 +1,148 @@
+// 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 YieldReturn
+{
+ public static IEnumerable SimpleYieldReturn()
+ {
+ yield return "A";
+ yield return "B";
+ yield return "C";
+ }
+
+ public static IEnumerable YieldReturnInLoop()
+ {
+ for (int i = 0; i < 100; i++) {
+ yield return i;
+ }
+ }
+
+ public static IEnumerable YieldReturnWithTryFinally()
+ {
+ yield return 0;
+ try {
+ yield return 1;
+ } finally {
+ Console.WriteLine("Finally!");
+ }
+ yield return 2;
+ }
+
+ public static IEnumerable YieldReturnInLock1(object o)
+ {
+ lock (o) {
+ yield return 1;
+ }
+ }
+
+ public static IEnumerable YieldReturnInLock2(object o)
+ {
+ lock (o) {
+ yield return 1;
+ o = null;
+ yield return 2;
+ }
+ }
+
+ public static IEnumerable YieldReturnWithNestedTryFinally(bool breakInMiddle)
+ {
+ Console.WriteLine("Start of method - 1");
+ yield return "Start of method";
+ Console.WriteLine("Start of method - 2");
+ try {
+ Console.WriteLine("Within outer try - 1");
+ yield return "Within outer try";
+ Console.WriteLine("Within outer try - 2");
+ try {
+ Console.WriteLine("Within inner try - 1");
+ yield return "Within inner try";
+ Console.WriteLine("Within inner try - 2");
+ if (breakInMiddle)
+ yield break;
+ Console.WriteLine("End of inner try - 1");
+ yield return "End of inner try";
+ Console.WriteLine("End of inner try - 2");
+ } finally {
+ Console.WriteLine("Inner Finally");
+ }
+ Console.WriteLine("End of outer try - 1");
+ yield return "End of outer try";
+ Console.WriteLine("End of outer try - 2");
+ } finally {
+ Console.WriteLine("Outer Finally");
+ }
+ Console.WriteLine("End of method - 1");
+ yield return "End of method";
+ Console.WriteLine("End of method - 2");
+ }
+
+ public static IEnumerable YieldReturnWithTwoNonNestedFinallyBlocks(IEnumerable input)
+ {
+ // outer try-finally block
+ foreach (string line in input) {
+ // nested try-finally block
+ try {
+ yield return line;
+ } finally {
+ Console.WriteLine("Processed " + line);
+ }
+ }
+ yield return "A";
+ yield return "B";
+ yield return "C";
+ yield return "D";
+ yield return "E";
+ yield return "F";
+ // outer try-finally block
+ foreach (string line in input)
+ yield return line.ToUpper();
+ }
+
+ public static IEnumerable> YieldReturnWithAnonymousMethods1(IEnumerable input)
+ {
+ foreach (string line in input) {
+ yield return () => line;
+ }
+ }
+
+ public static IEnumerable> YieldReturnWithAnonymousMethods2(IEnumerable input)
+ {
+ foreach (string line in input) {
+ string copy = line;
+ yield return () => copy;
+ }
+ }
+
+ public static IEnumerable GetEvenNumbers(int n)
+ {
+ for (int i = 0; i < n; i++) {
+ if (i % 2 == 0)
+ yield return i;
+ }
+ }
+
+ public static IEnumerable YieldChars()
+ {
+ yield return 'a';
+ yield return 'b';
+ yield return 'c';
+ }
+}
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/nunit.framework.dll b/src/Libraries/ICSharpCode.Decompiler/Tests/nunit.framework.dll
new file mode 100644
index 0000000000..875e098420
Binary files /dev/null and b/src/Libraries/ICSharpCode.Decompiler/Tests/nunit.framework.dll differ
diff --git a/src/Libraries/ICSharpCode.Decompiler/Tests/packages.config b/src/Libraries/ICSharpCode.Decompiler/Tests/packages.config
new file mode 100644
index 0000000000..4d992b4245
--- /dev/null
+++ b/src/Libraries/ICSharpCode.Decompiler/Tests/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/.gitignore b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/.gitignore
new file mode 100644
index 0000000000..9ce745d95d
--- /dev/null
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/.gitignore
@@ -0,0 +1,3 @@
+
+bin/
+obj/
\ No newline at end of file
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
index 1660e99b14..bfaaf11f52 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/ControlFlow.cs
@@ -143,7 +143,12 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Dictionary labels;
List gotoStatements;
- public IList BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
+ public IList BuildControlFlowGraph(Statement statement, ITypeResolveContext context)
+ {
+ return BuildControlFlowGraph(statement, context, CancellationToken.None);
+ }
+
+ public IList BuildControlFlowGraph(Statement statement, ITypeResolveContext context, CancellationToken cancellationToken)
{
return BuildControlFlowGraph(statement, new ResolveVisitor(
new CSharpResolver(context, cancellationToken),
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
index d4cc6e1c53..edef14f633 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/DefiniteAssignmentAnalysis.cs
@@ -79,12 +79,22 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
Queue nodesWithModifiedInput = new Queue();
- public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken = default(CancellationToken))
+ public DefiniteAssignmentAnalysis(Statement rootStatement)
+ : this(rootStatement, null, CancellationToken.None)
+ {
+ }
+
+ public DefiniteAssignmentAnalysis(Statement rootStatement, CancellationToken cancellationToken)
: this(rootStatement, null, cancellationToken)
{
}
- public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken = default(CancellationToken))
+ public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context)
+ : this(rootStatement, context, CancellationToken.None)
+ {
+ }
+
+ public DefiniteAssignmentAnalysis(Statement rootStatement, ITypeResolveContext context, CancellationToken cancellationToken)
: this(rootStatement, new ResolveVisitor(new CSharpResolver(context ?? MinimalResolveContext.Instance, cancellationToken),
null, ConstantModeResolveVisitorNavigator.Skip))
{
@@ -167,12 +177,14 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
/// This method can be used to restrict the analysis to only a part of the method.
/// Only the control flow paths that are fully contained within the selected part will be analyzed.
///
- /// Both 'start' and 'end' are inclusive.
- public void SetAnalyzedRange(Statement start, Statement end)
+ /// By default, both 'start' and 'end' are inclusive.
+ public void SetAnalyzedRange(Statement start, Statement end, bool startInclusive = true, bool endInclusive = true)
{
- Debug.Assert(beginNodeDict.ContainsKey(start) && endNodeDict.ContainsKey(end));
- int startIndex = beginNodeDict[start].Index;
- int endIndex = endNodeDict[end].Index;
+ var dictForStart = startInclusive ? beginNodeDict : endNodeDict;
+ var dictForEnd = endInclusive ? endNodeDict : beginNodeDict;
+ Debug.Assert(dictForStart.ContainsKey(start) && dictForEnd.ContainsKey(end));
+ int startIndex = dictForStart[start].Index;
+ int endIndex = dictForEnd[end].Index;
if (startIndex > endIndex)
throw new ArgumentException("The start statement must be lexically preceding the end statement");
this.analyzedRangeStart = startIndex;
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs
index d9909d0d4d..64fc134935 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Analysis/MinimalResolveContext.cs
@@ -1,4 +1,4 @@
-// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// 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;
@@ -14,7 +14,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
///
/// Resolve context represents the minimal mscorlib required for evaluating constants.
///
- sealed class MinimalResolveContext : IProjectContent, ISynchronizedTypeResolveContext
+ sealed class MinimalResolveContext : AbstractAnnotatable, IProjectContent, ISynchronizedTypeResolveContext
{
static readonly Lazy instance = new Lazy(() => new MinimalResolveContext());
@@ -58,7 +58,7 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
};
}
- public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
+ public ITypeDefinition GetTypeDefinition(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer)
{
foreach (ITypeDefinition type in types) {
if (nameComparer.Equals(type.Name, name) && nameComparer.Equals(type.Namespace, nameSpace) && type.TypeParameterCount == typeParameterCount)
@@ -67,12 +67,12 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
return null;
}
- public IEnumerable GetClasses()
+ public IEnumerable GetTypes()
{
return types;
}
- public IEnumerable GetClasses(string nameSpace, StringComparer nameComparer)
+ public IEnumerable GetTypes(string nameSpace, StringComparer nameComparer)
{
return types.Where(t => nameComparer.Equals(t.Namespace, nameSpace));
}
@@ -112,5 +112,16 @@ namespace ICSharpCode.NRefactory.CSharp.Analysis
{
// exit from Synchronize() block
}
+
+ IParsedFile IProjectContent.GetFile(string fileName)
+ {
+ return null;
+ }
+
+ IEnumerable IProjectContent.Files {
+ get {
+ return EmptyList.Instance;
+ }
+ }
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs
index 07927d1cd8..a535257ef1 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/AstNode.cs
@@ -23,7 +23,6 @@
// 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;
@@ -33,7 +32,7 @@ using System.Threading;
namespace ICSharpCode.NRefactory.CSharp
{
- public abstract class AstNode : PatternMatching.INode
+ public abstract class AstNode : AbstractAnnotatable, PatternMatching.INode
{
#region Null
public static readonly AstNode Null = new NullAstNode ();
@@ -57,7 +56,7 @@ namespace ICSharpCode.NRefactory.CSharp
return default (S);
}
- protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
+ protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
{
return other == null || other.IsNull;
}
@@ -65,16 +64,16 @@ namespace ICSharpCode.NRefactory.CSharp
#endregion
#region PatternPlaceholder
- public static implicit operator AstNode(PatternMatching.Pattern pattern)
+ public static implicit operator AstNode (PatternMatching.Pattern pattern)
{
- return pattern != null ? new PatternPlaceholder(pattern) : null;
+ return pattern != null ? new PatternPlaceholder (pattern) : null;
}
sealed class PatternPlaceholder : AstNode, PatternMatching.INode
{
readonly PatternMatching.Pattern child;
- public PatternPlaceholder(PatternMatching.Pattern child)
+ public PatternPlaceholder (PatternMatching.Pattern child)
{
this.child = child;
}
@@ -83,19 +82,19 @@ namespace ICSharpCode.NRefactory.CSharp
get { return NodeType.Pattern; }
}
- public override S AcceptVisitor(IAstVisitor visitor, T data)
+ public override S AcceptVisitor (IAstVisitor visitor, T data)
{
- return visitor.VisitPatternPlaceholder(this, child, data);
+ return visitor.VisitPatternPlaceholder (this, child, data);
}
- protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
+ protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
{
- return child.DoMatch(other, match);
+ return child.DoMatch (other, match);
}
- bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
+ bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
{
- return child.DoMatchCollection(role, pos, match, backtrackingInfo);
+ return child.DoMatchCollection (role, pos, match, backtrackingInfo);
}
}
#endregion
@@ -163,7 +162,7 @@ namespace ICSharpCode.NRefactory.CSharp
get {
AstNode next;
for (AstNode cur = firstChild; cur != null; cur = next) {
- Debug.Assert(cur.parent == this);
+ Debug.Assert (cur.parent == this);
// Remember next before yielding cur.
// This allows removing/replacing nodes while iterating through the list.
next = cur.nextSibling;
@@ -188,7 +187,7 @@ namespace ICSharpCode.NRefactory.CSharp
///
public IEnumerable Descendants {
get {
- return Utils.TreeTraversal.PreOrder(this.Children, n => n.Children);
+ return Utils.TreeTraversal.PreOrder (this.Children, n => n.Children);
}
}
@@ -197,7 +196,7 @@ namespace ICSharpCode.NRefactory.CSharp
///
public IEnumerable DescendantsAndSelf {
get {
- return Utils.TreeTraversal.PreOrder(this, n => n.Children);
+ return Utils.TreeTraversal.PreOrder (this, n => n.Children);
}
}
@@ -205,10 +204,10 @@ namespace ICSharpCode.NRefactory.CSharp
/// Gets the first child with the specified role.
/// Returns the role's null object if the child is not found.
///
- public T GetChildByRole(Role role) where T : AstNode
+ public T GetChildByRole (Role role) where T : AstNode
{
if (role == null)
- throw new ArgumentNullException("role");
+ throw new ArgumentNullException ("role");
for (var cur = firstChild; cur != null; cur = cur.nextSibling) {
if (cur.role == role)
return (T)cur;
@@ -216,37 +215,37 @@ namespace ICSharpCode.NRefactory.CSharp
return role.NullObject;
}
- public AstNodeCollection GetChildrenByRole(Role role) where T : AstNode
+ public AstNodeCollection GetChildrenByRole (Role role) where T : AstNode
{
- return new AstNodeCollection(this, role);
+ return new AstNodeCollection (this, role);
}
- protected void SetChildByRole(Role role, T newChild) where T : AstNode
+ protected void SetChildByRole (Role role, T newChild) where T : AstNode
{
- AstNode oldChild = GetChildByRole(role);
+ AstNode oldChild = GetChildByRole (role);
if (oldChild.IsNull)
- AddChild(newChild, role);
+ AddChild (newChild, role);
else
- oldChild.ReplaceWith(newChild);
+ oldChild.ReplaceWith (newChild);
}
- public void AddChild(T child, Role role) where T : AstNode
+ public void AddChild (T child, Role role) where T : AstNode
{
if (role == null)
- throw new ArgumentNullException("role");
+ throw new ArgumentNullException ("role");
if (child == null || child.IsNull)
return;
if (this.IsNull)
- throw new InvalidOperationException("Cannot add children to null nodes");
+ throw new InvalidOperationException ("Cannot add children to null nodes");
if (child.parent != null)
throw new ArgumentException ("Node is already used in another tree.", "child");
- AddChildUnsafe(child, role);
+ AddChildUnsafe (child, role);
}
///
/// Adds a child without performing any safety checks.
///
- void AddChildUnsafe(AstNode child, Role role)
+ void AddChildUnsafe (AstNode child, Role role)
{
child.parent = this;
child.role = role;
@@ -259,12 +258,12 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
- public void InsertChildBefore(AstNode nextSibling, T child, Role role) where T : AstNode
+ public void InsertChildBefore (AstNode nextSibling, T child, Role role) where T : AstNode
{
if (role == null)
- throw new ArgumentNullException("role");
+ throw new ArgumentNullException ("role");
if (nextSibling == null || nextSibling.IsNull) {
- AddChild(child, role);
+ AddChild (child, role);
return;
}
@@ -276,11 +275,10 @@ namespace ICSharpCode.NRefactory.CSharp
throw new ArgumentException ("NextSibling is not a child of this node.", "nextSibling");
// No need to test for "Cannot add children to null nodes",
// as there isn't any valid nextSibling in null nodes.
- InsertChildBeforeUnsafe(nextSibling, child, role);
+ InsertChildBeforeUnsafe (nextSibling, child, role);
}
-
- void InsertChildBeforeUnsafe(AstNode nextSibling, AstNode child, Role role)
+ void InsertChildBeforeUnsafe (AstNode nextSibling, AstNode child, Role role)
{
child.parent = this;
child.role = role;
@@ -288,38 +286,38 @@ namespace ICSharpCode.NRefactory.CSharp
child.prevSibling = nextSibling.prevSibling;
if (nextSibling.prevSibling != null) {
- Debug.Assert(nextSibling.prevSibling.nextSibling == nextSibling);
+ Debug.Assert (nextSibling.prevSibling.nextSibling == nextSibling);
nextSibling.prevSibling.nextSibling = child;
} else {
- Debug.Assert(firstChild == nextSibling);
+ Debug.Assert (firstChild == nextSibling);
firstChild = child;
}
nextSibling.prevSibling = child;
}
- public void InsertChildAfter(AstNode prevSibling, T child, Role role) where T : AstNode
+ public void InsertChildAfter (AstNode prevSibling, T child, Role role) where T : AstNode
{
- InsertChildBefore((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role);
+ InsertChildBefore ((prevSibling == null || prevSibling.IsNull) ? firstChild : prevSibling.nextSibling, child, role);
}
///
/// Removes this node from its parent.
///
- public void Remove()
+ public void Remove ()
{
if (parent != null) {
if (prevSibling != null) {
- Debug.Assert(prevSibling.nextSibling == this);
+ Debug.Assert (prevSibling.nextSibling == this);
prevSibling.nextSibling = nextSibling;
} else {
- Debug.Assert(parent.firstChild == this);
+ Debug.Assert (parent.firstChild == this);
parent.firstChild = nextSibling;
}
if (nextSibling != null) {
- Debug.Assert(nextSibling.prevSibling == this);
+ Debug.Assert (nextSibling.prevSibling == this);
nextSibling.prevSibling = prevSibling;
} else {
- Debug.Assert(parent.lastChild == this);
+ Debug.Assert (parent.lastChild == this);
parent.lastChild = prevSibling;
}
parent = null;
@@ -332,28 +330,28 @@ namespace ICSharpCode.NRefactory.CSharp
///
/// Replaces this node with the new node.
///
- public void ReplaceWith(AstNode newNode)
+ public void ReplaceWith (AstNode newNode)
{
if (newNode == null || newNode.IsNull) {
- Remove();
+ Remove ();
return;
}
if (newNode == this)
return; // nothing to do...
if (parent == null) {
- throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
+ throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
}
// Because this method doesn't statically check the new node's type with the role,
// we perform a runtime test:
- if (!role.IsValid(newNode)) {
- throw new ArgumentException (string.Format("The new node '{0}' is not valid in the role {1}", newNode.GetType().Name, role.ToString()), "newNode");
+ if (!role.IsValid (newNode)) {
+ throw new ArgumentException (string.Format ("The new node '{0}' is not valid in the role {1}", newNode.GetType ().Name, role.ToString ()), "newNode");
}
if (newNode.parent != null) {
// newNode is used within this tree?
- if (newNode.Ancestors.Contains(this)) {
+ if (newNode.Ancestors.Contains (this)) {
// e.g. "parenthesizedExpr.ReplaceWith(parenthesizedExpr.Expression);"
// enable automatic removal
- newNode.Remove();
+ newNode.Remove ();
} else {
throw new ArgumentException ("Node is already used in another tree.", "newNode");
}
@@ -365,17 +363,17 @@ namespace ICSharpCode.NRefactory.CSharp
newNode.nextSibling = nextSibling;
if (parent != null) {
if (prevSibling != null) {
- Debug.Assert(prevSibling.nextSibling == this);
+ Debug.Assert (prevSibling.nextSibling == this);
prevSibling.nextSibling = newNode;
} else {
- Debug.Assert(parent.firstChild == this);
+ Debug.Assert (parent.firstChild == this);
parent.firstChild = newNode;
}
if (nextSibling != null) {
- Debug.Assert(nextSibling.prevSibling == this);
+ Debug.Assert (nextSibling.prevSibling == this);
nextSibling.prevSibling = newNode;
} else {
- Debug.Assert(parent.lastChild == this);
+ Debug.Assert (parent.lastChild == this);
parent.lastChild = newNode;
}
parent = null;
@@ -385,31 +383,31 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
- public AstNode ReplaceWith(Func replaceFunction)
+ public AstNode ReplaceWith (Func replaceFunction)
{
if (replaceFunction == null)
- throw new ArgumentNullException("replaceFunction");
+ throw new ArgumentNullException ("replaceFunction");
if (parent == null) {
- throw new InvalidOperationException(this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
+ throw new InvalidOperationException (this.IsNull ? "Cannot replace the null nodes" : "Cannot replace the root node");
}
AstNode oldParent = parent;
AstNode oldSuccessor = nextSibling;
Role oldRole = role;
- Remove();
- AstNode replacement = replaceFunction(this);
+ Remove ();
+ AstNode replacement = replaceFunction (this);
if (oldSuccessor != null && oldSuccessor.parent != oldParent)
- throw new InvalidOperationException("replace function changed nextSibling of node being replaced?");
+ throw new InvalidOperationException ("replace function changed nextSibling of node being replaced?");
if (!(replacement == null || replacement.IsNull)) {
if (replacement.parent != null)
- throw new InvalidOperationException("replace function must return the root of a tree");
- if (!oldRole.IsValid(replacement)) {
- throw new InvalidOperationException (string.Format("The new node '{0}' is not valid in the role {1}", replacement.GetType().Name, oldRole.ToString()));
+ throw new InvalidOperationException ("replace function must return the root of a tree");
+ if (!oldRole.IsValid (replacement)) {
+ throw new InvalidOperationException (string.Format ("The new node '{0}' is not valid in the role {1}", replacement.GetType ().Name, oldRole.ToString ()));
}
if (oldSuccessor != null)
- oldParent.InsertChildBeforeUnsafe(oldSuccessor, replacement, oldRole);
+ oldParent.InsertChildBeforeUnsafe (oldSuccessor, replacement, oldRole);
else
- oldParent.AddChildUnsafe(replacement, oldRole);
+ oldParent.AddChildUnsafe (replacement, oldRole);
}
return replacement;
}
@@ -418,9 +416,9 @@ namespace ICSharpCode.NRefactory.CSharp
/// Clones the whole subtree starting at this AST node.
///
/// Annotations are copied over to the new nodes; and any annotations implementing ICloneable will be cloned.
- public AstNode Clone()
+ public AstNode Clone ()
{
- AstNode copy = (AstNode)MemberwiseClone();
+ AstNode copy = (AstNode)MemberwiseClone ();
// First, reset the shallow pointer copies
copy.parent = null;
copy.role = Roles.Root;
@@ -431,7 +429,7 @@ namespace ICSharpCode.NRefactory.CSharp
// Then perform a deep copy:
for (AstNode cur = firstChild; cur != null; cur = cur.nextSibling) {
- copy.AddChildUnsafe(cur.Clone(), cur.role);
+ copy.AddChildUnsafe (cur.Clone (), cur.role);
}
// Finally, clone the annotation, if necessary
@@ -442,175 +440,24 @@ namespace ICSharpCode.NRefactory.CSharp
return copy;
}
- #region Annotation support
- // Annotations: points either null (no annotations), to the single annotation,
- // or to an AnnotationList.
- // Once it is pointed at an AnnotationList, it will never change (this allows thread-safety support by locking the list)
- object annotations;
-
- sealed class AnnotationList : List, ICloneable
- {
- // There are two uses for this custom list type:
- // 1) it's private, and thus (unlike List) cannot be confused with real annotations
- // 2) It allows us to simplify the cloning logic by making the list behave the same as a clonable annotation.
- public AnnotationList(int initialCapacity) : base(initialCapacity)
- {
- }
-
- public object Clone()
- {
- lock (this) {
- AnnotationList copy = new AnnotationList(this.Count);
- for (int i = 0; i < this.Count; i++) {
- object obj = this[i];
- ICloneable c = obj as ICloneable;
- copy.Add(c != null ? c.Clone() : obj);
- }
- return copy;
- }
- }
- }
-
- public void AddAnnotation(object annotation)
- {
- if (annotation == null)
- throw new ArgumentNullException("annotation");
- if (this.IsNull)
- throw new InvalidOperationException("Cannot add annotations to the null node");
- retry: // Retry until successful
- object oldAnnotation = Interlocked.CompareExchange(ref this.annotations, annotation, null);
- if (oldAnnotation == null) {
- return; // we successfully added a single annotation
- }
- AnnotationList list = oldAnnotation as AnnotationList;
- if (list == null) {
- // we need to transform the old annotation into a list
- list = new AnnotationList(4);
- list.Add(oldAnnotation);
- list.Add(annotation);
- if (Interlocked.CompareExchange(ref this.annotations, list, oldAnnotation) != oldAnnotation) {
- // the transformation failed (some other thread wrote to this.annotations first)
- goto retry;
- }
- } else {
- // once there's a list, use simple locking
- lock (list) {
- list.Add(annotation);
- }
- }
- }
-
- public void RemoveAnnotations() where T : class
- {
- retry: // Retry until successful
- object oldAnnotations = this.annotations;
- AnnotationList list = oldAnnotations as AnnotationList;
- if (list != null) {
- lock (list)
- list.RemoveAll(obj => obj is T);
- } else if (oldAnnotations is T) {
- if (Interlocked.CompareExchange(ref this.annotations, null, oldAnnotations) != oldAnnotations) {
- // Operation failed (some other thread wrote to this.annotations first)
- goto retry;
- }
- }
- }
-
- public void RemoveAnnotations(Type type)
- {
- if (type == null)
- throw new ArgumentNullException("type");
- retry: // Retry until successful
- object oldAnnotations = this.annotations;
- AnnotationList list = oldAnnotations as AnnotationList;
- if (list != null) {
- lock (list)
- list.RemoveAll(obj => type.IsInstanceOfType(obj));
- } else if (type.IsInstanceOfType(oldAnnotations)) {
- if (Interlocked.CompareExchange(ref this.annotations, null, oldAnnotations) != oldAnnotations) {
- // Operation failed (some other thread wrote to this.annotations first)
- goto retry;
- }
- }
- }
-
- public T Annotation() where T: class
- {
- object annotations = this.annotations;
- AnnotationList list = annotations as AnnotationList;
- if (list != null) {
- lock (list) {
- foreach (object obj in list) {
- T t = obj as T;
- if (t != null)
- return t;
- }
- return null;
- }
- } else {
- return annotations as T;
- }
- }
-
- public object Annotation(Type type)
- {
- if (type == null)
- throw new ArgumentNullException("type");
- object annotations = this.annotations;
- AnnotationList list = annotations as AnnotationList;
- if (list != null) {
- lock (list) {
- foreach (object obj in list) {
- if (type.IsInstanceOfType(obj))
- return obj;
- }
- }
- } else {
- if (type.IsInstanceOfType(annotations))
- return annotations;
- }
- return null;
- }
-
- ///
- /// Gets all annotations stored on this AstNode.
- ///
- public IEnumerable Annotations {
- get {
- object annotations = this.annotations;
- AnnotationList list = annotations as AnnotationList;
- if (list != null) {
- lock (list) {
- return list.ToArray();
- }
- } else {
- if (annotations != null)
- return new object[] { annotations };
- else
- return Enumerable.Empty();
- }
- }
- }
- #endregion
-
public abstract S AcceptVisitor (IAstVisitor visitor, T data);
#region Pattern Matching
- protected static bool MatchString(string name1, string name2)
+ protected static bool MatchString (string name1, string name2)
{
- return string.IsNullOrEmpty(name1) || name1 == name2;
+ return string.IsNullOrEmpty (name1) || name1 == name2;
}
- protected internal abstract bool DoMatch(AstNode other, PatternMatching.Match match);
+ protected internal abstract bool DoMatch (AstNode other, PatternMatching.Match match);
- bool PatternMatching.INode.DoMatch(PatternMatching.INode other, PatternMatching.Match match)
+ bool PatternMatching.INode.DoMatch (PatternMatching.INode other, PatternMatching.Match match)
{
AstNode o = other as AstNode;
// try matching if other is null, or if other is an AstNode
- return (other == null || o != null) && DoMatch(o, match);
+ return (other == null || o != null) && DoMatch (o, match);
}
- bool PatternMatching.INode.DoMatchCollection(Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
+ bool PatternMatching.INode.DoMatchCollection (Role role, PatternMatching.INode pos, PatternMatching.Match match, PatternMatching.BacktrackingInfo backtrackingInfo)
{
AstNode o = pos as AstNode;
return (pos == null || o != null) && DoMatch (o, match);
@@ -623,6 +470,7 @@ namespace ICSharpCode.NRefactory.CSharp
PatternMatching.INode PatternMatching.INode.FirstChild {
get { return firstChild; }
}
+
#endregion
public AstNode GetNextNode ()
@@ -655,8 +503,168 @@ namespace ICSharpCode.NRefactory.CSharp
return null;
}
+ public AstNode GetNodeAt (int line, int column)
+ {
+ return GetNodeAt (new AstLocation (line, column));
+ }
+
+ public AstNode GetNodeAt (AstLocation location, Predicate pred = null)
+ {
+ AstNode result = null;
+ AstNode node = this;
+ while (node.FirstChild != null) {
+ var child = node.FirstChild;
+ while (child != null) {
+ if (child.StartLocation <= location && location < child.EndLocation) {
+ if (pred == null || pred (child))
+ result = child;
+ node = child;
+ break;
+ }
+ child = child.NextSibling;
+ }
+ // found no better child node - therefore the parent is the right one.
+ if (child == null)
+ break;
+ }
+ return result;
+ }
+
+ public T GetNodeAt (int line, int column) where T : AstNode
+ {
+ return GetNodeAt (new AstLocation (line, column));
+ }
+
+ ///
+ /// Gets the node specified by T at location. This is useful for getting a specific node from the tree. For example searching
+ /// the current method declaration.
+ ///
+ public T GetNodeAt (AstLocation location) where T : AstNode
+ {
+ T result = null;
+ AstNode node = this;
+ while (node.FirstChild != null) {
+ var child = node.FirstChild;
+ while (child != null) {
+ if (child.StartLocation <= location && location < child.EndLocation) {
+ if (child is T)
+ result = (T)child;
+ node = child;
+ break;
+ }
+ child = child.NextSibling;
+ }
+ // found no better child node - therefore the parent is the right one.
+ if (child == null)
+ break;
+ }
+ return result;
+ }
+
+ public AstNode GetResolveableNodeAt (int line, int column)
+ {
+ return GetResolveableNodeAt (new AstLocation (line, column));
+ }
+
+ ///
+ /// Gets a node that can be resolved at location.
+ ///
+ public AstNode GetResolveableNodeAt (AstLocation location)
+ {
+ return GetNodeAt (location, delegate (AstNode n) {
+
+ if (n is TypeDeclaration) {
+ var decl = (TypeDeclaration)n;
+ return decl.NameToken.StartLocation <= location && location <= decl.NameToken.EndLocation;
+ }
+
+ if (n is DelegateDeclaration) {
+ var decl = (DelegateDeclaration)n;
+ return decl.NameToken.StartLocation <= location && location <= decl.NameToken.EndLocation;
+ }
+
+ if (n is MemberDeclaration) {
+ var decl = (MemberDeclaration)n;
+ return decl.NameToken.StartLocation <= location && location <= decl.NameToken.EndLocation;
+ }
+
+ if (n is ConstructorDeclaration) {
+ var decl = (ConstructorDeclaration)n;
+ return decl.IdentifierToken.StartLocation <= location && location <= decl.IdentifierToken.EndLocation;
+ }
+
+ if (n is DestructorDeclaration) {
+ var decl = (DestructorDeclaration)n;
+ return decl.IdentifierToken.StartLocation <= location && location <= decl.IdentifierToken.EndLocation;
+ }
+
+ if (n is VariableInitializer) {
+ var decl = (VariableInitializer)n;
+ return decl.NameToken.StartLocation <= location && location <= decl.NameToken.EndLocation;
+ }
+
+ if (n is ParameterDeclaration) {
+ var decl = (ParameterDeclaration)n;
+ return decl.NameToken.StartLocation <= location && location <= decl.NameToken.EndLocation;
+ }
+
+ if (n is MemberReferenceExpression) {
+ var decl = (MemberReferenceExpression)n;
+ return decl.MemberNameToken.StartLocation <= location && location <= decl.MemberNameToken.EndLocation;
+ }
+
+ return n is IdentifierExpression || n is AstType;
+ });
+ }
+
+ public IEnumerable GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn)
+ {
+ return GetNodesBetween (new AstLocation (startLine, startColumn), new AstLocation (endLine, endColumn));
+ }
+
+ public IEnumerable GetNodesBetween (AstLocation start, AstLocation end)
+ {
+ AstNode node = this;
+ while (node != null) {
+ AstNode next;
+ if (start <= node.StartLocation && node.EndLocation <= end) {
+ // Remember next before yielding node.
+ // This allows iteration to continue when the caller removes/replaces the node.
+ next = node.NextSibling;
+ yield return node;
+ } else {
+ if (node.EndLocation <= start) {
+ next = node.NextSibling;
+ } else {
+ next = node.FirstChild;
+ }
+ }
+
+ if (next != null && next.StartLocation > end)
+ yield break;
+ node = next;
+ }
+ }
+
+ public bool Contains (int line, int column)
+ {
+ return Contains (new AstLocation (line, column));
+ }
+
+ public bool Contains (AstLocation location)
+ {
+ return this.StartLocation <= location && location < this.EndLocation;
+ }
+
+ public override void AddAnnotation (object annotation)
+ {
+ if (this.IsNull)
+ throw new InvalidOperationException ("Cannot add annotations to the null node");
+ base.AddAnnotation (annotation);
+ }
+
// the Root role must be available when creating the null nodes, so we can't put it in the Roles class
- static readonly Role RootRole = new Role("Root");
+ static readonly Role RootRole = new Role ("Root");
public static class Roles
{
@@ -666,41 +674,38 @@ namespace ICSharpCode.NRefactory.CSharp
public static readonly Role Root = RootRole;
// some pre defined constants for common roles
- public static readonly Role Identifier = new Role("Identifier", CSharp.Identifier.Null);
-
- public static readonly Role Body = new Role("Body", CSharp.BlockStatement.Null);
- public static readonly Role Parameter = new Role("Parameter");
- public static readonly Role Argument = new Role("Argument", CSharp.Expression.Null);
- public static readonly Role Type = new Role("Type", CSharp.AstType.Null);
- public static readonly Role Expression = new Role("Expression", CSharp.Expression.Null);
- public static readonly Role TargetExpression = new Role("Target", CSharp.Expression.Null);
- public readonly static Role Condition = new Role("Condition", CSharp.Expression.Null);
-
- public static readonly Role TypeParameter = new Role("TypeParameter");
- public static readonly Role TypeArgument = new Role("TypeArgument", CSharp.AstType.Null);
- public readonly static Role Constraint = new Role("Constraint");
- public static readonly Role Variable = new Role("Variable");
- public static readonly Role EmbeddedStatement = new Role("EmbeddedStatement", CSharp.Statement.Null);
-
- public static readonly Role Keyword = new Role("Keyword", CSharpTokenNode.Null);
- public static readonly Role InKeyword = new Role("InKeyword", CSharpTokenNode.Null);
+ public static readonly Role Identifier = new Role ("Identifier", CSharp.Identifier.Null);
+ public static readonly Role Body = new Role ("Body", CSharp.BlockStatement.Null);
+ public static readonly Role Parameter = new Role ("Parameter");
+ public static readonly Role Argument = new Role ("Argument", CSharp.Expression.Null);
+ public static readonly Role Type = new Role ("Type", CSharp.AstType.Null);
+ public static readonly Role Expression = new Role ("Expression", CSharp.Expression.Null);
+ public static readonly Role TargetExpression = new Role ("Target", CSharp.Expression.Null);
+ public readonly static Role Condition = new Role ("Condition", CSharp.Expression.Null);
+ public static readonly Role TypeParameter = new Role ("TypeParameter");
+ public static readonly Role TypeArgument = new Role ("TypeArgument", CSharp.AstType.Null);
+ public readonly static Role Constraint = new Role ("Constraint");
+ public static readonly Role Variable = new Role ("Variable");
+ public static readonly Role EmbeddedStatement = new Role ("EmbeddedStatement", CSharp.Statement.Null);
+ public static readonly Role Keyword = new Role ("Keyword", CSharpTokenNode.Null);
+ public static readonly Role InKeyword = new Role ("InKeyword", CSharpTokenNode.Null);
// some pre defined constants for most used punctuation
- public static readonly Role LPar = new Role("LPar", CSharpTokenNode.Null);
- public static readonly Role RPar = new Role("RPar", CSharpTokenNode.Null);
- public static readonly Role LBracket = new Role("LBracket", CSharpTokenNode.Null);
- public static readonly Role RBracket = new Role("RBracket", CSharpTokenNode.Null);
- public static readonly Role LBrace = new Role("LBrace", CSharpTokenNode.Null);
- public static readonly Role RBrace = new Role("RBrace", CSharpTokenNode.Null);
- public static readonly Role LChevron = new Role("LChevron", CSharpTokenNode.Null);
- public static readonly Role RChevron = new Role("RChevron", CSharpTokenNode.Null);
- public static readonly Role Comma = new Role("Comma", CSharpTokenNode.Null);
- public static readonly Role Dot = new Role("Dot", CSharpTokenNode.Null);
- public static readonly Role Semicolon = new Role("Semicolon", CSharpTokenNode.Null);
- public static readonly Role Assign = new Role("Assign", CSharpTokenNode.Null);
- public static readonly Role Colon = new Role("Colon", CSharpTokenNode.Null);
-
- public static readonly Role Comment = new Role("Comment");
+ public static readonly Role LPar = new Role ("LPar", CSharpTokenNode.Null);
+ public static readonly Role RPar = new Role ("RPar", CSharpTokenNode.Null);
+ public static readonly Role LBracket = new Role ("LBracket", CSharpTokenNode.Null);
+ public static readonly Role RBracket = new Role ("RBracket", CSharpTokenNode.Null);
+ public static readonly Role LBrace = new Role ("LBrace", CSharpTokenNode.Null);
+ public static readonly Role RBrace = new Role ("RBrace", CSharpTokenNode.Null);
+ public static readonly Role LChevron = new Role ("LChevron", CSharpTokenNode.Null);
+ public static readonly Role RChevron = new Role ("RChevron", CSharpTokenNode.Null);
+ public static readonly Role Comma = new Role ("Comma", CSharpTokenNode.Null);
+ public static readonly Role Dot = new Role ("Dot", CSharpTokenNode.Null);
+ public static readonly Role Semicolon = new Role ("Semicolon", CSharpTokenNode.Null);
+ public static readonly Role Assign = new Role ("Assign", CSharpTokenNode.Null);
+ public static readonly Role Colon = new Role ("Colon", CSharpTokenNode.Null);
+ public static readonly Role Comment = new Role ("Comment");
+ public static readonly Role Error = new Role ("Error");
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs
index a776f259f5..b125413d0f 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpTokenNode.cs
@@ -1,4 +1,4 @@
-//
+//
// TokenNode.cs
//
// Author:
@@ -27,7 +27,7 @@ using System;
namespace ICSharpCode.NRefactory.CSharp
{
- public class CSharpTokenNode : AstNode
+ public class CSharpTokenNode : AstNode, IRelocatable
{
public static new readonly CSharpTokenNode Null = new NullCSharpTokenNode ();
class NullCSharpTokenNode : CSharpTokenNode
@@ -80,6 +80,13 @@ namespace ICSharpCode.NRefactory.CSharp
this.tokenLength = tokenLength;
}
+ #region IRelocationable implementation
+ void IRelocatable.SetStartLocation (AstLocation startLocation)
+ {
+ this.startLocation = startLocation;
+ }
+ #endregion
+
public override S AcceptVisitor (IAstVisitor visitor, T data)
{
return visitor.VisitCSharpTokenNode (this, data);
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpUtil.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpUtil.cs
new file mode 100644
index 0000000000..46f528d46b
--- /dev/null
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CSharpUtil.cs
@@ -0,0 +1,95 @@
+//
+// CSharpUtil.cs
+//
+// Author:
+// Mike Krüger
+//
+// Copyright (c) 2011 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+using ICSharpCode.NRefactory.CSharp;
+
+namespace ICSharpCode.NRefactory.CSharp
+{
+ public static class CSharpUtil
+ {
+ public static Expression InvertCondition (Expression condition)
+ {
+ return InvertConditionInternal (condition.Clone ());
+ }
+
+ static Expression InvertConditionInternal (Expression condition)
+ {
+ if (condition is ParenthesizedExpression) {
+ ((ParenthesizedExpression)condition).Expression = InvertCondition (((ParenthesizedExpression)condition).Expression);
+ return condition;
+ }
+
+ if (condition is UnaryOperatorExpression) {
+ var uOp = (UnaryOperatorExpression)condition;
+ if (uOp.Operator == UnaryOperatorType.Not)
+ return uOp.Expression;
+ return new UnaryOperatorExpression (UnaryOperatorType.Not, uOp);
+ }
+
+ if (condition is BinaryOperatorExpression) {
+ var bOp = (BinaryOperatorExpression)condition;
+ switch (bOp.Operator) {
+ case BinaryOperatorType.GreaterThan:
+ bOp.Operator = BinaryOperatorType.LessThanOrEqual;
+ return bOp;
+ case BinaryOperatorType.GreaterThanOrEqual:
+ bOp.Operator = BinaryOperatorType.LessThan;
+ return bOp;
+ case BinaryOperatorType.Equality:
+ bOp.Operator = BinaryOperatorType.InEquality;
+ return bOp;
+ case BinaryOperatorType.InEquality:
+ bOp.Operator = BinaryOperatorType.Equality;
+ return bOp;
+ case BinaryOperatorType.LessThan:
+ bOp.Operator = BinaryOperatorType.GreaterThanOrEqual;
+ return bOp;
+ case BinaryOperatorType.LessThanOrEqual:
+ bOp.Operator = BinaryOperatorType.GreaterThan;
+ return bOp;
+ default:
+ return new UnaryOperatorExpression (UnaryOperatorType.Not, new ParenthesizedExpression (condition));
+ }
+ }
+
+ if (condition is ConditionalExpression) {
+ var cEx = condition as ConditionalExpression;
+ cEx.Condition = InvertCondition (cEx.Condition);
+ return cEx;
+ }
+ if (condition is PrimitiveExpression) {
+ var pex = condition as PrimitiveExpression;
+ if (pex.Value is bool) {
+ pex.Value = !((bool)pex.Value);
+ return pex;
+ }
+ }
+
+ return new UnaryOperatorExpression (UnaryOperatorType.Not, condition);
+ }
+ }
+}
+
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs
index 9a50477191..889e8d048b 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/CompilationUnit.cs
@@ -25,6 +25,7 @@
// THE SOFTWARE.
using System;
using System.Collections.Generic;
+using ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.CSharp
{
@@ -38,67 +39,48 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
- public CompilationUnit ()
- {
+ List errors = new List ();
+
+ public List Errors {
+ get { return errors; }
}
- protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
- {
- CompilationUnit o = other as CompilationUnit;
- return o != null && GetChildrenByRole(MemberRole).DoMatch(o.GetChildrenByRole(MemberRole), match);
+ ///
+ /// Gets the expression that was on top of the parse stack.
+ /// This is the only way to get an expression that isn't part of a statment.
+ /// (eg. when an error follows an expression).
+ ///
+ /// This is used for code completion to 'get the expression before a token - like ., <, ('.
+ ///
+ public AstNode TopExpression {
+ get;
+ internal set;
}
- public AstNode GetNodeAt (int line, int column)
+ public CompilationUnit ()
{
- return GetNodeAt (new AstLocation (line, column));
}
- public AstNode GetNodeAt (AstLocation location)
+ public IEnumerable GetTypes (bool includeInnerTypes = false)
{
- AstNode node = this;
- while (node.FirstChild != null) {
- var child = node.FirstChild;
- while (child != null) {
- if (child.StartLocation <= location && location < child.EndLocation) {
- node = child;
- break;
- }
- child = child.NextSibling;
+ Stack nodeStack = new Stack ();
+ nodeStack.Push (this);
+ while (nodeStack.Count > 0) {
+ var curNode = nodeStack.Pop ();
+ if (curNode is TypeDeclaration)
+ yield return (TypeDeclaration)curNode;
+ foreach (var child in curNode.Children) {
+ if (!(child is Statement || child is Expression) &&
+ (child.Role != TypeDeclaration.MemberRole || (child is TypeDeclaration && includeInnerTypes)))
+ nodeStack.Push (child);
}
- // found no better child node - therefore the parent is the right one.
- if (child == null)
- break;
}
- return node;
}
- public IEnumerable GetNodesBetween (int startLine, int startColumn, int endLine, int endColumn)
- {
- return GetNodesBetween (new AstLocation (startLine, startColumn), new AstLocation (endLine, endColumn));
- }
-
- public IEnumerable GetNodesBetween (AstLocation start, AstLocation end)
+ protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
{
- AstNode node = this;
- while (node != null) {
- AstNode next;
- if (start <= node.StartLocation && node.EndLocation <= end) {
- // Remember next before yielding node.
- // This allows iteration to continue when the caller removes/replaces the node.
- next = node.NextSibling;
- yield return node;
- } else {
- if (node.EndLocation < start) {
- next = node.NextSibling;
- } else {
- next = node.FirstChild;
- }
- }
-
- if (next != null && next.StartLocation > end)
- yield break;
- node = next;
- }
+ CompilationUnit o = other as CompilationUnit;
+ return o != null && GetChildrenByRole(MemberRole).DoMatch(o.GetChildrenByRole(MemberRole), match);
}
public override S AcceptVisitor (IAstVisitor visitor, T data)
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs
index b888cdea23..813f472137 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ComposedType.cs
@@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp
public int PointerRank {
get {
- return GetChildrenByRole(PointerRole).Count();
+ return GetChildrenByRole(PointerRole).Count;
}
set {
if (value < 0)
@@ -141,7 +141,7 @@ namespace ICSharpCode.NRefactory.CSharp
}
public int Dimensions {
- get { return 1 + GetChildrenByRole(Roles.Comma).Count(); }
+ get { return 1 + GetChildrenByRole(Roles.Comma).Count; }
set {
int d = this.Dimensions;
while (d > value) {
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ErrorNode.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ErrorNode.cs
new file mode 100644
index 0000000000..af2fed7506
--- /dev/null
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ErrorNode.cs
@@ -0,0 +1,79 @@
+//
+// ErrorNode.cs
+//
+// Author:
+// Mike Krüger
+//
+// Copyright (c) 2011 Xamarin (http://www.xamarin.com);
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+
+namespace ICSharpCode.NRefactory.CSharp
+{
+ ///
+ /// Represents a parsing error in the ast. At the moment it only represents missing closing bracket.
+ /// This closing bracket is replaced by a node at the highest possible position.
+ /// (To make GetAstNodeAt (line, col) working).
+ ///
+ public class ErrorNode : AstNode
+ {
+ static AstLocation maxLoc = new AstLocation (int.MaxValue, int.MaxValue);
+
+ public override NodeType NodeType {
+ get {
+ return NodeType.Unknown;
+ }
+ }
+
+ public override AstLocation StartLocation {
+ get {
+ return maxLoc;
+ }
+ }
+
+ public override AstLocation EndLocation {
+ get {
+ return maxLoc;
+ }
+ }
+
+ public ErrorNode ()
+ {
+ }
+
+ public override S AcceptVisitor (IAstVisitor visitor, T data)
+ {
+ // nothing
+ return default (S);
+ }
+
+ protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
+ {
+ var o = other as ErrorNode;
+ return o != null;
+ }
+
+ public override string ToString ()
+ {
+ return "[ErrorNode]";
+ }
+ }
+}
+
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs
index b057acbdf6..3c23f54f88 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/EmptyExpression.cs
@@ -30,7 +30,7 @@ namespace ICSharpCode.NRefactory.CSharp
///
/// Type<[EMPTY]>
///
- public class EmptyExpression : Expression
+ public class EmptyExpression : Expression, IRelocatable
{
AstLocation location;
@@ -39,7 +39,7 @@ namespace ICSharpCode.NRefactory.CSharp
return location;
}
}
-
+
public override AstLocation EndLocation {
get {
return location;
@@ -54,7 +54,14 @@ namespace ICSharpCode.NRefactory.CSharp
{
this.location = location;
}
-
+
+ #region IRelocationable implementation
+ void IRelocatable.SetStartLocation (AstLocation startLocation)
+ {
+ this.location = startLocation;
+ }
+ #endregion
+
public override S AcceptVisitor (IAstVisitor visitor, T data)
{
return visitor.VisitEmptyExpression (this, data);
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs
index bbc6898c1d..e982f13201 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/IdentifierExpression.cs
@@ -40,7 +40,7 @@ namespace ICSharpCode.NRefactory.CSharp
public IdentifierExpression(string identifier, AstLocation location)
{
- SetChildByRole(Roles.Identifier, new Identifier(identifier, location));
+ SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (identifier, location));
}
// public Identifier IdentifierToken {
@@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value, AstLocation.Empty));
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs
index 60d7a17edc..4a7decbe0e 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/MemberReferenceExpression.cs
@@ -1,4 +1,4 @@
-//
+//
// MemberReferenceExpression.cs
//
// Author:
@@ -43,7 +43,16 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole (Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
+ }
+ }
+
+ public Identifier MemberNameToken {
+ get {
+ return GetChildByRole (Roles.Identifier);
+ }
+ set {
+ SetChildByRole (Roles.Identifier, value);
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/NamedArgumentExpression.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/NamedArgumentExpression.cs
index df51dcf937..73bcb2e274 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/NamedArgumentExpression.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/NamedArgumentExpression.cs
@@ -15,7 +15,16 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value, AstLocation.Empty));
+ }
+ }
+
+ public Identifier IdentifierToken {
+ get {
+ return GetChildByRole (Roles.Identifier);
+ }
+ set {
+ SetChildByRole(Roles.Identifier, value);
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs
index 3ac10d3874..21e805b671 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PointerReferenceExpression.cs
@@ -1,4 +1,4 @@
-//
+//
// PointerReferenceExpression.cs
//
// Author:
@@ -45,7 +45,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs
index cc4fbe1c04..22e16e29ba 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/PrimitiveExpression.cs
@@ -29,7 +29,7 @@ namespace ICSharpCode.NRefactory.CSharp
///
/// Represents a literal value.
///
- public class PrimitiveExpression : Expression
+ public class PrimitiveExpression : Expression, IRelocatable
{
public static readonly object AnyValue = new object();
@@ -40,16 +40,22 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
- int length;
+ string literalValue;
public override AstLocation EndLocation {
get {
- return new AstLocation (StartLocation.Line, StartLocation.Column + length);
+ return new AstLocation (StartLocation.Line, StartLocation.Column + literalValue.Length);
}
}
public object Value {
get;
- private set;
+ set;
+ }
+
+ public string LiteralValue {
+ get {
+ return literalValue;
+ }
}
public PrimitiveExpression (object value)
@@ -57,12 +63,25 @@ namespace ICSharpCode.NRefactory.CSharp
this.Value = value;
}
- public PrimitiveExpression (object value, AstLocation startLocation, int length)
+ public PrimitiveExpression (object value, string literalValue)
+ {
+ this.Value = value;
+ this.literalValue = literalValue ?? "";
+ }
+
+ public PrimitiveExpression (object value, AstLocation startLocation, string literalValue)
{
this.Value = value;
this.startLocation = startLocation;
- this.length = length;
+ this.literalValue = literalValue ?? "";
+ }
+
+ #region IRelocationable implementation
+ void IRelocatable.SetStartLocation (AstLocation startLocation)
+ {
+ this.startLocation = startLocation;
}
+ #endregion
public override S AcceptVisitor (IAstVisitor visitor, T data)
{
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs
index 9841a74685..5cf8877030 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Expressions/QueryExpression.cs
@@ -89,7 +89,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value, AstLocation.Empty));
}
}
@@ -120,7 +120,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value, AstLocation.Empty));
}
}
@@ -153,7 +153,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole(Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, CSharp.Identifier.Create (value, AstLocation.Empty));
}
}
@@ -237,7 +237,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole(JoinIdentifierRole).Name;
}
set {
- SetChildByRole(JoinIdentifierRole, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(JoinIdentifierRole, Identifier.Create (value, AstLocation.Empty));
}
}
@@ -277,7 +277,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (IntoIdentifierRole).Name;
}
set {
- SetChildByRole(IntoIdentifierRole, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(IntoIdentifierRole, Identifier.Create (value, AstLocation.Empty));
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs
index 877771736a..95235527f3 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Attribute.cs
@@ -47,16 +47,31 @@ namespace ICSharpCode.NRefactory.CSharp
public AstNodeCollection Arguments {
get { return base.GetChildrenByRole (Roles.Argument); }
}
-
+
+ // HasArgumentList == false: [Empty]
+ public bool HasArgumentList {
+ get;
+ set;
+ }
+
public override S AcceptVisitor (IAstVisitor visitor, T data)
{
return visitor.VisitAttribute (this, data);
}
- protected internal override bool DoMatch(AstNode other, PatternMatching.Match match)
+ protected internal override bool DoMatch (AstNode other, PatternMatching.Match match)
{
Attribute o = other as Attribute;
- return o != null && this.Type.DoMatch(o.Type, match) && this.Arguments.DoMatch(o.Arguments, match);
+ return o != null && this.Type.DoMatch (o.Type, match) && this.Arguments.DoMatch (o.Arguments, match);
+ }
+
+ public override string ToString ()
+ {
+ if (IsNull)
+ return "Null";
+ var w = new System.IO.StringWriter ();
+ AcceptVisitor (new OutputVisitor (w, new CSharpFormattingOptions ()), null);
+ return w.ToString ();
}
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs
index 4f757b2640..61b424f491 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Comment.cs
@@ -1,4 +1,4 @@
-//
+//
// Comment.cs
//
// Author:
@@ -32,7 +32,7 @@ namespace ICSharpCode.NRefactory.CSharp
Documentation
}
- public class Comment : AstNode
+ public class Comment : AstNode, IRelocatable
{
public override NodeType NodeType {
get {
@@ -81,6 +81,15 @@ namespace ICSharpCode.NRefactory.CSharp
this.startLocation = startLocation;
this.endLocation = endLocation;
}
+
+ #region IRelocationable implementation
+ void IRelocatable.SetStartLocation (AstLocation startLocation)
+ {
+ int lineDelta = startLocation.Line - this.startLocation.Line;
+ endLocation = new AstLocation (endLocation.Line + lineDelta, lineDelta != 0 ? endLocation.Column : endLocation.Column + startLocation.Column - this.startLocation.Column);
+ this.startLocation = startLocation;
+ }
+ #endregion
public override S AcceptVisitor (IAstVisitor visitor, T data)
{
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs
index 8bac245c76..48c4e0ecb4 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/Constraint.cs
@@ -1,4 +1,4 @@
-//
+//
// Constraint.cs
//
// Author:
@@ -50,7 +50,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs
index e014a4b3f8..c33c0bf246 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/DelegateDeclaration.cs
@@ -1,4 +1,4 @@
-//
+//
// DelegateDeclaration.cs
//
// Author:
@@ -50,7 +50,16 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole (Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole (Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
+ }
+ }
+
+ public Identifier NameToken {
+ get {
+ return GetChildByRole (Roles.Identifier);
+ }
+ set {
+ SetChildByRole (Roles.Identifier, value);
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs
index bdd8c0f043..7d9a33df4f 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/ExternAliasDeclaration.cs
@@ -52,7 +52,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole (Roles.Identifier, new Identifier (value, AstLocation.Empty));
+ SetChildByRole (Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs
index 07dee45b76..c580f3790c 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/NamespaceDeclaration.cs
@@ -1,4 +1,4 @@
-//
+//
// NamespaceDeclaration.cs
//
// Author:
@@ -54,7 +54,7 @@ namespace ICSharpCode.NRefactory.CSharp
return builder.ToString ();
}
set {
- GetChildrenByRole(Roles.Identifier).ReplaceWith(value.Split('.').Select(ident => new Identifier(ident, AstLocation.Empty)));
+ GetChildrenByRole(Roles.Identifier).ReplaceWith(value.Split('.').Select(ident => Identifier.Create (ident, AstLocation.Empty)));
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs
index df7ac21388..945c936cd6 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeDeclaration.cs
@@ -1,4 +1,4 @@
-//
+//
// TypeDeclaration.cs
//
// Author:
@@ -55,7 +55,16 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole (Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole (Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
+ }
+ }
+
+ public Identifier NameToken {
+ get {
+ return GetChildByRole (Roles.Identifier);
+ }
+ set {
+ SetChildByRole (Roles.Identifier, value);
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs
index 84c9b2f507..ee2d58eb37 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/TypeParameterDeclaration.cs
@@ -35,7 +35,16 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole(Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
+ }
+ }
+
+ public Identifier NameToken {
+ get {
+ return GetChildByRole (Roles.Identifier);
+ }
+ set {
+ SetChildByRole (Roles.Identifier, value);
}
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs
index a157ce62fc..7fd3dfa096 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/GeneralScope/UsingAliasDeclaration.cs
@@ -1,4 +1,4 @@
-//
+//
// UsingAliasDeclaration.cs
//
// Author:
@@ -49,7 +49,7 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (AliasRole).Name;
}
set {
- SetChildByRole(AliasRole, new Identifier(value, AstLocation.Empty));
+ SetChildByRole(AliasRole, Identifier.Create (value, AstLocation.Empty));
}
}
@@ -72,13 +72,13 @@ namespace ICSharpCode.NRefactory.CSharp
public UsingAliasDeclaration (string alias, string nameSpace)
{
- AddChild (new Identifier (alias, AstLocation.Empty), AliasRole);
+ AddChild (Identifier.Create (alias, AstLocation.Empty), AliasRole);
AddChild (new SimpleType (nameSpace), ImportRole);
}
public UsingAliasDeclaration (string alias, AstType import)
{
- AddChild (new Identifier (alias, AstLocation.Empty), AliasRole);
+ AddChild (Identifier.Create (alias, AstLocation.Empty), AliasRole);
AddChild (import, ImportRole);
}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/IRelocatable.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/IRelocatable.cs
new file mode 100644
index 0000000000..48205191fc
--- /dev/null
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/IRelocatable.cs
@@ -0,0 +1,35 @@
+//
+// IRelocationable.cs
+//
+// Author:
+// Mike Krüger
+//
+// Copyright (c) 2011 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+
+namespace ICSharpCode.NRefactory.CSharp
+{
+ public interface IRelocatable
+ {
+ void SetStartLocation (AstLocation startLocation);
+ }
+}
+
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs
index 09639ab0b7..12bd644752 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/Identifier.cs
@@ -1,4 +1,4 @@
-//
+//
// Identifier.cs
//
// Author:
@@ -28,9 +28,9 @@ using System;
namespace ICSharpCode.NRefactory.CSharp
{
- public class Identifier : AstNode
+ public class Identifier : AstNode, IRelocatable
{
- public static readonly new Identifier Null = new NullIdentifier ();
+ public static readonly Identifier Null = new NullIdentifier ();
class NullIdentifier : Identifier
{
public override bool IsNull {
@@ -66,37 +66,69 @@ namespace ICSharpCode.NRefactory.CSharp
}
}
- public bool IsQuoted {
- get;
- set;
- }
-
AstLocation startLocation;
public override AstLocation StartLocation {
get {
return startLocation;
}
+
+ }
+
+ public virtual bool IsVerbatim {
+ get {
+ return false;
+ }
+ }
+
+ #region IRelocationable implementation
+ void IRelocatable.SetStartLocation (AstLocation startLocation)
+ {
+ this.startLocation = startLocation;
}
+ #endregion
public override AstLocation EndLocation {
get {
- return new AstLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length + (IsQuoted ? 1 : 0));
+ return new AstLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length);
}
}
- private Identifier ()
+ Identifier ()
{
this.name = string.Empty;
}
- public Identifier (string name, AstLocation location)
+ protected Identifier (string name, AstLocation location)
{
if (name == null)
throw new ArgumentNullException("name");
- IsQuoted = name.StartsWith ("@");
- this.Name = IsQuoted ? name.Substring (1) : name;
+ this.Name = name;
this.startLocation = location;
}
+
+ public static Identifier Create (string name)
+ {
+ return Create (name, AstLocation.Empty);
+ }
+
+ public static Identifier Create (string name, AstLocation location)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+ if (name.Length > 0 && name[0] == '@')
+ return new VerbatimIdentifier(name.Substring (1), location);
+ return new Identifier (name, location);
+ }
+
+ public static Identifier Create (string name, AstLocation location, bool isVerbatim)
+ {
+ if (name == null)
+ throw new ArgumentNullException("name");
+
+ if (isVerbatim)
+ return new VerbatimIdentifier(name, location);
+ return new Identifier (name, location);
+ }
public override S AcceptVisitor (IAstVisitor visitor, T data)
{
@@ -108,5 +140,24 @@ namespace ICSharpCode.NRefactory.CSharp
Identifier o = other as Identifier;
return o != null && !o.IsNull && MatchString(this.Name, o.Name);
}
+
+ class VerbatimIdentifier : Identifier
+ {
+ public override AstLocation EndLocation {
+ get {
+ return new AstLocation (StartLocation.Line, StartLocation.Column + (Name ?? "").Length + 1); // @"..."
+ }
+ }
+
+ public override bool IsVerbatim {
+ get {
+ return true;
+ }
+ }
+
+ public VerbatimIdentifier(string name, AstLocation location) : base (name, location)
+ {
+ }
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs
index b91048a5c9..771623eac5 100644
--- a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/MemberType.cs
@@ -1,4 +1,4 @@
-//
+//
// FullTypeName.cs
//
// Author:
@@ -47,7 +47,16 @@ namespace ICSharpCode.NRefactory.CSharp
return GetChildByRole (Roles.Identifier).Name;
}
set {
- SetChildByRole (Roles.Identifier, new Identifier(value, AstLocation.Empty));
+ SetChildByRole (Roles.Identifier, Identifier.Create (value, AstLocation.Empty));
+ }
+ }
+
+ public Identifier MemberNameToken {
+ get {
+ return GetChildByRole (Roles.Identifier);
+ }
+ set {
+ SetChildByRole (Roles.Identifier, value);
}
}
@@ -55,6 +64,29 @@ namespace ICSharpCode.NRefactory.CSharp
get { return GetChildrenByRole (Roles.TypeArgument); }
}
+ public MemberType ()
+ {
+ }
+
+ public MemberType (AstType target, string memberName)
+ {
+ this.Target = target;
+ this.MemberName = memberName;
+ }
+
+ public MemberType (AstType target, string memberName, IEnumerable typeArguments)
+ {
+ this.Target = target;
+ this.MemberName = memberName;
+ foreach (var arg in typeArguments) {
+ AddChild (arg, Roles.TypeArgument);
+ }
+ }
+
+ public MemberType (AstType target, string memberName, params AstType[] typeArguments) : this (target, memberName, (IEnumerable)typeArguments)
+ {
+ }
+
public override S AcceptVisitor (IAstVisitor visitor, T data)
{
return visitor.VisitMemberType (this, data);
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/NotImplementedAstVisitor.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/NotImplementedAstVisitor.cs
new file mode 100644
index 0000000000..cb8115be8e
--- /dev/null
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/NotImplementedAstVisitor.cs
@@ -0,0 +1,560 @@
+// 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 ICSharpCode.NRefactory.CSharp
+{
+ public class NotImplementedAstVisitor : IAstVisitor
+ {
+ public virtual S VisitAnonymousMethodExpression(AnonymousMethodExpression anonymousMethodExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitArrayCreateExpression(ArrayCreateExpression arrayCreateExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitArrayInitializerExpression(ArrayInitializerExpression arrayInitializerExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitAsExpression(AsExpression asExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitAssignmentExpression(AssignmentExpression assignmentExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitBaseReferenceExpression(BaseReferenceExpression baseReferenceExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitBinaryOperatorExpression(BinaryOperatorExpression binaryOperatorExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCastExpression(CastExpression castExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCheckedExpression(CheckedExpression checkedExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitConditionalExpression(ConditionalExpression conditionalExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitDefaultValueExpression(DefaultValueExpression defaultValueExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitDirectionExpression(DirectionExpression directionExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitIdentifierExpression(IdentifierExpression identifierExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitIndexerExpression(IndexerExpression indexerExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitInvocationExpression(InvocationExpression invocationExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitIsExpression(IsExpression isExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitLambdaExpression(LambdaExpression lambdaExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitMemberReferenceExpression(MemberReferenceExpression memberReferenceExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitNamedArgumentExpression(NamedArgumentExpression namedArgumentExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitNullReferenceExpression(NullReferenceExpression nullReferenceExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitObjectCreateExpression(ObjectCreateExpression objectCreateExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitParenthesizedExpression(ParenthesizedExpression parenthesizedExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitPointerReferenceExpression(PointerReferenceExpression pointerReferenceExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitPrimitiveExpression(PrimitiveExpression primitiveExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitSizeOfExpression(SizeOfExpression sizeOfExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitStackAllocExpression(StackAllocExpression stackAllocExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitThisReferenceExpression(ThisReferenceExpression thisReferenceExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitTypeOfExpression(TypeOfExpression typeOfExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitTypeReferenceExpression(TypeReferenceExpression typeReferenceExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitUnaryOperatorExpression(UnaryOperatorExpression unaryOperatorExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitUncheckedExpression(UncheckedExpression uncheckedExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryExpression(QueryExpression queryExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryContinuationClause(QueryContinuationClause queryContinuationClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryFromClause(QueryFromClause queryFromClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryLetClause(QueryLetClause queryLetClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryWhereClause(QueryWhereClause queryWhereClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryJoinClause(QueryJoinClause queryJoinClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryOrderClause(QueryOrderClause queryOrderClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryOrdering(QueryOrdering queryOrdering, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQuerySelectClause(QuerySelectClause querySelectClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitQueryGroupClause(QueryGroupClause queryGroupClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitAttribute(Attribute attribute, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitAttributeSection(AttributeSection attributeSection, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitDelegateDeclaration(DelegateDeclaration delegateDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitTypeDeclaration(TypeDeclaration typeDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitUsingAliasDeclaration(UsingAliasDeclaration usingAliasDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitUsingDeclaration(UsingDeclaration usingDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitBlockStatement(BlockStatement blockStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitBreakStatement(BreakStatement breakStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCheckedStatement(CheckedStatement checkedStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitContinueStatement(ContinueStatement continueStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitDoWhileStatement(DoWhileStatement doWhileStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitEmptyStatement(EmptyStatement emptyStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitExpressionStatement(ExpressionStatement expressionStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitFixedStatement(FixedStatement fixedStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitForeachStatement(ForeachStatement foreachStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitForStatement(ForStatement forStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitGotoCaseStatement(GotoCaseStatement gotoCaseStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitGotoDefaultStatement(GotoDefaultStatement gotoDefaultStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitGotoStatement(GotoStatement gotoStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitIfElseStatement(IfElseStatement ifElseStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitLabelStatement(LabelStatement labelStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitLockStatement(LockStatement lockStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitReturnStatement(ReturnStatement returnStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitSwitchStatement(SwitchStatement switchStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitSwitchSection(SwitchSection switchSection, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCaseLabel(CaseLabel caseLabel, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitThrowStatement(ThrowStatement throwStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitTryCatchStatement(TryCatchStatement tryCatchStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCatchClause(CatchClause catchClause, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitUncheckedStatement(UncheckedStatement uncheckedStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitUnsafeStatement(UnsafeStatement unsafeStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitUsingStatement(UsingStatement usingStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitVariableDeclarationStatement(VariableDeclarationStatement variableDeclarationStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitWhileStatement(WhileStatement whileStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitYieldBreakStatement(YieldBreakStatement yieldBreakStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitYieldStatement(YieldStatement yieldStatement, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitAccessor(Accessor accessor, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitConstructorDeclaration(ConstructorDeclaration constructorDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitConstructorInitializer(ConstructorInitializer constructorInitializer, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitDestructorDeclaration(DestructorDeclaration destructorDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitEnumMemberDeclaration(EnumMemberDeclaration enumMemberDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitEventDeclaration(EventDeclaration eventDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCustomEventDeclaration(CustomEventDeclaration customEventDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitFieldDeclaration(FieldDeclaration fieldDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitMethodDeclaration(MethodDeclaration methodDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitOperatorDeclaration(OperatorDeclaration operatorDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitParameterDeclaration(ParameterDeclaration parameterDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitVariableInitializer(VariableInitializer variableInitializer, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCompilationUnit(CompilationUnit compilationUnit, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitSimpleType(SimpleType simpleType, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitMemberType(MemberType memberType, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitComposedType(ComposedType composedType, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitArraySpecifier(ArraySpecifier arraySpecifier, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitPrimitiveType(PrimitiveType primitiveType, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitComment(Comment comment, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitTypeParameterDeclaration(TypeParameterDeclaration typeParameterDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitConstraint(Constraint constraint, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitCSharpTokenNode(CSharpTokenNode cSharpTokenNode, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitIdentifier(Identifier identifier, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitEmptyExpression(EmptyExpression emptyExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitExternAliasDeclaration(ExternAliasDeclaration externAliasDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitFixedFieldDeclaration(FixedFieldDeclaration fixedFieldDeclaration, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public virtual S VisitFixedVariableInitializer(FixedVariableInitializer fixedVariableInitializer, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public S VisitUndocumentedExpression(UndocumentedExpression undocumentedExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public S VisitAnonymousTypeCreateExpression(AnonymousTypeCreateExpression anonymousTypeCreateExpression, T data)
+ {
+ throw new NotImplementedException();
+ }
+
+ public S VisitPatternPlaceholder(AstNode placeholder, ICSharpCode.NRefactory.PatternMatching.Pattern pattern, T data)
+ {
+ throw new NotImplementedException();
+ }
+ }
+}
diff --git a/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ObservableAstVisitor.cs b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ObservableAstVisitor.cs
new file mode 100644
index 0000000000..253f6ef3c3
--- /dev/null
+++ b/src/Libraries/NewNRefactory/ICSharpCode.NRefactory/CSharp/Ast/ObservableAstVisitor.cs
@@ -0,0 +1,1141 @@
+//
+// ObservableAstVisitor.cs
+//
+// Author:
+// Mike Krüger
+//
+// Copyright (c) 2011 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+using System;
+
+namespace ICSharpCode.NRefactory.CSharp
+{
+ public class ObservableAstVisitor: IAstVisitor
+ {
+ S VisitChildren (AstNode node, T data)
+ {
+ AstNode next;
+ for (var child = node.FirstChild; child != null; child = next) {
+ // Store next to allow the loop to continue
+ // if the visitor removes/replaces child.
+ next = child.NextSibling;
+ child.AcceptVisitor (this, data);
+ }
+ return default (S);
+ }
+
+ public event Action CompilationUnitVisited;
+
+ S IAstVisitor.VisitCompilationUnit (CompilationUnit unit, T data)
+ {
+ var handler = CompilationUnitVisited;
+ if (handler != null)
+ handler (unit, data);
+ return VisitChildren (unit, data);
+ }
+
+ public event Action CommentVisited;
+
+ S IAstVisitor.VisitComment (Comment comment, T data)
+ {
+ var handler = CommentVisited;
+ if (handler != null)
+ handler (comment, data);
+ return VisitChildren (comment, data);
+ }
+
+ public event Action IdentifierVisited;
+
+ S IAstVisitor.VisitIdentifier (Identifier identifier, T data)
+ {
+ var handler = IdentifierVisited;
+ if (handler != null)
+ handler (identifier, data);
+ return VisitChildren (identifier, data);
+ }
+
+ public event Action CSharpTokenNodeVisited;
+
+ S IAstVisitor.VisitCSharpTokenNode (CSharpTokenNode token, T data)
+ {
+ var handler = CSharpTokenNodeVisited;
+ if (handler != null)
+ handler (token, data);
+ return VisitChildren (token, data);
+ }
+
+ public event Action PrimitiveTypeVisited;
+
+ S IAstVisitor.VisitPrimitiveType (PrimitiveType primitiveType, T data)
+ {
+ var handler = PrimitiveTypeVisited;
+ if (handler != null)
+ handler (primitiveType, data);
+ return VisitChildren (primitiveType, data);
+ }
+
+ public event Action ComposedTypeVisited;
+
+ S IAstVisitor.VisitComposedType (ComposedType composedType, T data)
+ {
+ var handler = ComposedTypeVisited;
+ if (handler != null)
+ handler (composedType, data);
+ return VisitChildren (composedType, data);
+ }
+
+ public event Action SimpleTypeVisited;
+
+ S IAstVisitor.VisitSimpleType (SimpleType simpleType, T data)
+ {
+ var handler = SimpleTypeVisited;
+ if (handler != null)
+ handler (simpleType, data);
+ return VisitChildren (simpleType, data);
+ }
+
+ public event Action MemberTypeVisited;
+
+ S IAstVisitor.VisitMemberType (MemberType memberType, T data)
+ {
+ var handler = MemberTypeVisited;
+ if (handler != null)
+ handler (memberType, data);
+ return VisitChildren (memberType, data);
+ }
+
+ public event Action AttributeVisited;
+
+ S IAstVisitor.VisitAttribute (Attribute attribute, T data)
+ {
+ var handler = AttributeVisited;
+ if (handler != null)
+ handler (attribute, data);
+ return VisitChildren (attribute, data);
+ }
+
+ public event Action AttributeSectionVisited;
+
+ S IAstVisitor.VisitAttributeSection (AttributeSection attributeSection, T data)
+ {
+ var handler = AttributeSectionVisited;
+ if (handler != null)
+ handler (attributeSection, data);
+ return VisitChildren (attributeSection, data);
+ }
+
+ public event Action DelegateDeclarationVisited;
+
+ S IAstVisitor.VisitDelegateDeclaration (DelegateDeclaration delegateDeclaration, T data)
+ {
+ var handler = DelegateDeclarationVisited;
+ if (handler != null)
+ handler (delegateDeclaration, data);
+ return VisitChildren (delegateDeclaration, data);
+ }
+
+ public event Action NamespaceDeclarationVisited;
+
+ S IAstVisitor.VisitNamespaceDeclaration (NamespaceDeclaration namespaceDeclaration, T data)
+ {
+ var handler = NamespaceDeclarationVisited;
+ if (handler != null)
+ handler (namespaceDeclaration, data);
+ return VisitChildren (namespaceDeclaration, data);
+ }
+
+ public event Action TypeDeclarationVisited;
+
+ S IAstVisitor.VisitTypeDeclaration (TypeDeclaration typeDeclaration, T data)
+ {
+ var handler = TypeDeclarationVisited;
+ if (handler != null)
+ handler (typeDeclaration, data);
+ return VisitChildren (typeDeclaration, data);
+ }
+
+ public event Action TypeParameterDeclarationVisited;
+
+ S IAstVisitor.VisitTypeParameterDeclaration (TypeParameterDeclaration typeParameterDeclaration, T data)
+ {
+ var handler = TypeParameterDeclarationVisited;
+ if (handler != null)
+ handler (typeParameterDeclaration, data);
+ return VisitChildren (typeParameterDeclaration, data);
+ }
+
+ public event Action EnumMemberDeclarationVisited;
+
+ S IAstVisitor.VisitEnumMemberDeclaration (EnumMemberDeclaration enumMemberDeclaration, T data)
+ {
+ var handler = EnumMemberDeclarationVisited;
+ if (handler != null)
+ handler (enumMemberDeclaration, data);
+ return VisitChildren (enumMemberDeclaration, data);
+ }
+
+ public event Action UsingDeclarationVisited;
+
+ S IAstVisitor.VisitUsingDeclaration (UsingDeclaration usingDeclaration, T data)
+ {
+ var handler = UsingDeclarationVisited;
+ if (handler != null)
+ handler (usingDeclaration, data);
+ return VisitChildren (usingDeclaration, data);
+ }
+
+ public event Action UsingAliasDeclarationVisited;
+
+ S IAstVisitor.VisitUsingAliasDeclaration (UsingAliasDeclaration usingDeclaration, T data)
+ {
+ var handler = UsingAliasDeclarationVisited;
+ if (handler != null)
+ handler (usingDeclaration, data);
+ return VisitChildren (usingDeclaration, data);
+ }
+
+ public event Action ExternAliasDeclarationVisited;
+
+ S IAstVisitor.VisitExternAliasDeclaration (ExternAliasDeclaration externAliasDeclaration, T data)
+ {
+ var handler = ExternAliasDeclarationVisited;
+ if (handler != null)
+ handler (externAliasDeclaration, data);
+ return VisitChildren (externAliasDeclaration, data);
+ }
+
+ public event Action ConstructorDeclarationVisited;
+
+ S IAstVisitor.VisitConstructorDeclaration (ConstructorDeclaration constructorDeclaration, T data)
+ {
+ var handler = ConstructorDeclarationVisited;
+ if (handler != null)
+ handler (constructorDeclaration, data);
+ return VisitChildren (constructorDeclaration, data);
+ }
+
+ public event Action ConstructorInitializerVisited;
+
+ S IAstVisitor