void Initialize() { foreach (string assembly in _coreAssembliesPaths) { AssemblyDefinition coreAssembly = CompatibilityAnalyzer.LoadAssembly(assembly); FillMapOfXmlNsToCsNs(coreAssembly); //now we go through all the types of this assembly and remember all their public methods: foreach (TypeDefinition type in AnalyzeHelper.GetAllTypesDefinedInAssembly(coreAssembly)) { bool hasPublicMethods = false; string baseTypeName = type.BaseType != null ? type.BaseType.Name : null; string baseTypeNamespace = type.BaseType != null ? type.BaseType.Namespace : null; foreach (MethodDefinition method in AnalyzeHelper.GetAllMethodsDefinedInType(type)) { if (method.IsPublic || method.IsFamily) //note: we only add the public methods because those are the only ones the user can use. { hasPublicMethods = true; Add(type.Namespace, type.Name, baseTypeNamespace, baseTypeName, method.Name); } } if (!hasPublicMethods) { Add(type.Namespace, type.Name, baseTypeNamespace, baseTypeName, null); } } } }
/// <summary> /// Main method of the generator. Look in every referenced assemblies to find the unsupported methods and types /// </summary> public void Run() { Init(); foreach (string assembly in _referencedAssembliesToLookInto) { Dictionary <string, HashSet <string> > unsupportedMethods; _unsupportedMethodsInfo.TryGetValue(assembly, out unsupportedMethods); if (unsupportedMethods != null) { AssemblyDefinition assemblyToLookInto = CompatibilityAnalyzer.LoadAssembly(Path.Combine(_referencedAssembliesFolderPath, assembly + ".dll"), Configuration.mscorlibFolderPath); _assemblyAnalyzer.Set(assemblyToLookInto, unsupportedMethods); _assemblyAnalyzer.Run(); } } GenerateAddtionalUnsupportedTypes(); }
private void Button_Click(object sender, RoutedEventArgs e) { string coreAssemblyPath = CoreAssemblyPathTextBox.Text; string supportedElementsPath = SupportedElementsPathTextBox.Text; string mscorlibFolderPath = MscorlibFolderPathTextBox.Text; string sdkFolderPath = SDKFolderPathTextBox.Text; string otherFoldersPath = OtherFoldersPathTextBox.Text; HashSet <string> xamlFilesToIgnore = null; try { xamlFilesToIgnore = new HashSet <string>(XamlFilesToIgnoreTextBox.Text.Split(',').Select(s => s.Trim().ToLower())); } catch (Exception ex) { MessageBox.Show("Invalid list of XAML files to exclude." + Environment.NewLine + Environment.NewLine + ex.ToString()); return; } if (string.IsNullOrEmpty(coreAssemblyPath) || string.IsNullOrEmpty(supportedElementsPath) || string.IsNullOrEmpty(mscorlibFolderPath)) { MessageBox.Show("Please fill all the fields before continuing."); return; } if (!File.Exists(supportedElementsPath)) { MessageBox.Show("File not found: " + supportedElementsPath); return; } var featuresAndEstimationsFileProcessor = new FeaturesAndEstimationsFileProcessor(FeaturesAndEstimationsPathTextBox.Text); // Save the content of the text boxes for reuse when relaunching the application: SaveContentOfTextBoxes(); #if ASK_USER_TO_CHOOSE_ASSEMBLIES_TO_ANALYZE // Create OpenFileDialog Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog(); // Set filter for file extension and default file extension dlg.DefaultExt = ".dll"; dlg.Filter = "Assemblies and executables (*.dll,*.exe)|*.dll;*.exe"; dlg.Multiselect = true; // Display OpenFileDialog by calling ShowDialog method Nullable <bool> result = dlg.ShowDialog(); if (result == true) { string[] fileNames = dlg.FileNames; #else string[] fileNames = Configuration.AssembliesToAnalyze; if (true) { #endif if (_coreSupportedMethods == null) { _coreSupportedMethods = new CoreSupportedMethodsContainer(coreAssemblyPath); } var logger = new LoggerThatAggregatesAllErrors(); var watch = Stopwatch.StartNew(); var listOfUnsupportedMethods = new List <UnsupportedMethodInfo>(); // Get the path of the folder where the first assembly is located, and add that path to the list of places where to look in for resolving referenced assemblies: string firstFileToAnalyze = fileNames.FirstOrDefault(); string additionalFolderWhereToResolveAssemblies = null; if (firstFileToAnalyze != null) { additionalFolderWhereToResolveAssemblies = System.IO.Path.GetDirectoryName(firstFileToAnalyze); } // Do the analysis: try { foreach (var filename in fileNames) { CompatibilityAnalyzer.Analyze( filename, logger, listOfUnsupportedMethods, _coreSupportedMethods, fileNames, Configuration.UrlNamespacesThatBelongToUserCode, Configuration.AttributesToIgnoreInXamlBecauseTheyAreFromBaseClasses, xamlFilesToIgnore, supportedElementsPath, mscorlibFolderPath, sdkFolderPath, otherFoldersPath, skipTypesWhereNoMethodIsActuallyCalled: true, additionalFolderWhereToResolveAssemblies: additionalFolderWhereToResolveAssemblies);; } } catch (Exception ex) { MessageBox.Show(ex.ToString()); } // Replace all telerik assembly names with "http://schemas.telerik.com/2008/xaml/presentation" so as to avoid differences between namespaces from .xaml files and namespaces from .cs files: foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods) { if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("Telerik.")) { unsupportedMethodInfo.MethodAssemblyName = "http://schemas.telerik.com/2008/xaml/presentation"; } } // Replace all Expression Blend-related namespaces with "http://schemas.microsoft.com/expression/2010/..." so as to avoid differences between namespaces from .xaml files and namespaces from .cs files: foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods) { if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("Microsoft.Expression.") || unsupportedMethodInfo.MethodAssemblyName.StartsWith("System.Windows.Interactivity") || unsupportedMethodInfo.MethodAssemblyName == "http://schemas.microsoft.com/expression/2010/drawing" || unsupportedMethodInfo.MethodAssemblyName == "http://schemas.microsoft.com/expression/2010/interactivity" || unsupportedMethodInfo.MethodAssemblyName == "http://schemas.microsoft.com/expression/2010/interactions" ) { unsupportedMethodInfo.MethodAssemblyName = "http://schemas.microsoft.com/expression/2010/..."; } } // Aggregate all MEF-related namespaces: foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods) { if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("System.ComponentModel.Composition.")) { unsupportedMethodInfo.MethodAssemblyName = "System.ComponentModel.Composition"; } } // Aggregate all DomainServices-related namespaces: foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods) { if (unsupportedMethodInfo.MethodAssemblyName.StartsWith("System.ServiceModel.DomainServices.Client.")) { unsupportedMethodInfo.MethodAssemblyName = "System.ServiceModel.DomainServices.Client"; } } // Replace all the native MS UI-related assembly names with "http://schemas.microsoft.com/winfx/2006/xaml/presentation" so as to avoid differences between namespaces from .xaml files and namespaces from .cs files: foreach (UnsupportedMethodInfo unsupportedMethodInfo in listOfUnsupportedMethods) { if (unsupportedMethodInfo.MethodAssemblyName == "System.Windows" || unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls" || unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls.Data" || unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Browser" || unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls.Navigation" || unsupportedMethodInfo.MethodAssemblyName == "System.Windows.Controls.Toolkit") { unsupportedMethodInfo.MethodAssemblyName = "http://schemas.microsoft.com/winfx/2006/xaml/presentation"; } } // Process the list of unsupported methods: SortedDictionary <Tuple <string, string>, HashSet <string> > unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed; ProcessUnsupportedMethods(listOfUnsupportedMethods, out unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed, rectifyMethodName: true); // Remove some stuff: foreach (Tuple <string, string> additionalEntryToRemove in Configuration.AdditionalEntriesToRemoveBeforeAggregation) { unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(additionalEntryToRemove); } // For classes that are not defined at all (ie. missing constructor), aggregate all the methods into the same line: List <Tuple <string, string> > entriesToRemoveFromDictionary = new List <Tuple <string, string> >(); // This is here because we cannot remove the keys while iterating in the dictionary, so we do it afterwards. List <KeyValuePair <Tuple <string, string>, HashSet <string> > > entriesToAddToDictionary = new List <KeyValuePair <Tuple <string, string>, HashSet <string> > >(); // This is here because we cannot add entries while iterating in the dictionary, so we do it afterwards. HashSet <Tuple <string, string> > classesForWhichTheMethodsHaveAlreadyBeenAggregated = new HashSet <Tuple <string, string> >(); foreach (KeyValuePair <Tuple <string, string>, HashSet <string> > pair in unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed) { string theFullMethodName = pair.Key.Item1; string theAssemblyName = pair.Key.Item2; HashSet <string> whereItIsUsed = pair.Value; if (!Configuration.EntriesToNotAggregateWithOtherEntries.Contains(theFullMethodName)) { #if ALWAYS_AGGREGATE_METHODS if (theFullMethodName.Contains(".")) #else if (theFullMethodName.Contains("..ctor")) #endif { #if ALWAYS_AGGREGATE_METHODS string className = theFullMethodName.Substring(0, theFullMethodName.IndexOf(".")); #else string className = theFullMethodName.Substring(0, theFullMethodName.IndexOf("..ctor")); #endif if (!classesForWhichTheMethodsHaveAlreadyBeenAggregated.Contains(new Tuple <string, string>(className, theAssemblyName))) { classesForWhichTheMethodsHaveAlreadyBeenAggregated.Add(new Tuple <string, string>(className, theAssemblyName)); List <string> methodNames = new List <string>(); // Find all other methods that concern the same class: foreach (KeyValuePair <Tuple <string, string>, HashSet <string> > pair2 in unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed) { //todo-perf: optimize performance by creating a dictionary beforehand instead of nesting two iterations! string theFullMethodName2 = pair2.Key.Item1; string theAssemblyName2 = pair2.Key.Item2; HashSet <string> whereItIsUsed2 = pair.Value; if (!Configuration.EntriesToNotAggregateWithOtherEntries.Contains(theFullMethodName2)) { if (theFullMethodName2.Contains('.') && !theFullMethodName2.Contains("..ctor") ) { string className2 = theFullMethodName2.Substring(0, theFullMethodName2.IndexOf('.')); if (className2 == className && theAssemblyName2 == theAssemblyName) { string methodName2 = theFullMethodName2.Substring(theFullMethodName2.IndexOf('.') + 1); methodNames.Add(methodName2); HashSetHelpers.AddItemsFromOneHashSetToAnother(whereItIsUsed2, whereItIsUsed); entriesToRemoveFromDictionary.Add(pair2.Key); } } } } // Aggregate them: string newMethodName; if (methodNames.Count > 0) { if (theFullMethodName.Contains("..ctor")) { newMethodName = className + " (members used: " + string.Join(", ", methodNames) + ")"; } else { newMethodName = className + "." + string.Join(", .", methodNames); } } else { newMethodName = className; } entriesToRemoveFromDictionary.Add(pair.Key); entriesToAddToDictionary.Add(new KeyValuePair <Tuple <string, string>, HashSet <string> >( new Tuple <string, string>(newMethodName, theAssemblyName), whereItIsUsed)); } } } } // Perform the actual removal and addition (note: this cannot be done before because it is not possible to remove or add an item from a collection while iterating it with "foreach"); foreach (var key in entriesToRemoveFromDictionary) { unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(key); } foreach (var entry in entriesToAddToDictionary) { unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Add(entry.Key, entry.Value); } //------------------------------------- // Merge classes that are related to each other: //------------------------------------- MergingRelatedClasses.MergeRelatedClasses(Configuration.ClassesRelatedToEachOther, unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed); //------------------------------------- // Remove elements with an empty assembly name because they are due to "clr-namespace:..." without any assembly being specified, which means that the assembly is the user assembly itself, so we should remove the entry: //------------------------------------- entriesToRemoveFromDictionary = new List <Tuple <string, string> >(); foreach (var entry in unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed) { if (entry.Key.Item2 == "") { entriesToRemoveFromDictionary.Add(entry.Key); } } foreach (var key in entriesToRemoveFromDictionary) { unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(key); } //------------------------------------- // Remove some additional stuff: //------------------------------------- foreach (Tuple <string, string> additionalEntryToRemove in Configuration.AdditionalEntriesToRemoveAfterAggregation) { unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed.Remove(additionalEntryToRemove); } //------------------------------------- // Save the result: //------------------------------------- // Save as Excel document: ExcelGenerator.Generate( OutputExcelFilePathTextBox.Text, featuresAndEstimationsFileProcessor, unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed, fileNames.Select(fileName => System.IO.Path.GetFileName(fileName))); #if SAVE_CSV_DOCUMENT // Save as CSV document: CsvGenerator.Generate(OutputCsvFilePathTextBox.Text, unsupportedMethodsAndTheirAssemblyToLocationsWhereTheyAreUsed); #endif var elapsedMs = watch.ElapsedMilliseconds; MessageBox.Show(string.Format("Operation completed in {0} seconds.", Math.Floor(elapsedMs / 1000d).ToString())); //------------------------------------- // Open the result: //------------------------------------- Process.Start(OutputExcelFilePathTextBox.Text); //------------------------------------- // Close this app: //------------------------------------- System.Windows.Application.Current.Shutdown(); } }
/// <summary> /// Retrieve all methods and types used in the _inputAssemblies and organize them in a Dictionary /// </summary> private void GetUnsupportedMethods() { ILogger logger = new LoggerThatAggregatesAllErrors(); List <UnsupportedMethodInfo> unsupportedMethodInfos = new List <UnsupportedMethodInfo>(); CoreSupportedMethodsContainer coreSupportedMethods = new CoreSupportedMethodsContainer(Configuration.SLMigrationCoreAssemblyFolderPath); foreach (string filename in _inputAssemblies) { CompatibilityAnalyzer.Analyze( filename, logger, unsupportedMethodInfos, coreSupportedMethods, _inputAssemblies, Configuration.UrlNamespacesThatBelongToUserCode, new HashSet <string>(), Configuration.ExcludedFiles, Configuration.supportedElementsPath, Configuration.mscorlibFolderPath, Configuration.sdkFolderPath, "", skipTypesWhereNoMethodIsActuallyCalled: false, addBothPropertyAndEventWhenNotFound: true, additionalFolderWhereToResolveAssemblies: Configuration.ReferencedAssembliesFolderPath); } if (_unsupportedMethodsInfo == null) { _unsupportedMethodsInfo = new Dictionary <string, Dictionary <string, HashSet <string> > >(); } HashSet <MethodInfo> unsupportedMethods = new HashSet <MethodInfo>(); foreach (UnsupportedMethodInfo method in unsupportedMethodInfos) { unsupportedMethods.Add(new MethodInfo(method)); } foreach (MethodInfo method in unsupportedMethods) { Tuple <string, string, string> methodInfo = GetMethodMainInfos(method); MethodInfo _method = new MethodInfo(methodInfo.Item1, methodInfo.Item2, methodInfo.Item3, method.NeedToBeCheckedBecauseOfInheritance); if (method.NeedToBeCheckedBecauseOfInheritance) { _method = GetMethodInfoResolvingInheritance(_method.AssemblyName, _method.TypeName, _method.MethodName); } if (!_unsupportedMethodsInfo.ContainsKey(_method.AssemblyName)) { HashSet <string> methodsSet = new HashSet <string>(); if (_method.MethodName != "") { methodsSet.Add(_method.MethodName); } Dictionary <string, HashSet <string> > currentTypesDictionary = new Dictionary <string, HashSet <string> > { { _method.TypeName, methodsSet } }; _unsupportedMethodsInfo.Add(_method.AssemblyName, currentTypesDictionary); } else { if (!_unsupportedMethodsInfo[_method.AssemblyName].ContainsKey(_method.TypeName)) { HashSet <string> methodsSet = new HashSet <string>(); if (_method.MethodName != "") { methodsSet.Add(_method.MethodName); } _unsupportedMethodsInfo[_method.AssemblyName].Add(_method.TypeName, methodsSet); } else { if (_method.MethodName != "") { _unsupportedMethodsInfo[_method.AssemblyName][_method.TypeName].Add(_method.MethodName); } } } } }