mirror of https://github.com/icsharpcode/ILSpy.git
51 changed files with 2008 additions and 759 deletions
@ -0,0 +1,148 @@
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.TypeSystem; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.Ast |
||||
{ |
||||
/// <summary>
|
||||
/// ITypeResolveContext implementation that lazily loads types from Cecil.
|
||||
/// </summary>
|
||||
public class CecilTypeResolveContext : ISynchronizedTypeResolveContext, IProjectContent |
||||
{ |
||||
readonly ModuleDefinition module; |
||||
readonly string[] namespaces; |
||||
readonly CecilLoader loader; |
||||
Dictionary<TypeDefinition, WeakReference> dict = new Dictionary<TypeDefinition, WeakReference>(); |
||||
int countUntilNextCleanup = 4; |
||||
|
||||
public CecilTypeResolveContext(ModuleDefinition module) |
||||
{ |
||||
this.loader = new CecilLoader(); |
||||
this.loader.IncludeInternalMembers = true; |
||||
this.module = module; |
||||
this.namespaces = module.Types.Select(t => t.Namespace).Distinct().ToArray(); |
||||
|
||||
List<IAttribute> assemblyAttributes = new List<IAttribute>(); |
||||
foreach (var attr in module.Assembly.CustomAttributes) { |
||||
assemblyAttributes.Add(loader.ReadAttribute(attr)); |
||||
} |
||||
this.AssemblyAttributes = assemblyAttributes.AsReadOnly(); |
||||
} |
||||
|
||||
ITypeDefinition GetClass(TypeDefinition cecilType) |
||||
{ |
||||
lock (dict) { |
||||
WeakReference wr; |
||||
ITypeDefinition type; |
||||
if (dict.TryGetValue(cecilType, out wr)) { |
||||
type = (ITypeDefinition)wr.Target; |
||||
} else { |
||||
wr = null; |
||||
type = null; |
||||
} |
||||
if (type == null) { |
||||
type = loader.LoadType(cecilType, this); |
||||
} |
||||
if (wr == null) { |
||||
if (--countUntilNextCleanup <= 0) |
||||
CleanupDict(); |
||||
wr = new WeakReference(type); |
||||
dict.Add(cecilType, wr); |
||||
} else { |
||||
wr.Target = type; |
||||
} |
||||
return type; |
||||
} |
||||
} |
||||
|
||||
void CleanupDict() |
||||
{ |
||||
List<TypeDefinition> deletedKeys = new List<TypeDefinition>(); |
||||
foreach (var pair in dict) { |
||||
if (!pair.Value.IsAlive) { |
||||
deletedKeys.Add(pair.Key); |
||||
} |
||||
} |
||||
foreach (var key in deletedKeys) { |
||||
dict.Remove(key); |
||||
} |
||||
countUntilNextCleanup = dict.Count + 4; |
||||
} |
||||
|
||||
public IList<IAttribute> AssemblyAttributes { get; private set; } |
||||
|
||||
public ITypeDefinition GetClass(string nameSpace, string name, int typeParameterCount, StringComparer nameComparer) |
||||
{ |
||||
if (typeParameterCount > 0) |
||||
name = name + "`" + typeParameterCount.ToString(); |
||||
if (nameComparer == StringComparer.Ordinal) { |
||||
TypeDefinition cecilType = module.GetType(nameSpace, name); |
||||
if (cecilType != null) |
||||
return GetClass(cecilType); |
||||
else |
||||
return null; |
||||
} |
||||
foreach (TypeDefinition cecilType in module.Types) { |
||||
if (nameComparer.Equals(name, cecilType.Name) |
||||
&& nameComparer.Equals(nameSpace, cecilType.Namespace) |
||||
&& cecilType.GenericParameters.Count == typeParameterCount) |
||||
{ |
||||
return GetClass(cecilType); |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public IEnumerable<ITypeDefinition> GetClasses() |
||||
{ |
||||
foreach (TypeDefinition cecilType in module.Types) { |
||||
yield return GetClass(cecilType); |
||||
} |
||||
} |
||||
|
||||
public IEnumerable<ITypeDefinition> GetClasses(string nameSpace, StringComparer nameComparer) |
||||
{ |
||||
foreach (TypeDefinition cecilType in module.Types) { |
||||
if (nameComparer.Equals(nameSpace, cecilType.Namespace)) |
||||
yield return GetClass(cecilType); |
||||
} |
||||
} |
||||
|
||||
public IEnumerable<string> GetNamespaces() |
||||
{ |
||||
return namespaces; |
||||
} |
||||
|
||||
public string GetNamespace(string nameSpace, StringComparer nameComparer) |
||||
{ |
||||
foreach (string ns in namespaces) { |
||||
if (nameComparer.Equals(ns, nameSpace)) |
||||
return ns; |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
ICSharpCode.NRefactory.Utils.CacheManager ITypeResolveContext.CacheManager { |
||||
get { |
||||
// We don't support caching
|
||||
return null; |
||||
} |
||||
} |
||||
|
||||
ISynchronizedTypeResolveContext ITypeResolveContext.Synchronize() |
||||
{ |
||||
// This class is logically immutable
|
||||
return this; |
||||
} |
||||
|
||||
void IDisposable.Dispose() |
||||
{ |
||||
// exit from Synchronize() block
|
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using ICSharpCode.NRefactory.CSharp; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.Ast.Transforms |
||||
{ |
||||
/// <summary>
|
||||
/// Introduces using declarations.
|
||||
/// </summary>
|
||||
public class IntroduceUsingDeclarations : DepthFirstAstVisitor<object, object>, IAstTransform |
||||
{ |
||||
DecompilerContext context; |
||||
|
||||
public IntroduceUsingDeclarations(DecompilerContext context) |
||||
{ |
||||
this.context = context; |
||||
currentNamespace = context.CurrentType != null ? context.CurrentType.Namespace : string.Empty; |
||||
} |
||||
|
||||
public void Run(AstNode compilationUnit) |
||||
{ |
||||
// Don't show using when decompiling a single method or nested types:
|
||||
if (context.CurrentMethod != null || (context.CurrentType != null && context.CurrentType.IsNested)) |
||||
return; |
||||
|
||||
// First determine all the namespaces that need to be imported:
|
||||
compilationUnit.AcceptVisitor(this, null); |
||||
|
||||
importedNamespaces.Add("System"); // always import System, even when not necessary
|
||||
|
||||
// Now add using declarations for those namespaces:
|
||||
foreach (string ns in importedNamespaces.OrderByDescending(n => n)) { |
||||
// we go backwards (OrderByDescending) through the list of namespaces because we insert them backwards
|
||||
// (always inserting at the start of the list)
|
||||
string[] parts = ns.Split('.'); |
||||
AstType nsType = new SimpleType(parts[0]); |
||||
for (int i = 1; i < parts.Length; i++) { |
||||
nsType = new MemberType { Target = nsType, MemberName = parts[i] }; |
||||
} |
||||
compilationUnit.InsertChildAfter(null, new UsingDeclaration { Import = nsType }, CompilationUnit.MemberRole); |
||||
} |
||||
|
||||
// TODO: verify that the SimpleTypes refer to the correct type (no ambiguities)
|
||||
} |
||||
|
||||
readonly HashSet<string> importedNamespaces = new HashSet<string>(); |
||||
string currentNamespace; |
||||
|
||||
bool IsParentOfCurrentNamespace(string ns) |
||||
{ |
||||
if (ns.Length == 0) |
||||
return true; |
||||
if (currentNamespace.StartsWith(ns, StringComparison.Ordinal)) { |
||||
if (currentNamespace.Length == ns.Length) |
||||
return true; |
||||
if (currentNamespace[ns.Length] == '.') |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public override object VisitSimpleType(SimpleType simpleType, object data) |
||||
{ |
||||
TypeReference tr = simpleType.Annotation<TypeReference>(); |
||||
if (tr != null && !IsParentOfCurrentNamespace(tr.Namespace)) { |
||||
importedNamespaces.Add(tr.Namespace); |
||||
} |
||||
return base.VisitSimpleType(simpleType, data); // also visit type arguments
|
||||
} |
||||
|
||||
public override object VisitNamespaceDeclaration(NamespaceDeclaration namespaceDeclaration, object data) |
||||
{ |
||||
string oldNamespace = currentNamespace; |
||||
foreach (Identifier ident in namespaceDeclaration.Identifiers) { |
||||
currentNamespace = NamespaceDeclaration.BuildQualifiedName(currentNamespace, ident.Name); |
||||
} |
||||
base.VisitNamespaceDeclaration(namespaceDeclaration, data); |
||||
currentNamespace = oldNamespace; |
||||
return null; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,259 @@
@@ -0,0 +1,259 @@
|
||||
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
|
||||
// This code is distributed under MIT X11 license (for details please see \doc\license.txt)
|
||||
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Diagnostics; |
||||
using System.Linq; |
||||
|
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.Decompiler.ILAst |
||||
{ |
||||
public class SimpleControlFlow |
||||
{ |
||||
Dictionary<ILLabel, int> labelGlobalRefCount = new Dictionary<ILLabel, int>(); |
||||
Dictionary<ILLabel, ILBasicBlock> labelToBasicBlock = new Dictionary<ILLabel, ILBasicBlock>(); |
||||
|
||||
DecompilerContext context; |
||||
TypeSystem typeSystem; |
||||
|
||||
public SimpleControlFlow(DecompilerContext context, ILBlock method) |
||||
{ |
||||
this.context = context; |
||||
this.typeSystem = context.CurrentMethod.Module.TypeSystem; |
||||
|
||||
foreach(ILLabel target in method.GetSelfAndChildrenRecursive<ILExpression>(e => e.IsBranch()).SelectMany(e => e.GetBranchTargets())) { |
||||
labelGlobalRefCount[target] = labelGlobalRefCount.GetOrDefault(target) + 1; |
||||
} |
||||
foreach(ILBasicBlock bb in method.GetSelfAndChildrenRecursive<ILBasicBlock>()) { |
||||
foreach(ILLabel label in bb.GetChildren().OfType<ILLabel>()) { |
||||
labelToBasicBlock[label] = bb; |
||||
} |
||||
} |
||||
} |
||||
|
||||
public bool SimplifyTernaryOperator(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
Debug.Assert(body.Contains(head)); |
||||
|
||||
ILExpression condExpr; |
||||
ILLabel trueLabel; |
||||
ILLabel falseLabel; |
||||
ILVariable trueLocVar = null; |
||||
ILExpression trueExpr; |
||||
ILLabel trueFall; |
||||
ILVariable falseLocVar = null; |
||||
ILExpression falseExpr; |
||||
ILLabel falseFall; |
||||
object unused; |
||||
|
||||
if (head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel) && |
||||
labelGlobalRefCount[trueLabel] == 1 && |
||||
labelGlobalRefCount[falseLabel] == 1 && |
||||
((labelToBasicBlock[trueLabel].MatchSingleAndBr(ILCode.Stloc, out trueLocVar, out trueExpr, out trueFall) && |
||||
labelToBasicBlock[falseLabel].MatchSingleAndBr(ILCode.Stloc, out falseLocVar, out falseExpr, out falseFall) && |
||||
trueLocVar == falseLocVar && trueFall == falseFall) || |
||||
(labelToBasicBlock[trueLabel].MatchSingle(ILCode.Ret, out unused, out trueExpr) && |
||||
labelToBasicBlock[falseLabel].MatchSingle(ILCode.Ret, out unused, out falseExpr))) && |
||||
body.Contains(labelToBasicBlock[trueLabel]) && |
||||
body.Contains(labelToBasicBlock[falseLabel]) |
||||
) |
||||
{ |
||||
bool isStloc = trueLocVar != null; |
||||
ILCode opCode = isStloc ? ILCode.Stloc : ILCode.Ret; |
||||
TypeReference retType = isStloc ? trueLocVar.Type : this.context.CurrentMethod.ReturnType; |
||||
int leftBoolVal; |
||||
int rightBoolVal; |
||||
ILExpression newExpr; |
||||
// a ? true:false is equivalent to a
|
||||
// a ? false:true is equivalent to !a
|
||||
// a ? true : b is equivalent to a || b
|
||||
// a ? b : true is equivalent to !a || b
|
||||
// a ? b : false is equivalent to a && b
|
||||
// a ? false : b is equivalent to !a && b
|
||||
if (retType == typeSystem.Boolean && |
||||
trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal) && |
||||
falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal) && |
||||
((leftBoolVal != 0 && rightBoolVal == 0) || (leftBoolVal == 0 && rightBoolVal != 0)) |
||||
) |
||||
{ |
||||
// It can be expressed as trivilal expression
|
||||
if (leftBoolVal != 0) { |
||||
newExpr = condExpr; |
||||
} else { |
||||
newExpr = new ILExpression(ILCode.LogicNot, null, condExpr); |
||||
} |
||||
} else if (retType == typeSystem.Boolean && trueExpr.Match(ILCode.Ldc_I4, out leftBoolVal)) { |
||||
// It can be expressed as logical expression
|
||||
if (leftBoolVal != 0) { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, condExpr, falseExpr); |
||||
} else { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, new ILExpression(ILCode.LogicNot, null, condExpr), falseExpr); |
||||
} |
||||
} else if (retType == typeSystem.Boolean && falseExpr.Match(ILCode.Ldc_I4, out rightBoolVal)) { |
||||
// It can be expressed as logical expression
|
||||
if (rightBoolVal != 0) { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, new ILExpression(ILCode.LogicNot, null, condExpr), trueExpr); |
||||
} else { |
||||
newExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, condExpr, trueExpr); |
||||
} |
||||
} else { |
||||
// Ternary operator tends to create long complicated return statements
|
||||
if (opCode == ILCode.Ret) |
||||
return false; |
||||
|
||||
// Only simplify generated variables
|
||||
if (opCode == ILCode.Stloc && !trueLocVar.IsGenerated) |
||||
return false; |
||||
|
||||
// Create ternary expression
|
||||
newExpr = new ILExpression(ILCode.TernaryOp, null, condExpr, trueExpr, falseExpr); |
||||
} |
||||
|
||||
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); |
||||
head.Body.Add(new ILExpression(opCode, trueLocVar, newExpr)); |
||||
if (isStloc) |
||||
head.Body.Add(new ILExpression(ILCode.Br, trueFall)); |
||||
|
||||
// Remove the old basic blocks
|
||||
body.RemoveOrThrow(labelToBasicBlock[trueLabel]); |
||||
body.RemoveOrThrow(labelToBasicBlock[falseLabel]); |
||||
|
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public bool SimplifyNullCoalescing(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
// ...
|
||||
// v = ldloc(leftVar)
|
||||
// brtrue(endBBLabel, ldloc(leftVar))
|
||||
// br(rightBBLabel)
|
||||
//
|
||||
// rightBBLabel:
|
||||
// v = rightExpr
|
||||
// br(endBBLabel)
|
||||
// ...
|
||||
// =>
|
||||
// ...
|
||||
// v = NullCoalescing(ldloc(leftVar), rightExpr)
|
||||
// br(endBBLabel)
|
||||
|
||||
ILVariable v, v2; |
||||
ILExpression leftExpr, leftExpr2; |
||||
ILVariable leftVar; |
||||
ILLabel endBBLabel, endBBLabel2; |
||||
ILLabel rightBBLabel; |
||||
ILBasicBlock rightBB; |
||||
ILExpression rightExpr; |
||||
if (head.Body.Count >= 3 && |
||||
head.Body[head.Body.Count - 3].Match(ILCode.Stloc, out v, out leftExpr) && |
||||
leftExpr.Match(ILCode.Ldloc, out leftVar) && |
||||
head.MatchLastAndBr(ILCode.Brtrue, out endBBLabel, out leftExpr2, out rightBBLabel) && |
||||
leftExpr2.Match(ILCode.Ldloc, leftVar) && |
||||
labelToBasicBlock.TryGetValue(rightBBLabel, out rightBB) && |
||||
rightBB.MatchSingleAndBr(ILCode.Stloc, out v2, out rightExpr, out endBBLabel2) && |
||||
v == v2 && |
||||
endBBLabel == endBBLabel2 && |
||||
labelGlobalRefCount.GetOrDefault(rightBBLabel) == 1 && |
||||
body.Contains(rightBB) |
||||
) |
||||
{ |
||||
head.Body.RemoveTail(ILCode.Stloc, ILCode.Brtrue, ILCode.Br); |
||||
head.Body.Add(new ILExpression(ILCode.Stloc, v, new ILExpression(ILCode.NullCoalescing, null, leftExpr, rightExpr))); |
||||
head.Body.Add(new ILExpression(ILCode.Br, endBBLabel)); |
||||
|
||||
body.RemoveOrThrow(labelToBasicBlock[rightBBLabel]); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
public bool SimplifyShortCircuit(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
Debug.Assert(body.Contains(head)); |
||||
|
||||
ILExpression condExpr; |
||||
ILLabel trueLabel; |
||||
ILLabel falseLabel; |
||||
if(head.MatchLastAndBr(ILCode.Brtrue, out trueLabel, out condExpr, out falseLabel)) { |
||||
for (int pass = 0; pass < 2; pass++) { |
||||
|
||||
// On the second pass, swap labels and negate expression of the first branch
|
||||
// It is slightly ugly, but much better then copy-pasting this whole block
|
||||
ILLabel nextLabel = (pass == 0) ? trueLabel : falseLabel; |
||||
ILLabel otherLablel = (pass == 0) ? falseLabel : trueLabel; |
||||
bool negate = (pass == 1); |
||||
|
||||
ILBasicBlock nextBasicBlock = labelToBasicBlock[nextLabel]; |
||||
ILExpression nextCondExpr; |
||||
ILLabel nextTrueLablel; |
||||
ILLabel nextFalseLabel; |
||||
if (body.Contains(nextBasicBlock) && |
||||
nextBasicBlock != head && |
||||
labelGlobalRefCount[(ILLabel)nextBasicBlock.Body.First()] == 1 && |
||||
nextBasicBlock.MatchSingleAndBr(ILCode.Brtrue, out nextTrueLablel, out nextCondExpr, out nextFalseLabel) && |
||||
(otherLablel == nextFalseLabel || otherLablel == nextTrueLablel)) |
||||
{ |
||||
// Create short cicuit branch
|
||||
ILExpression logicExpr; |
||||
if (otherLablel == nextFalseLabel) { |
||||
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicAnd, negate ? new ILExpression(ILCode.LogicNot, null, condExpr) : condExpr, nextCondExpr); |
||||
} else { |
||||
logicExpr = MakeLeftAssociativeShortCircuit(ILCode.LogicOr, negate ? condExpr : new ILExpression(ILCode.LogicNot, null, condExpr), nextCondExpr); |
||||
} |
||||
head.Body.RemoveTail(ILCode.Brtrue, ILCode.Br); |
||||
head.Body.Add(new ILExpression(ILCode.Brtrue, nextTrueLablel, logicExpr)); |
||||
head.Body.Add(new ILExpression(ILCode.Br, nextFalseLabel)); |
||||
|
||||
// Remove the inlined branch from scope
|
||||
body.RemoveOrThrow(nextBasicBlock); |
||||
|
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
ILExpression MakeLeftAssociativeShortCircuit(ILCode code, ILExpression left, ILExpression right) |
||||
{ |
||||
// Assuming that the inputs are already left associative
|
||||
if (right.Match(code)) { |
||||
// Find the leftmost logical expression
|
||||
ILExpression current = right; |
||||
while(current.Arguments[0].Match(code)) |
||||
current = current.Arguments[0]; |
||||
current.Arguments[0] = new ILExpression(code, null, left, current.Arguments[0]); |
||||
return right; |
||||
} else { |
||||
return new ILExpression(code, null, left, right); |
||||
} |
||||
} |
||||
|
||||
public bool JoinBasicBlocks(List<ILNode> body, ILBasicBlock head, int pos) |
||||
{ |
||||
ILLabel nextLabel; |
||||
ILBasicBlock nextBB; |
||||
if (!head.Body.ElementAtOrDefault(head.Body.Count - 2).IsConditionalControlFlow() && |
||||
head.Body.Last().Match(ILCode.Br, out nextLabel) && |
||||
labelGlobalRefCount[nextLabel] == 1 && |
||||
labelToBasicBlock.TryGetValue(nextLabel, out nextBB) && |
||||
body.Contains(nextBB) && |
||||
nextBB.Body.First() == nextLabel && |
||||
!nextBB.Body.OfType<ILTryCatchBlock>().Any() |
||||
) |
||||
{ |
||||
head.Body.RemoveTail(ILCode.Br); |
||||
nextBB.Body.RemoveAt(0); // Remove label
|
||||
head.Body.AddRange(nextBB.Body); |
||||
|
||||
body.RemoveOrThrow(nextBB); |
||||
return true; |
||||
} |
||||
return false; |
||||
} |
||||
} |
||||
} |
||||
@ -0,0 +1,203 @@
@@ -0,0 +1,203 @@
|
||||
using System; |
||||
using System.Collections.Generic; |
||||
using System.Linq; |
||||
using System.Text; |
||||
using System.Threading; |
||||
using ICSharpCode.NRefactory.Utils; |
||||
using ICSharpCode.TreeView; |
||||
using Mono.Cecil; |
||||
|
||||
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer |
||||
{ |
||||
/// <summary>
|
||||
/// Searches for overrides of the analyzed method.
|
||||
/// </summary>
|
||||
class AnalyzerMethodOverridesTreeNode : AnalyzerTreeNode |
||||
{ |
||||
readonly MethodDefinition analyzedMethod; |
||||
readonly ThreadingSupport threading; |
||||
|
||||
/// <summary>
|
||||
/// Controls whether overrides of already overriden method should be included.
|
||||
/// </summary>
|
||||
readonly bool onlyDirectOverrides = false; |
||||
|
||||
public AnalyzerMethodOverridesTreeNode(MethodDefinition analyzedMethod) |
||||
{ |
||||
if (analyzedMethod == null) |
||||
throw new ArgumentNullException("analyzedMethod"); |
||||
|
||||
this.analyzedMethod = analyzedMethod; |
||||
this.threading = new ThreadingSupport(); |
||||
this.LazyLoading = true; |
||||
} |
||||
|
||||
public override object Text |
||||
{ |
||||
get { return "Overrided By"; } |
||||
} |
||||
|
||||
public override object Icon |
||||
{ |
||||
get { return Images.Search; } |
||||
} |
||||
|
||||
protected override void LoadChildren() |
||||
{ |
||||
threading.LoadChildren(this, FetchChildren); |
||||
} |
||||
|
||||
protected override void OnCollapsing() |
||||
{ |
||||
if (threading.IsRunning) |
||||
{ |
||||
this.LazyLoading = true; |
||||
threading.Cancel(); |
||||
this.Children.Clear(); |
||||
} |
||||
} |
||||
|
||||
IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct) |
||||
{ |
||||
return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); |
||||
} |
||||
|
||||
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly[] assemblies, CancellationToken ct) |
||||
{ |
||||
// use parallelism only on the assembly level (avoid locks within Cecil)
|
||||
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct)); |
||||
} |
||||
|
||||
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct) |
||||
{ |
||||
string asmName = asm.AssemblyDefinition.Name.Name; |
||||
string name = analyzedMethod.Name; |
||||
string declTypeName = analyzedMethod.DeclaringType.FullName; |
||||
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) |
||||
{ |
||||
ct.ThrowIfCancellationRequested(); |
||||
|
||||
if (!IsDerived(type, analyzedMethod.DeclaringType)) |
||||
continue; |
||||
|
||||
foreach (MethodDefinition method in type.Methods) |
||||
{ |
||||
ct.ThrowIfCancellationRequested(); |
||||
|
||||
if (HasCompatibleSpecification(method) && !method.IsNewSlot && DoesOverrideCorrectMethod(method)) |
||||
{ |
||||
yield return new AnalyzedMethodTreeNode(method); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Tests whether the method could override analyzed method by comparing its name, return type and parameters.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to test.</param>
|
||||
/// <returns>true if the method has the same specyfication as analyzed method, otherwise false.</returns>
|
||||
private bool HasCompatibleSpecification(MethodDefinition method) |
||||
{ |
||||
return method.Name == analyzedMethod.Name |
||||
&& method.IsVirtual |
||||
&& AreSameType(method.ReturnType, analyzedMethod.ReturnType) |
||||
&& HaveTheSameParameters(method); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Checks whether between given and analyzed method are overrides with <code>new</code> (newSlot) modifier.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to test.</param>
|
||||
/// <returns>true if the method overrides analyzed method, false if it overrides some other method that hides analyzed method.</returns>
|
||||
private bool DoesOverrideCorrectMethod(MethodDefinition method) |
||||
{ |
||||
var type = method.DeclaringType.BaseType.Resolve(); |
||||
while (type != analyzedMethod.DeclaringType) |
||||
{ |
||||
var parentOverride = type.Methods.Where(m => HasCompatibleSpecification(m)).SingleOrDefault(); |
||||
if (parentOverride != null) |
||||
{ |
||||
if (parentOverride.IsNewSlot) |
||||
return false; |
||||
else |
||||
return !onlyDirectOverrides; |
||||
} |
||||
type = type.BaseType.Resolve(); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Checks whether one type derives (directly or indirectly) from base type.
|
||||
/// </summary>
|
||||
/// <param name="derivedType">The possible derived type.</param>
|
||||
/// <param name="baseType">The base type.</param>
|
||||
/// <returns>true if <paramref name="derivedType"/> derives from <paramref name="baseType"/>, overwise false.</returns>
|
||||
private static bool IsDerived(TypeDefinition derivedType, TypeDefinition baseType) |
||||
{ |
||||
while (derivedType != null && derivedType.BaseType != null) |
||||
{ |
||||
if (AreSameType(derivedType.BaseType, baseType)) |
||||
return true; |
||||
derivedType = derivedType.BaseType.Resolve(); |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Checks whether both <see cref="TypeReference"/> instances references the same type.
|
||||
/// </summary>
|
||||
/// <param name="ref1">The first type reference.</param>
|
||||
/// <param name="ref2">The second type reference.</param>
|
||||
/// <returns>true if both instances references the same type, overwise false.</returns>
|
||||
private static bool AreSameType(TypeReference ref1, TypeReference ref2) |
||||
{ |
||||
if (ref1 == ref2) |
||||
return true; |
||||
|
||||
if (ref1.Name != ref2.Name || ref1.FullName != ref2.FullName) |
||||
return false; |
||||
|
||||
return ref1.Resolve() == ref2.Resolve(); |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Checkes whether the given method and the analyzed one has identical lists of parameters.
|
||||
/// </summary>
|
||||
/// <param name="method">The method to test.</param>
|
||||
/// <returns>true if both methods has the same parameters, otherwise false.</returns>
|
||||
private bool HaveTheSameParameters(MethodDefinition method) |
||||
{ |
||||
if (analyzedMethod.HasParameters) |
||||
{ |
||||
return CompareParameterLists(analyzedMethod.Parameters, method.Parameters); |
||||
} |
||||
else |
||||
{ |
||||
return !method.HasParameters; |
||||
} |
||||
} |
||||
|
||||
/// <summary>
|
||||
/// Compares the list of method's parameters.
|
||||
/// </summary>
|
||||
/// <param name="coll1">The first list to compare.</param>
|
||||
/// <param name="coll2">The second list to copare.</param>
|
||||
/// <returns>true if both list have parameters of the same types at the same positions.</returns>
|
||||
private static bool CompareParameterLists(Mono.Collections.Generic.Collection<ParameterDefinition> coll1, Mono.Collections.Generic.Collection<ParameterDefinition> coll2) |
||||
{ |
||||
if (coll1.Count != coll2.Count) |
||||
return false; |
||||
|
||||
for (int index = 0; index < coll1.Count; index++) |
||||
{ |
||||
var param1 = coll1[index]; |
||||
var param2 = coll2[index]; |
||||
if (param1.Attributes != param2.Attributes || !AreSameType(param1.ParameterType, param2.ParameterType)) |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
} |
||||
} |
||||
Loading…
Reference in new issue