diff --git a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs index d45db6388..31e7023c1 100644 --- a/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs +++ b/Debugger/ILSpy.Debugger/Commands/BreakpointCommand.cs @@ -1,10 +1,15 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.Linq; using System.Windows; + using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.AvalonEdit; +using ICSharpCode.ILSpy.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Bookmarks; using ICSharpCode.ILSpy.Debugger.Services; +using Mono.Cecil; namespace ICSharpCode.ILSpy.Debugger.Commands { @@ -23,8 +28,8 @@ namespace ICSharpCode.ILSpy.Debugger.Commands // check if the codemappings exists for this line var storage = DebugInformation.CodeMappings; int token = 0; - foreach (var key in storage.Keys) { - var instruction = storage[key].GetInstructionByLineNumber(line, out token); + foreach (var storageEntry in storage) { + var instruction = storageEntry.Value.GetInstructionByLineNumber(line, out token); if (instruction == null) { continue; @@ -32,8 +37,9 @@ namespace ICSharpCode.ILSpy.Debugger.Commands // no bookmark on the line: create a new breakpoint DebuggerService.ToggleBreakpointAt( - DebugInformation.DecompiledMemberReferences[key], + instruction.MemberMapping.MemberReference, line, + token, instruction.ILInstructionOffset, DebugInformation.Language); break; @@ -47,4 +53,23 @@ namespace ICSharpCode.ILSpy.Debugger.Commands } } } + + [ExportBookmarkContextMenuEntry(Header="Disable Breakpoint", Category="Debugger")] + public class DisableBreakpointCommand : IBookmarkContextMenuEntry + { + public bool IsVisible(IBookmark[] bookmarks) + { + return bookmarks.Any(b => b is BreakpointBookmark && (b as BreakpointBookmark).IsEnabled); + } + + public bool IsEnabled(IBookmark[] bookmarks) + { + return true; + } + + public void Execute(IBookmark[] bookmarks) + { + throw new NotImplementedException(); + } + } } diff --git a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs index c3bbff34d..e54dfa1ef 100644 --- a/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs +++ b/Debugger/ILSpy.Debugger/Commands/DebuggerCommands.cs @@ -138,7 +138,7 @@ namespace ICSharpCode.ILSpy.Debugger.Commands foreach (var item in items.First().Items.OfType()) { string header = (string)item.Header; - if (header.StartsWith("Remove")) continue; + if (header.StartsWith("Remove") || header.StartsWith("Show")) continue; if (header.StartsWith("Attach") || header.StartsWith("Debug")) item.IsEnabled = enable; @@ -307,6 +307,7 @@ namespace ICSharpCode.ILSpy.Debugger.Commands public override void Execute(object parameter) { if (CurrentDebugger.IsDebugging && !CurrentDebugger.IsProcessRunning) { + CurrentLineBookmark.Remove(); CurrentDebugger.Continue(); MainWindow.Instance.SetStatus("Running...", Brushes.Black); } diff --git a/Debugger/ILSpy.Debugger/DebuggerSettings.cs b/Debugger/ILSpy.Debugger/DebuggerSettings.cs index d723f0998..ab9f7e6d9 100644 --- a/Debugger/ILSpy.Debugger/DebuggerSettings.cs +++ b/Debugger/ILSpy.Debugger/DebuggerSettings.cs @@ -3,15 +3,73 @@ using System; using System.ComponentModel; +using System.Xml.Linq; namespace ICSharpCode.ILSpy.Debugger { public class DebuggerSettings : INotifyPropertyChanged { - bool showWarnings = true; - bool askArguments = true; - bool debugWholeTypesOnly = false; + #region members + private static readonly string DEBUGGER_SETTINGS = "DebuggerSettings"; + private static readonly string SHOW_WARNINGS = "showWarnings"; + private static readonly string ASK_ARGUMENTS = "askForArguments"; + private static readonly string SHOW_BOOKMARKS = "showAllBookmarks"; + private static readonly string SHOW_MODULE = "showModuleName"; + private static readonly string SHOW_ARGUMENTS = "showArguments"; + private static readonly string SHOW_ARGUMENTVALUE = "showArgumentValues"; + + private bool showWarnings = true; + private bool askArguments = true; + private bool debugWholeTypesOnly = false; + private bool showAllBookmarks = false; + private bool showModuleName = true; + private bool showArguments = false; + private bool showArgumentValues = false; + private static DebuggerSettings s_instance; + #endregion + + public static DebuggerSettings Instance + { + get { + if (null == s_instance) + s_instance = new DebuggerSettings(); + return s_instance; + } + } + + private DebuggerSettings() + { + } + + public void Load(ILSpySettings settings) + { + XElement e = settings[DEBUGGER_SETTINGS]; + ShowWarnings = (bool?)e.Attribute(SHOW_WARNINGS) ?? ShowWarnings; + AskForArguments = (bool?)e.Attribute(ASK_ARGUMENTS) ?? AskForArguments; + ShowAllBookmarks = (bool?)e.Attribute(SHOW_BOOKMARKS) ?? ShowAllBookmarks; + ShowModuleName = (bool?)e.Attribute(SHOW_MODULE) ?? ShowModuleName; + ShowArguments = (bool?)e.Attribute(SHOW_ARGUMENTS) ?? ShowArguments; + ShowArgumentValues = (bool?)e.Attribute(SHOW_ARGUMENTVALUE) ?? ShowArgumentValues; + } + + public void Save(XElement root) + { + XElement section = new XElement(DEBUGGER_SETTINGS); + section.SetAttributeValue(SHOW_WARNINGS, ShowWarnings); + section.SetAttributeValue(ASK_ARGUMENTS, AskForArguments); + section.SetAttributeValue(SHOW_BOOKMARKS, ShowAllBookmarks); + section.SetAttributeValue(SHOW_MODULE, ShowModuleName); + section.SetAttributeValue(SHOW_ARGUMENTS, ShowArguments); + section.SetAttributeValue(SHOW_ARGUMENTVALUE, ShowArgumentValues); + + XElement existingElement = root.Element(DEBUGGER_SETTINGS); + if (existingElement != null) + existingElement.ReplaceWith(section); + else + root.Add(section); + } + /// /// Show warnings messages. /// Default value is true. @@ -56,6 +114,61 @@ namespace ICSharpCode.ILSpy.Debugger } } + /// + /// Show all bookmarks in breakpoints window. + /// + [DefaultValue(false)] + public bool ShowAllBookmarks { + get { return showAllBookmarks; } + set { + if (showAllBookmarks != value) { + showAllBookmarks = value; + OnPropertyChanged("ShowAllBookmarks"); + } + } + } + + /// + /// Show module name in callstack panel. + /// + [DefaultValue(true)] + public bool ShowModuleName { + get { return showModuleName; } + set { + if (showModuleName != value) { + showModuleName = value; + OnPropertyChanged("ShowModuleName"); + } + } + } + + /// + /// Show module name in callstack panel. + /// + [DefaultValue(false)] + public bool ShowArguments { + get { return showArguments; } + set { + if (showArguments != value) { + showArguments = value; + OnPropertyChanged("ShowArguments"); + } + } + } + /// + /// Show module name in callstack panel. + /// + [DefaultValue(false)] + public bool ShowArgumentValues { + get { return showArgumentValues; } + set { + if (showArgumentValues != value) { + showArgumentValues = value; + OnPropertyChanged("ShowArgumentValues"); + } + } + } + public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) @@ -64,5 +177,6 @@ namespace ICSharpCode.ILSpy.Debugger PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } + } } diff --git a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj index f5feab262..bb48da354 100644 --- a/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj +++ b/Debugger/ILSpy.Debugger/ILSpy.Debugger.csproj @@ -87,8 +87,6 @@ - - @@ -96,19 +94,25 @@ - DebuggerTooltipControl.xaml - AttachToProcessWindow.xaml Code + + BreakpointPanel.xaml + Code + + + CallStackPanel.xaml + Code + DebuggerSettingsPanel.xaml Code @@ -123,13 +127,14 @@ - + + diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs index 8aa104bdd..7bd9b2dc4 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs +++ b/Debugger/ILSpy.Debugger/Services/Debugger/WindowsDebugger.cs @@ -1,6 +1,7 @@ // Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) // This code is distributed under the GNU LGPL (for details please see \doc\license.txt) using System; +using System.ComponentModel.Composition; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; @@ -27,6 +28,7 @@ using StackFrame = Debugger.StackFrame; namespace ICSharpCode.ILSpy.Debugger.Services { + [Export(typeof(IDebugger))] public class WindowsDebugger : IDebugger { enum StopAttachedProcessDialogResult { @@ -283,7 +285,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services int key = frame.MethodInfo.MetadataToken; // get the mapped instruction from the current line marker or the next one - if (!DebugInformation.CodeMappings.ContainsKey(key)) + if (DebugInformation.CodeMappings == null || !DebugInformation.CodeMappings.ContainsKey(key)) return null; return DebugInformation.CodeMappings[key].GetInstructionByTokenAndOffset(key, frame.IP, out isMatch); @@ -550,7 +552,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services debugger, bookmark.MemberReference.DeclaringType.FullName, bookmark.LineNumber, - bookmark.MemberReference.MetadataToken.ToInt32(), + bookmark.FunctionToken, bookmark.ILRange.From, bookmark.IsEnabled); @@ -796,11 +798,12 @@ namespace ICSharpCode.ILSpy.Debugger.Services int line; MemberReference memberReference; - if (DebugInformation.CodeMappings.ContainsKey(token) && + if (DebugInformation.CodeMappings != null && + DebugInformation.CodeMappings.ContainsKey(token) && DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out memberReference, out line)) { DebugInformation.DebugStepInformation = null; // we do not need to step into/out DebuggerService.RemoveCurrentLineMarker(); - DebuggerService.JumpToCurrentLine(memberReference, line, 0, line, 0); + DebuggerService.JumpToCurrentLine(memberReference, line, 0, line, 0, ilOffset); } else { StepIntoUnknownFrame(frame); diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml new file mode 100644 index 000000000..7f8f8a032 --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs new file mode 100644 index 000000000..8dea0b867 --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/BreakpointPanel.xaml.cs @@ -0,0 +1,110 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +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 ICSharpCode.ILSpy; +using ICSharpCode.ILSpy.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Bookmarks; +using ICSharpCode.ILSpy.Options; + +namespace ICSharpCode.ILSpy.Debugger.UI +{ + /// + /// Interaction logic for BreakpointPanel.xaml + /// + public partial class BreakpointPanel : UserControl, IPane + { + static BreakpointPanel s_instance; + + public static BreakpointPanel Instance + { + get { + if (null == s_instance) + { + App.Current.VerifyAccess(); + s_instance = new BreakpointPanel(); + } + return s_instance; + } + } + + private BreakpointPanel() + { + InitializeComponent(); + } + + public void Show() + { + if (!IsVisible) + { + SetItemSource(); + + MainWindow.Instance.ShowInBottomPane("Breakpoints", this); + + BookmarkManager.Added += delegate { SetItemSource(); }; + BookmarkManager.Removed += delegate { SetItemSource(); }; + DebuggerSettings.Instance.PropertyChanged += + delegate(object s, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowAllBookmarks") SetItemSource(); }; + } + } + + private void SetItemSource() + { + view.ItemsSource = null; + if (DebuggerSettings.Instance.ShowAllBookmarks) + view.ItemsSource = BookmarkManager.Bookmarks; + else + view.ItemsSource = BookmarkManager.Bookmarks.Where(b => b is BreakpointBookmark); + } + + public void Closed() + { + BookmarkManager.Added -= delegate { SetItemSource(); }; + BookmarkManager.Removed -= delegate { SetItemSource(); }; + DebuggerSettings.Instance.PropertyChanged -= + delegate(object s, PropertyChangedEventArgs e) { if (e.PropertyName == "ShowAllBookmarks") SetItemSource(); }; + } + + void view_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (MouseButton.Left != e.ChangedButton) + return; + var selectedItem = view.SelectedItem as BookmarkBase; + if (null == selectedItem) + return; + MainWindow.Instance.JumpToReference(selectedItem.MemberReference); + MainWindow.Instance.TextView.UnfoldAndScroll(selectedItem.LineNumber); + e.Handled = true; + } + + void view_KeyUp(object sender, KeyEventArgs e) + { + if (e.Key != Key.Delete) + return; + var selectedItem = view.SelectedItem as BookmarkBase; + if (null == selectedItem) + return; + BookmarkManager.RemoveMark(selectedItem); + e.Handled = true; + } + } + + [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Breakpoints", MenuCategory="View", MenuOrder=8)] + public class BookmarkManagerPanelCommand : SimpleCommand + { + public override void Execute(object parameter) + { + BreakpointPanel.Instance.Show(); + } + } +} \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml new file mode 100644 index 000000000..675b63af8 --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs new file mode 100644 index 000000000..2b743841c --- /dev/null +++ b/Debugger/ILSpy.Debugger/UI/CallStackPanel.xaml.cs @@ -0,0 +1,275 @@ +// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt) +// This code is distributed under the GNU LGPL (for details please see \doc\license.txt) +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +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 Debugger; +using ICSharpCode.ILSpy; +using ICSharpCode.ILSpy.Debugger.Models.TreeModel; +using ICSharpCode.ILSpy.Debugger.Services; +using ICSharpCode.ILSpy.XmlDoc; +using Mono.Cecil; +using Mono.CSharp; + +namespace ICSharpCode.ILSpy.Debugger.UI +{ + /// + /// Interaction logic for CallStackPanel.xaml + /// + public partial class CallStackPanel : UserControl, IPane + { + static CallStackPanel s_instance; + IDebugger m_currentDebugger; + + public static CallStackPanel Instance + { + get { + if (null == s_instance) + { + App.Current.VerifyAccess(); + s_instance = new CallStackPanel(); + } + return s_instance; + } + } + + private CallStackPanel() + { + InitializeComponent(); + } + + public void Show() + { + if (!IsVisible) + { + // load debugger settings (to update context menu) + ILSpySettings settings = ILSpySettings.Load(); + DebuggerSettings.Instance.Load(settings); + DebuggerSettings.Instance.PropertyChanged += new PropertyChangedEventHandler(OnDebuggerSettingChanged); + + SwitchModuleColumn(); + MainWindow.Instance.ShowInBottomPane("Callstack", this); + + DebuggerService.DebugStarted += new EventHandler(OnDebugStarted); + DebuggerService.DebugStopped += new EventHandler(OnDebugStopped); + if (DebuggerService.IsDebuggerStarted) + OnDebugStarted(null, EventArgs.Empty); + } + } + + public void Closed() + { + DebuggerService.DebugStarted -= new EventHandler(OnDebugStarted); + DebuggerService.DebugStopped -= new EventHandler(OnDebugStopped); + if (null != m_currentDebugger) + OnDebugStopped(null, EventArgs.Empty); + + // save settings + DebuggerSettings.Instance.PropertyChanged -= new PropertyChangedEventHandler(OnDebuggerSettingChanged); + ILSpySettings.Update( + delegate (XElement root) { + DebuggerSettings.Instance.Save(root); + }); + } + + void OnDebuggerSettingChanged(object sender, PropertyChangedEventArgs args) + { + if (args.PropertyName == "ShowModuleName") { + SwitchModuleColumn(); + } + else if (args.PropertyName == "ShowArguments" + || args.PropertyName == "ShowArgumentValues") { + RefreshPad(); + } + + } + + void OnDebugStarted(object sender, EventArgs args) + { + m_currentDebugger = DebuggerService.CurrentDebugger; + m_currentDebugger.IsProcessRunningChanged += new EventHandler(OnProcessRunningChanged); + + OnProcessRunningChanged(null, EventArgs.Empty); + } + + void OnDebugStopped(object sender, EventArgs args) + { + m_currentDebugger.IsProcessRunningChanged -= new EventHandler(OnProcessRunningChanged); + m_currentDebugger = null; + view.ItemsSource = null; + } + + void OnProcessRunningChanged(object sender, EventArgs args) + { + if (m_currentDebugger.IsProcessRunning) + return; + RefreshPad(); + } + + void SwitchModuleColumn() + { + foreach (GridViewColumn c in ((GridView)view.View).Columns) { + if ((string)c.Header == "Module") { + c.Width = DebuggerSettings.Instance.ShowModuleName ? double.NaN : 0d; + } + } + } + + void RefreshPad() + { + Process debuggedProcess = ((WindowsDebugger)m_currentDebugger).DebuggedProcess; + if (debuggedProcess == null || debuggedProcess.IsRunning || debuggedProcess.SelectedThread == null) { + view.ItemsSource = null; + return; + } + + IList items = null; + StackFrame activeFrame = null; + try { + Utils.DoEvents(debuggedProcess); + items = CreateItems(debuggedProcess); + activeFrame = debuggedProcess.SelectedThread.SelectedStackFrame; + } catch(AbortedBecauseDebuggeeResumedException) { + } catch(System.Exception) { + if (debuggedProcess == null || debuggedProcess.HasExited) { + // Process unexpectedly exited + } else { + throw; + } + } + view.ItemsSource = items; + view.SelectedItem = items != null ? items.FirstOrDefault(item => object.Equals(activeFrame, item.Frame)) : null; + } + + IList CreateItems(Process debuggedProcess) + { + List items = new List(); + foreach (StackFrame frame in debuggedProcess.SelectedThread.GetCallstack(100)) { + CallStackItem item; + + // show modules names + string moduleName = frame.MethodInfo.DebugModule.ToString(); + + item = new CallStackItem() { + Name = GetFullName(frame), ModuleName = moduleName + }; + item.Frame = frame; + items.Add(item); + Utils.DoEvents(debuggedProcess); + } + return items; + } + + internal static string GetFullName(StackFrame frame) + { + // disabled by default, my be switched if options / context menu is added + bool showArgumentNames = DebuggerSettings.Instance.ShowArguments; + bool showArgumentValues = DebuggerSettings.Instance.ShowArgumentValues; + + StringBuilder name = new StringBuilder(); + name.Append(frame.MethodInfo.DeclaringType.FullName); + name.Append('.'); + name.Append(frame.MethodInfo.Name); + if (showArgumentNames || showArgumentValues) { + name.Append("("); + for (int i = 0; i < frame.ArgumentCount; i++) { + string parameterName = null; + string argValue = null; + if (showArgumentNames) { + try { + parameterName = frame.MethodInfo.GetParameters()[i].Name; + } catch { } + if (parameterName == "") parameterName = null; + } + if (showArgumentValues) { + try { + argValue = frame.GetArgumentValue(i).AsString(100); + } catch { } + } + if (parameterName != null && argValue != null) { + name.Append(parameterName); + name.Append("="); + name.Append(argValue); + } + if (parameterName != null && argValue == null) { + name.Append(parameterName); + } + if (parameterName == null && argValue != null) { + name.Append(argValue); + } + if (parameterName == null && argValue == null) { + name.Append("Global.NA"); + } + if (i < frame.ArgumentCount - 1) { + name.Append(", "); + } + } + name.Append(")"); + } + + return name.ToString(); + } + + + void view_MouseDoubleClick(object sender, MouseButtonEventArgs e) + { + if (MouseButton.Left != e.ChangedButton) + return; + var selectedItem = view.SelectedItem as CallStackItem; + if (null == selectedItem) + return; + + var foundAssembly = MainWindow.Instance.CurrentAssemblyList.OpenAssembly(selectedItem.Frame.MethodInfo.DebugModule.FullPath); + if (null == foundAssembly || null == foundAssembly.AssemblyDefinition) + return; + + MemberReference mr = XmlDocKeyProvider.FindMemberByKey(foundAssembly.AssemblyDefinition.MainModule, "M:" + selectedItem.Name); + if (mr == null) + return; + MainWindow.Instance.JumpToReference(mr); + // TODO: jump to associated line + // MainWindow.Instance.TextView.UnfoldAndScroll(selectedItem.LineNumber); + e.Handled = true; + } + + void SwitchIsChecked(object sender, EventArgs args) + { + if (sender is MenuItem) { + var mi = (MenuItem)sender; + mi.IsChecked = !mi.IsChecked; + } + } + } + + public class CallStackItem + { + public string Name { get; set; } + public string Language { get; set; } + public StackFrame Frame { get; set; } + public string Line { get; set; } + public string ModuleName { get; set; } + + public Brush FontColor { + get { return Brushes.Black; } + } + } + + [ExportMainMenuCommand(Menu="_Debugger", Header="Show _Callstack", MenuCategory="View", MenuOrder=9)] + public class CallstackPanelcommand : SimpleCommand + { + public override void Execute(object parameter) + { + CallStackPanel.Instance.Show(); + } + } +} \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml index 37581e90c..92acbd1c9 100644 --- a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml +++ b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml @@ -6,6 +6,7 @@ Show warning messages Ask for arguments and working directory before executing a process + Show all bookmarks in breakpoints window \ No newline at end of file diff --git a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs index 0c30e14d6..690a2a5ce 100644 --- a/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs +++ b/Debugger/ILSpy.Debugger/UI/DebuggerSettingsPanel.xaml.cs @@ -19,10 +19,6 @@ namespace ICSharpCode.ILSpy.Options [ExportOptionPage(Title = "Debugger", Order = 2)] partial class DebuggerSettingsPanel : UserControl, IOptionPage { - private const string DEBUGGER_SETTINGS = "DebuggerSettings"; - private const string SHOW_WARNINGS = "showWarnings"; - private const string ASK_ARGUMENTS = "askForArguments"; - public DebuggerSettingsPanel() { InitializeComponent(); @@ -30,41 +26,15 @@ namespace ICSharpCode.ILSpy.Options public void Load(ILSpySettings settings) { - this.DataContext = LoadDebuggerSettings(settings); - } - - static DebuggerSettings currentDebuggerSettings; - - public static DebuggerSettings CurrentDebuggerSettings { - get { - return currentDebuggerSettings ?? (currentDebuggerSettings = LoadDebuggerSettings(ILSpySettings.Load())); - } - } - - public static DebuggerSettings LoadDebuggerSettings(ILSpySettings settings) - { - XElement e = settings[DEBUGGER_SETTINGS]; - DebuggerSettings s = new DebuggerSettings(); - s.ShowWarnings = (bool?)e.Attribute(SHOW_WARNINGS) ?? s.ShowWarnings; - s.AskForArguments = (bool?)e.Attribute(ASK_ARGUMENTS) ?? s.AskForArguments; - - return s; + var s = DebuggerSettings.Instance; + s.Load(settings); + this.DataContext = s; } public void Save(XElement root) { var s = (DebuggerSettings)this.DataContext; - XElement section = new XElement(DEBUGGER_SETTINGS); - section.SetAttributeValue(SHOW_WARNINGS, s.ShowWarnings); - section.SetAttributeValue(ASK_ARGUMENTS, s.AskForArguments); - - XElement existingElement = root.Element(DEBUGGER_SETTINGS); - if (existingElement != null) - existingElement.ReplaceWith(section); - else - root.Add(section); - - currentDebuggerSettings = null; // invalidate cached settings + s.Save(root); } } } \ No newline at end of file diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 3852aec80..f10a4c7f0 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -128,6 +128,7 @@ namespace ICSharpCode.Decompiler.Ast else type = AstBuilder.ConvertType(v.Type); var newVarDecl = new VariableDeclarationStatement(type, v.Name); + newVarDecl.Variables.Single().AddAnnotation(v); astBlock.Statements.InsertBefore(insertionPoint, newVarDecl); } @@ -210,7 +211,7 @@ namespace ICSharpCode.Decompiler.Ast Type = AstBuilder.ConvertType(catchClause.ExceptionType), VariableName = catchClause.ExceptionVariable == null ? null : catchClause.ExceptionVariable.Name, Body = TransformBlock(catchClause) - }); + }.WithAnnotation(catchClause.ExceptionVariable)); } } if (tryCatchNode.FinallyBlock != null) @@ -976,7 +977,7 @@ namespace ICSharpCode.Decompiler.Ast if (cecilMethodDef != null) { if (cecilMethodDef.IsGetter && methodArgs.Count == 0) { foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.GetMethod == cecilMethodDef) + if (prop.GetMethod == cecilMethodDef) return target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod); } } else if (cecilMethodDef.IsGetter) { // with parameters @@ -985,20 +986,20 @@ namespace ICSharpCode.Decompiler.Ast return target.Indexer(methodArgs).WithAnnotation(indexer).WithAnnotation(cecilMethod); } else if (cecilMethodDef.IsSetter && methodArgs.Count == 1) { foreach (var prop in cecilMethodDef.DeclaringType.Properties) { - if (prop.SetMethod == cecilMethodDef) + if (prop.SetMethod == cecilMethodDef) return new Ast.AssignmentExpression(target.Member(prop.Name).WithAnnotation(prop).WithAnnotation(cecilMethod), methodArgs[0]); } } else if (cecilMethodDef.IsSetter && methodArgs.Count > 1) { PropertyDefinition indexer = GetIndexer(cecilMethodDef); if (indexer != null) - return new AssignmentExpression( + return new AssignmentExpression( target.Indexer(methodArgs.GetRange(0, methodArgs.Count - 1)).WithAnnotation(indexer).WithAnnotation(cecilMethod), methodArgs[methodArgs.Count - 1] ); } else if (cecilMethodDef.IsAddOn && methodArgs.Count == 1) { foreach (var ev in cecilMethodDef.DeclaringType.Events) { if (ev.AddMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { + return new Ast.AssignmentExpression { Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), Operator = AssignmentOperatorType.Add, Right = methodArgs[0] @@ -1008,7 +1009,7 @@ namespace ICSharpCode.Decompiler.Ast } else if (cecilMethodDef.IsRemoveOn && methodArgs.Count == 1) { foreach (var ev in cecilMethodDef.DeclaringType.Events) { if (ev.RemoveMethod == cecilMethodDef) { - return new Ast.AssignmentExpression { + return new Ast.AssignmentExpression { Left = target.Member(ev.Name).WithAnnotation(ev).WithAnnotation(cecilMethod), Operator = AssignmentOperatorType.Subtract, Right = methodArgs[0] @@ -1016,7 +1017,7 @@ namespace ICSharpCode.Decompiler.Ast } } } else if (cecilMethodDef.Name == "Invoke" && cecilMethodDef.DeclaringType.BaseType != null && cecilMethodDef.DeclaringType.BaseType.FullName == "System.MulticastDelegate") { - AdjustArgumentsForMethodCall(cecilMethod, methodArgs); + AdjustArgumentsForMethodCall(cecilMethod, methodArgs); return target.Invoke(methodArgs).WithAnnotation(cecilMethod); } } diff --git a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs index ec83e695c..25a878d13 100644 --- a/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs +++ b/ICSharpCode.Decompiler/Ast/TextOutputFormatter.cs @@ -43,12 +43,26 @@ namespace ICSharpCode.Decompiler.Ast public void WriteIdentifier(string identifier) { - MemberReference memberRef = GetCurrentMemberReference(); - - if (memberRef != null) + object memberRef = GetCurrentMemberReference(); + + if (memberRef != null) { output.WriteReference(identifier, memberRef); - else - output.Write(identifier); + return; + } + + var definition = GetCurrentLocalDefinition(); + if (definition != null) { + output.WriteDefinition(identifier, definition); + return; + } + + memberRef = GetCurrentLocalReference(); + if (memberRef != null) { + output.WriteReference(identifier, memberRef, true); + return; + } + + output.Write(identifier); } MemberReference GetCurrentMemberReference() @@ -60,6 +74,43 @@ namespace ICSharpCode.Decompiler.Ast } return memberRef; } + + object GetCurrentLocalReference() + { + AstNode node = nodeStack.Peek(); + ILVariable variable = node.Annotation(); + if (variable != null) { + if (variable.OriginalParameter != null) + return variable.OriginalParameter; + //if (variable.OriginalVariable != null) + // return variable.OriginalVariable; + return variable; + } + return null; + } + + object GetCurrentLocalDefinition() + { + AstNode node = nodeStack.Peek(); + var parameterDef = node.Annotation(); + if (parameterDef != null) + return parameterDef; + + if (node is VariableInitializer || node is CatchClause || node is ForeachStatement) { + var variable = node.Annotation(); + if (variable != null) { + if (variable.OriginalParameter != null) + return variable.OriginalParameter; + //if (variable.OriginalVariable != null) + // return variable.OriginalVariable; + return variable; + } else { + + } + } + + return null; + } public void WriteKeyword(string keyword) { diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs index ed2144085..62f98e7fa 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DeclareVariables.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Threading; +using ICSharpCode.Decompiler.ILAst; using ICSharpCode.NRefactory.CSharp; using ICSharpCode.NRefactory.CSharp.Analysis; @@ -35,6 +36,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms { public AstType Type; public string Name; + public ILVariable ILVariable; public AssignmentExpression ReplacedAssignment; public Statement InsertionPoint; @@ -67,9 +69,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (v.ReplacedAssignment != null) { // We clone the right expression so that it doesn't get removed from the old ExpressionStatement, // which might be still in use by the definite assignment graph. + VariableInitializer initializer = new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment).WithAnnotation(v.ILVariable); VariableDeclarationStatement varDecl = new VariableDeclarationStatement { Type = (AstType)v.Type.Clone(), - Variables = { new VariableInitializer(v.Name, v.ReplacedAssignment.Right.Detach()).CopyAnnotationsFrom(v.ReplacedAssignment) } + Variables = { initializer } }; ExpressionStatement es = v.ReplacedAssignment.Parent as ExpressionStatement; if (es != null) { @@ -100,9 +103,11 @@ namespace ICSharpCode.Decompiler.Ast.Transforms daa = new DefiniteAssignmentAnalysis(block, cancellationToken); } foreach (VariableDeclarationStatement varDecl in variables) { - string variableName = varDecl.Variables.Single().Name; - bool allowPassIntoLoops = varDecl.Variables.Single().Annotation() == null; - DeclareVariableInBlock(daa, block, varDecl.Type, variableName, allowPassIntoLoops); + VariableInitializer initializer = varDecl.Variables.Single(); + string variableName = initializer.Name; + ILVariable v = initializer.Annotation(); + bool allowPassIntoLoops = initializer.Annotation() == null; + DeclareVariableInBlock(daa, block, varDecl.Type, variableName, v, allowPassIntoLoops); } } } @@ -111,7 +116,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } } - void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, bool allowPassIntoLoops) + void DeclareVariableInBlock(DefiniteAssignmentAnalysis daa, BlockStatement block, AstType type, string variableName, ILVariable v, bool allowPassIntoLoops) { // declarationPoint: The point where the variable would be declared, if we decide to declare it in this block Statement declarationPoint = null; @@ -139,10 +144,10 @@ namespace ICSharpCode.Decompiler.Ast.Transforms foreach (AstNode child in stmt.Children) { BlockStatement subBlock = child as BlockStatement; if (subBlock != null) { - DeclareVariableInBlock(daa, subBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, subBlock, type, variableName, v, allowPassIntoLoops); } else if (HasNestedBlocks(child)) { foreach (BlockStatement nestedSubBlock in child.Children.OfType()) { - DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, allowPassIntoLoops); + DeclareVariableInBlock(daa, nestedSubBlock, type, variableName, v, allowPassIntoLoops); } } } @@ -151,7 +156,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Try converting an assignment expression into a VariableDeclarationStatement if (!TryConvertAssignmentExpressionIntoVariableDeclaration(declarationPoint, type, variableName)) { // Declare the variable in front of declarationPoint - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, InsertionPoint = declarationPoint }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = v, InsertionPoint = declarationPoint }); } } } @@ -172,7 +177,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms if (ae != null && ae.Operator == AssignmentOperatorType.Assign) { IdentifierExpression ident = ae.Left as IdentifierExpression; if (ident != null && ident.Identifier == variableName) { - variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ReplacedAssignment = ae }); + variablesToDeclare.Add(new VariableToDeclare { Type = type, Name = variableName, ILVariable = ident.Annotation(), ReplacedAssignment = ae }); return true; } } diff --git a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs index 0e431e3f8..17cca7650 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms } // Now create variables for all fields of the display class (except for those that we already handled as parameters) - List> variablesToDeclare = new List>(); + List> variablesToDeclare = new List>(); foreach (FieldDefinition field in type.Fields) { if (field.IsStatic) continue; // skip static fields @@ -397,8 +397,14 @@ namespace ICSharpCode.Decompiler.Ast.Transforms capturedVariableName = capturedVariableName.Substring(10); EnsureVariableNameIsAvailable(blockStatement, capturedVariableName); currentlyUsedVariableNames.Add(capturedVariableName); - variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), capturedVariableName)); - dict[field] = new IdentifierExpression(capturedVariableName); + ILVariable ilVar = new ILVariable + { + IsGenerated = true, + Name = capturedVariableName, + Type = field.FieldType, + }; + variablesToDeclare.Add(Tuple.Create(AstBuilder.ConvertType(field.FieldType, field), ilVar)); + dict[field] = new IdentifierExpression(capturedVariableName).WithAnnotation(ilVar); } // Now figure out where the closure was accessed and use the simpler replacement expression there: @@ -414,15 +420,16 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // Now insert the variable declarations (we can do this after the replacements only so that the scope detection works): Statement insertionPoint = blockStatement.Statements.FirstOrDefault(); foreach (var tuple in variablesToDeclare) { - var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2); + var newVarDecl = new VariableDeclarationStatement(tuple.Item1, tuple.Item2.Name); newVarDecl.Variables.Single().AddAnnotation(new CapturedVariableAnnotation()); + newVarDecl.Variables.Single().AddAnnotation(tuple.Item2); blockStatement.Statements.InsertBefore(insertionPoint, newVarDecl); } } currentlyUsedVariableNames.RemoveRange(numberOfVariablesOutsideBlock, currentlyUsedVariableNames.Count - numberOfVariablesOutsideBlock); return null; } - + void EnsureVariableNameIsAvailable(AstNode currentNode, string name) { int pos = currentlyUsedVariableNames.IndexOf(name); diff --git a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs index 3d5c0e596..900ef0b4f 100644 --- a/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/Ast/Transforms/PatternStatementTransform.cs @@ -227,6 +227,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms Name = variableName, Initializer = m1.Get("initializer").Single().Detach() }.CopyAnnotationsFrom(node.Expression) + .WithAnnotation(m1.Get("variable").Single().Annotation()) } }.CopyAnnotationsFrom(node); } else { @@ -382,7 +383,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms VariableName = itemVar.Identifier, InExpression = m.Get("collection").Single().Detach(), EmbeddedStatement = newBody - }; + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); if (foreachStatement.InExpression is BaseReferenceExpression) { foreachStatement.InExpression = new ThisReferenceExpression().CopyAnnotationsFrom(foreachStatement.InExpression); } @@ -471,10 +472,12 @@ namespace ICSharpCode.Decompiler.Ast.Transforms // We just care that we can move it in front of the loop: if (declarationPoint != loop) return null; - - ForeachStatement foreachStatement = new ForeachStatement(); - foreachStatement.VariableType = itemVarDecl.Type.Clone(); - foreachStatement.VariableName = itemVar.Identifier; + + ForeachStatement foreachStatement = new ForeachStatement + { + VariableType = itemVarDecl.Type.Clone(), + VariableName = itemVar.Identifier, + }.WithAnnotation(itemVarDecl.Variables.Single().Annotation()); BlockStatement body = new BlockStatement(); foreachStatement.EmbeddedStatement = body; ((BlockStatement)node.Parent).Statements.InsertBefore(node, foreachStatement); diff --git a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs index 446496883..01ac48beb 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstBuilder.cs @@ -634,6 +634,10 @@ namespace ICSharpCode.Decompiler.ILAst foreach (ParameterDefinition p in methodDef.Parameters) { this.Parameters.Add(new ILVariable { Type = p.ParameterType, Name = p.Name, OriginalParameter = p }); } + if (this.Parameters.Count > 0 && (methodDef.IsSetter || methodDef.IsAddOn || methodDef.IsRemoveOn)) { + // last parameter must be 'value', so rename it + this.Parameters.Last().Name = "value"; + } foreach (ByteCode byteCode in body) { ParameterDefinition p; switch (byteCode.Code) { diff --git a/ICSharpCode.Decompiler/ITextOutput.cs b/ICSharpCode.Decompiler/ITextOutput.cs index cfbd5751a..f143ea371 100644 --- a/ICSharpCode.Decompiler/ITextOutput.cs +++ b/ICSharpCode.Decompiler/ITextOutput.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler void Write(string text); void WriteLine(); void WriteDefinition(string text, object definition); - void WriteReference(string text, object reference); + void WriteReference(string text, object reference, bool isLocal = false); void MarkFoldStart(string collapsedText = "...", bool defaultCollapsed = false); void MarkFoldEnd(); diff --git a/ICSharpCode.Decompiler/PlainTextOutput.cs b/ICSharpCode.Decompiler/PlainTextOutput.cs index d8b001167..c520b0f50 100644 --- a/ICSharpCode.Decompiler/PlainTextOutput.cs +++ b/ICSharpCode.Decompiler/PlainTextOutput.cs @@ -99,7 +99,7 @@ namespace ICSharpCode.Decompiler Write(text); } - public void WriteReference(string text, object reference) + public void WriteReference(string text, object reference, bool isLocal) { Write(text); } diff --git a/ILSpy.BamlDecompiler/CecilTypeResolver.cs b/ILSpy.BamlDecompiler/CecilTypeResolver.cs index 1bc79ab11..8d57fa705 100644 --- a/ILSpy.BamlDecompiler/CecilTypeResolver.cs +++ b/ILSpy.BamlDecompiler/CecilTypeResolver.cs @@ -48,15 +48,38 @@ namespace ILSpy.BamlDecompiler string assemblyName = name.Substring(comma + 1).Trim(); var type = thisAssembly.MainModule.GetType(fullName); + + if (type == null) { + type = TryFindInExportedTypes(fullName, thisAssembly); + } + if (type == null) { var otherAssembly = resolver.Resolve(assemblyName); if (otherAssembly == null) throw new Exception("could not resolve '" + assemblyName + "'!"); type = otherAssembly.MainModule.GetType(fullName.Replace('+', '/')); + + if (type == null) { + type = TryFindInExportedTypes(fullName, otherAssembly); + } } + if (type == null) + throw new Exception("could not resolve '" + name + "'!"); + return new CecilType(type); } + + TypeDefinition TryFindInExportedTypes(string fullName, AssemblyDefinition asm) + { + foreach (var exportedType in asm.MainModule.ExportedTypes) { + if (exportedType.IsForwarder && exportedType.FullName == fullName) { + return exportedType.Resolve(); + } + } + + return null; + } public IDependencyPropertyDescriptor GetDependencyPropertyDescriptor(string name, IType ownerType, IType targetType) { diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs index 907c1a137..ca4322932 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/IconBarMargin.cs @@ -15,6 +15,7 @@ using ICSharpCode.Decompiler; using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.Debugger; using ICSharpCode.ILSpy.Debugger.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.NRefactory.CSharp; using Mono.Cecil; @@ -26,8 +27,8 @@ namespace ICSharpCode.ILSpy.AvalonEdit public IconBarMargin(IconBarManager manager) { - BookmarkManager.Added += delegate { InvalidateVisual(); }; - BookmarkManager.Removed += delegate { InvalidateVisual(); }; + BookmarkManager.Added += new BookmarkEventHandler(OnBookmarkAdded); + BookmarkManager.Removed += new BookmarkEventHandler(OnBookmarkRemoved); this.manager = manager; } @@ -70,10 +71,15 @@ namespace ICSharpCode.ILSpy.AvalonEdit // create a dictionary line number => first bookmark Dictionary bookmarkDict = new Dictionary(); foreach (var bm in BookmarkManager.Bookmarks) { - if (DebugInformation.DecompiledMemberReferences == null || DebugInformation.DecompiledMemberReferences.Count == 0 || - !DebugInformation.DecompiledMemberReferences.ContainsKey(bm.MemberReference.MetadataToken.ToInt32())) - continue; - + if (bm is BreakpointBookmark) { + if (DebugInformation.CodeMappings == null || DebugInformation.CodeMappings.Count == 0 || + !DebugInformation.CodeMappings.ContainsKey(((BreakpointBookmark)bm).FunctionToken)) + continue; + } else { + if (DebugInformation.DecompiledMemberReferences == null || DebugInformation.DecompiledMemberReferences.Count == 0 || + !DebugInformation.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)bm).MemberReference.MetadataToken.ToInt32())) + continue; + } int line = bm.LineNumber; IBookmark existingBookmark; if (!bookmarkDict.TryGetValue(line, out existingBookmark) || bm.ZOrder > existingBookmark.ZOrder) @@ -242,40 +248,76 @@ namespace ICSharpCode.ILSpy.AvalonEdit } } + public void OnBookmarkAdded(object sender, BookmarkEventArgs args) + { + var breakpoint = args.Bookmark as BreakpointBookmark; + if (null == breakpoint) + return; + var storage = DebugInformation.CodeMappings; + if (storage == null || storage.Count == 0) + return; + var key = breakpoint.MemberReference.MetadataToken.ToInt32(); + if (storage.ContainsKey(key)) + { + // register to show enabled/disabled state + breakpoint.ImageChanged += delegate { InvalidateVisual(); }; + InvalidateVisual(); + } + } + + public void OnBookmarkRemoved(object sender, BookmarkEventArgs args) + { + var breakpoint = args.Bookmark as BreakpointBookmark; + if (null == breakpoint) + return; + var storage = DebugInformation.CodeMappings; + if (storage == null || storage.Count == 0) + return; + var key = breakpoint.MemberReference.MetadataToken.ToInt32(); + if (storage.ContainsKey(key)) + { + breakpoint.ImageChanged -= delegate { InvalidateVisual(); }; + InvalidateVisual(); + } + } + public void SyncBookmarks() { var storage = DebugInformation.CodeMappings; if (storage == null || storage.Count == 0) return; - //remove existing bookmarks and create new ones + // TODO: handle other types of bookmarks + // remove existing bookmarks and create new ones + // update of existing bookmarks for new position does not update TextMarker + // this is only done in TextMarkerService handlers for BookmarkManager.Added/Removed List newBookmarks = new List(); for (int i = BookmarkManager.Bookmarks.Count - 1; i >= 0; --i) { var breakpoint = BookmarkManager.Bookmarks[i] as BreakpointBookmark; if (breakpoint == null) continue; - var key = breakpoint.MemberReference.MetadataToken.ToInt32(); + var key = breakpoint.FunctionToken; if (!storage.ContainsKey(key)) - continue; - - var member = DebugInformation.DecompiledMemberReferences[key]; + { + continue; + } bool isMatch; - SourceCodeMapping map = storage[key].GetInstructionByTokenAndOffset( - member.MetadataToken.ToInt32(), breakpoint.ILRange.From, out isMatch); + SourceCodeMapping map = storage[key].GetInstructionByTokenAndOffset(key, breakpoint.ILRange.From, out isMatch); if (map != null) { - newBookmarks.Add(new BreakpointBookmark( - member, new AstLocation(map.SourceCodeLine, 0), - map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language)); - - BookmarkManager.RemoveMark(breakpoint); + BreakpointBookmark newBookmark = new BreakpointBookmark( + breakpoint.MemberReference, new AstLocation(map.SourceCodeLine, 0), breakpoint.FunctionToken, + map.ILInstructionOffset, BreakpointAction.Break, DebugInformation.Language); + newBookmark.IsEnabled = breakpoint.IsEnabled; + + newBookmarks.Add(newBookmark); + + BookmarkManager.RemoveMark(breakpoint); } } - newBookmarks.ForEach(m => BookmarkManager.AddMark(m)); - SyncCurrentLineBookmark(); } @@ -285,33 +327,25 @@ namespace ICSharpCode.ILSpy.AvalonEdit if (CurrentLineBookmark.Instance == null) return; - var oldMappings = DebugInformation.OldCodeMappings; - var newMappings = DebugInformation.CodeMappings; - - if (oldMappings == null || newMappings == null) + var codeMappings = DebugInformation.CodeMappings; + if (codeMappings == null) return; // 1. Save it's data int line = CurrentLineBookmark.Instance.LineNumber; var markerType = CurrentLineBookmark.Instance.MemberReference; + int token = markerType.MetadataToken.ToInt32(); + int offset = CurrentLineBookmark.Instance.ILOffset; - if (!oldMappings.ContainsKey(markerType.MetadataToken.ToInt32()) || !newMappings.ContainsKey(markerType.MetadataToken.ToInt32())) + if (!codeMappings.ContainsKey(token)) return; - // 2. Remove it - CurrentLineBookmark.Remove(); - - // 3. map the marker line - int token; - var instruction = oldMappings[markerType.MetadataToken.ToInt32()].GetInstructionByLineNumber(line, out token); - if (instruction == null) - return; - + // 2. map the marker line MemberReference memberReference; int newline; - if (newMappings[markerType.MetadataToken.ToInt32()].GetInstructionByTokenAndOffset(token, instruction.ILInstructionOffset.From, out memberReference, out newline)) { - // 4. create breakpoint for new languages - CurrentLineBookmark.SetPosition(memberReference, newline, 0, newline, 0); + if (codeMappings[token].GetInstructionByTokenAndOffset(token, offset, out memberReference, out newline)) { + // 3. create breakpoint for new languages + DebuggerService.JumpToCurrentLine(memberReference, newline, 0, newline, 0, offset); } } } diff --git a/ILSpy.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs b/ILSpy.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs index 40abc720a..2750dc0e1 100644 --- a/ILSpy.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs +++ b/ILSpy.SharpDevelop.LGPL/AvalonEdit/TextMarkerService.cs @@ -123,7 +123,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit int lineStart = line.Offset; int lineEnd = lineStart + line.Length; foreach (TextMarker marker in markers.FindOverlappingSegments(lineStart, line.Length).Reverse()) { - if (!marker.IsVisible(marker.Bookmark)) + if (marker.Bookmark != null && !marker.IsVisible(marker.Bookmark)) continue; Brush foregroundBrush = null; @@ -166,7 +166,7 @@ namespace ICSharpCode.ILSpy.AvalonEdit int viewStart = visualLines.First().FirstDocumentLine.Offset; int viewEnd = visualLines.Last().LastDocumentLine.Offset + visualLines.Last().LastDocumentLine.Length; foreach (TextMarker marker in markers.FindOverlappingSegments(viewStart, viewEnd - viewStart).Reverse()) { - if (!marker.IsVisible(marker.Bookmark)) + if (marker.Bookmark != null && !marker.IsVisible(marker.Bookmark)) continue; if (marker.BackgroundColor != null) { diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs index 9212a4adb..6e4b6b36d 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/BreakpointBookmark.cs @@ -40,6 +40,15 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks } } + /// + /// Gets the function/method where the breakpoint is set. + /// + /// In case of methods, it is the same as the MemberReference metadata token.
+ /// In case of properties and events, it's the GetMethod/SetMethod|AddMethod/RemoveMethod token. + ///
+ ///
+ public int FunctionToken { get; private set; } + public ILRange ILRange { get; private set; } public virtual bool IsHealthy { @@ -63,6 +72,8 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks isEnabled = value; if (IsEnabledChanged != null) IsEnabledChanged(this, EventArgs.Empty); + if (ImageChanged != null) + ImageChanged(this, EventArgs.Empty); // Image property reflects IsEnabled property Redraw(); } } @@ -72,27 +83,31 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks public string Tooltip { get; private set; } - public BreakpointBookmark(MemberReference member, AstLocation location, ILRange range, BreakpointAction action, DecompiledLanguages language) : base(member, location) + public BreakpointBookmark(MemberReference member, AstLocation location, int functionToken, ILRange range, BreakpointAction action, DecompiledLanguages language) + : base(member, location) { this.action = action; + this.FunctionToken = functionToken; this.ILRange = range; this.Tooltip = string.Format("Language:{0}, Line:{1}, IL range:{2}-{3}", language.ToString(), location.Line, range.From, range.To); - this.Language = language; + this.Language = language; } public override ImageSource Image { get { - return Images.Breakpoint; + return IsEnabled ? Images.Breakpoint : Images.DisabledBreakpoint; } } + public event EventHandler ImageChanged; + public override ITextMarker CreateMarker(ITextMarkerService markerService, int offset, int length) { ITextMarker marker = markerService.Create(offset, length); marker.BackgroundColor = Color.FromRgb(180, 38, 38); marker.ForegroundColor = Colors.White; - marker.IsVisible = b => b is MarkerBookmark && DebugInformation.DecompiledMemberReferences != null && - DebugInformation.DecompiledMemberReferences.ContainsKey(((MarkerBookmark)b).MemberReference.MetadataToken.ToInt32()); + marker.IsVisible = b => b is BreakpointBookmark && DebugInformation.CodeMappings != null && + DebugInformation.CodeMappings.ContainsKey(((BreakpointBookmark)b).FunctionToken); marker.Bookmark = this; this.Marker = marker; diff --git a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs index f7772a642..a59d5abe1 100644 --- a/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs +++ b/ILSpy.SharpDevelop.LGPL/Bookmarks/CurrentLineBookmark.cs @@ -24,7 +24,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks static int endLine; static int endColumn; - public static void SetPosition(MemberReference memberReference, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn) + public static void SetPosition(MemberReference memberReference, int makerStartLine, int makerStartColumn, int makerEndLine, int makerEndColumn, int ilOffset) { Remove(); @@ -33,7 +33,7 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks endLine = makerEndLine; endColumn = makerEndColumn; - instance = new CurrentLineBookmark(memberReference, new AstLocation(startLine, startColumn)); + instance = new CurrentLineBookmark(memberReference, new AstLocation(startLine, startColumn), ilOffset); BookmarkManager.AddMark(instance); } @@ -53,11 +53,13 @@ namespace ICSharpCode.ILSpy.Debugger.Bookmarks get { return 100; } } - public CurrentLineBookmark(MemberReference member, AstLocation location) : base(member, location) + private CurrentLineBookmark(MemberReference member, AstLocation location, int ilOffset) : base(member, location) { - + this.ILOffset = ilOffset; } + public int ILOffset { get; private set; } + public override ImageSource Image { get { return Images.CurrentLine; } } diff --git a/ILSpy.SharpDevelop.LGPL/DebugInformation.cs b/ILSpy.SharpDevelop.LGPL/DebugInformation.cs index 8c6aa4390..adeac55b3 100644 --- a/ILSpy.SharpDevelop.LGPL/DebugInformation.cs +++ b/ILSpy.SharpDevelop.LGPL/DebugInformation.cs @@ -47,11 +47,6 @@ namespace ICSharpCode.ILSpy.Debugger ///
public static ConcurrentDictionary> LocalVariables { get; set; } - /// - /// Gets or sets the old code mappings. - /// - public static Dictionary> OldCodeMappings { get; set; } - /// /// Gets or sets the MembeReference that was decompiled (a TypeDefinition, MethodDefinition, etc) /// diff --git a/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj b/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj index 4fb997b25..114718512 100644 --- a/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj +++ b/ILSpy.SharpDevelop.LGPL/ILSpy.SharpDevelop.LGPL.csproj @@ -33,7 +33,7 @@ True False TRACE - + AnyCPU @@ -77,11 +77,17 @@ + + + + + + diff --git a/ILSpy.SharpDevelop.LGPL/Images.cs b/ILSpy.SharpDevelop.LGPL/Images.cs index 50915ab48..47cda62b8 100644 --- a/ILSpy.SharpDevelop.LGPL/Images.cs +++ b/ILSpy.SharpDevelop.LGPL/Images.cs @@ -25,6 +25,7 @@ namespace ICSharpCode.ILSpy.SharpDevelop } public static readonly BitmapImage Breakpoint = LoadBitmap("Breakpoint"); + public static readonly BitmapImage DisabledBreakpoint = LoadBitmap("DisabledBreakpoint"); public static readonly BitmapImage CurrentLine = LoadBitmap("CurrentLine"); public static ImageSource GetImage(string imageName) diff --git a/Debugger/ILSpy.Debugger/ToolTips/Models/ToolTipRequestEventArgs.cs b/ILSpy.SharpDevelop.LGPL/Models/ToolTipRequestEventArgs.cs similarity index 96% rename from Debugger/ILSpy.Debugger/ToolTips/Models/ToolTipRequestEventArgs.cs rename to ILSpy.SharpDevelop.LGPL/Models/ToolTipRequestEventArgs.cs index e2559a27a..d6a3a9f0c 100644 --- a/Debugger/ILSpy.Debugger/ToolTips/Models/ToolTipRequestEventArgs.cs +++ b/ILSpy.SharpDevelop.LGPL/Models/ToolTipRequestEventArgs.cs @@ -7,7 +7,7 @@ using ICSharpCode.NRefactory.CSharp; namespace ICSharpCode.ILSpy.Debugger.Tooltips { - internal class ToolTipRequestEventArgs : EventArgs + public class ToolTipRequestEventArgs : EventArgs { /// /// Gets whether the tool tip request was handled. diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs similarity index 92% rename from Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs rename to ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs index 96c59f889..d1c8b75bc 100644 --- a/Debugger/ILSpy.Debugger/Services/Debugger/DebuggerService.cs +++ b/ILSpy.SharpDevelop.LGPL/Services/DebuggerService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using ICSharpCode.AvalonEdit.Document; using ICSharpCode.Decompiler; using ICSharpCode.Decompiler.ILAst; +using ICSharpCode.ILSpy; using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.Debugger.Bookmarks; using ICSharpCode.ILSpy.Debugger.Tooltips; @@ -26,7 +27,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services static IDebugger GetCompatibleDebugger() { - return currentDebugger = new WindowsDebugger(); + return currentDebugger; } /// @@ -38,6 +39,8 @@ namespace ICSharpCode.ILSpy.Debugger.Services get { if (currentDebugger == null) { currentDebugger = GetCompatibleDebugger(); + if (currentDebugger == null) + return null; currentDebugger.DebugStarting += new EventHandler(OnDebugStarting); currentDebugger.DebugStarted += new EventHandler(OnDebugStarted); currentDebugger.DebugStopped += new EventHandler(OnDebugStopped); @@ -165,12 +168,12 @@ namespace ICSharpCode.ILSpy.Debugger.Services } } - public static void ToggleBreakpointAt(MemberReference member, int lineNumber, ILRange range, DecompiledLanguages language) + public static void ToggleBreakpointAt(MemberReference member, int lineNumber, int functionToken, ILRange range, DecompiledLanguages language) { BookmarkManager.ToggleBookmark( member.FullName, lineNumber, b => b.CanToggle && b is BreakpointBookmark, - location => new BreakpointBookmark(member, location, range, BreakpointAction.Break, language)); + location => new BreakpointBookmark(member, location, functionToken, range, BreakpointAction.Break, language)); } /* TODO: reimplement this stuff @@ -186,9 +189,9 @@ namespace ICSharpCode.ILSpy.Debugger.Services CurrentLineBookmark.Remove(); } - public static void JumpToCurrentLine(MemberReference memberReference, int startLine, int startColumn, int endLine, int endColumn) + public static void JumpToCurrentLine(MemberReference memberReference, int startLine, int startColumn, int endLine, int endColumn, int ilOffset) { - CurrentLineBookmark.SetPosition(memberReference, startLine, startColumn, endLine, endColumn); + CurrentLineBookmark.SetPosition(memberReference, startLine, startColumn, endLine, endColumn, ilOffset); } #region Tool tips @@ -198,7 +201,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services /// showing its current value (when in debugging mode) can be returned /// through the ToolTipRequestEventArgs.SetTooltip() method. /// - internal static void HandleToolTipRequest(ToolTipRequestEventArgs e) + public static void HandleToolTipRequest(ToolTipRequestEventArgs e) { if (!e.InDocument) return; @@ -384,5 +387,22 @@ namespace ICSharpCode.ILSpy.Debugger.Services // return text.ToString(); // } #endregion + + public static void SetDebugger(Lazy debugger) + { + if (currentDebugger != null) + { + currentDebugger.DebugStarting -= new EventHandler(OnDebugStarting); + currentDebugger.DebugStarted -= new EventHandler(OnDebugStarted); + currentDebugger.DebugStopped -= new EventHandler(OnDebugStopped); + } + currentDebugger = debugger.Value; + if (currentDebugger != null) + { + currentDebugger.DebugStarting += new EventHandler(OnDebugStarting); + currentDebugger.DebugStarted += new EventHandler(OnDebugStarted); + currentDebugger.DebugStopped += new EventHandler(OnDebugStopped); + } + } } } diff --git a/Debugger/ILSpy.Debugger/Services/Debugger/IDebugger.cs b/ILSpy.SharpDevelop.LGPL/Services/IDebugger.cs similarity index 100% rename from Debugger/ILSpy.Debugger/Services/Debugger/IDebugger.cs rename to ILSpy.SharpDevelop.LGPL/Services/IDebugger.cs diff --git a/Debugger/ILSpy.Debugger/Services/ParserService/ParserService.cs b/ILSpy.SharpDevelop.LGPL/Services/ParserService.cs similarity index 90% rename from Debugger/ILSpy.Debugger/Services/ParserService/ParserService.cs rename to ILSpy.SharpDevelop.LGPL/Services/ParserService.cs index d31b499a0..8cd603d36 100644 --- a/Debugger/ILSpy.Debugger/Services/ParserService/ParserService.cs +++ b/ILSpy.SharpDevelop.LGPL/Services/ParserService.cs @@ -15,7 +15,7 @@ namespace ICSharpCode.ILSpy.Debugger.Services static ParserService() { - mySet.AddRange((new [] { + mySet.AddRange((new string [] { ".", "{", "}", @@ -41,7 +41,14 @@ namespace ICSharpCode.ILSpy.Debugger.Services @"\t", @"\r", "|" - }).AsReadOnly()); + })); + } + + static void AddRange(this ICollection list, IEnumerable items) + { + foreach (T item in items) + if (!list.Contains(item)) + list.Add(item); } /// diff --git a/ILSpy/AboutPage.cs b/ILSpy/AboutPage.cs index 60c576369..91237ddf8 100644 --- a/ILSpy/AboutPage.cs +++ b/ILSpy/AboutPage.cs @@ -183,7 +183,9 @@ namespace ICSharpCode.ILSpy { var tcs = new TaskCompletionSource(); WebClient wc = new WebClient(); - wc.UseDefaultCredentials = true; + IWebProxy systemWebProxy = WebRequest.GetSystemWebProxy(); + systemWebProxy.Credentials = CredentialCache.DefaultCredentials; + wc.Proxy = systemWebProxy; wc.DownloadDataCompleted += delegate(object sender, DownloadDataCompletedEventArgs e) { if (e.Error != null) { tcs.SetException(e.Error); diff --git a/ILSpy/App.xaml.cs b/ILSpy/App.xaml.cs index 735991119..573a86542 100644 --- a/ILSpy/App.xaml.cs +++ b/ILSpy/App.xaml.cs @@ -17,16 +17,16 @@ // DEALINGS IN THE SOFTWARE. using System; -using System.Collections.Generic; using System.ComponentModel.Composition.Hosting; using System.Diagnostics; using System.IO; using System.Linq; -using System.Threading; using System.Windows; using System.Windows.Documents; using System.Windows.Navigation; using System.Windows.Threading; + +using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.ILSpy.TextView; namespace ICSharpCode.ILSpy @@ -73,6 +73,12 @@ namespace ICSharpCode.ILSpy EventManager.RegisterClassHandler(typeof(Window), Hyperlink.RequestNavigateEvent, new RequestNavigateEventHandler(Window_RequestNavigate)); + + try { + DebuggerService.SetDebugger(compositionContainer.GetExport()); + } catch { + // unable to find a IDebugger + } } string FullyQualifyPath(string argument) diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index d6e585c34..093d8eec2 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -224,6 +224,7 @@ + diff --git a/ILSpy/Images/DisabledBreakpoint.png b/ILSpy/Images/DisabledBreakpoint.png new file mode 100644 index 000000000..773244ee8 Binary files /dev/null and b/ILSpy/Images/DisabledBreakpoint.png differ diff --git a/ILSpy/TextView/AvalonEditTextOutput.cs b/ILSpy/TextView/AvalonEditTextOutput.cs index 996e182bc..6ce170d2a 100644 --- a/ILSpy/TextView/AvalonEditTextOutput.cs +++ b/ILSpy/TextView/AvalonEditTextOutput.cs @@ -36,6 +36,8 @@ namespace ICSharpCode.ILSpy.TextView sealed class ReferenceSegment : TextSegment { public object Reference; + public bool IsLocal; + public bool IsLocalTarget; } /// @@ -204,17 +206,20 @@ namespace ICSharpCode.ILSpy.TextView public void WriteDefinition(string text, object definition) { WriteIndent(); + int start = this.TextLength; b.Append(text); + int end = this.TextLength; this.DefinitionLookup.AddDefinition(definition, this.TextLength); + references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = definition, IsLocal = true, IsLocalTarget = true }); } - public void WriteReference(string text, object reference) + public void WriteReference(string text, object reference, bool isLocal) { WriteIndent(); int start = this.TextLength; b.Append(text); int end = this.TextLength; - references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference }); + references.Add(new ReferenceSegment { StartOffset = start, EndOffset = end, Reference = reference, IsLocal = isLocal }); } public void MarkFoldStart(string collapsedText, bool defaultCollapsed) diff --git a/ILSpy/TextView/DecompilerTextView.cs b/ILSpy/TextView/DecompilerTextView.cs index 0c16ac2ea..39eb1e59a 100644 --- a/ILSpy/TextView/DecompilerTextView.cs +++ b/ILSpy/TextView/DecompilerTextView.cs @@ -29,6 +29,7 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Input; +using System.Windows.Media; using System.Windows.Media.Animation; using System.Windows.Threading; using System.Xml; @@ -45,6 +46,7 @@ using ICSharpCode.ILSpy.AvalonEdit; using ICSharpCode.ILSpy.Bookmarks; using ICSharpCode.ILSpy.Debugger; using ICSharpCode.ILSpy.Debugger.Bookmarks; +using ICSharpCode.ILSpy.Debugger.Services; using ICSharpCode.ILSpy.Options; using ICSharpCode.ILSpy.TreeNodes; using ICSharpCode.ILSpy.XmlDoc; @@ -68,11 +70,13 @@ namespace ICSharpCode.ILSpy.TextView ILSpyTreeNode[] decompiledNodes; DefinitionLookup definitionLookup; + TextSegmentCollection references; CancellationTokenSource currentCancellationTokenSource; internal readonly IconBarManager manager; readonly IconBarMargin iconMargin; readonly TextMarkerService textMarkerService; + readonly List localReferenceMarks = new List(); [ImportMany(typeof(ITextEditorListener))] IEnumerable textEditorListeners = null; @@ -315,7 +319,8 @@ namespace ICSharpCode.ILSpy.TextView { Debug.WriteLine("Showing {0} characters of output", textOutput.TextLength); Stopwatch w = Stopwatch.StartNew(); - + + ClearLocalReferenceMarks(); textEditor.ScrollToHome(); if (foldingManager != null) { FoldingManager.Uninstall(foldingManager); @@ -324,6 +329,7 @@ namespace ICSharpCode.ILSpy.TextView textEditor.Document = null; // clear old document while we're changing the highlighting uiElementGenerator.UIElements = textOutput.UIElements; referenceElementGenerator.References = textOutput.References; + references = textOutput.References; definitionLookup = textOutput.DefinitionLookup; textEditor.SyntaxHighlighting = highlighting; @@ -447,7 +453,7 @@ namespace ICSharpCode.ILSpy.TextView iconMargin.SyncBookmarks(); if (isDecompilationOk) { - if (DebugInformation.DebugStepInformation != null) { + if (DebugInformation.DebugStepInformation != null && DebuggerService.CurrentDebugger != null) { // repaint bookmarks iconMargin.InvalidateVisual(); @@ -462,8 +468,7 @@ namespace ICSharpCode.ILSpy.TextView DebugInformation.CodeMappings[token].GetInstructionByTokenAndOffset(token, ilOffset, out member, out line); // update marker - CurrentLineBookmark.Remove(); - CurrentLineBookmark.SetPosition(member, line, 0, line, 0); + DebuggerService.JumpToCurrentLine(member, line, 0, line, 0, ilOffset); var bm = CurrentLineBookmark.Instance; DocumentLine docline = textEditor.Document.GetLineByNumber(line); @@ -525,7 +530,6 @@ namespace ICSharpCode.ILSpy.TextView void DecompileNodes(DecompilationContext context, ITextOutput textOutput) { // reset data - DebugInformation.OldCodeMappings = DebugInformation.CodeMappings; DebugInformation.CodeMappings = null; DebugInformation.LocalVariables = null; DebugInformation.DecompiledMemberReferences = null; @@ -609,6 +613,19 @@ namespace ICSharpCode.ILSpy.TextView internal void JumpToReference(ReferenceSegment referenceSegment) { object reference = referenceSegment.Reference; + if (referenceSegment.IsLocal) { + ClearLocalReferenceMarks(); + if (references != null) { + foreach (var r in references) { + if (r.Reference == reference) { + var mark = textMarkerService.Create(r.StartOffset, r.Length); + mark.BackgroundColor = r.IsLocalTarget ? Colors.LightSeaGreen : Colors.GreenYellow; + localReferenceMarks.Add(mark); + } + } + } + return; + } if (definitionLookup != null) { int pos = definitionLookup.GetDefinitionPosition(reference); if (pos >= 0) { @@ -624,6 +641,14 @@ namespace ICSharpCode.ILSpy.TextView } MainWindow.Instance.JumpToReference(reference); } + + void ClearLocalReferenceMarks() + { + foreach (var mark in localReferenceMarks) { + textMarkerService.Remove(mark); + } + localReferenceMarks.Clear(); + } /// /// Filters all ReferenceSegments that are no real links. diff --git a/ILSpy/TextView/ReferenceElementGenerator.cs b/ILSpy/TextView/ReferenceElementGenerator.cs index e83d89f64..330bdaa0c 100644 --- a/ILSpy/TextView/ReferenceElementGenerator.cs +++ b/ILSpy/TextView/ReferenceElementGenerator.cs @@ -102,7 +102,7 @@ namespace ICSharpCode.ILSpy.TextView protected override void OnQueryCursor(QueryCursorEventArgs e) { e.Handled = true; - e.Cursor = Cursors.Hand; + e.Cursor = referenceSegment.IsLocal ? Cursors.Arrow : Cursors.Hand; } /// @@ -110,7 +110,8 @@ namespace ICSharpCode.ILSpy.TextView { if (e.ChangedButton == MouseButton.Left && !e.Handled) { parent.JumpToReference(referenceSegment); - e.Handled = true; + if(!referenceSegment.IsLocal) + e.Handled = true; } } diff --git a/ILSpy/TreeNodes/EventTreeNode.cs b/ILSpy/TreeNodes/EventTreeNode.cs index 465048177..2f585369c 100644 --- a/ILSpy/TreeNodes/EventTreeNode.cs +++ b/ILSpy/TreeNodes/EventTreeNode.cs @@ -108,6 +108,14 @@ namespace ICSharpCode.ILSpy.TreeNodes language.DecompileEvent(ev, output, options); } + + public override bool IsPublicAPI { + get { + MethodDefinition accessor = ev.AddMethod ?? ev.RemoveMethod; + return accessor != null && (accessor.IsPublic || accessor.IsFamilyOrAssembly || accessor.IsFamily); + } + } + MemberReference IMemberTreeNode.Member { get { return ev; } diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index ecd28f28f..90942a7a9 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -112,6 +112,12 @@ namespace ICSharpCode.ILSpy.TreeNodes { language.DecompileField(field, output, options); } + + public override bool IsPublicAPI { + get { + return field.IsPublic || field.IsFamily || field.IsFamilyOrAssembly; + } + } MemberReference IMemberTreeNode.Member { diff --git a/ILSpy/TreeNodes/ILSpyTreeNode.cs b/ILSpy/TreeNodes/ILSpyTreeNode.cs index 8b4835fe5..f43e6fb5e 100644 --- a/ILSpy/TreeNodes/ILSpyTreeNode.cs +++ b/ILSpy/TreeNodes/ILSpyTreeNode.cs @@ -166,5 +166,18 @@ namespace ICSharpCode.ILSpy.TreeNodes ApplyFilterToChild(node); } } + + public virtual bool IsPublicAPI { + get { return true; } + } + + public override System.Windows.Media.Brush Foreground { + get { + if (IsPublicAPI) + return base.Foreground; + else + return System.Windows.SystemColors.GrayTextBrush; + } + } } } \ No newline at end of file diff --git a/ILSpy/TreeNodes/MethodTreeNode.cs b/ILSpy/TreeNodes/MethodTreeNode.cs index 6435cbfb7..d16cce924 100644 --- a/ILSpy/TreeNodes/MethodTreeNode.cs +++ b/ILSpy/TreeNodes/MethodTreeNode.cs @@ -137,6 +137,12 @@ namespace ICSharpCode.ILSpy.TreeNodes return FilterResult.Hidden; } + public override bool IsPublicAPI { + get { + return method.IsPublic || method.IsFamily || method.IsFamilyOrAssembly; + } + } + MemberReference IMemberTreeNode.Member { get { return method; } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 2ead5dc71..4872b93e8 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -148,6 +148,19 @@ namespace ICSharpCode.ILSpy.TreeNodes { language.DecompileProperty(property, output, options); } + + public override bool IsPublicAPI { + get { + switch (GetAttributesOfMostAccessibleMethod(property) & MethodAttributes.MemberAccessMask) { + case MethodAttributes.Public: + case MethodAttributes.Family: + case MethodAttributes.FamORAssem: + return true; + default: + return false; + } + } + } MemberReference IMemberTreeNode.Member { diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 15bb054d8..cd25c916d 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -62,7 +62,7 @@ namespace ICSharpCode.ILSpy.TreeNodes get { return HighlightSearchMatch(this.Language.FormatTypeName(type)); } } - public bool IsPublicAPI { + public override bool IsPublicAPI { get { switch (type.Attributes & TypeAttributes.VisibilityMask) { case TypeAttributes.Public: diff --git a/ILSpy/XmlDoc/XmlDocLoader.cs b/ILSpy/XmlDoc/XmlDocLoader.cs index 3cc8384bb..4b91ad84d 100644 --- a/ILSpy/XmlDoc/XmlDocLoader.cs +++ b/ILSpy/XmlDoc/XmlDocLoader.cs @@ -99,6 +99,9 @@ namespace ICSharpCode.ILSpy.XmlDoc static string LookupLocalizedXmlDoc(string fileName) { + if (string.IsNullOrEmpty(fileName)) + return null; + string xmlFileName = Path.ChangeExtension(fileName, ".xml"); string currentCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName; string localizedXmlDocFile = GetLocalizedName(xmlFileName, currentCulture); diff --git a/SharpTreeView/SharpTreeNode.cs b/SharpTreeView/SharpTreeNode.cs index ae0a0e646..65561ea93 100644 --- a/SharpTreeView/SharpTreeNode.cs +++ b/SharpTreeView/SharpTreeNode.cs @@ -12,6 +12,7 @@ using System.Collections.ObjectModel; using System.Windows.Controls; using System.Collections.Specialized; using System.Windows.Input; +using System.Windows.Media; namespace ICSharpCode.TreeView { @@ -101,6 +102,10 @@ namespace ICSharpCode.TreeView get { return null; } } + public virtual Brush Foreground { + get { return SystemColors.WindowTextBrush; } + } + public virtual object Icon { get { return null; } diff --git a/SharpTreeView/Themes/Generic.xaml b/SharpTreeView/Themes/Generic.xaml index a10e19c75..00b807af0 100644 --- a/SharpTreeView/Themes/Generic.xaml +++ b/SharpTreeView/Themes/Generic.xaml @@ -181,6 +181,7 @@ @@ -210,7 +211,8 @@ --> -