.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.
 
 
 
 

250 lines
8.2 KiB

// Copyright (c) 2018 Siegfried Pammer
//
// 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.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata;
using System.Threading;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.ILSpyX.Analyzers
{
using ICSharpCode.Decompiler.TypeSystem;
public class AnalyzerScope
{
readonly ITypeDefinition typeScope;
readonly AssemblyListSnapshot assemblyListSnapshot;
/// <summary>
/// Returns whether this scope is local, i.e., AnalyzedSymbol is only reachable
/// from the current module or containing type.
/// </summary>
public bool IsLocal { get; }
public ISymbol AnalyzedSymbol { get; }
public ITypeDefinition TypeScope => typeScope;
readonly Accessibility effectiveAccessibility;
public AnalyzerScope(AssemblyList assemblyList, IEntity entity)
{
assemblyListSnapshot = assemblyList.GetSnapshot();
AnalyzedSymbol = entity;
DetermineEffectiveAccessibility(entity, out typeScope, out effectiveAccessibility);
IsLocal = effectiveAccessibility.LessThanOrEqual(Accessibility.Private);
}
public IEnumerable<MetadataFile> GetModulesInScope(CancellationToken ct)
{
if (IsLocal)
return new[] { TypeScope.ParentModule!.MetadataFile! };
if (effectiveAccessibility.LessThanOrEqual(Accessibility.Internal))
return GetModuleAndAnyFriends(TypeScope, ct);
return GetReferencingModules(TypeScope.ParentModule!.MetadataFile!, ct);
}
public IEnumerable<MetadataFile> GetAllModules()
{
return assemblyListSnapshot.GetAllAssembliesAsync().GetAwaiter().GetResult()
.Select(asm => asm.GetMetadataFileOrNull())
.Where(x => x != null && !x.IsMetadataOnly)!;
}
public DecompilerTypeSystem ConstructTypeSystem(MetadataFile module)
{
return new DecompilerTypeSystem(module, module.GetAssemblyResolver(assemblyListSnapshot, loadOnDemand: false));
}
public IEnumerable<ITypeDefinition> GetTypesInScope(CancellationToken ct)
{
if (IsLocal)
{
foreach (var type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes))
{
yield return type;
}
}
else
{
foreach (var module in GetModulesInScope(ct))
{
var typeSystem = ConstructTypeSystem(module);
foreach (var type in typeSystem.MainModule.TypeDefinitions)
{
yield return type;
}
}
}
}
static void DetermineEffectiveAccessibility(IEntity input, out ITypeDefinition typeScope, out Accessibility accessibility)
{
if (input is ITypeDefinition td)
{
accessibility = Accessibility.Public;
typeScope = td;
}
else
{
accessibility = input.Accessibility;
typeScope = input.DeclaringTypeDefinition!;
}
// Once we reach a private entity, we leave the loop with typeScope set to the class that
// contains the private entity = the scope that needs to be searched.
// Otherwise (if we don't find a private entity) we return the top-level class.
var prevTypeScope = typeScope;
while (typeScope != null && !accessibility.LessThanOrEqual(Accessibility.Private))
{
accessibility = accessibility.Intersect(typeScope.Accessibility);
prevTypeScope = typeScope;
typeScope = prevTypeScope.DeclaringTypeDefinition!;
}
if (typeScope == null)
{
typeScope = prevTypeScope;
}
}
#region Find modules
IEnumerable<MetadataFile> GetReferencingModules(MetadataFile self, CancellationToken ct)
{
yield return self;
string reflectionTypeScopeName = typeScope.Name;
if (typeScope.TypeParameterCount > 0)
reflectionTypeScopeName += "`" + typeScope.TypeParameterCount;
var toWalkFiles = new Stack<MetadataFile>();
var checkedFiles = new HashSet<MetadataFile>();
toWalkFiles.Push(self);
checkedFiles.Add(self);
IList<LoadedAssembly> assemblies = assemblyListSnapshot.GetAllAssembliesAsync().GetAwaiter().GetResult();
do
{
MetadataFile curFile = toWalkFiles.Pop();
foreach (var assembly in assemblies)
{
ct.ThrowIfCancellationRequested();
bool found = false;
var module = assembly.GetMetadataFileOrNull();
if (module == null || !module.IsAssembly)
continue;
if (checkedFiles.Contains(module))
continue;
var resolver = assembly.GetAssemblyResolver(assemblyListSnapshot, loadOnDemand: false);
foreach (var reference in module.AssemblyReferences)
{
if (resolver.Resolve(reference) == curFile)
{
found = true;
break;
}
}
if (found && checkedFiles.Add(module))
{
if (ModuleReferencesScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace))
yield return module;
if (ModuleForwardsScopeType(module.Metadata, reflectionTypeScopeName, typeScope.Namespace))
toWalkFiles.Push(module);
}
}
} while (toWalkFiles.Count > 0);
}
IEnumerable<MetadataFile> GetModuleAndAnyFriends(ITypeDefinition typeScope, CancellationToken ct)
{
var self = typeScope.ParentModule!.MetadataFile!;
yield return self;
var typeProvider = MetadataExtensions.MinimalAttributeTypeProvider;
var attributes = self.Metadata.CustomAttributes.Select(h => self.Metadata.GetCustomAttribute(h))
.Where(ca => ca.GetAttributeType(self.Metadata).GetFullTypeName(self.Metadata).ToString() == "System.Runtime.CompilerServices.InternalsVisibleToAttribute");
var friendAssemblies = new HashSet<string>();
foreach (var attribute in attributes)
{
string? assemblyName = attribute.DecodeValue(typeProvider).FixedArguments[0].Value as string;
assemblyName = assemblyName?.Split(',')[0]; // strip off any public key info
if (assemblyName != null)
friendAssemblies.Add(assemblyName);
}
if (friendAssemblies.Count > 0)
{
IEnumerable<LoadedAssembly> assemblies = assemblyListSnapshot.GetAllAssembliesAsync()
.GetAwaiter().GetResult();
foreach (var assembly in assemblies)
{
ct.ThrowIfCancellationRequested();
if (friendAssemblies.Contains(assembly.ShortName))
{
var module = assembly.GetMetadataFileOrNull();
if (module == null || module.IsMetadataOnly)
continue;
if (ModuleReferencesScopeType(module.Metadata, typeScope.Name, typeScope.Namespace))
yield return module;
}
}
}
}
bool ModuleReferencesScopeType(MetadataReader metadata, string typeScopeName, string typeScopeNamespace)
{
bool hasRef = false;
foreach (var h in metadata.TypeReferences)
{
var typeRef = metadata.GetTypeReference(h);
if (metadata.StringComparer.Equals(typeRef.Name, typeScopeName)
&& metadata.StringComparer.Equals(typeRef.Namespace, typeScopeNamespace))
{
hasRef = true;
break;
}
}
return hasRef;
}
bool ModuleForwardsScopeType(MetadataReader metadata, string typeScopeName, string typeScopeNamespace)
{
bool hasForward = false;
foreach (var h in metadata.ExportedTypes)
{
var exportedType = metadata.GetExportedType(h);
if (exportedType.IsForwarder
&& metadata.StringComparer.Equals(exportedType.Name, typeScopeName)
&& metadata.StringComparer.Equals(exportedType.Namespace, typeScopeNamespace))
{
hasForward = true;
break;
}
}
return hasForward;
}
#endregion
}
}