.NET Decompiler with support for PDB generation, ReadyToRun, Metadata (&more) - cross-platform!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

443 lines
16 KiB

// 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.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Threading;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpy.Docking;
using ICSharpCode.ILSpy.Options;
using ICSharpCode.ILSpy.Search;
using ICSharpCode.ILSpy.ViewModels;
namespace ICSharpCode.ILSpy
{
/// <summary>
/// Search pane
/// </summary>
public partial class SearchPane : UserControl
{
const int MAX_RESULTS = 1000;
const int MAX_REFRESH_TIME_MS = 10; // More means quicker forward of data, less means better responsibility
RunningSearch currentSearch;
bool runSearchOnNextShow;
IComparer<SearchResult> resultsComparer;
FilterSettings filterSettings;
public static readonly DependencyProperty ResultsProperty =
DependencyProperty.Register("Results", typeof(ObservableCollection<SearchResult>), typeof(SearchPane),
new PropertyMetadata(new ObservableCollection<SearchResult>()));
public ObservableCollection<SearchResult> Results {
get { return (ObservableCollection<SearchResult>)GetValue(ResultsProperty); }
}
public SearchPane()
{
InitializeComponent();
searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Types and Members" });
searchModeComboBox.Items.Add(new { Image = Images.Class, Name = "Type" });
searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Member" });
searchModeComboBox.Items.Add(new { Image = Images.Method, Name = "Method" });
searchModeComboBox.Items.Add(new { Image = Images.Field, Name = "Field" });
searchModeComboBox.Items.Add(new { Image = Images.Property, Name = "Property" });
searchModeComboBox.Items.Add(new { Image = Images.Event, Name = "Event" });
searchModeComboBox.Items.Add(new { Image = Images.Literal, Name = "Constant" });
searchModeComboBox.Items.Add(new { Image = Images.Library, Name = "Metadata Token" });
searchModeComboBox.Items.Add(new { Image = Images.Resource, Name = "Resource" });
searchModeComboBox.Items.Add(new { Image = Images.Assembly, Name = "Assembly" });
searchModeComboBox.Items.Add(new { Image = Images.Namespace, Name = "Namespace" });
ContextMenuProvider.Add(listBox);
MainWindow.Instance.CurrentAssemblyListChanged += MainWindow_Instance_CurrentAssemblyListChanged;
DockWorkspace.Instance.PropertyChanged += DockWorkspace_PropertyChanged;
filterSettings = MainWindow.Instance.SessionSettings.FilterSettings;
filterSettings.PropertyChanged += FilterSettings_PropertyChanged;
CompositionTarget.Rendering += UpdateResults;
// This starts empty search right away, so do at the end (we're still in ctor)
searchModeComboBox.SelectedIndex = (int)MainWindow.Instance.SessionSettings.SelectedSearchMode;
}
private void DockWorkspace_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
switch (e.PropertyName)
{
case nameof(DockWorkspace.Instance.ActiveTabPage):
filterSettings.PropertyChanged -= FilterSettings_PropertyChanged;
filterSettings = DockWorkspace.Instance.ActiveTabPage.FilterSettings;
filterSettings.PropertyChanged += FilterSettings_PropertyChanged;
break;
}
}
void MainWindow_Instance_CurrentAssemblyListChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (IsVisible)
{
StartSearch(this.SearchTerm);
}
else
{
StartSearch(null);
runSearchOnNextShow = true;
}
}
void FilterSettings_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(FilterSettings.ShowApiLevel))
return;
if (IsVisible)
{
StartSearch(this.SearchTerm);
}
else
{
StartSearch(null);
runSearchOnNextShow = true;
}
}
public void Show()
{
if (!IsVisible)
{
DockWorkspace.Instance.ToolPanes.Single(p => p.ContentId == SearchPaneModel.PaneContentId).IsVisible = true;
if (runSearchOnNextShow)
{
runSearchOnNextShow = false;
StartSearch(this.SearchTerm);
}
}
Dispatcher.BeginInvoke(
DispatcherPriority.Background,
new Action(
delegate {
searchBox.Focus();
searchBox.SelectAll();
}));
}
public static readonly DependencyProperty SearchTermProperty =
DependencyProperty.Register("SearchTerm", typeof(string), typeof(SearchPane),
new FrameworkPropertyMetadata(string.Empty, OnSearchTermChanged));
public string SearchTerm {
get { return (string)GetValue(SearchTermProperty); }
set { SetValue(SearchTermProperty, value); }
}
static void OnSearchTermChanged(DependencyObject o, DependencyPropertyChangedEventArgs e)
{
((SearchPane)o).StartSearch((string)e.NewValue);
}
void SearchModeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
MainWindow.Instance.SessionSettings.SelectedSearchMode = (SearchMode)searchModeComboBox.SelectedIndex;
StartSearch(this.SearchTerm);
}
void ListBox_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
JumpToSelectedItem();
e.Handled = true;
}
void ListBox_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Return)
{
e.Handled = true;
JumpToSelectedItem();
}
else if (e.Key == Key.Up && listBox.SelectedIndex == 0)
{
e.Handled = true;
listBox.SelectedIndex = -1;
searchBox.Focus();
}
}
protected override void OnKeyDown(KeyEventArgs e)
{
base.OnKeyDown(e);
if (e.Key == Key.T && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{
searchModeComboBox.SelectedIndex = (int)SearchMode.Type;
e.Handled = true;
}
else if (e.Key == Key.M && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{
searchModeComboBox.SelectedIndex = (int)SearchMode.Member;
e.Handled = true;
}
else if (e.Key == Key.S && e.KeyboardDevice.Modifiers == ModifierKeys.Control)
{
searchModeComboBox.SelectedIndex = (int)SearchMode.Literal;
e.Handled = true;
}
}
void SearchBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Down && listBox.HasItems)
{
e.Handled = true;
listBox.MoveFocus(new TraversalRequest(FocusNavigationDirection.First));
listBox.SelectedIndex = 0;
}
}
void UpdateResults(object sender, EventArgs e)
{
if (currentSearch == null)
return;
var timer = Stopwatch.StartNew();
int resultsAdded = 0;
while (Results.Count < MAX_RESULTS && timer.ElapsedMilliseconds < MAX_REFRESH_TIME_MS && currentSearch.resultQueue.TryTake(out var result))
{
Results.InsertSorted(result, resultsComparer);
++resultsAdded;
}
if (resultsAdded > 0 && Results.Count == MAX_RESULTS)
{
Results.Add(new SearchResult { Name = Properties.Resources.SearchAbortedMoreThan1000ResultsFound });
currentSearch.Cancel();
}
}
async void StartSearch(string searchTerm)
{
if (currentSearch != null)
{
currentSearch.Cancel();
currentSearch = null;
}
resultsComparer = DisplaySettingsPanel.CurrentDisplaySettings.SortResults ?
SearchResult.ComparerByFitness :
SearchResult.ComparerByName;
Results.Clear();
RunningSearch startedSearch = null;
if (!string.IsNullOrEmpty(searchTerm))
{
MainWindow mainWindow = MainWindow.Instance;
searchProgressBar.IsIndeterminate = true;
startedSearch = new RunningSearch(await mainWindow.CurrentAssemblyList.GetAllAssemblies(), searchTerm,
(SearchMode)searchModeComboBox.SelectedIndex, mainWindow.CurrentLanguage,
mainWindow.SessionSettings.FilterSettings.ShowApiLevel);
currentSearch = startedSearch;
await startedSearch.Run();
}
if (currentSearch == startedSearch)
{ //are we still running the same search
searchProgressBar.IsIndeterminate = false;
}
}
void JumpToSelectedItem()
{
if (listBox.SelectedItem is SearchResult result)
{
MainWindow.Instance.JumpToReference(result.Reference);
}
}
sealed class RunningSearch
{
readonly CancellationTokenSource cts = new CancellationTokenSource();
readonly IList<LoadedAssembly> assemblies;
readonly string[] searchTerm;
readonly SearchMode searchMode;
readonly Language language;
readonly ApiVisibility apiVisibility;
public readonly IProducerConsumerCollection<SearchResult> resultQueue = new ConcurrentQueue<SearchResult>();
public RunningSearch(IList<LoadedAssembly> assemblies, string searchTerm, SearchMode searchMode, Language language, ApiVisibility apiVisibility)
{
this.assemblies = assemblies;
this.searchTerm = NativeMethods.CommandLineToArgumentArray(searchTerm);
this.language = language;
this.searchMode = searchMode;
this.apiVisibility = apiVisibility;
}
public void Cancel()
{
cts.Cancel();
}
public async Task Run()
{
try
{
await Task.Factory.StartNew(() => {
var searcher = GetSearchStrategy();
try
{
foreach (var loadedAssembly in assemblies)
{
var module = loadedAssembly.GetPEFileOrNull();
if (module == null)
continue;
searcher.Search(module, cts.Token);
}
}
catch (OperationCanceledException)
{
// ignore cancellation
}
}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false);
}
catch (TaskCanceledException)
{
// ignore cancellation
}
}
AbstractSearchStrategy GetSearchStrategy()
{
if (searchTerm.Length == 1)
{
if (searchTerm[0].StartsWith("tm:", StringComparison.Ordinal))
return new MemberSearchStrategy(language, apiVisibility, searchTerm[0].Substring(3), resultQueue);
if (searchTerm[0].StartsWith("t:", StringComparison.Ordinal))
return new MemberSearchStrategy(language, apiVisibility, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Type);
if (searchTerm[0].StartsWith("m:", StringComparison.Ordinal))
return new MemberSearchStrategy(language, apiVisibility, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Member);
if (searchTerm[0].StartsWith("md:", StringComparison.Ordinal))
return new MemberSearchStrategy(language, apiVisibility, searchTerm[0].Substring(3), resultQueue, MemberSearchKind.Method);
if (searchTerm[0].StartsWith("f:", StringComparison.Ordinal))
return new MemberSearchStrategy(language, apiVisibility, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Field);
if (searchTerm[0].StartsWith("p:", StringComparison.Ordinal))
return new MemberSearchStrategy(language, apiVisibility, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Property);
if (searchTerm[0].StartsWith("e:", StringComparison.Ordinal))
return new MemberSearchStrategy(language, apiVisibility, searchTerm[0].Substring(2), resultQueue, MemberSearchKind.Event);
if (searchTerm[0].StartsWith("c:", StringComparison.Ordinal))
return new LiteralSearchStrategy(language, apiVisibility, resultQueue, searchTerm[0].Substring(2));
if (searchTerm[0].StartsWith("@", StringComparison.Ordinal))
return new MetadataTokenSearchStrategy(language, apiVisibility, resultQueue, searchTerm[0].Substring(1));
if (searchTerm[0].StartsWith("r:", StringComparison.Ordinal))
return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm[0].Substring(2));
if (searchTerm[0].StartsWith("a:", StringComparison.Ordinal))
return new AssemblySearchStrategy(searchTerm[0].Substring(2), resultQueue, AssemblySearchKind.NameOrFileName);
if (searchTerm[0].StartsWith("af:", StringComparison.Ordinal))
return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.FilePath);
if (searchTerm[0].StartsWith("an:", StringComparison.Ordinal))
return new AssemblySearchStrategy(searchTerm[0].Substring(3), resultQueue, AssemblySearchKind.FullName);
if (searchTerm[0].StartsWith("n:", StringComparison.Ordinal))
return new NamespaceSearchStrategy(searchTerm[0].Substring(2), resultQueue);
}
switch (searchMode)
{
case SearchMode.TypeAndMember:
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm);
case SearchMode.Type:
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Type);
case SearchMode.Member:
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Member);
case SearchMode.Literal:
return new LiteralSearchStrategy(language, apiVisibility, resultQueue, searchTerm);
case SearchMode.Method:
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Method);
case SearchMode.Field:
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Field);
case SearchMode.Property:
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Property);
case SearchMode.Event:
return new MemberSearchStrategy(language, apiVisibility, resultQueue, searchTerm, MemberSearchKind.Event);
case SearchMode.Token:
return new MetadataTokenSearchStrategy(language, apiVisibility, resultQueue, searchTerm);
case SearchMode.Resource:
return new ResourceSearchStrategy(apiVisibility, resultQueue, searchTerm);
case SearchMode.Assembly:
return new AssemblySearchStrategy(resultQueue, searchTerm, AssemblySearchKind.NameOrFileName);
case SearchMode.Namespace:
return new NamespaceSearchStrategy(resultQueue, searchTerm);
}
return null;
}
}
}
[ExportMainMenuCommand(Menu = nameof(Properties.Resources._Window), Header = nameof(Properties.Resources.Search), MenuIcon = "Images/Search", MenuCategory = "pane", MenuOrder = 100)]
[ExportToolbarCommand(ToolTip = nameof(Properties.Resources.SearchCtrlShiftFOrCtrlE), ToolbarIcon = "Images/Search", ToolbarCategory = nameof(Properties.Resources.View), ToolbarOrder = 100)]
sealed class ShowSearchCommand : CommandWrapper
{
public ShowSearchCommand()
: base(NavigationCommands.Search)
{
NavigationCommands.Search.InputGestures.Clear();
NavigationCommands.Search.InputGestures.Add(new KeyGesture(Key.F, ModifierKeys.Control | ModifierKeys.Shift));
NavigationCommands.Search.InputGestures.Add(new KeyGesture(Key.E, ModifierKeys.Control));
}
}
public enum SearchMode
{
TypeAndMember,
Type,
Member,
Method,
Field,
Property,
Event,
Literal,
Token,
Resource,
Assembly,
Namespace
}
}