diff --git a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
index 7ea5c0b02..9235e7698 100644
--- a/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
+++ b/ICSharpCode.Decompiler/Ast/DecompilerContext.cs
@@ -12,6 +12,7 @@ namespace ICSharpCode.Decompiler
public CancellationToken CancellationToken;
public TypeDefinition CurrentType;
public MethodDefinition CurrentMethod;
+ public DecompilerSettings Settings;
public DecompilerContext Clone()
{
diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
index c0fcabab3..7684b1702 100644
--- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
+++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
@@ -105,6 +105,9 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
bool HandleAnonymousMethod(ObjectCreateExpression objectCreateExpression, Expression target, MethodReference methodRef)
{
+ if (!context.Settings.AnonymousMethods)
+ return false; // anonymous method decompilation is disabled
+
// Anonymous methods are defined in the same assembly, so there's no need to Resolve().
MethodDefinition method = methodRef as MethodDefinition;
if (!IsAnonymousMethod(context, method))
diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs
new file mode 100644
index 000000000..53736311a
--- /dev/null
+++ b/ICSharpCode.Decompiler/DecompilerSettings.cs
@@ -0,0 +1,55 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+
+using System;
+using System.ComponentModel;
+
+namespace ICSharpCode.Decompiler
+{
+ ///
+ /// Settings for the decompiler.
+ ///
+ public class DecompilerSettings : INotifyPropertyChanged
+ {
+ bool anonymousMethods = true;
+
+ ///
+ /// Decompile anonymous methods/lambdas.
+ ///
+ public bool AnonymousMethods {
+ get { return anonymousMethods; }
+ set {
+ if (anonymousMethods != value) {
+ anonymousMethods = value;
+ OnPropertyChanged("AnonymousMethods");
+ }
+ }
+ }
+
+ bool yieldReturn = true;
+
+ ///
+ /// Decompile enumerators.
+ ///
+ public bool YieldReturn {
+ get { return yieldReturn; }
+ set {
+ if (yieldReturn != value) {
+ yieldReturn = value;
+ OnPropertyChanged("YieldReturn");
+ }
+ }
+ }
+
+ public event EventHandler YieldReturnChanged;
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ protected virtual void OnPropertyChanged(string propertyName)
+ {
+ if (PropertyChanged != null) {
+ PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
+ }
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
index 3896d43f1..104543360 100644
--- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
+++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
@@ -66,6 +66,7 @@
+
@@ -95,6 +96,7 @@
+
diff --git a/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
new file mode 100644
index 000000000..b1ec1b78b
--- /dev/null
+++ b/ICSharpCode.Decompiler/ILAst/YieldReturnDecompiler.cs
@@ -0,0 +1,15 @@
+// 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.Decompiler.ILAst
+{
+ public class YieldReturnDecompiler
+ {
+ // For a description on the code generated by the C# compiler for yield return:
+ // http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx
+
+ // not implemented yet...
+ }
+}
diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
index a24ef753b..42ee0a0cd 100644
--- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -53,6 +53,7 @@
+
diff --git a/ICSharpCode.Decompiler/Tests/YieldReturn.cs b/ICSharpCode.Decompiler/Tests/YieldReturn.cs
new file mode 100644
index 000000000..de9bd1efb
--- /dev/null
+++ b/ICSharpCode.Decompiler/Tests/YieldReturn.cs
@@ -0,0 +1,103 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+
+using System;
+using System.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 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;
+ }
+ }
+}
diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs
index c01caaf1c..60fc12388 100644
--- a/ILSpy/CSharpLanguage.cs
+++ b/ILSpy/CSharpLanguage.cs
@@ -376,7 +376,8 @@ namespace ICSharpCode.ILSpy
return new AstBuilder(
new DecompilerContext {
CancellationToken = options.CancellationToken,
- CurrentType = currentType
+ CurrentType = currentType,
+ Settings = options.DecompilerSettings
});
}
diff --git a/ILSpy/DecompilationOptions.cs b/ILSpy/DecompilationOptions.cs
index 8dc953c6d..bcd59af6b 100644
--- a/ILSpy/DecompilationOptions.cs
+++ b/ILSpy/DecompilationOptions.cs
@@ -18,6 +18,7 @@
using System;
using System.Threading;
+using ICSharpCode.Decompiler;
namespace ICSharpCode.ILSpy
{
@@ -45,5 +46,15 @@ namespace ICSharpCode.ILSpy
/// to allow for cooperative cancellation of the decompilation task.
///
public CancellationToken CancellationToken { get; set; }
+
+ ///
+ /// Gets the settings for the decompiler.
+ ///
+ public DecompilerSettings DecompilerSettings { get; set; }
+
+ public DecompilationOptions()
+ {
+ this.DecompilerSettings = DecompilerSettingsPanel.LoadDecompilerSettings(ILSpySettings.Load());
+ }
}
}
diff --git a/ILSpy/DecompilerSettingsPanel.xaml b/ILSpy/DecompilerSettingsPanel.xaml
new file mode 100644
index 000000000..00732070c
--- /dev/null
+++ b/ILSpy/DecompilerSettingsPanel.xaml
@@ -0,0 +1,9 @@
+
+
+ Decompile anonymous methods/lambdas
+ Decompile enumerators (yield return)
+
+
\ No newline at end of file
diff --git a/ILSpy/DecompilerSettingsPanel.xaml.cs b/ILSpy/DecompilerSettingsPanel.xaml.cs
new file mode 100644
index 000000000..694ad1040
--- /dev/null
+++ b/ILSpy/DecompilerSettingsPanel.xaml.cs
@@ -0,0 +1,57 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Xml.Linq;
+using ICSharpCode.Decompiler;
+
+namespace ICSharpCode.ILSpy
+{
+ ///
+ /// Interaction logic for DecompilerSettingsPanel.xaml
+ ///
+ [ExportOptionPage("Decompiler")]
+ partial class DecompilerSettingsPanel : UserControl, IOptionPage
+ {
+ public DecompilerSettingsPanel()
+ {
+ InitializeComponent();
+ }
+
+ public void Load(ILSpySettings settings)
+ {
+ this.DataContext = LoadDecompilerSettings(settings);
+ }
+
+ public static DecompilerSettings LoadDecompilerSettings(ILSpySettings settings)
+ {
+ XElement e = settings["DecompilerSettings"];
+ DecompilerSettings s = new DecompilerSettings();
+ s.AnonymousMethods = (bool?)e.Attribute("anonymousMethods") ?? s.AnonymousMethods;
+ s.YieldReturn = (bool?)e.Attribute("yieldReturn") ?? s.YieldReturn;
+ return s;
+ }
+
+ public void Save(XElement root)
+ {
+ DecompilerSettings s = (DecompilerSettings)this.DataContext;
+ XElement section = new XElement("DecompilerSettings");
+ section.SetAttributeValue("anonymousMethods", s.AnonymousMethods);
+ section.SetAttributeValue("yieldReturn", s.YieldReturn);
+
+ XElement existingElement = root.Element("DecompilerSettings");
+ if (existingElement != null)
+ existingElement.ReplaceWith(section);
+ else
+ root.Add(section);
+ }
+ }
+}
\ No newline at end of file
diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj
index 41598a0a1..b094e107b 100644
--- a/ILSpy/ILSpy.csproj
+++ b/ILSpy/ILSpy.csproj
@@ -89,6 +89,10 @@
+
+ DecompilerSettingsPanel.xaml
+ Code
+
@@ -111,6 +115,10 @@
OpenFromGacDialog.xaml
Code
+
+ OptionsDialog.xaml
+ Code
+
README.txt
@@ -168,8 +176,10 @@
SearchBox.cs
+
+
DecompilerTextView.cs
diff --git a/ILSpy/MainWindow.xaml.cs b/ILSpy/MainWindow.xaml.cs
index 5056a19a9..90867ade9 100644
--- a/ILSpy/MainWindow.xaml.cs
+++ b/ILSpy/MainWindow.xaml.cs
@@ -429,6 +429,11 @@ namespace ICSharpCode.ILSpy
decompilerTextView.Decompile(this.CurrentLanguage, this.SelectedNodes, new DecompilationOptions());
}
+ public void RefreshDecompiledView()
+ {
+ TreeView_SelectionChanged(null, null);
+ }
+
public DecompilerTextView TextView {
get { return decompilerTextView; }
}
diff --git a/ILSpy/OptionsDialog.xaml b/ILSpy/OptionsDialog.xaml
new file mode 100644
index 000000000..c5a9b8fe7
--- /dev/null
+++ b/ILSpy/OptionsDialog.xaml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/ILSpy/OptionsDialog.xaml.cs b/ILSpy/OptionsDialog.xaml.cs
new file mode 100644
index 000000000..dc87b88bf
--- /dev/null
+++ b/ILSpy/OptionsDialog.xaml.cs
@@ -0,0 +1,93 @@
+// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
+// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
+
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.Composition;
+using System.Text;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Xml.Linq;
+
+namespace ICSharpCode.ILSpy
+{
+ ///
+ /// Interaction logic for OptionsDialog.xaml
+ ///
+ public partial class OptionsDialog : Window
+ {
+ [ImportMany("OptionPages", typeof(UIElement), RequiredCreationPolicy = CreationPolicy.NonShared)]
+ Lazy[] optionPages = null;
+
+ public OptionsDialog()
+ {
+ InitializeComponent();
+ App.CompositionContainer.ComposeParts(this);
+ ILSpySettings settings = ILSpySettings.Load();
+ foreach (var optionPage in optionPages) {
+ TabItem tabItem = new TabItem();
+ tabItem.Header = optionPage.Metadata.Title;
+ tabItem.Content = optionPage.Value;
+ tabControl.Items.Add(tabItem);
+
+ IOptionPage page = optionPage.Value as IOptionPage;
+ if (page != null)
+ page.Load(settings);
+ }
+ }
+
+ void OKButton_Click(object sender, RoutedEventArgs e)
+ {
+ ILSpySettings.Update(
+ delegate (XElement root) {
+ foreach (var optionPage in optionPages) {
+ IOptionPage page = optionPage.Value as IOptionPage;
+ if (page != null)
+ page.Save(root);
+ }
+ });
+ this.DialogResult = true;
+ Close();
+ }
+ }
+
+ public interface IOptionsMetadata
+ {
+ string Title { get; }
+ }
+
+ public interface IOptionPage
+ {
+ void Load(ILSpySettings settings);
+ void Save(XElement root);
+ }
+
+ [MetadataAttribute]
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple=false)]
+ public class ExportOptionPageAttribute : ExportAttribute
+ {
+ public ExportOptionPageAttribute(string title)
+ : base("OptionPages", typeof(UIElement))
+ {
+ this.Title = title;
+ }
+
+ public string Title { get; private set; }
+ }
+
+ [ExportMainMenuCommand(Menu = "_View", Header = "_Options", MenuCategory = "Options", MenuOrder = 999)]
+ sealed class ShowOptionsCommand : SimpleCommand
+ {
+ public override void Execute(object parameter)
+ {
+ OptionsDialog dlg = new OptionsDialog();
+ dlg.Owner = MainWindow.Instance;
+ if (dlg.ShowDialog() == true)
+ MainWindow.Instance.RefreshDecompiledView();
+ }
+ }
+}
\ No newline at end of file