Skip to content

Commit

Permalink
Merge pull request #3186 from icsharpcode/take2
Browse files Browse the repository at this point in the history
Move non-UI analyzer code to ILSpyX
  • Loading branch information
christophwille authored Mar 29, 2024
2 parents cda1f8c + 7e69247 commit fd0acb0
Show file tree
Hide file tree
Showing 41 changed files with 287 additions and 240 deletions.
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
<PackageVersion Include="NuGet.Protocol" Version="6.9.1" />
<PackageVersion Include="PowerShellStandard.Library" Version="5.1.1" />
<PackageVersion Include="System.Collections.Immutable" Version="8.0.0" />
<PackageVersion Include="System.ComponentModel.Composition" Version="8.0.0" />
<PackageVersion Include="System.Composition" Version="8.0.0" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="System.Reflection.Metadata" Version="8.0.0" />
Expand Down
3 changes: 3 additions & 0 deletions ICSharpCode.ILSpyX/Abstractions/ILanguage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

using System.Reflection.Metadata;

using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;

Expand All @@ -24,6 +26,7 @@ namespace ICSharpCode.ILSpyX.Abstractions
public interface ILanguage
{
bool ShowMember(IEntity member);
CodeMappingInfo GetCodeMappingInfo(MetadataFile module, EntityHandle member);
string GetEntityName(MetadataFile module, System.Reflection.Metadata.EntityHandle handle, bool fullName, bool omitGenerics);
string GetTooltip(IEntity entity);

Expand Down
82 changes: 82 additions & 0 deletions ICSharpCode.ILSpyX/Analyzers/AnalyzerContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// 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;
using System.Collections.Concurrent;
using System.Reflection.Metadata;
using System.Threading;

using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX.Abstractions;

namespace ICSharpCode.ILSpyX.Analyzers
{
/// <summary>
/// Provides additional context for analyzers.
/// </summary>
public class AnalyzerContext
{
public required AssemblyList AssemblyList { get; init; }

/// <summary>
/// CancellationToken. Currently Analyzers do not support cancellation from the UI, but it should be checked nonetheless.
/// </summary>
public CancellationToken CancellationToken { get; init; }

/// <summary>
/// Currently used language.
/// </summary>
public required ILanguage Language { get; init; }

/// <summary>
/// Allows the analyzer to control whether the tree nodes will be sorted.
/// Must be set within <see cref="IAnalyzer.Analyze(ISymbol, AnalyzerContext)"/>
/// before the results are enumerated.
/// </summary>
public bool SortResults { get; set; }

public MethodBodyBlock? GetMethodBody(IMethod method)
{
if (!method.HasBody || method.MetadataToken.IsNil || method.ParentModule?.MetadataFile == null)
return null;
var module = method.ParentModule.MetadataFile;
var md = module.Metadata.GetMethodDefinition((MethodDefinitionHandle)method.MetadataToken);
try
{
return module.GetMethodBody(md.RelativeVirtualAddress);
}
catch (BadImageFormatException)
{
return null;
}
}

public AnalyzerScope GetScopeOf(IEntity entity)
{
return new AnalyzerScope(AssemblyList, entity);
}

readonly ConcurrentDictionary<MetadataFile, DecompilerTypeSystem> typeSystemCache = new();

public DecompilerTypeSystem GetOrCreateTypeSystem(MetadataFile module)
{
return typeSystemCache.GetOrAdd(module, m => new DecompilerTypeSystem(m, m.GetAssemblyResolver()));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;

namespace ICSharpCode.ILSpy.Analyzers
namespace ICSharpCode.ILSpyX.Analyzers
{
internal static class AnalyzerHelpers
{
Expand All @@ -34,7 +34,7 @@ public static bool IsPossibleReferenceTo(EntityHandle member, MetadataFile modul
{
case HandleKind.MethodDefinition:
return member == analyzedMethod.MetadataToken
&& module == analyzedMethod.ParentModule.MetadataFile;
&& module == analyzedMethod.ParentModule?.MetadataFile;
case HandleKind.MemberReference:
var mr = metadata.GetMemberReference((MemberReferenceHandle)member);
if (mr.GetKind() != MemberReferenceKind.Method)
Expand All @@ -48,7 +48,7 @@ public static bool IsPossibleReferenceTo(EntityHandle member, MetadataFile modul
}
}

public static ISymbol GetParentEntity(DecompilerTypeSystem ts, CustomAttribute customAttribute)
public static ISymbol? GetParentEntity(DecompilerTypeSystem ts, CustomAttribute customAttribute)
{
var metadata = ts.MainModule.MetadataFile.Metadata;
switch (customAttribute.Parent.Kind)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.Util;
using ICSharpCode.ILSpyX;

namespace ICSharpCode.ILSpy.Analyzers
namespace ICSharpCode.ILSpyX.Analyzers
{
using ICSharpCode.Decompiler.TypeSystem;

Expand Down Expand Up @@ -61,19 +60,19 @@ public AnalyzerScope(AssemblyList assemblyList, IEntity entity)
public IEnumerable<MetadataFile> GetModulesInScope(CancellationToken ct)
{
if (IsLocal)
return new[] { TypeScope.ParentModule.MetadataFile };
return new[] { TypeScope.ParentModule!.MetadataFile! };

if (effectiveAccessibility.LessThanOrEqual(Accessibility.Internal))
return GetModuleAndAnyFriends(TypeScope, ct);

return GetReferencingModules(TypeScope.ParentModule.MetadataFile, ct);
return GetReferencingModules(TypeScope.ParentModule!.MetadataFile!, ct);
}

public IEnumerable<MetadataFile> GetAllModules()
{
return assemblyListSnapshot.GetAllAssembliesAsync().GetAwaiter().GetResult()
.Select(asm => asm.GetMetadataFileOrNull())
.Where(x => x != null);
.Where(x => x != null)!;
}

public DecompilerTypeSystem ConstructTypeSystem(MetadataFile module)
Expand Down Expand Up @@ -113,7 +112,7 @@ static void DetermineEffectiveAccessibility(IEntity input, out ITypeDefinition t
else
{
accessibility = input.Accessibility;
typeScope = input.DeclaringTypeDefinition;
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.
Expand All @@ -123,7 +122,7 @@ static void DetermineEffectiveAccessibility(IEntity input, out ITypeDefinition t
{
accessibility = accessibility.Intersect(typeScope.Accessibility);
prevTypeScope = typeScope;
typeScope = prevTypeScope.DeclaringTypeDefinition;
typeScope = prevTypeScope.DeclaringTypeDefinition!;
}
if (typeScope == null)
{
Expand Down Expand Up @@ -181,7 +180,7 @@ IEnumerable<MetadataFile> GetReferencingModules(MetadataFile self, CancellationT

IEnumerable<MetadataFile> GetModuleAndAnyFriends(ITypeDefinition typeScope, CancellationToken ct)
{
var self = typeScope.ParentModule.MetadataFile;
var self = typeScope.ParentModule!.MetadataFile!;

yield return self;

Expand All @@ -191,9 +190,10 @@ IEnumerable<MetadataFile> GetModuleAndAnyFriends(ITypeDefinition typeScope, Canc
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
friendAssemblies.Add(assemblyName);
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;

namespace ICSharpCode.ILSpy.Analyzers.Builtin
namespace ICSharpCode.ILSpyX.Analyzers.Builtin
{
[ExportAnalyzer(Header = "Applied To", Order = 10)]
class AttributeAppliedToAnalyzer : IAnalyzer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@
// DEALINGS IN THE SOFTWARE.

using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Linq;

using ICSharpCode.Decompiler.TypeSystem;

namespace ICSharpCode.ILSpy.Analyzers.Builtin
namespace ICSharpCode.ILSpyX.Analyzers.Builtin
{
/// <summary>
/// Shows events that implement an interface event.
Expand All @@ -44,17 +43,19 @@ public IEnumerable<ISymbol> Analyze(ISymbol analyzedSymbol, AnalyzerContext cont

IEnumerable<IEntity> AnalyzeType(IEvent analyzedEntity, ITypeDefinition type)
{
if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null)
yield break;
var token = analyzedEntity.MetadataToken;
var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken;
var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile;
var allTypes = type.GetAllBaseTypeDefinitions();
if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule.MetadataFile == module))
if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module))
yield break;

foreach (var @event in type.Events)
{
var baseMembers = InheritanceHelper.GetBaseMembers(@event, true);
if (baseMembers.Any(m => m.MetadataToken == token && m.ParentModule.MetadataFile == module))
if (baseMembers.Any(m => m.MetadataToken == token && m.ParentModule?.MetadataFile == module))
yield return @event;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,12 @@
// DEALINGS IN THE SOFTWARE.

using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Linq;

using ICSharpCode.Decompiler.TypeSystem;

namespace ICSharpCode.ILSpy.Analyzers.Builtin
namespace ICSharpCode.ILSpyX.Analyzers.Builtin
{
/// <summary>
/// Shows events that override an event.
Expand All @@ -44,19 +43,21 @@ public IEnumerable<ISymbol> Analyze(ISymbol analyzedSymbol, AnalyzerContext cont

IEnumerable<IEntity> AnalyzeType(IEvent analyzedEntity, ITypeDefinition type)
{
if (analyzedEntity.DeclaringTypeDefinition?.ParentModule?.MetadataFile == null)
yield break;
var token = analyzedEntity.MetadataToken;
var declaringTypeToken = analyzedEntity.DeclaringTypeDefinition.MetadataToken;
var module = analyzedEntity.DeclaringTypeDefinition.ParentModule.MetadataFile;
var allTypes = type.GetAllBaseTypeDefinitions();
if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule.MetadataFile == module))
if (!allTypes.Any(t => t.MetadataToken == declaringTypeToken && t.ParentModule?.MetadataFile == module))
yield break;

foreach (var @event in type.Events)
{
if (!@event.IsOverride)
continue;
var baseMembers = InheritanceHelper.GetBaseMembers(@event, false);
if (baseMembers.Any(p => p.MetadataToken == token && p.ParentModule.MetadataFile == module))
if (baseMembers.Any(p => p.MetadataToken == token && p.ParentModule?.MetadataFile == module))
{
yield return @event;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@

using ILOpCode = System.Reflection.Metadata.ILOpCode;

namespace ICSharpCode.ILSpy.Analyzers.Builtin
namespace ICSharpCode.ILSpyX.Analyzers.Builtin
{
/// <summary>
/// Finds methods where this field is read.
Expand Down Expand Up @@ -74,6 +74,8 @@ public IEnumerable<ISymbol> Analyze(ISymbol analyzedSymbol, AnalyzerContext cont
var scope = context.GetScopeOf((IEntity)analyzedSymbol);
foreach (var type in scope.GetTypesInScope(context.CancellationToken))
{
if (type.ParentModule?.MetadataFile == null)
continue;
var mappingInfo = context.Language.GetCodeMappingInfo(type.ParentModule.MetadataFile, type.MetadataToken);
var methods = type.GetMembers(m => m is IMethod, Options).OfType<IMethod>();
foreach (var method in methods)
Expand Down Expand Up @@ -119,7 +121,7 @@ public IEnumerable<ISymbol> Analyze(ISymbol analyzedSymbol, AnalyzerContext cont

bool IsUsedInMethod(IField analyzedField, IMethod method, CodeMappingInfo mappingInfo, AnalyzerContext context)
{
if (method.MetadataToken.IsNil)
if (method.MetadataToken.IsNil || method.ParentModule?.MetadataFile == null)
return false;
var module = method.ParentModule.MetadataFile;
foreach (var part in mappingInfo.GetMethodParts((MethodDefinitionHandle)method.MetadataToken))
Expand All @@ -144,7 +146,7 @@ bool IsUsedInMethod(IField analyzedField, IMethod method, CodeMappingInfo mappin

bool ScanMethodBody(IField analyzedField, IMethod method, MethodBodyBlock methodBody)
{
if (methodBody == null)
if (methodBody == null || method.ParentModule?.MetadataFile == null)
return false;

var mainModule = (MetadataModule)method.ParentModule;
Expand All @@ -170,7 +172,7 @@ bool ScanMethodBody(IField analyzedField, IMethod method, MethodBodyBlock method
EntityHandle fieldHandle = MetadataTokenHelpers.EntityHandleOrNil(blob.ReadInt32());
if (!fieldHandle.Kind.IsMemberKind())
continue;
IField field;
IField? field;
try
{
field = mainModule.ResolveEntity(fieldHandle, genericContext) as IField;
Expand All @@ -183,7 +185,7 @@ bool ScanMethodBody(IField analyzedField, IMethod method, MethodBodyBlock method
continue;

if (field.MetadataToken == analyzedField.MetadataToken
&& field.ParentModule.MetadataFile == analyzedField.ParentModule.MetadataFile)
&& field.ParentModule?.MetadataFile == analyzedField.ParentModule!.MetadataFile)
return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
using ICSharpCode.Decompiler.Metadata;
using ICSharpCode.Decompiler.TypeSystem;

namespace ICSharpCode.ILSpy.Analyzers.Builtin
namespace ICSharpCode.ILSpyX.Analyzers.Builtin
{
public enum TokenSearchResult : byte
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

using ICSharpCode.Decompiler.TypeSystem;

namespace ICSharpCode.ILSpy.Analyzers.Builtin
namespace ICSharpCode.ILSpyX.Analyzers.Builtin
{
/// <summary>
/// Shows members from all corresponding interfaces the selected member implements.
Expand All @@ -38,7 +38,7 @@ public IEnumerable<ISymbol> Analyze(ISymbol analyzedSymbol, AnalyzerContext cont
Debug.Assert(!member.IsStatic);

var baseMembers = InheritanceHelper.GetBaseMembers(member, includeImplementedInterfaces: true);
return baseMembers.Where(m => m.DeclaringTypeDefinition.Kind == TypeKind.Interface);
return baseMembers.Where(m => m.DeclaringTypeDefinition?.Kind == TypeKind.Interface);
}

public bool Show(ISymbol symbol)
Expand Down
Loading

0 comments on commit fd0acb0

Please sign in to comment.