// 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.Generic; using System.Diagnostics; using System.Linq; using System.Threading; using dnlib.DotNet; using dnSpy.Contracts.Documents; using dnSpy.Contracts.Images; using dnSpy.Contracts.TreeView; namespace dnSpy.Analyzer.TreeNodes { /// /// Base class for analyzer nodes that perform a search. /// abstract class SearchNode : AnalyzerTreeNodeData, IAsyncCancellable { protected SearchNode() { } public override void Initialize() => TreeNode.LazyLoading = true; protected override ImageReference GetIcon(IDotNetImageService dnImgMgr) => DsImages.Search; public override IEnumerable CreateChildren() { Debug2.Assert(asyncFetchChildrenHelper is null); asyncFetchChildrenHelper = new AsyncFetchChildrenHelper(this, () => asyncFetchChildrenHelper = null); yield break; } AsyncFetchChildrenHelper? asyncFetchChildrenHelper; protected abstract IEnumerable FetchChildren(CancellationToken ct); internal IEnumerable FetchChildrenInternal(CancellationToken token) => FetchChildren(token); public override void OnIsVisibleChanged() { if (!TreeNode.IsVisible && asyncFetchChildrenHelper is not null && !asyncFetchChildrenHelper.CompletedSuccessfully) { CancelAndClearChildren(); TreeNode.LazyLoading = true; } } public override void OnIsExpandedChanged(bool isExpanded) { if (!isExpanded && asyncFetchChildrenHelper is not null && !asyncFetchChildrenHelper.CompletedSuccessfully) { CancelAndClearChildren(); TreeNode.LazyLoading = true; } } public override bool HandleAssemblyListChanged(IDsDocument[] removedAssemblies, IDsDocument[] addedAssemblies) { // only cancel a running analysis if user has manually added/removed assemblies bool manualAdd = false; foreach (var asm in addedAssemblies) { if (!asm.IsAutoLoaded) manualAdd = true; } if (removedAssemblies.Length > 0 || manualAdd) { CancelAndClearChildren(); } return true; } public override bool HandleModelUpdated(IDsDocument[] documents) { CancelAndClearChildren(); return true; } void CancelAndClearChildren() { AnalyzerTreeNodeData.CancelSelfAndChildren(this); TreeNode.Children.Clear(); TreeNode.LazyLoading = true; } public void Cancel() { asyncFetchChildrenHelper?.Cancel(); asyncFetchChildrenHelper = null; } internal static bool CanIncludeModule(ModuleDef targetModule, ModuleDef? module) { if (module is null) return false; if (targetModule == module) return false; if (targetModule.Assembly is not null && targetModule.Assembly == module.Assembly) return false; return true; } internal static HashSet GetFriendAssemblies(IDsDocumentService documentService, ModuleDef mod, out IDsDocument[] modules) { var asm = mod.Assembly; Debug2.Assert(asm is not null); var friendAssemblies = new HashSet(StringComparer.OrdinalIgnoreCase); foreach (var attribute in asm.CustomAttributes.FindAll("System.Runtime.CompilerServices.InternalsVisibleToAttribute")) { if (attribute.ConstructorArguments.Count == 0) continue; string assemblyName = attribute.ConstructorArguments[0].Value as UTF8String; if (assemblyName is null) continue; assemblyName = new AssemblyNameInfo(assemblyName).Name; friendAssemblies.Add(assemblyName); } modules = documentService.GetDocuments().Where(a => CanIncludeModule(mod, a.ModuleDef)).ToArray(); foreach (var module in modules) { Debug2.Assert(module.ModuleDef is not null); var asm2 = module.AssemblyDef; if (asm2 is null) continue; foreach (var attribute in asm2.CustomAttributes.FindAll("System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute")) { string assemblyName = attribute.ConstructorArguments[0].Value as UTF8String; if (assemblyName is null) continue; assemblyName = new AssemblyNameInfo(assemblyName).Name; if (StringComparer.OrdinalIgnoreCase.Equals(asm.Name.String, assemblyName)) friendAssemblies.Add(asm2.Name); } } return friendAssemblies; } internal static void AddTypeEquivalentTypes(IDsDocumentService documentService, TypeDef analyzedType, List analyzedTypes) { Debug.Assert(analyzedTypes.Count == 1 && analyzedTypes[0] == analyzedType); if (!TIAHelper.IsTypeDefEquivalent(analyzedType)) return; foreach (var document in documentService.GetDocuments().Where(a => a.ModuleDef is not null)) { foreach (var type in GetTypeEquivalentTypes(document.AssemblyDef, document.ModuleDef, analyzedType)) { if (type != analyzedType) analyzedTypes.Add(type); } } } static IEnumerable GetTypeEquivalentTypes(AssemblyDef? assembly, ModuleDef? module, TypeDef type) { Debug.Assert(TIAHelper.IsTypeDefEquivalent(type)); var typeRef = new ModuleDefUser("dummy").Import(type); foreach (var mod in GetModules(assembly, module)) { var otherType = mod.Find(typeRef); if (otherType != type && TIAHelper.IsTypeDefEquivalent(otherType) && new SigComparer().Equals(otherType, type) && !new SigComparer(SigComparerOptions.DontCheckTypeEquivalence).Equals(otherType, type)) { yield return otherType; } } } static IEnumerable GetModules(AssemblyDef? assembly, ModuleDef? module) { if (assembly is not null) { foreach (var mod in assembly.Modules) yield return mod; } else { if (module is not null) yield return module; } } protected static IEnumerable<(ModuleDef module, ITypeDefOrRef type)> GetTypeEquivalentModulesAndTypes(List analyzedTypes) { foreach (var type in analyzedTypes) yield return (type.Module, type); } internal static IEnumerable GetTypeEquivalentModules(List analyzedTypes) { foreach (var type in analyzedTypes) yield return type.Module; } protected static bool CheckEquals(IMemberRef? mr1, IMemberRef? mr2) => Helpers.CheckEquals(mr1, mr2); } }