From 4ef74877d9c05b1ff50f21c58df222d44c44e6dd Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Sun, 29 Jan 2023 22:46:47 +0100 Subject: [PATCH 1/7] Add support for generic types and methods --- Disasmo/Resources/DisasmoLoader4.cs_template | 294 ++++++++++++++++++- Disasmo/Utils/SymbolUtils.cs | 32 +- src/Vsix/Properties/Settings.Designer.cs | 12 + src/Vsix/Properties/Settings.settings | 3 + src/Vsix/ViewModels/MainViewModel.cs | 9 +- src/Vsix/ViewModels/SettingsViewModel.cs | 103 +++++++ src/Vsix/Views/DisasmWindowControl.xaml | 15 + src/Vsix/Views/DisasmWindowControl.xaml.cs | 53 ++++ src/Vsix/app.config | 3 + 9 files changed, 494 insertions(+), 30 deletions(-) diff --git a/Disasmo/Resources/DisasmoLoader4.cs_template b/Disasmo/Resources/DisasmoLoader4.cs_template index 320a3b7..d9ede24 100644 --- a/Disasmo/Resources/DisasmoLoader4.cs_template +++ b/Disasmo/Resources/DisasmoLoader4.cs_template @@ -1,4 +1,7 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; @@ -28,11 +31,15 @@ public class DisasmoLoader var alc = new AssemblyLoadContext("DisasmoALC", unloadable == "True"); Assembly asm = alc.LoadFromAssemblyPath(Path.Combine(Environment.CurrentDirectory, assemblyName)); + var generics = ParseGenerics(args[4], alc); Type fastType = asm.GetType(typeName); if (fastType != null) { - PrecompileMethods(fastType, methodName); - PrecompileProperties(fastType, methodName); + foreach (var instance in MakeGenericInstances(fastType, generics)) + { + PrecompileMethods(instance, methodName, generics); + PrecompileProperties(instance, methodName, generics); + } return; } @@ -44,47 +51,47 @@ public class DisasmoLoader // This is the easiest solution to that problem if (type.FullName?.Replace('+', '.').Contains(typeName) == true) { - PrecompileMethods(type, methodName); - PrecompileProperties(type, methodName); + foreach (var instance in MakeGenericInstances(type, generics)) + { + PrecompileMethods(instance, methodName, generics); + PrecompileProperties(instance, methodName, generics); + } } } } - private static void PrecompileProperties(Type type, string propertyName) + private static void PrecompileProperties(Type type, string propertyName, Dictionary> generics) { foreach (PropertyInfo propInfo in type.GetProperties((BindingFlags)60)) { if (propInfo.Name == "*" || propInfo.Name == propertyName) { if (propInfo.GetMethod != null) - RuntimeHelpers.PrepareMethod(propInfo.GetMethod.MethodHandle); + Prepare(propInfo.GetMethod, generics); if (propInfo.SetMethod != null) - RuntimeHelpers.PrepareMethod(propInfo.SetMethod.MethodHandle); + Prepare(propInfo.SetMethod, generics); } } } - private static void PrecompileMethods(Type type, string methodName) + private static void PrecompileMethods(Type type, string methodName, Dictionary> generics) { foreach (MethodBase method in type.GetMethods((BindingFlags)60).Concat( type.GetConstructors((BindingFlags)60).Select(c => (MethodBase)c))) { - if (method.IsGenericMethod) - continue; - try { if (method.DeclaringType == type || method.DeclaringType == null) { if (methodName == "*" || method.Name == methodName) { - RuntimeHelpers.PrepareMethod(method.MethodHandle); + Prepare(method, generics); } else if (method.Name.Contains(">g__" + methodName)) { // Special case for local functions - RuntimeHelpers.PrepareMethod(method.MethodHandle); + Prepare(method, generics); } } } @@ -93,4 +100,265 @@ public class DisasmoLoader } } } + + private static void Prepare(MethodBase method, Dictionary> generics) + { + if (method is MethodInfo) + { + foreach (var instance in MakeGenericInstances((MethodInfo)method, generics)) + { + try + { + RuntimeHelpers.PrepareMethod(instance.MethodHandle); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + } + } + else + { + try + { + RuntimeHelpers.PrepareMethod(method.MethodHandle); + } + catch (Exception ex) + { + Console.WriteLine(ex); + } + } + } + + private static Dictionary> ParseGenerics(ReadOnlySpan data, AssemblyLoadContext alc) + { + var result = new Dictionary>(); + while (!data.IsEmpty) + { + int eq = data.IndexOf('='); + string name = data[..eq].ToString(); + data = data[(eq + 1)..]; + Type type = ParseType(ref data, alc); + + if (type != null) + { + if (!result.ContainsKey(name)) + { + result[name] = new List() { type }; + } + else + { + result[name].Add(type); + } + } + + data = data[1..]; + } + + return result; + } + + private static Type ParseType(ref ReadOnlySpan data, AssemblyLoadContext alc) + { + int nameEnd = data.IndexOfAny("[],;"); + string name = data[..nameEnd].ToString(); + Type type = FindType(name, alc); + data = data[nameEnd..]; + + if (data[0] == ']' || data[0] == ',' || data[0] == ';') + { + return type; + } + + int argEnd = data.IndexOf(']'); + + if (argEnd == 1) + { + data = data[2..]; + return type?.MakeArrayType(); + } + + if (argEnd == 2 && data[1] == '*') + { + data = data[3..]; + return type?.MakePointerType(); + } + + if (int.TryParse(data[1..argEnd], NumberStyles.Integer, CultureInfo.InvariantCulture, out int rank)) + { + data = data[(argEnd + 1)..]; + return type?.MakeArrayType(rank); + } + + List args = new List(); + + while (data[0] != ']') + { + data = data[1..]; + args.Add(ParseType(ref data, alc)); + } + + data = data[1..]; + if (args.Contains(null)) + { + return null; + } + return type?.MakeGenericType(args.ToArray()); + } + + private static Type FindType(string name, AssemblyLoadContext alc) + { + foreach (Assembly assembly in alc.Assemblies) + { + Type type = assembly.GetType(name); + if (type != null) + { + return type; + } + } + + foreach (Assembly assembly in AssemblyLoadContext.Default.Assemblies) + { + Type type = assembly.GetType(name); + if (type != null) + { + return type; + } + } + + return null; + } + + private static IEnumerable MakeGenericInstances(Type type, Dictionary> arguments) + { + if (!type.IsGenericType) + { + yield return type; + yield break; + } + + var hasVariant = false; + type = type.GetGenericTypeDefinition(); + var variants = MakeGenericVariants(type.GetGenericArguments(), arguments); + + foreach (var variant in variants) + { + Type instance = null; + try + { + instance = type.MakeGenericType(variant); + } + catch (ArgumentException ex) + { + // probably fails some constraint + } + + if (instance != null) + { + hasVariant = true; + yield return instance; + } + } + + if (!hasVariant) + { + Console.WriteLine("Unable to create instance for " + type + ". Check that the provided arguments fullfill the constraints."); + } + } + + + private static IEnumerable MakeGenericInstances(MethodBase method, Dictionary> arguments) + { + if (method is not MethodInfo info || !method.IsGenericMethod) + { + yield return method; + yield break; + } + + var hasVariant = false; + info = info.GetGenericMethodDefinition(); + var variants = MakeGenericVariants(info.GetGenericArguments(), arguments); + + foreach (var variant in variants) + { + MethodInfo instance = null; + try + { + instance = info.MakeGenericMethod(variant); + } + catch (ArgumentException ex) + { + // probably fails some constraint + } + + if (instance != null) + { + hasVariant = true; + yield return instance; + } + } + + if (!hasVariant) + { + Console.WriteLine("Unable to create instance for " + method + ". Check that the provided arguments fullfill the constraints."); + } + } + + private static List MakeGenericVariants(Type[] parameters, Dictionary> arguments) + { + var results = new List() { new Type[parameters.Length] }; + + arguments.TryGetValue("*", out var applyAlways); + + for (int i = 0; i < parameters.Length; i++) + { + arguments.TryGetValue(parameters[i].Name, out var applyArgument); + + var count = (applyAlways?.Count ?? 0) + (applyArgument?.Count ?? 0); + + if (count == 0) + { + Console.WriteLine("No arguments to apply for generic parameter " + parameters[i].Name); + results.Clear(); // cross product will be empty, don't return to print this warning potentially multiple times + } + else if (count == 1) + { + var argument = applyAlways?[0] ?? applyArgument?[0]; + + foreach (var variant in results) + { + variant[i] = argument; + } + } + else + { + var preResults = results; + results = new List(); + + foreach (var variant in preResults) + { + if (applyAlways != null) + { + foreach (var argument in applyAlways) + { + var copy = variant.ToArray(); + copy[i] = argument; + results.Add(copy); + } + } + if (applyArgument != null) + { + foreach (var argument in applyArgument) + { + var copy = variant.ToArray(); + copy[i] = argument; + results.Add(copy); + } + } + } + } + } + + return results; + } } diff --git a/Disasmo/Utils/SymbolUtils.cs b/Disasmo/Utils/SymbolUtils.cs index 7b197c7..ede9bd6 100644 --- a/Disasmo/Utils/SymbolUtils.cs +++ b/Disasmo/Utils/SymbolUtils.cs @@ -2,7 +2,7 @@ namespace Disasmo.Utils; -public class SymbolUtils +public static class SymbolUtils { public static DisasmoSymbolInfo FromSymbol(ISymbol symbol) { @@ -16,35 +16,49 @@ public static DisasmoSymbolInfo FromSymbol(ISymbol symbol) { // hack for mangled names target = "*" + symbol.Name + "*"; - hostType = symbol.ContainingType.ToString(); + hostType = symbol.ContainingType.MetadataName; methodName = "*"; } else if (ms.MethodKind == MethodKind.Constructor) { - target = "*" + symbol.ContainingType.Name + ":.ctor"; - hostType = symbol.ContainingType.ToString(); + target = "*" + symbol.ContainingType.MetadataName + ":.ctor"; + hostType = symbol.ContainingType.MetadataName(); methodName = "*"; } else { - target = "*" + symbol.ContainingType.Name + ":" + symbol.Name; - hostType = symbol.ContainingType.ToString(); + target = "*" + symbol.ContainingType.MetadataName + ":" + symbol.Name; + hostType = symbol.ContainingType.MetadataName(); methodName = symbol.Name; } } else if (symbol is IPropertySymbol prop) { - target = "*" + symbol.ContainingType.Name + ":get_" + symbol.Name + " " + "*" + symbol.ContainingType.Name + ":set_" + symbol.Name; - hostType = symbol.ContainingType.ToString(); + target = "*" + symbol.ContainingType.MetadataName + ":get_" + symbol.Name + " " + "*" + symbol.ContainingType.MetadataName + ":set_" + symbol.Name; + hostType = symbol.ContainingType.MetadataName(); methodName = symbol.Name; } else { // the whole class target = symbol.Name + ":*"; - hostType = symbol.ToString(); + hostType = symbol.MetadataName; methodName = "*"; } return new DisasmoSymbolInfo(target, hostType, methodName); } + + private static string MetadataName(this INamedTypeSymbol type) + { + string name = ""; + if (type.ContainingType != null) + { + name = type.ContainingType.MetadataName() + "+"; + } + else if (type.ContainingNamespace != null && !type.ContainingNamespace.IsGlobalNamespace) + { + name = type.ContainingNamespace.ToString() + "."; + } + return name + type.MetadataName; + } } \ No newline at end of file diff --git a/src/Vsix/Properties/Settings.Designer.cs b/src/Vsix/Properties/Settings.Designer.cs index a4f8f16..accc9ad 100644 --- a/src/Vsix/Properties/Settings.Designer.cs +++ b/src/Vsix/Properties/Settings.Designer.cs @@ -252,5 +252,17 @@ public bool DontGuessTFM { this["DontGuessTFM"] = value; } } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("*=System.Int32;*=System.Tuple`2[System.Object,System.Int32];")] + public string GenericArguments { + get { + return ((string)(this["GenericArguments"])); + } + set { + this["GenericArguments"] = value; + } + } } } diff --git a/src/Vsix/Properties/Settings.settings b/src/Vsix/Properties/Settings.settings index e523cdd..b99cef6 100644 --- a/src/Vsix/Properties/Settings.settings +++ b/src/Vsix/Properties/Settings.settings @@ -59,5 +59,8 @@ False + + *=System.Int32;*=System.Tuple`2[System.Object,System.Int32]; + \ No newline at end of file diff --git a/src/Vsix/ViewModels/MainViewModel.cs b/src/Vsix/ViewModels/MainViewModel.cs index 0bf65d9..579c34d 100644 --- a/src/Vsix/ViewModels/MainViewModel.cs +++ b/src/Vsix/ViewModels/MainViewModel.cs @@ -225,7 +225,7 @@ public async Task RunFinalExe(DisasmoSymbolInfo symbolInfo) envVars["DOTNET_JitDumpFgFile"] = currentFgFile; } - string command = $"\"{LoaderAppManager.DisasmoLoaderName}.dll\" \"{fileName}.dll\" \"{symbolInfo.ClassName}\" \"{symbolInfo.MethodName}\" {SettingsVm.UseUnloadableContext}"; + string command = $"\"{LoaderAppManager.DisasmoLoaderName}.dll\" \"{fileName}.dll\" \"{symbolInfo.ClassName}\" \"{symbolInfo.MethodName}\" {SettingsVm.UseUnloadableContext} \"{string.Concat(SettingsVm.GenericArguments)}\""; if (SettingsVm.RunAppMode) { command = $"\"{fileName}.dll\""; @@ -535,13 +535,6 @@ public async void RunOperationAsync(ISymbol symbol) clrCheckedFilesDir = dir; } - if (symbol is IMethodSymbol { IsGenericMethod: true }) - { - // TODO: ask user to specify type parameters - Output = "Generic methods are not supported yet."; - return; - } - ThrowIfCanceled(); // Find Release-x64 configuration: diff --git a/src/Vsix/ViewModels/SettingsViewModel.cs b/src/Vsix/ViewModels/SettingsViewModel.cs index ec6f6fd..1a9d585 100644 --- a/src/Vsix/ViewModels/SettingsViewModel.cs +++ b/src/Vsix/ViewModels/SettingsViewModel.cs @@ -1,8 +1,10 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.ComponentModel; using System.IO; using System.Linq; +using System.Runtime.CompilerServices; using System.Windows.Forms; using System.Windows.Input; using Disasmo.Properties; @@ -38,6 +40,7 @@ public class SettingsViewModel : ViewModelBase private string _selectedCustomJit; private string _graphvisDot; private bool _fgEnable; + private ObservableCollection _genericArguments; public SettingsViewModel() { @@ -61,6 +64,9 @@ public SettingsViewModel() UseUnloadableContext = Settings.Default.UseUnloadableContext; DisableLightBulb = Settings.Default.DisableLightBulb; DontGuessTFM = Settings.Default.DontGuessTFM; + GenericArguments = new ObservableCollection( + Settings.Default.GenericArguments.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenericArgument(s)) + ); CheckUpdates(); } @@ -408,6 +414,61 @@ public string IlcArgs } } + public ObservableCollection GenericArguments + { + get => _genericArguments; + set + { + if (_genericArguments != null) + { + foreach (var argument in _genericArguments) + argument.PropertyChanged -= GenericArgumentsPropertyChanged; + _genericArguments.CollectionChanged -= GenericArgumentsCollectionChanged; + } + Set(ref _genericArguments, value); + if (_genericArguments != null) + { + _genericArguments.CollectionChanged += GenericArgumentsCollectionChanged; + foreach (var argument in _genericArguments) + argument.PropertyChanged += GenericArgumentsPropertyChanged; + } + } + } + + private void GenericArgumentsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) + { + if (e.OldItems != null) + { + foreach (var oldItem in e.OldItems) + { + if (oldItem is INotifyPropertyChanged changed) + { + changed.PropertyChanged -= GenericArgumentsPropertyChanged; + } + } + } + + if (e.NewItems != null) + { + foreach (var newItem in e.NewItems) + { + if (newItem is INotifyPropertyChanged changed) + { + changed.PropertyChanged += GenericArgumentsPropertyChanged; + } + } + } + + Settings.Default.GenericArguments = string.Concat(_genericArguments); + Settings.Default.Save(); + } + + private void GenericArgumentsPropertyChanged(object sender, PropertyChangedEventArgs e) + { + Settings.Default.GenericArguments = string.Concat(_genericArguments); + Settings.Default.Save(); + } + public bool UpdateIsAvailable { get => _updateIsAvailable; @@ -465,4 +526,46 @@ private static string FindJitDirectory(string basePath) return null; } } + + public class GenericArgument : INotifyPropertyChanged + { + private string _argument; + private string _type; + + public event PropertyChangedEventHandler PropertyChanged; + + public GenericArgument(string value) + { + var parts = value.Split('='); + _argument = parts[0]; + _type = parts[1]; + } + + public string Argument + { + get => _argument; + set => SetField(ref _argument, value); + } + + public string Type + { + get => _type; + set => SetField(ref _type, value); + } + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) + { + if (EqualityComparer.Default.Equals(field, value)) return false; + field = value; + OnPropertyChanged(propertyName); + return true; + } + + public override string ToString() => $"{_argument}={_type};"; + } } diff --git a/src/Vsix/Views/DisasmWindowControl.xaml b/src/Vsix/Views/DisasmWindowControl.xaml index e177baa..dbe7ffc 100644 --- a/src/Vsix/Views/DisasmWindowControl.xaml +++ b/src/Vsix/Views/DisasmWindowControl.xaml @@ -278,6 +278,21 @@ TextWrapping="Wrap" Text="{Binding IlcArgs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> + + + + + + + + + + + + + + + diff --git a/src/Vsix/Views/DisasmWindowControl.xaml.cs b/src/Vsix/Views/DisasmWindowControl.xaml.cs index a730ad4..2c135a9 100644 --- a/src/Vsix/Views/DisasmWindowControl.xaml.cs +++ b/src/Vsix/Views/DisasmWindowControl.xaml.cs @@ -1,6 +1,8 @@ using System; +using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; +using System.Linq; using System.Runtime.InteropServices; using System.Windows.Data; using System.Windows.Navigation; @@ -114,6 +116,57 @@ private async void OnOpenFolderWithFlowGraphs(object sender, RequestNavigateEven } } } + + private void OnAddGenericArgument(object sender, RoutedEventArgs e) + { + MainViewModel.SettingsVm.GenericArguments.Add(new GenericArgument("*=System.Int32")); + } + + private void OnRemoveGenericArgument(object sender, RoutedEventArgs e) + { + foreach (var item in GenericArgumentGrid.SelectedItems.Cast().ToArray()) + { + MainViewModel.SettingsVm.GenericArguments.Remove(item); + } + } + + private void OnMoveUpGenericArgument(object sender, RoutedEventArgs e) + { + foreach (int index in GenericArgumentGrid.SelectedItems.Cast().Select(MainViewModel.SettingsVm.GenericArguments.IndexOf).OrderBy(v => v)) + { + if (index > 0) + { + MainViewModel.SettingsVm.GenericArguments.Move(index, index - 1); + } + } + } + + private void OnMoveDownGenericArgument(object sender, RoutedEventArgs e) + { + foreach (int index in GenericArgumentGrid.SelectedItems.Cast().Select(MainViewModel.SettingsVm.GenericArguments.IndexOf).OrderByDescending(v => v)) + { + if (index >= 0 && index < MainViewModel.SettingsVm.GenericArguments.Count - 1) + { + MainViewModel.SettingsVm.GenericArguments.Move(index, index + 1); + } + } + } + + private void OnCopyGenericArgument(object sender, RoutedEventArgs e) + { + Clipboard.SetText(string.Join("\n", MainViewModel.SettingsVm.GenericArguments)); + } + + private void OnPasteGenericArgument(object sender, RoutedEventArgs e) + { + string text = Clipboard.GetText(); + if (!string.IsNullOrWhiteSpace(text)) + { + MainViewModel.SettingsVm.GenericArguments = new ObservableCollection( + text.Split(new[] { ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenericArgument(s)) + ); + } + } } [Guid("97cd0cd6-1d77-4848-8b6e-dc82cdccc6d7")] diff --git a/src/Vsix/app.config b/src/Vsix/app.config index 9fc6d6e..84bf142 100644 --- a/src/Vsix/app.config +++ b/src/Vsix/app.config @@ -64,6 +64,9 @@ False + + *=System.Int32;*=System.Tuple`2[System.Object,System.Int32]; + From 087d75eaf14f312368f3633edba7abcd42ff857f Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Mon, 20 Feb 2023 11:04:34 +0100 Subject: [PATCH 2/7] Initial support for providing generic arguments as comments --- Disasmo/DisasmoSymbolInfo.cs | 4 +- Disasmo/Resources/DisasmoLoader4.cs_template | 92 ++++++++++++++++---- Disasmo/Utils/SymbolUtils.cs | 71 ++++++++++++++- src/Vsix/ViewModels/MainViewModel.cs | 2 +- 4 files changed, 146 insertions(+), 23 deletions(-) diff --git a/Disasmo/DisasmoSymbolInfo.cs b/Disasmo/DisasmoSymbolInfo.cs index 52527b7..d67efdf 100644 --- a/Disasmo/DisasmoSymbolInfo.cs +++ b/Disasmo/DisasmoSymbolInfo.cs @@ -2,14 +2,16 @@ public class DisasmoSymbolInfo { - public DisasmoSymbolInfo(string target, string className, string methodName) + public DisasmoSymbolInfo(string target, string className, string methodName, string genericArguments) { Target = target; ClassName = className; MethodName = methodName; + GenericArguments = genericArguments; } public string Target { get; } public string ClassName { get; } public string MethodName { get; } + public string GenericArguments { get; } } \ No newline at end of file diff --git a/Disasmo/Resources/DisasmoLoader4.cs_template b/Disasmo/Resources/DisasmoLoader4.cs_template index d9ede24..8429b30 100644 --- a/Disasmo/Resources/DisasmoLoader4.cs_template +++ b/Disasmo/Resources/DisasmoLoader4.cs_template @@ -13,6 +13,13 @@ using System.Runtime.Loader; public class DisasmoLoader { + // Disasmo-Generic: System.Int32,System.String + // Disasmo-Generic: System.Byte,System.Single + static void Foo() + { + + } + public static void Main(string[] args) { PrecompileAllMethodsInType(args); @@ -25,6 +32,8 @@ public class DisasmoLoader string methodName = args[2]; string unloadable = args[3]; + Debugger.Launch(); + // Another ugly workaround for mangled names till I figure out a proper solution if (typeName.Contains('_') && typeName.Contains('.')) typeName = typeName.Substring(typeName.LastIndexOf('.') + 1); @@ -32,13 +41,14 @@ public class DisasmoLoader var alc = new AssemblyLoadContext("DisasmoALC", unloadable == "True"); Assembly asm = alc.LoadFromAssemblyPath(Path.Combine(Environment.CurrentDirectory, assemblyName)); var generics = ParseGenerics(args[4], alc); + var genericArgs = ParseGenericsArgs(args[5], alc); Type fastType = asm.GetType(typeName); if (fastType != null) { - foreach (var instance in MakeGenericInstances(fastType, generics)) + foreach (var instance in MakeGenericInstances(fastType, generics, genericArgs)) { - PrecompileMethods(instance, methodName, generics); - PrecompileProperties(instance, methodName, generics); + PrecompileMethods(instance, methodName, generics, genericArgs); + PrecompileProperties(instance, methodName, generics, genericArgs); } return; } @@ -51,30 +61,30 @@ public class DisasmoLoader // This is the easiest solution to that problem if (type.FullName?.Replace('+', '.').Contains(typeName) == true) { - foreach (var instance in MakeGenericInstances(type, generics)) + foreach (var instance in MakeGenericInstances(type, generics, genericArgs)) { - PrecompileMethods(instance, methodName, generics); - PrecompileProperties(instance, methodName, generics); + PrecompileMethods(instance, methodName, generics, genericArgs); + PrecompileProperties(instance, methodName, generics, genericArgs); } } } } - private static void PrecompileProperties(Type type, string propertyName, Dictionary> generics) + private static void PrecompileProperties(Type type, string propertyName, Dictionary> generics, Dictionary> genericArgs) { foreach (PropertyInfo propInfo in type.GetProperties((BindingFlags)60)) { if (propInfo.Name == "*" || propInfo.Name == propertyName) { if (propInfo.GetMethod != null) - Prepare(propInfo.GetMethod, generics); + Prepare(propInfo.GetMethod, generics, genericArgs); if (propInfo.SetMethod != null) - Prepare(propInfo.SetMethod, generics); + Prepare(propInfo.SetMethod, generics, genericArgs); } } } - private static void PrecompileMethods(Type type, string methodName, Dictionary> generics) + private static void PrecompileMethods(Type type, string methodName, Dictionary> generics, Dictionary> genericArgs) { foreach (MethodBase method in type.GetMethods((BindingFlags)60).Concat( @@ -86,12 +96,12 @@ public class DisasmoLoader { if (methodName == "*" || method.Name == methodName) { - Prepare(method, generics); + Prepare(method, generics, genericArgs); } else if (method.Name.Contains(">g__" + methodName)) { // Special case for local functions - Prepare(method, generics); + Prepare(method, generics, genericArgs); } } } @@ -101,11 +111,11 @@ public class DisasmoLoader } } - private static void Prepare(MethodBase method, Dictionary> generics) + private static void Prepare(MethodBase method, Dictionary> generics, Dictionary> genericArgs) { if (method is MethodInfo) { - foreach (var instance in MakeGenericInstances((MethodInfo)method, generics)) + foreach (var instance in MakeGenericInstances((MethodInfo)method, generics, genericArgs)) { try { @@ -158,6 +168,39 @@ public class DisasmoLoader return result; } + private static Dictionary> ParseGenericsArgs(ReadOnlySpan data, AssemblyLoadContext alc) + { + var result = new Dictionary>(); + while (!data.IsEmpty) + { + int eq = data.IndexOf('='); + string name = data[..eq].ToString(); + data = data[eq..]; + List types = new List(); + while (data[0] != ';') + { + data = data[1..]; + types.Add(ParseType(ref data, alc)); + } + + if (!types.Contains(null)) + { + if (!result.ContainsKey(name)) + { + result[name] = new List() { types.ToArray() }; + } + else + { + result[name].Add(types.ToArray()); + } + } + + data = data[1..]; + } + + return result; + } + private static Type ParseType(ref ReadOnlySpan data, AssemblyLoadContext alc) { int nameEnd = data.IndexOfAny("[],;"); @@ -229,7 +272,7 @@ public class DisasmoLoader return null; } - private static IEnumerable MakeGenericInstances(Type type, Dictionary> arguments) + private static IEnumerable MakeGenericInstances(Type type, Dictionary> arguments, Dictionary> genericArgs) { if (!type.IsGenericType) { @@ -239,7 +282,7 @@ public class DisasmoLoader var hasVariant = false; type = type.GetGenericTypeDefinition(); - var variants = MakeGenericVariants(type.GetGenericArguments(), arguments); + var variants = MakeGenericVariants(type, arguments, genericArgs); foreach (var variant in variants) { @@ -266,8 +309,7 @@ public class DisasmoLoader } } - - private static IEnumerable MakeGenericInstances(MethodBase method, Dictionary> arguments) + private static IEnumerable MakeGenericInstances(MethodBase method, Dictionary> arguments, Dictionary> genericArgs) { if (method is not MethodInfo info || !method.IsGenericMethod) { @@ -277,7 +319,7 @@ public class DisasmoLoader var hasVariant = false; info = info.GetGenericMethodDefinition(); - var variants = MakeGenericVariants(info.GetGenericArguments(), arguments); + var variants = MakeGenericVariants(info, arguments, genericArgs); foreach (var variant in variants) { @@ -304,6 +346,18 @@ public class DisasmoLoader } } + private static List MakeGenericVariants(Type type, Dictionary> arguments, Dictionary> genericArgs) + { + if (genericArgs.TryGetValue(type.FullName, out var result)) return result; + return MakeGenericVariants(type.GetGenericArguments(), arguments); + } + + private static List MakeGenericVariants(MethodInfo info, Dictionary> arguments, Dictionary> genericArgs) + { + if (genericArgs.TryGetValue(info.Name, out var result)) return result; + return MakeGenericVariants(info.GetGenericArguments(), arguments); + } + private static List MakeGenericVariants(Type[] parameters, Dictionary> arguments) { var results = new List() { new Type[parameters.Length] }; diff --git a/Disasmo/Utils/SymbolUtils.cs b/Disasmo/Utils/SymbolUtils.cs index ede9bd6..f090cfd 100644 --- a/Disasmo/Utils/SymbolUtils.cs +++ b/Disasmo/Utils/SymbolUtils.cs @@ -1,4 +1,6 @@ -using Microsoft.CodeAnalysis; +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; namespace Disasmo.Utils; @@ -9,6 +11,7 @@ public static DisasmoSymbolInfo FromSymbol(ISymbol symbol) string target; string hostType; string methodName; + string genericArguments; if (symbol is IMethodSymbol ms) { @@ -31,12 +34,21 @@ public static DisasmoSymbolInfo FromSymbol(ISymbol symbol) hostType = symbol.ContainingType.MetadataName(); methodName = symbol.Name; } + + genericArguments = GenericsForSymbol(symbol); } else if (symbol is IPropertySymbol prop) { target = "*" + symbol.ContainingType.MetadataName + ":get_" + symbol.Name + " " + "*" + symbol.ContainingType.MetadataName + ":set_" + symbol.Name; hostType = symbol.ContainingType.MetadataName(); methodName = symbol.Name; + genericArguments = (prop.GetMethod, prop.SetMethod) switch + { + (not null, not null) => GenericsForSymbol(prop.GetMethod, false, false) + GenericsForSymbol(prop.SetMethod), + (null, not null) => GenericsForSymbol(prop.GetMethod), + (not null, null) => GenericsForSymbol(prop.SetMethod), + _ => "" + }; } else { @@ -44,8 +56,9 @@ public static DisasmoSymbolInfo FromSymbol(ISymbol symbol) target = symbol.Name + ":*"; hostType = symbol.MetadataName; methodName = "*"; + genericArguments = GenericsForSymbol(symbol); } - return new DisasmoSymbolInfo(target, hostType, methodName); + return new DisasmoSymbolInfo(target, hostType, methodName, genericArguments); } private static string MetadataName(this INamedTypeSymbol type) @@ -61,4 +74,58 @@ private static string MetadataName(this INamedTypeSymbol type) } return name + type.MetadataName; } + + private static string GenericsForSymbol(ISymbol symbol, bool includeNested = true, bool includeContaining = true) + { + const string MARKER = "Disasmo-Generic:"; + + if (symbol is null) + { + return ""; + } + + string result = ""; + + foreach (var syntaxReference in symbol.DeclaringSyntaxReferences) + { + foreach (var trivia in syntaxReference.GetSyntax().GetLeadingTrivia()) + { + if (!trivia.IsKind(SyntaxKind.SingleLineCommentTrivia) && !trivia.IsKind(SyntaxKind.MultiLineCommentTrivia)) continue; + + var str = trivia.ToFullString().AsSpan(); + while (!str.IsEmpty) + { + var idxStart = str.IndexOf(MARKER.AsSpan(), StringComparison.OrdinalIgnoreCase); + if (idxStart < 0) break; + var len = str.Slice(idxStart + MARKER.Length).IndexOf('\n'); + if (len < 0) len = str.Length - idxStart - MARKER.Length; + result = result + SymbolName(symbol) + "=" + str.Slice(idxStart + MARKER.Length, len).Trim().ToString() + ";"; + str = str.Slice(idxStart + MARKER.Length + len); + } + } + } + + if (includeContaining) + { + result += GenericsForSymbol(symbol.ContainingType, false); + } + + if (includeNested && symbol is ITypeSymbol typeSymbol) + { + foreach (var member in typeSymbol.GetMembers()) + { + if (member is IMethodSymbol) + { + result += GenericsForSymbol(member, false, false); + } + } + } + + return result; + } + + private static string SymbolName(ISymbol symbol) + { + return symbol.MetadataName; + } } \ No newline at end of file diff --git a/src/Vsix/ViewModels/MainViewModel.cs b/src/Vsix/ViewModels/MainViewModel.cs index 579c34d..be8f468 100644 --- a/src/Vsix/ViewModels/MainViewModel.cs +++ b/src/Vsix/ViewModels/MainViewModel.cs @@ -225,7 +225,7 @@ public async Task RunFinalExe(DisasmoSymbolInfo symbolInfo) envVars["DOTNET_JitDumpFgFile"] = currentFgFile; } - string command = $"\"{LoaderAppManager.DisasmoLoaderName}.dll\" \"{fileName}.dll\" \"{symbolInfo.ClassName}\" \"{symbolInfo.MethodName}\" {SettingsVm.UseUnloadableContext} \"{string.Concat(SettingsVm.GenericArguments)}\""; + string command = $"\"{LoaderAppManager.DisasmoLoaderName}.dll\" \"{fileName}.dll\" \"{symbolInfo.ClassName}\" \"{symbolInfo.MethodName}\" {SettingsVm.UseUnloadableContext} \"{string.Concat(SettingsVm.GenericArguments)}\" \"{symbolInfo.GenericArguments}\""; if (SettingsVm.RunAppMode) { command = $"\"{fileName}.dll\""; From 1daedc4e81ee3512b59ad79af4f80c50fb99f0bd Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Mon, 20 Feb 2023 17:26:34 +0100 Subject: [PATCH 3/7] Ignore spaces inside the generic argument comment --- Disasmo/Utils/SymbolUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Disasmo/Utils/SymbolUtils.cs b/Disasmo/Utils/SymbolUtils.cs index f090cfd..a3f2395 100644 --- a/Disasmo/Utils/SymbolUtils.cs +++ b/Disasmo/Utils/SymbolUtils.cs @@ -99,7 +99,7 @@ private static string GenericsForSymbol(ISymbol symbol, bool includeNested = tru if (idxStart < 0) break; var len = str.Slice(idxStart + MARKER.Length).IndexOf('\n'); if (len < 0) len = str.Length - idxStart - MARKER.Length; - result = result + SymbolName(symbol) + "=" + str.Slice(idxStart + MARKER.Length, len).Trim().ToString() + ";"; + result = result + SymbolName(symbol) + "=" + str.Slice(idxStart + MARKER.Length, len).Trim().ToString().Replace(" ", "") + ";"; str = str.Slice(idxStart + MARKER.Length + len); } } From 059e88db5f96fa4dbbba9d3edac2ddd39365d830 Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Mon, 20 Feb 2023 18:21:50 +0100 Subject: [PATCH 4/7] Support short names for builtin types and nullable value types --- Disasmo/Resources/DisasmoLoader4.cs_template | 37 ++++++++++++++++++-- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/Disasmo/Resources/DisasmoLoader4.cs_template b/Disasmo/Resources/DisasmoLoader4.cs_template index 8429b30..2df1234 100644 --- a/Disasmo/Resources/DisasmoLoader4.cs_template +++ b/Disasmo/Resources/DisasmoLoader4.cs_template @@ -14,7 +14,7 @@ using System.Runtime.Loader; public class DisasmoLoader { // Disasmo-Generic: System.Int32,System.String - // Disasmo-Generic: System.Byte,System.Single + // Disasmo-Generic: sbyte,float? static void Foo() { @@ -249,14 +249,45 @@ public class DisasmoLoader return type?.MakeGenericType(args.ToArray()); } + private static Type MakeNullableType(Type type, bool nullable, AssemblyLoadContext alc) + { + if (!type.IsValueType || !nullable) return type; + return FindType("System.Nullable`1", alc).MakeGenericType(type); + } + private static Type FindType(string name, AssemblyLoadContext alc) { + bool nullable = name.EndsWith("?"); + if (nullable) name = name.TrimEnd('?'); + + name = name switch + { + "bool" => "System.Boolean", + "byte" => "System.Byte", + "sbyte" => "System.SByte", + "char" => "System.Char", + "decimal" => "System.Decimal", + "double" => "System.Double", + "float" => "System.Single", + "int" => "System.Int32", + "uint" => "System.UInt32", + "nint" => "System.IntPtr", + "nuint" => "System.UIntPtr", + "long" => "System.Int64", + "ulong" => "System.UInt64", + "short" => "System.Int16", + "ushort" => "System.UInt16", + "object" => "System.Object", + "string" => "System.String", + _ => name + }; + foreach (Assembly assembly in alc.Assemblies) { Type type = assembly.GetType(name); if (type != null) { - return type; + return MakeNullableType(type, nullable, alc); } } @@ -265,7 +296,7 @@ public class DisasmoLoader Type type = assembly.GetType(name); if (type != null) { - return type; + return MakeNullableType(type, nullable, alc); } } From d28cab82ef4c2d4d1fcc31d7dc5b6d6744360743 Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Tue, 21 Feb 2023 00:47:37 +0100 Subject: [PATCH 5/7] Add tutorial message --- Disasmo/Resources/DisasmoLoader4.cs_template | 40 +++++++++++++++++++- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/Disasmo/Resources/DisasmoLoader4.cs_template b/Disasmo/Resources/DisasmoLoader4.cs_template index 2df1234..be8f3b2 100644 --- a/Disasmo/Resources/DisasmoLoader4.cs_template +++ b/Disasmo/Resources/DisasmoLoader4.cs_template @@ -20,6 +20,8 @@ public class DisasmoLoader } + private static bool _shownGenericsMessage; + public static void Main(string[] args) { PrecompileAllMethodsInType(args); @@ -317,6 +319,12 @@ public class DisasmoLoader foreach (var variant in variants) { + if (variant.Length != type.GetGenericArguments().Length) + { + Console.WriteLine($"Type {type} has {type.GetGenericArguments().Length} generic parameters, but {variant.Length} arguments were provided."); + continue; + } + Type instance = null; try { @@ -325,6 +333,8 @@ public class DisasmoLoader catch (ArgumentException ex) { // probably fails some constraint + Console.WriteLine($"Failed to apply arguments {string.Join(", ", variant.Select(t => t.Name))} to type {type}: {ex.Message}"); + continue; } if (instance != null) @@ -336,7 +346,8 @@ public class DisasmoLoader if (!hasVariant) { - Console.WriteLine("Unable to create instance for " + type + ". Check that the provided arguments fullfill the constraints."); + Console.WriteLine("Unable to create instance for type " + type + "."); + GenericsInfoMessage(); } } @@ -354,6 +365,12 @@ public class DisasmoLoader foreach (var variant in variants) { + if (variant.Length != info.GetGenericArguments().Length) + { + Console.WriteLine($"Method {info} has {info.GetGenericArguments().Length} generic parameters, but {variant.Length} arguments were provided."); + continue; + } + MethodInfo instance = null; try { @@ -362,6 +379,8 @@ public class DisasmoLoader catch (ArgumentException ex) { // probably fails some constraint + Console.WriteLine($"Failed to apply arguments {string.Join(", ", variant.Select(t => t.Name))} to type {info}: {ex.Message}"); + continue; } if (instance != null) @@ -373,10 +392,27 @@ public class DisasmoLoader if (!hasVariant) { - Console.WriteLine("Unable to create instance for " + method + ". Check that the provided arguments fullfill the constraints."); + Console.WriteLine("Unable to create instance for method " + method + "."); + GenericsInfoMessage(); } } + private static void GenericsInfoMessage() + { + if (_shownGenericsMessage) return; + _shownGenericsMessage = true; + + Console.WriteLine("Generic parameters for types and methods can be specified by prefixing them with a comment like this:"); + Console.WriteLine("// Disasmo-Generic: System.Int32,System.String"); + Console.WriteLine("// Disasmo-Generic: System.Byte,System.Single"); + Console.WriteLine("void Foo() { }"); + Console.WriteLine("Types can either be specified by their C# alias or FullName. Generic types take their arguments in brackets:"); + Console.WriteLine("// Disasmo-Generic: System.ValueTuple`2[System.Int32,string]"); + Console.WriteLine("A ? suffix is short for a nullable value type. Arrays are specified by appending the number of dimensions in bracket or empty brackets for SZArrays:"); + Console.WriteLine("// Disasmo-Generic: System.Int32?,int[],byte[4]"); + + } + private static List MakeGenericVariants(Type type, Dictionary> arguments, Dictionary> genericArgs) { if (genericArgs.TryGetValue(type.FullName, out var result)) return result; From 1111cd3dc52b30181e41f825f587bd3bb27c849a Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Tue, 21 Feb 2023 13:14:52 +0100 Subject: [PATCH 6/7] fix nested types --- Disasmo/Utils/SymbolUtils.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Disasmo/Utils/SymbolUtils.cs b/Disasmo/Utils/SymbolUtils.cs index a3f2395..50d0baa 100644 --- a/Disasmo/Utils/SymbolUtils.cs +++ b/Disasmo/Utils/SymbolUtils.cs @@ -126,6 +126,7 @@ private static string GenericsForSymbol(ISymbol symbol, bool includeNested = tru private static string SymbolName(ISymbol symbol) { + if (symbol is INamedTypeSymbol namedTypeSymbol) return namedTypeSymbol.MetadataName(); return symbol.MetadataName; } } \ No newline at end of file From ea95cdc1c19d8e802290058753565ddff15849f1 Mon Sep 17 00:00:00 2001 From: RedworkDE <10944644+RedworkDE@users.noreply.github.com> Date: Tue, 21 Feb 2023 14:17:22 +0100 Subject: [PATCH 7/7] Remove the old generics configuration UI --- Disasmo/Resources/DisasmoLoader4.cs_template | 41 +------- src/Vsix/Properties/Settings.Designer.cs | 12 --- src/Vsix/Properties/Settings.settings | 3 - src/Vsix/ViewModels/MainViewModel.cs | 2 +- src/Vsix/ViewModels/SettingsViewModel.cs | 103 ------------------- src/Vsix/Views/DisasmWindowControl.xaml | 15 --- src/Vsix/Views/DisasmWindowControl.xaml.cs | 53 ---------- src/Vsix/app.config | 3 - 8 files changed, 3 insertions(+), 229 deletions(-) diff --git a/Disasmo/Resources/DisasmoLoader4.cs_template b/Disasmo/Resources/DisasmoLoader4.cs_template index be8f3b2..01b8be0 100644 --- a/Disasmo/Resources/DisasmoLoader4.cs_template +++ b/Disasmo/Resources/DisasmoLoader4.cs_template @@ -13,13 +13,6 @@ using System.Runtime.Loader; public class DisasmoLoader { - // Disasmo-Generic: System.Int32,System.String - // Disasmo-Generic: sbyte,float? - static void Foo() - { - - } - private static bool _shownGenericsMessage; public static void Main(string[] args) @@ -34,16 +27,14 @@ public class DisasmoLoader string methodName = args[2]; string unloadable = args[3]; - Debugger.Launch(); - // Another ugly workaround for mangled names till I figure out a proper solution if (typeName.Contains('_') && typeName.Contains('.')) typeName = typeName.Substring(typeName.LastIndexOf('.') + 1); var alc = new AssemblyLoadContext("DisasmoALC", unloadable == "True"); Assembly asm = alc.LoadFromAssemblyPath(Path.Combine(Environment.CurrentDirectory, assemblyName)); - var generics = ParseGenerics(args[4], alc); - var genericArgs = ParseGenericsArgs(args[5], alc); + var generics = new Dictionary>(); + var genericArgs = ParseGenericsArgs(args[4], alc); Type fastType = asm.GetType(typeName); if (fastType != null) { @@ -142,34 +133,6 @@ public class DisasmoLoader } } - private static Dictionary> ParseGenerics(ReadOnlySpan data, AssemblyLoadContext alc) - { - var result = new Dictionary>(); - while (!data.IsEmpty) - { - int eq = data.IndexOf('='); - string name = data[..eq].ToString(); - data = data[(eq + 1)..]; - Type type = ParseType(ref data, alc); - - if (type != null) - { - if (!result.ContainsKey(name)) - { - result[name] = new List() { type }; - } - else - { - result[name].Add(type); - } - } - - data = data[1..]; - } - - return result; - } - private static Dictionary> ParseGenericsArgs(ReadOnlySpan data, AssemblyLoadContext alc) { var result = new Dictionary>(); diff --git a/src/Vsix/Properties/Settings.Designer.cs b/src/Vsix/Properties/Settings.Designer.cs index accc9ad..a4f8f16 100644 --- a/src/Vsix/Properties/Settings.Designer.cs +++ b/src/Vsix/Properties/Settings.Designer.cs @@ -252,17 +252,5 @@ public bool DontGuessTFM { this["DontGuessTFM"] = value; } } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("*=System.Int32;*=System.Tuple`2[System.Object,System.Int32];")] - public string GenericArguments { - get { - return ((string)(this["GenericArguments"])); - } - set { - this["GenericArguments"] = value; - } - } } } diff --git a/src/Vsix/Properties/Settings.settings b/src/Vsix/Properties/Settings.settings index b99cef6..e523cdd 100644 --- a/src/Vsix/Properties/Settings.settings +++ b/src/Vsix/Properties/Settings.settings @@ -59,8 +59,5 @@ False - - *=System.Int32;*=System.Tuple`2[System.Object,System.Int32]; - \ No newline at end of file diff --git a/src/Vsix/ViewModels/MainViewModel.cs b/src/Vsix/ViewModels/MainViewModel.cs index be8f468..c24c5c4 100644 --- a/src/Vsix/ViewModels/MainViewModel.cs +++ b/src/Vsix/ViewModels/MainViewModel.cs @@ -225,7 +225,7 @@ public async Task RunFinalExe(DisasmoSymbolInfo symbolInfo) envVars["DOTNET_JitDumpFgFile"] = currentFgFile; } - string command = $"\"{LoaderAppManager.DisasmoLoaderName}.dll\" \"{fileName}.dll\" \"{symbolInfo.ClassName}\" \"{symbolInfo.MethodName}\" {SettingsVm.UseUnloadableContext} \"{string.Concat(SettingsVm.GenericArguments)}\" \"{symbolInfo.GenericArguments}\""; + string command = $"\"{LoaderAppManager.DisasmoLoaderName}.dll\" \"{fileName}.dll\" \"{symbolInfo.ClassName}\" \"{symbolInfo.MethodName}\" {SettingsVm.UseUnloadableContext} \"{symbolInfo.GenericArguments}\""; if (SettingsVm.RunAppMode) { command = $"\"{fileName}.dll\""; diff --git a/src/Vsix/ViewModels/SettingsViewModel.cs b/src/Vsix/ViewModels/SettingsViewModel.cs index 1a9d585..ec6f6fd 100644 --- a/src/Vsix/ViewModels/SettingsViewModel.cs +++ b/src/Vsix/ViewModels/SettingsViewModel.cs @@ -1,10 +1,8 @@ using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using System.Windows.Forms; using System.Windows.Input; using Disasmo.Properties; @@ -40,7 +38,6 @@ public class SettingsViewModel : ViewModelBase private string _selectedCustomJit; private string _graphvisDot; private bool _fgEnable; - private ObservableCollection _genericArguments; public SettingsViewModel() { @@ -64,9 +61,6 @@ public SettingsViewModel() UseUnloadableContext = Settings.Default.UseUnloadableContext; DisableLightBulb = Settings.Default.DisableLightBulb; DontGuessTFM = Settings.Default.DontGuessTFM; - GenericArguments = new ObservableCollection( - Settings.Default.GenericArguments.Split(new[] { ';' }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenericArgument(s)) - ); CheckUpdates(); } @@ -414,61 +408,6 @@ public string IlcArgs } } - public ObservableCollection GenericArguments - { - get => _genericArguments; - set - { - if (_genericArguments != null) - { - foreach (var argument in _genericArguments) - argument.PropertyChanged -= GenericArgumentsPropertyChanged; - _genericArguments.CollectionChanged -= GenericArgumentsCollectionChanged; - } - Set(ref _genericArguments, value); - if (_genericArguments != null) - { - _genericArguments.CollectionChanged += GenericArgumentsCollectionChanged; - foreach (var argument in _genericArguments) - argument.PropertyChanged += GenericArgumentsPropertyChanged; - } - } - } - - private void GenericArgumentsCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) - { - if (e.OldItems != null) - { - foreach (var oldItem in e.OldItems) - { - if (oldItem is INotifyPropertyChanged changed) - { - changed.PropertyChanged -= GenericArgumentsPropertyChanged; - } - } - } - - if (e.NewItems != null) - { - foreach (var newItem in e.NewItems) - { - if (newItem is INotifyPropertyChanged changed) - { - changed.PropertyChanged += GenericArgumentsPropertyChanged; - } - } - } - - Settings.Default.GenericArguments = string.Concat(_genericArguments); - Settings.Default.Save(); - } - - private void GenericArgumentsPropertyChanged(object sender, PropertyChangedEventArgs e) - { - Settings.Default.GenericArguments = string.Concat(_genericArguments); - Settings.Default.Save(); - } - public bool UpdateIsAvailable { get => _updateIsAvailable; @@ -526,46 +465,4 @@ private static string FindJitDirectory(string basePath) return null; } } - - public class GenericArgument : INotifyPropertyChanged - { - private string _argument; - private string _type; - - public event PropertyChangedEventHandler PropertyChanged; - - public GenericArgument(string value) - { - var parts = value.Split('='); - _argument = parts[0]; - _type = parts[1]; - } - - public string Argument - { - get => _argument; - set => SetField(ref _argument, value); - } - - public string Type - { - get => _type; - set => SetField(ref _type, value); - } - - protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - protected bool SetField(ref T field, T value, [CallerMemberName] string propertyName = null) - { - if (EqualityComparer.Default.Equals(field, value)) return false; - field = value; - OnPropertyChanged(propertyName); - return true; - } - - public override string ToString() => $"{_argument}={_type};"; - } } diff --git a/src/Vsix/Views/DisasmWindowControl.xaml b/src/Vsix/Views/DisasmWindowControl.xaml index dbe7ffc..e177baa 100644 --- a/src/Vsix/Views/DisasmWindowControl.xaml +++ b/src/Vsix/Views/DisasmWindowControl.xaml @@ -278,21 +278,6 @@ TextWrapping="Wrap" Text="{Binding IlcArgs, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> - - - - - - - - - - - - - - - diff --git a/src/Vsix/Views/DisasmWindowControl.xaml.cs b/src/Vsix/Views/DisasmWindowControl.xaml.cs index 2c135a9..a730ad4 100644 --- a/src/Vsix/Views/DisasmWindowControl.xaml.cs +++ b/src/Vsix/Views/DisasmWindowControl.xaml.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; -using System.Linq; using System.Runtime.InteropServices; using System.Windows.Data; using System.Windows.Navigation; @@ -116,57 +114,6 @@ private async void OnOpenFolderWithFlowGraphs(object sender, RequestNavigateEven } } } - - private void OnAddGenericArgument(object sender, RoutedEventArgs e) - { - MainViewModel.SettingsVm.GenericArguments.Add(new GenericArgument("*=System.Int32")); - } - - private void OnRemoveGenericArgument(object sender, RoutedEventArgs e) - { - foreach (var item in GenericArgumentGrid.SelectedItems.Cast().ToArray()) - { - MainViewModel.SettingsVm.GenericArguments.Remove(item); - } - } - - private void OnMoveUpGenericArgument(object sender, RoutedEventArgs e) - { - foreach (int index in GenericArgumentGrid.SelectedItems.Cast().Select(MainViewModel.SettingsVm.GenericArguments.IndexOf).OrderBy(v => v)) - { - if (index > 0) - { - MainViewModel.SettingsVm.GenericArguments.Move(index, index - 1); - } - } - } - - private void OnMoveDownGenericArgument(object sender, RoutedEventArgs e) - { - foreach (int index in GenericArgumentGrid.SelectedItems.Cast().Select(MainViewModel.SettingsVm.GenericArguments.IndexOf).OrderByDescending(v => v)) - { - if (index >= 0 && index < MainViewModel.SettingsVm.GenericArguments.Count - 1) - { - MainViewModel.SettingsVm.GenericArguments.Move(index, index + 1); - } - } - } - - private void OnCopyGenericArgument(object sender, RoutedEventArgs e) - { - Clipboard.SetText(string.Join("\n", MainViewModel.SettingsVm.GenericArguments)); - } - - private void OnPasteGenericArgument(object sender, RoutedEventArgs e) - { - string text = Clipboard.GetText(); - if (!string.IsNullOrWhiteSpace(text)) - { - MainViewModel.SettingsVm.GenericArguments = new ObservableCollection( - text.Split(new[] { ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries).Select(s => new GenericArgument(s)) - ); - } - } } [Guid("97cd0cd6-1d77-4848-8b6e-dc82cdccc6d7")] diff --git a/src/Vsix/app.config b/src/Vsix/app.config index 84bf142..9fc6d6e 100644 --- a/src/Vsix/app.config +++ b/src/Vsix/app.config @@ -64,9 +64,6 @@ False - - *=System.Int32;*=System.Tuple`2[System.Object,System.Int32]; -