Browse Source

Merge branch 'master' of git://github.com/icsharpcode/ILSpy into Debugger

pull/191/merge
Eusebiu Marcu 14 years ago
parent
commit
a63a66cb5c
  1. 5
      ICSharpCode.Decompiler/Ast/AstBuilder.cs
  2. 79
      ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs
  3. 28
      ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs
  4. 52
      ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs
  5. 15
      ICSharpCode.Decompiler/DecompilerSettings.cs
  6. 5
      ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs
  7. 38
      ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs
  8. 8
      ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs
  9. 14
      ICSharpCode.Decompiler/ILAst/ILCodes.cs
  10. 1
      ICSharpCode.Decompiler/ILAst/ILInlining.cs
  11. 247
      ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs
  12. 19
      ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs
  13. 25
      ICSharpCode.Decompiler/Tests/DelegateConstruction.cs
  14. 104
      ICSharpCode.Decompiler/Tests/InitializerTests.cs
  15. 18
      ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs
  16. 18
      ILSpy/AssemblyList.cs
  17. 4
      ILSpy/ILSpy.csproj
  18. 6
      ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs
  19. 58
      ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs
  20. 93
      ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs
  21. 81
      ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs
  22. 66
      ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs
  23. 62
      ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs
  24. 2
      ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs
  25. 2
      ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs
  26. 250
      ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs
  27. 71
      ILSpy/TreeNodes/AssemblyListTreeNode.cs
  28. 27
      ILSpy/TreeNodes/EventTreeNode.cs

5
ICSharpCode.Decompiler/Ast/AstBuilder.cs

@ -880,6 +880,11 @@ namespace ICSharpCode.Decompiler.Ast @@ -880,6 +880,11 @@ namespace ICSharpCode.Decompiler.Ast
astEvent.RemoveAccessor.WithAnnotation(methodMapping);
}
MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
if (accessor.IsVirtual ^ !accessor.IsNewSlot) {
if (TypesHierarchyHelpers.FindBaseMethods(accessor).Any())
astEvent.Modifiers |= Modifiers.New;
}
return astEvent;
}
}

79
ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs

@ -703,24 +703,73 @@ namespace ICSharpCode.Decompiler.Ast @@ -703,24 +703,73 @@ namespace ICSharpCode.Decompiler.Ast
return new Ast.YieldBreakStatement();
case ILCode.YieldReturn:
return new Ast.YieldStatement { Expression = arg1 };
case ILCode.InitCollection: {
ObjectCreateExpression oce = (ObjectCreateExpression)arg1;
oce.Initializer = new ArrayInitializerExpression();
case ILCode.InitObject:
case ILCode.InitCollection:
{
ArrayInitializerExpression initializer = new ArrayInitializerExpression();
for (int i = 1; i < args.Count; i++) {
ArrayInitializerExpression aie = args[i] as ArrayInitializerExpression;
if (aie != null && aie.Elements.Count == 1)
oce.Initializer.Elements.Add(aie.Elements.Single().Detach());
else
oce.Initializer.Elements.Add(args[i]);
Match m = objectInitializerPattern.Match(args[i]);
if (m.Success) {
MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("left").Single();
initializer.Elements.Add(
new NamedArgumentExpression {
Identifier = mre.MemberName,
Expression = m.Get<Expression>("right").Single().Detach()
}.CopyAnnotationsFrom(mre));
} else {
m = collectionInitializerPattern.Match(args[i]);
if (m.Success) {
if (m.Get("arg").Count() == 1) {
initializer.Elements.Add(m.Get<Expression>("arg").Single().Detach());
} else {
ArrayInitializerExpression argList = new ArrayInitializerExpression();
foreach (var expr in m.Get<Expression>("arg")) {
argList.Elements.Add(expr.Detach());
}
initializer.Elements.Add(argList);
}
} else {
initializer.Elements.Add(args[i]);
}
}
}
ObjectCreateExpression oce = arg1 as ObjectCreateExpression;
if (oce != null) {
oce.Initializer = initializer;
return oce;
} else {
return new AssignmentExpression(arg1, initializer);
}
return oce;
}
case ILCode.InitCollectionAddMethod: {
var collectionInit = new ArrayInitializerExpression();
collectionInit.Elements.AddRange(args);
return collectionInit;
}
default: throw new Exception("Unknown OpCode: " + byteCode.Code);
case ILCode.InitializedObject:
return new InitializedObjectExpression();
default:
throw new Exception("Unknown OpCode: " + byteCode.Code);
}
}
static readonly AstNode objectInitializerPattern = new AssignmentExpression(
new MemberReferenceExpression {
Target = new InitializedObjectExpression()
}.WithName("left"),
new AnyNode("right")
);
static readonly AstNode collectionInitializerPattern = new InvocationExpression {
Target = new MemberReferenceExpression {
Target = new InitializedObjectExpression(),
MemberName = "Add"
},
Arguments = { new Repeat(new AnyNode("arg")) }
};
sealed class InitializedObjectExpression : IdentifierExpression
{
public InitializedObjectExpression() : base("__initialized_object__") {}
protected override bool DoMatch(AstNode other, Match match)
{
return other is InitializedObjectExpression;
}
}

28
ICSharpCode.Decompiler/Ast/Transforms/DelegateConstruction.cs

@ -111,6 +111,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -111,6 +111,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
{
if (!context.Settings.AnonymousMethods)
return false; // anonymous method decompilation is disabled
if (target != null && !(target is IdentifierExpression || target is ThisReferenceExpression || target is NullReferenceExpression))
return false; // don't copy arbitrary expressions, deal with identifiers only
// Anonymous methods are defined in the same assembly
MethodDefinition method = methodRef.ResolveWithinSameModule();
@ -175,7 +177,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -175,7 +177,7 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
return true;
}
static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass)
internal static bool IsPotentialClosure(DecompilerContext context, TypeDefinition potentialDisplayClass)
{
if (potentialDisplayClass == null || !potentialDisplayClass.IsCompilerGeneratedOrIsInCompilerGeneratedClass())
return false;
@ -328,12 +330,30 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -328,12 +330,30 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
if (right is ThisReferenceExpression) {
isParameter = true;
} else if (right is IdentifierExpression) {
// handle parameters only if the whole method contains no other occurrance except for 'right'
// handle parameters only if the whole method contains no other occurrence except for 'right'
ILVariable v = right.Annotation<ILVariable>();
isParameter = v.IsParameter && parameterOccurrances.Count(c => c == v) == 1;
if (!isParameter && TypeAnalysis.IsSameType(v.Type, fieldDef.FieldType) && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
if (!isParameter && IsPotentialClosure(context, v.Type.ResolveWithinSameModule())) {
// parent display class within the same method
// (closure2.localsX = closure1;)
isDisplayClassParentPointerAssignment = true;
}
} else if (right is MemberReferenceExpression) {
// copy of parent display class reference from an outer lambda
// closure2.localsX = this.localsY
MemberReferenceExpression mre = m.Get<MemberReferenceExpression>("right").Single();
do {
// descend into the targets of the mre as long as the field types are closures
FieldDefinition fieldDef2 = mre.Annotation<FieldReference>().ResolveWithinSameModule();
if (fieldDef2 == null || !IsPotentialClosure(context, fieldDef2.FieldType.ResolveWithinSameModule())) {
break;
}
// if we finally get to a this reference, it's copying a display class parent pointer
if (mre.Target is ThisReferenceExpression) {
isDisplayClassParentPointerAssignment = true;
}
mre = mre.Target as MemberReferenceExpression;
} while (mre != null);
}
if (isParameter || isDisplayClassParentPointerAssignment) {
dict[fieldDef] = right;
@ -349,6 +369,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms @@ -349,6 +369,8 @@ namespace ICSharpCode.Decompiler.Ast.Transforms
// Now create variables for all fields of the display class (except for those that we already handled as parameters)
List<Tuple<AstType, string>> variablesToDeclare = new List<Tuple<AstType, string>>();
foreach (FieldDefinition field in type.Fields) {
if (field.IsStatic)
continue; // skip static fields
if (dict.ContainsKey(field)) // skip field if it already was handled as parameter
continue;
EnsureVariableNameIsAvailable(blockStatement, field.Name);

52
ICSharpCode.Decompiler/Ast/TypesHierarchyHelpers.cs

@ -61,6 +61,14 @@ namespace ICSharpCode.Decompiler.Ast @@ -61,6 +61,14 @@ namespace ICSharpCode.Decompiler.Ast
return FindBaseProperties(childProperty).Any(m => m == parentProperty);
}
public static bool IsBaseEvent(EventDefinition parentEvent, EventDefinition childEvent)
{
if (parentEvent.Name != childEvent.Name)
return false;
return FindBaseEvents(childEvent).Any(m => m == parentEvent);
}
public static IEnumerable<MethodDefinition> FindBaseMethods(MethodDefinition method)
{
if (method == null)
@ -97,6 +105,25 @@ namespace ICSharpCode.Decompiler.Ast @@ -97,6 +105,25 @@ namespace ICSharpCode.Decompiler.Ast
}
public static IEnumerable<EventDefinition> FindBaseEvents(EventDefinition eventDef)
{
if (eventDef == null)
throw new ArgumentNullException("eventDef");
var typeContext = CreateGenericContext(eventDef.DeclaringType);
var gEvent = typeContext.ApplyTo(eventDef);
foreach (var baseType in BaseTypes(eventDef.DeclaringType))
foreach (var baseEvent in baseType.Item.Events)
if (MatchEvent(baseType.ApplyTo(baseEvent), gEvent) && IsVisbleFrom(baseEvent, eventDef)) {
yield return baseEvent;
var anyEventAccessor = baseEvent.AddMethod ?? baseEvent.RemoveMethod;
if (!(anyEventAccessor.IsNewSlot ^ anyEventAccessor.IsVirtual))
yield break;
}
}
private static bool IsVisbleFrom(MethodDefinition baseCandidate, MethodDefinition method)
{
if (baseCandidate.IsPrivate)
@ -115,6 +142,15 @@ namespace ICSharpCode.Decompiler.Ast @@ -115,6 +142,15 @@ namespace ICSharpCode.Decompiler.Ast
return false;
}
private static bool IsVisbleFrom(EventDefinition baseCandidate, EventDefinition eventDef)
{
if (baseCandidate.AddMethod != null && eventDef.AddMethod != null && IsVisbleFrom(baseCandidate.AddMethod, eventDef.AddMethod))
return true;
if (baseCandidate.RemoveMethod != null && eventDef.RemoveMethod != null && IsVisbleFrom(baseCandidate.RemoveMethod, eventDef.RemoveMethod))
return true;
return false;
}
private static bool MatchMethod(GenericContext<MethodDefinition> candidate, GenericContext<MethodDefinition> method)
{
var mCandidate = candidate.Item;
@ -172,6 +208,22 @@ namespace ICSharpCode.Decompiler.Ast @@ -172,6 +208,22 @@ namespace ICSharpCode.Decompiler.Ast
return true;
}
private static bool MatchEvent(GenericContext<EventDefinition> candidate, GenericContext<EventDefinition> ev)
{
var mCandidate = candidate.Item;
var mEvent = ev.Item;
if (mCandidate.Name != mEvent.Name)
return false;
if ((mCandidate.AddMethod ?? mCandidate.RemoveMethod).HasOverrides)
return false;
if (!IsSameType(candidate.ResolveWithContext(mCandidate.EventType), ev.ResolveWithContext(mEvent.EventType)))
return false;
return true;
}
private static bool MatchParameters(GenericContext<ParameterDefinition> baseParameterType, GenericContext<ParameterDefinition> parameterType)
{
var baseParam = baseParameterType.ResolveWithContext(baseParameterType.Item.ParameterType);

15
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -179,6 +179,21 @@ namespace ICSharpCode.Decompiler @@ -179,6 +179,21 @@ namespace ICSharpCode.Decompiler
}
}
bool objectCollectionInitializers = true;
/// <summary>
/// Gets/Sets whether to use C# 3.0 object/collection initializers
/// </summary>
public bool ObjectOrCollectionInitializers {
get { return objectCollectionInitializers; }
set {
if (objectCollectionInitializers != value) {
objectCollectionInitializers = value;
OnPropertyChanged("ObjectCollectionInitializers");
}
}
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)

5
ICSharpCode.Decompiler/Disassembler/DisassemblerHelpers.cs

@ -126,7 +126,10 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -126,7 +126,10 @@ namespace ICSharpCode.Decompiler.Disassembler
writer.Write(string.Join(", ", at.Dimensions));
writer.Write(']');
} else if (type is GenericParameter) {
writer.WriteReference(type.Name, type);
writer.Write('!');
if (((GenericParameter)type).Owner.GenericParameterType == GenericParameterType.Method)
writer.Write('!');
writer.Write(type.Name);
} else if (type is ByReferenceType) {
((ByReferenceType)type).ElementType.WriteTo(writer, onlyName, shortName);
writer.Write('&');

38
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs

@ -123,6 +123,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -123,6 +123,7 @@ namespace ICSharpCode.Decompiler.Disassembler
method.ReturnType.WriteTo(output);
output.Write(' ');
output.Write(DisassemblerHelpers.Escape(method.Name));
WriteTypeParameters(output, method);
//( params )
output.Write(" (");
@ -331,6 +332,7 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -331,6 +332,7 @@ namespace ICSharpCode.Decompiler.Disassembler
WriteFlags(type.Attributes & ~masks, typeAttributes);
output.Write(DisassemblerHelpers.Escape(type.Name));
WriteTypeParameters(output, type);
output.MarkFoldStart(defaultCollapsed: isInType);
output.WriteLine();
@ -416,6 +418,42 @@ namespace ICSharpCode.Decompiler.Disassembler @@ -416,6 +418,42 @@ namespace ICSharpCode.Decompiler.Disassembler
CloseBlock("End of class " + type.FullName);
isInType = oldIsInType;
}
void WriteTypeParameters(ITextOutput output, IGenericParameterProvider p)
{
if (p.HasGenericParameters) {
output.Write('<');
for (int i = 0; i < p.GenericParameters.Count; i++) {
if (i > 0)
output.Write(", ");
GenericParameter gp = p.GenericParameters[i];
if (gp.HasReferenceTypeConstraint) {
output.Write("class ");
} else if (gp.HasNotNullableValueTypeConstraint) {
output.Write("valuetype ");
}
if (gp.HasConstraints) {
output.Write('(');
for (int j = 0; j < gp.Constraints.Count; j++) {
if (j > 0)
output.Write(", ");
gp.Constraints[j].WriteTo(output, true);
}
output.Write(") ");
}
if (gp.HasDefaultConstructorConstraint) {
output.Write(".ctor ");
}
if (gp.IsContravariant) {
output.Write('-');
} else if (gp.IsCovariant) {
output.Write('+');
}
output.Write(DisassemblerHelpers.Escape(gp.Name));
}
output.Write('>');
}
}
#endregion
#region Helper methods

8
ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs

@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -27,7 +27,7 @@ namespace ICSharpCode.Decompiler.ILAst
TransformDecimalCtorToConstant,
SimplifyLdObjAndStObj,
TransformArrayInitializers,
TransformCollectionInitializers,
TransformObjectInitializers,
MakeAssignmentExpression,
IntroducePostIncrement,
InlineVariables2,
@ -119,10 +119,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -119,10 +119,10 @@ namespace ICSharpCode.Decompiler.ILAst
modified |= block.RunOptimization(SimplifyLdObjAndStObj);
if (abortBeforeStep == ILAstOptimizationStep.TransformArrayInitializers) return;
modified |= block.RunOptimization(Initializers.TransformArrayInitializers);
modified |= block.RunOptimization(TransformArrayInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformCollectionInitializers) return;
modified |= block.RunOptimization(Initializers.TransformCollectionInitializers);
if (abortBeforeStep == ILAstOptimizationStep.TransformObjectInitializers) return;
modified |= block.RunOptimization(TransformObjectInitializers);
if (abortBeforeStep == ILAstOptimizationStep.MakeAssignmentExpression) return;
modified |= block.RunOptimization(MakeAssignmentExpression);

14
ICSharpCode.Decompiler/ILAst/ILCodes.cs

@ -238,8 +238,18 @@ namespace ICSharpCode.Decompiler.ILAst @@ -238,8 +238,18 @@ namespace ICSharpCode.Decompiler.ILAst
LogicOr,
NullCoalescing,
InitArray, // Array Initializer
InitCollection, // Collection Initializer: first arg is newobj, remaining args are InitCollectionAddMethod method calls
InitCollectionAddMethod,
// new Class { Prop = 1, Collection = { { 2, 3 }, {4, 5} }}
// is represented as:
// InitObject(newobj Class,
// CallSetter(Prop, InitializedObject, 1),
// InitCollection(CallGetter(Collection, InitializedObject))),
// Call(Add, InitializedObject, 2, 3),
// Call(Add, InitializedObject, 4, 5)))
InitObject, // Object initializer: first arg is newobj, remaining args are the initializing statements
InitCollection, // Collection initializer: first arg is newobj, remaining args are the initializing statements
InitializedObject, // Refers the the object being initialized (refers to first arg in parent InitObject or InitCollection instruction)
TernaryOp, // ?:
LoopOrSwitchBreak,
LoopContinue,

1
ICSharpCode.Decompiler/ILAst/ILInlining.cs

@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.ILAst @@ -197,6 +197,7 @@ namespace ICSharpCode.Decompiler.ILAst
{
switch (inlinedExpression.Code) {
case ILCode.InitArray:
case ILCode.InitObject:
case ILCode.InitCollection:
case ILCode.DefaultValue:
return true;

247
ICSharpCode.Decompiler/ILAst/InitializerPeepholeTransforms.cs

@ -3,8 +3,8 @@ @@ -3,8 +3,8 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Mono.Cecil;
namespace ICSharpCode.Decompiler.ILAst
@ -12,9 +12,10 @@ namespace ICSharpCode.Decompiler.ILAst @@ -12,9 +12,10 @@ namespace ICSharpCode.Decompiler.ILAst
/// <summary>
/// IL AST transformation that introduces array, object and collection initializers.
/// </summary>
public class Initializers
partial class ILAstOptimizer
{
public static bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
#region Array Initializers
bool TransformArrayInitializers(List<ILNode> body, ILExpression expr, int pos)
{
ILVariable v, v2, v3;
ILExpression newarrExpr;
@ -138,50 +139,228 @@ namespace ICSharpCode.Decompiler.ILAst @@ -138,50 +139,228 @@ namespace ICSharpCode.Decompiler.ILAst
return false;
}
}
#endregion
public static bool TransformCollectionInitializers(List<ILNode> body, ILExpression expr, int pos)
/// <summary>
/// Handles both object and collection initializers.
/// </summary>
bool TransformObjectInitializers(List<ILNode> body, ILExpression expr, int pos)
{
ILVariable v, v2;
if (!context.Settings.ObjectOrCollectionInitializers)
return false;
Debug.Assert(body[pos] == expr); // should be called for top-level expressions only
ILVariable v;
ILExpression newObjExpr;
MethodReference ctor;
List<ILExpression> ctorArgs;
if (expr.Match(ILCode.Stloc, out v, out newObjExpr) &&
newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs))
{
TypeDefinition td = ctor.DeclaringType.Resolve();
if (td == null || !td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections"))
// v = newObj(ctor, ctorArgs)
if (!(expr.Match(ILCode.Stloc, out v, out newObjExpr) && newObjExpr.Match(ILCode.Newobj, out ctor, out ctorArgs)))
return false;
int originalPos = pos;
// don't use object initializer syntax for closures
if (Ast.Transforms.DelegateConstruction.IsPotentialClosure(context, ctor.DeclaringType.ResolveWithinSameModule()))
return false;
ILExpression initializer = ParseObjectInitializer(body, ref pos, v, newObjExpr, IsCollectionType(ctor.DeclaringType));
if (initializer.Arguments.Count == 1) // only newobj argument, no initializer elements
return false;
int totalElementCount = pos - originalPos - 1; // totalElementCount: includes elements from nested collections
Debug.Assert(totalElementCount >= initializer.Arguments.Count - 1);
// Verify that we can inline 'v' into the next instruction:
if (pos >= body.Count)
return false; // reached end of block, but there should be another instruction which consumes the initialized object
ILInlining inlining = new ILInlining(method);
// one ldloc for each initializer argument, and another ldloc for the use of the initialized object
if (inlining.numLdloc.GetOrDefault(v) != totalElementCount + 1)
return false;
if (!(inlining.numStloc.GetOrDefault(v) == 1 && inlining.numLdloca.GetOrDefault(v) == 0))
return false;
ILExpression nextExpr = body[pos] as ILExpression;
if (!inlining.CanInlineInto(nextExpr, v, initializer))
return false;
expr.Arguments[0] = initializer;
// remove all the instructions that were pulled into the initializer
body.RemoveRange(originalPos + 1, pos - originalPos - 1);
// now that we know that it's an object initializer, change all the first arguments to 'InitializedObject'
ChangeFirstArgumentToInitializedObject(initializer);
inlining = new ILInlining(method);
inlining.InlineIfPossible(body, ref originalPos);
return true;
}
/// <summary>
/// Gets whether the type supports collection initializers.
/// </summary>
static bool IsCollectionType(TypeReference tr)
{
if (tr == null)
return false;
TypeDefinition td = tr.Resolve();
return td != null && td.Interfaces.Any(intf => intf.Name == "IEnumerable" && intf.Namespace == "System.Collections");
}
/// <summary>
/// Gets whether 'expr' represents a setter in an object initializer.
/// ('CallvirtSetter(Property, v, value)')
/// </summary>
static bool IsSetterInObjectInitializer(ILExpression expr)
{
if (expr == null)
return false;
if (expr.Code == ILCode.CallvirtSetter || expr.Code == ILCode.Stfld) {
return expr.Arguments.Count == 2;
}
return false;
}
/// <summary>
/// Gets whether 'expr' represents the invocation of an 'Add' method in a collection initializer.
/// </summary>
static bool IsAddMethodCall(ILExpression expr)
{
MethodReference addMethod;
List<ILExpression> args;
if (expr.Match(ILCode.Callvirt, out addMethod, out args)) {
if (addMethod.Name == "Add" && addMethod.HasThis) {
return args.Count >= 2;
}
}
return false;
}
/// <summary>
/// Parses an object initializer.
/// </summary>
/// <param name="body">ILAst block</param>
/// <param name="pos">
/// Input: position of the instruction assigning to 'v'.
/// Output: first position after the object initializer
/// </param>
/// <param name="v">The variable that holds the object being initialized</param>
/// <param name="newObjExpr">The newobj instruction</param>
/// <returns>InitObject instruction</returns>
ILExpression ParseObjectInitializer(List<ILNode> body, ref int pos, ILVariable v, ILExpression newObjExpr, bool isCollection)
{
Debug.Assert(((ILExpression)body[pos]).Code == ILCode.Stloc);
// Take care not to modify any existing ILExpressions in here.
// We just construct new ones around the old ones, any modifications must wait until the whole
// object/collection initializer was analyzed.
ILExpression objectInitializer = new ILExpression(isCollection ? ILCode.InitCollection : ILCode.InitObject, null, newObjExpr);
List<ILExpression> initializerStack = new List<ILExpression>();
initializerStack.Add(objectInitializer);
while (++pos < body.Count) {
ILExpression nextExpr = body[pos] as ILExpression;
if (IsSetterInObjectInitializer(nextExpr)) {
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, false))
break;
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
} else if (IsAddMethodCall(nextExpr)) {
if (!AdjustInitializerStack(initializerStack, nextExpr.Arguments[0], v, true))
break;
initializerStack[initializerStack.Count - 1].Arguments.Add(nextExpr);
} else {
// can't match any more initializers: end of object initializer
break;
}
}
return objectInitializer;
}
static bool AdjustInitializerStack(List<ILExpression> initializerStack, ILExpression argument, ILVariable v, bool isCollection)
{
// Argument is of the form 'getter(getter(...(v)))'
// Unpack it into a list of getters:
List<ILExpression> getters = new List<ILExpression>();
while (argument.Code == ILCode.CallvirtGetter || argument.Code == ILCode.Ldfld) {
getters.Add(argument);
if (argument.Arguments.Count != 1)
return false;
argument = argument.Arguments[0];
}
// Ensure that the final argument is 'v'
if (!argument.MatchLdloc(v))
return false;
// Now compare the getters with those that are currently active on the initializer stack:
int i;
for (i = 1; i <= Math.Min(getters.Count, initializerStack.Count - 1); i++) {
ILExpression g1 = initializerStack[i].Arguments[0]; // getter stored in initializer
ILExpression g2 = getters[getters.Count - i]; // matching getter from argument
if (g1.Operand != g2.Operand) {
// operands differ, so we abort the comparison
break;
}
}
// Remove all initializers from the stack that were not matched with one from the argument:
initializerStack.RemoveRange(i, initializerStack.Count - i);
// Now create new initializers for the remaining arguments:
for (; i <= getters.Count; i++) {
ILExpression g = getters[getters.Count - i];
MemberReference mr = (MemberReference)g.Operand;
TypeReference returnType;
if (mr is FieldReference)
returnType = TypeAnalysis.GetFieldType((FieldReference)mr);
else
returnType = TypeAnalysis.SubstituteTypeArgs(((MethodReference)mr).ReturnType, mr);
// This is a collection: we can convert Add() calls into a collection initializer
ILExpression collectionInitializer = new ILExpression(ILCode.InitCollection, null, newObjExpr);
bool anyAdded = false;
while(pos + 1 < body.Count) {
ILExpression nextExpr = body[pos + 1] as ILExpression;
MethodReference addMethod;
List<ILExpression> args;
if (nextExpr.Match(ILCode.Callvirt, out addMethod, out args) &&
addMethod.Name == "Add" &&
addMethod.HasThis &&
args.Count >= 2 &&
args[0].Match(ILCode.Ldloc, out v2) &&
v == v2)
{
nextExpr.Code = ILCode.InitCollectionAddMethod;
nextExpr.Arguments.RemoveAt(0);
collectionInitializer.Arguments.Add(nextExpr);
body.RemoveAt(pos + 1);
anyAdded = true;
ILExpression nestedInitializer = new ILExpression(
IsCollectionType(returnType) ? ILCode.InitCollection : ILCode.InitObject,
null, g);
// add new initializer to its parent:
ILExpression parentInitializer = initializerStack[initializerStack.Count - 1];
if (parentInitializer.Code == ILCode.InitCollection) {
// can't add children to collection initializer
if (parentInitializer.Arguments.Count == 1) {
// convert empty collection initializer to object initializer
parentInitializer.Code = ILCode.InitObject;
} else {
break;
return false;
}
}
// ensure we added at least one additional arg to the collection initializer:
if (anyAdded) {
expr.Arguments[0] = collectionInitializer;
parentInitializer.Arguments.Add(nestedInitializer);
initializerStack.Add(nestedInitializer);
}
ILExpression lastInitializer = initializerStack[initializerStack.Count - 1];
if (isCollection) {
return lastInitializer.Code == ILCode.InitCollection;
} else {
if (lastInitializer.Code == ILCode.InitCollection) {
if (lastInitializer.Arguments.Count == 1) {
// convert empty collection initializer to object initializer
lastInitializer.Code = ILCode.InitObject;
return true;
} else {
return false;
}
} else {
return true;
}
}
return false;
}
static void ChangeFirstArgumentToInitializedObject(ILExpression initializer)
{
// Go through all elements in the initializer (so skip the newobj-instr. at the start)
for (int i = 1; i < initializer.Arguments.Count; i++) {
ILExpression element = initializer.Arguments[i];
if (element.Code == ILCode.InitCollection || element.Code == ILCode.InitObject) {
// nested collection/object initializer
ILExpression getCollection = element.Arguments[0];
getCollection.Arguments[0] = new ILExpression(ILCode.InitializedObject, null);
ChangeFirstArgumentToInitializedObject(element); // handle the collection elements
} else {
element.Arguments[0] = new ILExpression(ILCode.InitializedObject, null);
}
}
}
}
}

19
ICSharpCode.Decompiler/ILAst/TypeAnalysis.cs

@ -335,18 +335,13 @@ namespace ICSharpCode.Decompiler.ILAst @@ -335,18 +335,13 @@ namespace ICSharpCode.Decompiler.ILAst
}
return ctor.DeclaringType;
}
case ILCode.InitObject:
case ILCode.InitCollection:
return InferTypeForExpression(expr.Arguments[0], expectedType);
case ILCode.InitCollectionAddMethod:
{
MethodReference addMethod = (MethodReference)expr.Operand;
if (forceInferChildren) {
for (int i = 0; i < addMethod.Parameters.Count; i++) {
InferTypeForExpression(expr.Arguments[i], SubstituteTypeArgs(addMethod.Parameters[i].ParameterType, addMethod));
}
}
return addMethod.DeclaringType;
}
case ILCode.InitializedObject:
// expectedType should always be known due to the parent method call / property setter
Debug.Assert(expectedType != null);
return expectedType;
#endregion
#region Load/Store Fields
case ILCode.Ldfld:
@ -753,12 +748,12 @@ namespace ICSharpCode.Decompiler.ILAst @@ -753,12 +748,12 @@ namespace ICSharpCode.Decompiler.ILAst
return resultType;
}
static TypeReference GetFieldType(FieldReference fieldReference)
public static TypeReference GetFieldType(FieldReference fieldReference)
{
return SubstituteTypeArgs(UnpackModifiers(fieldReference.FieldType), fieldReference);
}
static TypeReference SubstituteTypeArgs(TypeReference type, MemberReference member)
public static TypeReference SubstituteTypeArgs(TypeReference type, MemberReference member)
{
if (type is TypeSpecification) {
ArrayType arrayType = type as ArrayType;

25
ICSharpCode.Decompiler/Tests/DelegateConstruction.cs

@ -25,21 +25,25 @@ public static class DelegateConstruction @@ -25,21 +25,25 @@ public static class DelegateConstruction
public Action CaptureOfThisAndParameterInForEach(int a)
{
foreach (var item in Enumerable.Empty<int>()) {
return delegate {
CaptureOfThisAndParameter(item + a);
};
foreach (int item in Enumerable.Empty<int>()) {
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a);
};
}
}
return null;
}
public Action CaptureOfThisAndParameterInForEachWithItemCopy(int a)
{
foreach (var item in Enumerable.Empty<int>()) {
foreach (int item in Enumerable.Empty<int>()) {
int copyOfItem = item;
return delegate {
CaptureOfThisAndParameter(item + a + copyOfItem);
};
if (item > 0) {
return delegate {
CaptureOfThisAndParameter(item + a + copyOfItem);
};
}
}
return null;
}
@ -162,4 +166,9 @@ public static class DelegateConstruction @@ -162,4 +166,9 @@ public static class DelegateConstruction
{
return b => c => a + b + c;
}
public static Func<int, Func<int, Func<int, int>>> CurriedAddition2(int a)
{
return b => c => d => a + b + c + d;
}
}

104
ICSharpCode.Decompiler/Tests/InitializerTests.cs

@ -17,6 +17,23 @@ public class InitializerTests @@ -17,6 +17,23 @@ public class InitializerTests
c,
d
}
class Data
{
public InitializerTests.MyEnum a
{
get;
set;
}
public List<InitializerTests.MyEnum2> PropertyList
{
get;
set;
}
public List<InitializerTests.MyEnum2> FieldList = new List<InitializerTests.MyEnum2>();
public InitializerTests.Data MoreData { get; set; }
}
// Helper methods used to ensure initializers used within expressions work correctly
static void X(object a, object b)
@ -140,4 +157,91 @@ public class InitializerTests @@ -140,4 +157,91 @@ public class InitializerTests
{ InitializerTests.MyEnum.b, InitializerTests.MyEnum2.d }
});
}
public static void NotACollectionInitializer()
{
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
X(Y(), list);
}
public static void ObjectInitializer()
{
X(Y(), new Data
{
a = InitializerTests.MyEnum.a
});
}
public static void NotAObjectInitializer()
{
Data data = new InitializerTests.Data();
data.a = InitializerTests.MyEnum.a;
X(Y(), data);
}
public static void ObjectInitializerAssignCollectionToField()
{
X(Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
FieldList = new List<InitializerTests.MyEnum2>
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerAddToCollectionInField()
{
X(Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
FieldList =
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerAssignCollectionToProperty()
{
X(Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
PropertyList = new List<InitializerTests.MyEnum2>
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerAddToCollectionInProperty()
{
X(Y(), new InitializerTests.Data
{
a = InitializerTests.MyEnum.a,
PropertyList =
{
InitializerTests.MyEnum2.c,
InitializerTests.MyEnum2.d
}
});
}
public static void ObjectInitializerWithInitializationOfNestedObjects()
{
X(Y(), new InitializerTests.Data
{
MoreData =
{
a = InitializerTests.MyEnum.a
}
});
}
}

18
ICSharpCode.Decompiler/Tests/Types/S_TypeMemberDeclarations.cs

@ -669,3 +669,21 @@ namespace MethodHideGeneric2 @@ -669,3 +669,21 @@ namespace MethodHideGeneric2
}
}
}
//$$ EventHiding
namespace EventHiding
{
public class A
{
public virtual event EventHandler E;
public event EventHandler F;
}
public class B : A
{
public new virtual event EventHandler E;
public new event EventHandler F;
}
public class C : B
{
public override event EventHandler E;
}
}

18
ILSpy/AssemblyList.cs

@ -17,7 +17,9 @@ @@ -17,7 +17,9 @@
// DEALINGS IN THE SOFTWARE.
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.IO;
@ -143,5 +145,21 @@ namespace ICSharpCode.ILSpy @@ -143,5 +145,21 @@ namespace ICSharpCode.ILSpy
assemblies.Remove(assembly);
}
}
public void Sort(IComparer<LoadedAssembly> comparer)
{
Sort(0, int.MaxValue, comparer);
}
public void Sort(int index, int count, IComparer<LoadedAssembly> comparer)
{
App.Current.Dispatcher.VerifyAccess();
lock (assemblies) {
List<LoadedAssembly> list = new List<LoadedAssembly>(assemblies);
list.Sort(index, Math.Min(count, list.Count - index), comparer);
assemblies.Clear();
assemblies.AddRange(list);
}
}
}
}

4
ILSpy/ILSpy.csproj

@ -137,7 +137,11 @@ @@ -137,7 +137,11 @@
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzeContextMenuEntry.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedEventAccessorsTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedEventOverridesTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedEventTreeNode.cs" />
<Compile Include="TreeNodes\Analyzer\Helpers.cs" />
<Compile Include="TreeNodes\Analyzer\ScopedWhereUsedAnalyzer.cs" />
<Compile Include="TreeNodes\IMemberTreeNode.cs" />
<Compile Include="TreeNodes\XamlResourceNode.cs" />
<Compile Include="TreeNodes\Analyzer\AnalyzedPropertyAccessorsTreeNode.cs" />

6
ILSpy/TreeNodes/Analyzer/AnalyzeContextMenuEntry.cs

@ -36,7 +36,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -36,7 +36,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
foreach (IMemberTreeNode node in selectedNodes) {
if (!(node.Member is FieldDefinition
|| node.Member is MethodDefinition
|| Analyzer.AnalyzedPropertyTreeNode.CanShow(node.Member)))
|| Analyzer.AnalyzedPropertyTreeNode.CanShow(node.Member)
|| Analyzer.AnalyzedEventTreeNode.CanShow(node.Member)))
return false;
}
return true;
@ -56,6 +57,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -56,6 +57,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
var propertyAnalyzer = Analyzer.AnalyzedPropertyTreeNode.TryCreateAnalyzer(node.Member);
if(propertyAnalyzer != null)
MainWindow.Instance.AddToAnalyzer(propertyAnalyzer);
var eventAnalyzer = Analyzer.AnalyzedEventTreeNode.TryCreateAnalyzer(node.Member);
if (eventAnalyzer != null)
MainWindow.Instance.AddToAnalyzer(eventAnalyzer);
}
}
}

58
ILSpy/TreeNodes/Analyzer/AnalyzedEventAccessorsTreeNode.cs

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
using System;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Description of AnalyzedEventAccessorsTreeNode.
/// </summary>
public class AnalyzedEventAccessorsTreeNode : AnalyzerTreeNode
{
EventDefinition analyzedEvent;
public AnalyzedEventAccessorsTreeNode(EventDefinition analyzedEvent)
{
if (analyzedEvent == null)
throw new ArgumentNullException("analyzedEvent");
this.analyzedEvent = analyzedEvent;
if (analyzedEvent.AddMethod != null)
this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.AddMethod, "add"));
if (analyzedEvent.RemoveMethod != null)
this.Children.Add(new AnalyzedEventAccessorTreeNode(analyzedEvent.RemoveMethod, "remove"));
foreach (var accessor in analyzedEvent.OtherMethods)
this.Children.Add(new AnalyzedEventAccessorTreeNode(accessor, null));
}
public override object Icon
{
get { return Images.Search; }
}
public override object Text
{
get { return "Accessors"; }
}
public static bool CanShow(EventDefinition property)
{
return !MainWindow.Instance.CurrentLanguage.ShowMember(property.AddMethod ?? property.RemoveMethod);
}
class AnalyzedEventAccessorTreeNode : AnalyzedMethodTreeNode
{
string name;
public AnalyzedEventAccessorTreeNode(MethodDefinition analyzedMethod, string name)
: base(analyzedMethod)
{
this.name = name;
}
public override object Text
{
get { return name ?? base.Text; }
}
}
}
}

93
ILSpy/TreeNodes/Analyzer/AnalyzedEventOverridesTreeNode.cs

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using ICSharpCode.Decompiler.Ast;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.TreeView;
using Mono.Cecil;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
class AnalyzedEventOverridesTreeNode : AnalyzerTreeNode
{
readonly EventDefinition analyzedEvent;
readonly ThreadingSupport threading;
public AnalyzedEventOverridesTreeNode(EventDefinition analyzedEvent)
{
if (analyzedEvent == null)
throw new ArgumentNullException("analyzedEvent");
this.analyzedEvent = analyzedEvent;
this.threading = new ThreadingSupport();
this.LazyLoading = true;
}
public override object Text
{
get { return "Overriden 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.CurrentAssemblyList.GetAssemblies(), ct);
}
IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> assemblies, CancellationToken ct)
{
assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null);
// 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 = analyzedEvent.Name;
string declTypeName = analyzedEvent.DeclaringType.FullName;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
if (!TypesHierarchyHelpers.IsBaseType(analyzedEvent.DeclaringType, type, resolveTypeArguments: false))
continue;
foreach (EventDefinition eventDef in type.Events) {
ct.ThrowIfCancellationRequested();
if (TypesHierarchyHelpers.IsBaseEvent(analyzedEvent, eventDef)) {
MethodDefinition anyAccessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
bool hidesParent = !anyAccessor.IsVirtual ^ anyAccessor.IsNewSlot;
yield return new AnalyzedEventTreeNode(eventDef, hidesParent ? "(hides) " : "");
}
}
}
}
public static bool CanShowAnalyzer(EventDefinition property)
{
var accessor = property.AddMethod ?? property.RemoveMethod;
return accessor.IsVirtual && !accessor.IsFinal && !accessor.DeclaringType.IsInterface;
}
}
}

81
ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs

@ -0,0 +1,81 @@ @@ -0,0 +1,81 @@
// 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 Mono.Cecil;
using ICSharpCode.Decompiler;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
class AnalyzedEventTreeNode : AnalyzerTreeNode
{
EventDefinition analyzedEvent;
string prefix;
public AnalyzedEventTreeNode(EventDefinition analyzedEvent, string prefix = "")
{
if (analyzedEvent == null)
throw new ArgumentNullException("analyzedMethod");
this.analyzedEvent = analyzedEvent;
this.prefix = prefix;
this.LazyLoading = true;
}
public override object Icon {
get { return EventTreeNode.GetIcon(analyzedEvent); }
}
public override object Text {
get {
// TODO: This way of formatting is not suitable for events which explicitly implement interfaces.
return prefix + Language.TypeToString(analyzedEvent.DeclaringType, true) + "." + EventTreeNode.GetText(analyzedEvent, Language); }
}
public override void ActivateItem(System.Windows.RoutedEventArgs e)
{
e.Handled = true;
MainWindow.Instance.JumpToReference(analyzedEvent);
}
protected override void LoadChildren()
{
if(AnalyzedEventAccessorsTreeNode.CanShow(analyzedEvent))
this.Children.Add(new AnalyzedEventAccessorsTreeNode(analyzedEvent));
if (AnalyzedEventOverridesTreeNode.CanShowAnalyzer(analyzedEvent))
this.Children.Add(new AnalyzedEventOverridesTreeNode(analyzedEvent));
}
public static AnalyzerTreeNode TryCreateAnalyzer(MemberReference member)
{
if (CanShow(member))
return new AnalyzedEventTreeNode(member as EventDefinition);
else
return null;
}
public static bool CanShow(MemberReference member)
{
var property = member as EventDefinition;
if (property == null)
return false;
return AnalyzedEventAccessorsTreeNode.CanShow(property)
|| AnalyzedEventOverridesTreeNode.CanShowAnalyzer(property);
}
}
}

66
ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs

@ -18,31 +18,33 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -18,31 +18,33 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
readonly bool showWrites; // true: show writes; false: show read access
readonly FieldDefinition analyzedField;
readonly ThreadingSupport threading;
public AnalyzedFieldAccessNode(FieldDefinition analyzedField, bool showWrites)
{
if (analyzedField == null)
throw new ArgumentNullException("analyzedField");
this.analyzedField = analyzedField;
this.showWrites = showWrites;
this.threading = new ThreadingSupport();
this.LazyLoading = true;
}
public override object Text {
public override object Text
{
get { return showWrites ? "Assigned By" : "Read By"; }
}
public override object Icon {
public override object Icon
{
get { return Images.Search; }
}
protected override void LoadChildren()
{
threading.LoadChildren(this, FetchChildren);
}
protected override void OnCollapsing()
{
if (threading.IsRunning) {
@ -51,44 +53,36 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -51,44 +53,36 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
this.Children.Clear();
}
}
IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct);
}
IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> assemblies, CancellationToken ct)
{
assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null);
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct));
var analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedField, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
string name = analyzedField.Name;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (MethodDefinition method in type.Methods) {
ct.ThrowIfCancellationRequested();
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
if (CanBeReference(instr.OpCode.Code)) {
FieldReference fr = instr.Operand as FieldReference;
if (fr != null && fr.Name == name && Helpers.IsReferencedBy(analyzedField.DeclaringType, fr.DeclaringType) && fr.Resolve() == analyzedField) {
found = true;
break;
}
string declTypeName = analyzedField.DeclaringType.FullName;
foreach (MethodDefinition method in type.Methods) {
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
if (CanBeReference(instr.OpCode.Code)) {
FieldReference fr = instr.Operand as FieldReference;
if (fr != null && fr.Name == name && Helpers.IsReferencedBy(analyzedField.DeclaringType, fr.DeclaringType) && fr.Resolve() == analyzedField) {
found = true;
break;
}
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
}
bool CanBeReference(Code code)
{
switch (code) {

62
ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs

@ -32,30 +32,32 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -32,30 +32,32 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
MethodDefinition analyzedMethod;
ThreadingSupport threading;
public AnalyzedMethodUsedByTreeNode(MethodDefinition analyzedMethod)
{
if (analyzedMethod == null)
throw new ArgumentNullException("analyzedMethod");
this.analyzedMethod = analyzedMethod;
this.threading = new ThreadingSupport();
this.LazyLoading = true;
}
public override object Text {
public override object Text
{
get { return "Used By"; }
}
public override object Icon {
public override object Icon
{
get { return Images.Search; }
}
protected override void LoadChildren()
{
threading.LoadChildren(this, FetchChildren);
}
protected override void OnCollapsing()
{
if (threading.IsRunning) {
@ -64,39 +66,31 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -64,39 +66,31 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
this.Children.Clear();
}
}
IEnumerable<SharpTreeNode> FetchChildren(CancellationToken ct)
{
return FindReferences(MainWindow.Instance.CurrentAssemblyList.GetAssemblies(), ct);
}
IEnumerable<SharpTreeNode> FindReferences(IEnumerable<LoadedAssembly> assemblies, CancellationToken ct)
{
assemblies = assemblies.Where(asm => asm.AssemblyDefinition != null);
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((LoadedAssembly asm) => FindReferences(asm, ct));
ScopedWhereUsedScopeAnalyzer<SharpTreeNode> analyzer;
analyzer = new ScopedWhereUsedScopeAnalyzer<SharpTreeNode>(analyzedMethod, FindReferencesInType);
return analyzer.PerformAnalysis(ct);
}
IEnumerable<SharpTreeNode> FindReferences(LoadedAssembly asm, CancellationToken ct)
IEnumerable<SharpTreeNode> FindReferencesInType(TypeDefinition type)
{
string name = analyzedMethod.Name;
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.AssemblyDefinition.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (MethodDefinition method in type.Methods) {
ct.ThrowIfCancellationRequested();
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
MethodReference mr = instr.Operand as MethodReference;
if (mr != null && mr.Name == name && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) {
found = true;
break;
}
foreach (MethodDefinition method in type.Methods) {
bool found = false;
if (!method.HasBody)
continue;
foreach (Instruction instr in method.Body.Instructions) {
MethodReference mr = instr.Operand as MethodReference;
if (mr != null && mr.Name == name && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) {
found = true;
break;
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
if (found)
yield return new AnalyzedMethodTreeNode(method);
}
}
}

2
ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs

@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -28,7 +28,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
public override object Text
{
get { return "Overrided By"; }
get { return "Overriden By"; }
}
public override object Icon

2
ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs

@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer @@ -31,7 +31,7 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
public override object Text
{
get { return "Overrided By"; }
get { return "Overriden By"; }
}
public override object Icon

250
ILSpy/TreeNodes/Analyzer/ScopedWhereUsedAnalyzer.cs

@ -0,0 +1,250 @@ @@ -0,0 +1,250 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using Mono.Cecil;
using ICSharpCode.NRefactory.Utils;
using ICSharpCode.TreeView;
namespace ICSharpCode.ILSpy.TreeNodes.Analyzer
{
/// <summary>
/// Determines the accessibility domain of a member for where-used analysis.
/// </summary>
internal class ScopedWhereUsedScopeAnalyzer<T>
{
private AssemblyDefinition assemblyScope;
private TypeDefinition typeScope;
private Accessibility memberAccessibility = Accessibility.Public;
private Accessibility typeAccessibility = Accessibility.Public;
private Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction;
private ScopedWhereUsedScopeAnalyzer(TypeDefinition type, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
{
this.typeScope = type;
this.assemblyScope = type.Module.Assembly;
this.typeAnalysisFunction = typeAnalysisFunction;
}
public ScopedWhereUsedScopeAnalyzer(MethodDefinition method, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(method.DeclaringType, typeAnalysisFunction)
{
switch (method.Attributes & MethodAttributes.MemberAccessMask) {
case MethodAttributes.Private:
default:
memberAccessibility = Accessibility.Private;
break;
case MethodAttributes.FamANDAssem:
memberAccessibility = Accessibility.FamilyAndInternal;
break;
case MethodAttributes.Family:
memberAccessibility = Accessibility.Family;
break;
case MethodAttributes.Assembly:
memberAccessibility = Accessibility.Internal;
break;
case MethodAttributes.FamORAssem:
memberAccessibility = Accessibility.FamilyOrInternal;
break;
case MethodAttributes.Public:
memberAccessibility = Accessibility.Public;
break;
}
}
public ScopedWhereUsedScopeAnalyzer(FieldDefinition field, Func<TypeDefinition, IEnumerable<T>> typeAnalysisFunction)
: this(field.DeclaringType, typeAnalysisFunction)
{
switch (field.Attributes & FieldAttributes.FieldAccessMask) {
case FieldAttributes.Private:
default:
memberAccessibility = Accessibility.Private;
break;
case FieldAttributes.FamANDAssem:
memberAccessibility = Accessibility.FamilyAndInternal;
break;
case FieldAttributes.Assembly:
memberAccessibility = Accessibility.Internal;
break;
case FieldAttributes.Family:
memberAccessibility = Accessibility.Family;
break;
case FieldAttributes.FamORAssem:
memberAccessibility = Accessibility.FamilyOrInternal;
break;
case FieldAttributes.Public:
memberAccessibility = Accessibility.Public;
break;
}
}
public IEnumerable<T> PerformAnalysis(CancellationToken ct)
{
if (memberAccessibility == Accessibility.Private) {
return FindReferencesInTypeScope(ct);
}
DetermineTypeAccessibility();
if (typeAccessibility == Accessibility.Private) {
return FindReferencesInTypeScope(ct);
}
if (memberAccessibility == Accessibility.Internal ||
memberAccessibility == Accessibility.FamilyAndInternal ||
typeAccessibility == Accessibility.Internal ||
typeAccessibility == Accessibility.FamilyAndInternal)
return FindReferencesInAssemblyAndFriends(ct);
return FindReferencesGlobal(ct);
}
private void DetermineTypeAccessibility()
{
while (typeScope.IsNested) {
Accessibility accessibility = GetNestedTypeAccessibility(typeScope);
if ((int)typeAccessibility > (int)accessibility) {
typeAccessibility = accessibility;
if (typeAccessibility == Accessibility.Private)
return;
}
typeScope = typeScope.DeclaringType;
}
if (typeScope.IsNotPublic &&
((int)typeAccessibility > (int)Accessibility.Internal)) {
typeAccessibility = Accessibility.Internal;
}
}
private Accessibility GetNestedTypeAccessibility(TypeDefinition type)
{
Accessibility result;
switch (type.Attributes & TypeAttributes.VisibilityMask) {
case TypeAttributes.NestedPublic:
result = Accessibility.Public;
break;
case TypeAttributes.NestedPrivate:
result = Accessibility.Private;
break;
case TypeAttributes.NestedFamily:
result = Accessibility.Family;
break;
case TypeAttributes.NestedAssembly:
result = Accessibility.Internal;
break;
case TypeAttributes.NestedFamANDAssem:
result = Accessibility.FamilyAndInternal;
break;
case TypeAttributes.NestedFamORAssem:
result = Accessibility.FamilyOrInternal;
break;
default:
throw new InvalidOperationException();
}
return result;
}
/// <summary>
/// The effective accessibility of a member
/// </summary>
private enum Accessibility
{
Private,
FamilyAndInternal,
Internal,
Family,
FamilyOrInternal,
Public
}
IEnumerable<T> FindReferencesInAssemblyAndFriends(CancellationToken ct)
{
var assemblies = GetAssemblyAndAnyFriends(assemblyScope, ct);
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((AssemblyDefinition a) => FindReferencesInAssembly(a, ct));
}
IEnumerable<T> FindReferencesGlobal(CancellationToken ct)
{
var assemblies = GetReferencingAssemblies(assemblyScope, ct);
// use parallelism only on the assembly level (avoid locks within Cecil)
return assemblies.AsParallel().WithCancellation(ct).SelectMany((AssemblyDefinition asm) => FindReferencesInAssembly(asm, ct));
}
IEnumerable<T> FindReferencesInAssembly(AssemblyDefinition asm, CancellationToken ct)
{
foreach (TypeDefinition type in TreeTraversal.PreOrder(asm.MainModule.Types, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (var result in typeAnalysisFunction(type)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<T> FindReferencesInTypeScope(CancellationToken ct)
{
foreach (TypeDefinition type in TreeTraversal.PreOrder(typeScope, t => t.NestedTypes)) {
ct.ThrowIfCancellationRequested();
foreach (var result in typeAnalysisFunction(type)) {
ct.ThrowIfCancellationRequested();
yield return result;
}
}
}
IEnumerable<AssemblyDefinition> GetReferencingAssemblies(AssemblyDefinition asm, CancellationToken ct)
{
yield return asm;
string requiredAssemblyFullName = asm.FullName;
IEnumerable<LoadedAssembly> assemblies = MainWindow.Instance.CurrentAssemblyList.GetAssemblies().Where(assy => assy.AssemblyDefinition != null);
foreach (var assembly in assemblies) {
ct.ThrowIfCancellationRequested();
bool found = false;
foreach (var reference in assembly.AssemblyDefinition.MainModule.AssemblyReferences) {
if (requiredAssemblyFullName == reference.FullName) {
found = true;
break;
}
}
if (found)
yield return assembly.AssemblyDefinition;
}
}
IEnumerable<AssemblyDefinition> GetAssemblyAndAnyFriends(AssemblyDefinition asm, CancellationToken ct)
{
yield return asm;
if (asm.HasCustomAttributes) {
var attributes = asm.CustomAttributes
.Where(attr => attr.AttributeType.FullName == "System.Runtime.CompilerServices.InternalsVisibleToAttribute");
var friendAssemblies = new HashSet<string>();
foreach (var attribute in attributes) {
string assemblyName = attribute.ConstructorArguments[0].Value as string;
assemblyName = assemblyName.Split(',')[0]; // strip off any public key info
friendAssemblies.Add(assemblyName);
}
if (friendAssemblies.Count > 0) {
IEnumerable<LoadedAssembly> assemblies = MainWindow.Instance.CurrentAssemblyList.GetAssemblies();
foreach (var assembly in assemblies) {
ct.ThrowIfCancellationRequested();
if (friendAssemblies.Contains(assembly.ShortName)) {
yield return assembly.AssemblyDefinition;
}
}
}
}
}
}
}

71
ILSpy/TreeNodes/AssemblyListTreeNode.cs

@ -34,11 +34,12 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -34,11 +34,12 @@ namespace ICSharpCode.ILSpy.TreeNodes
sealed class AssemblyListTreeNode : ILSpyTreeNode
{
readonly AssemblyList assemblyList;
public AssemblyList AssemblyList {
public AssemblyList AssemblyList
{
get { return assemblyList; }
}
public AssemblyListTreeNode(AssemblyList assemblyList)
{
if (assemblyList == null)
@ -46,11 +47,12 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -46,11 +47,12 @@ namespace ICSharpCode.ILSpy.TreeNodes
this.assemblyList = assemblyList;
BindToObservableCollection(assemblyList.assemblies);
}
public override object Text {
public override object Text
{
get { return assemblyList.ListName; }
}
void BindToObservableCollection(ObservableCollection<LoadedAssembly> collection)
{
this.Children.Clear();
@ -75,7 +77,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -75,7 +77,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
};
}
public override bool CanDrop(DragEventArgs e, int index)
{
e.Effects = DragDropEffects.Move;
@ -88,7 +90,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -88,7 +90,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
return false;
}
}
public override void Drop(DragEventArgs e, int index)
{
string[] files = e.Data.GetData(AssemblyTreeNode.DataFormat) as string[];
@ -97,10 +99,10 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -97,10 +99,10 @@ namespace ICSharpCode.ILSpy.TreeNodes
if (files != null) {
lock (assemblyList.assemblies) {
var assemblies = (from file in files
where file != null
select assemblyList.OpenAssembly(file) into node
where node != null
select node).Distinct().ToList();
where file != null
select assemblyList.OpenAssembly(file) into node
where node != null
select node).Distinct().ToList();
foreach (LoadedAssembly asm in assemblies) {
int nodeIndex = assemblyList.assemblies.IndexOf(asm);
if (nodeIndex < index)
@ -114,9 +116,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -114,9 +116,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
}
public Action<SharpTreeNode> Select = delegate {};
public Action<SharpTreeNode> Select = delegate { };
public override void Decompile(Language language, ITextOutput output, DecompilationOptions options)
{
language.WriteCommentLine(output, "List: " + assemblyList.ListName);
@ -127,9 +129,9 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -127,9 +129,9 @@ namespace ICSharpCode.ILSpy.TreeNodes
asm.Decompile(language, output, options);
}
}
#region Find*Node
public AssemblyTreeNode FindAssemblyNode(AssemblyDefinition asm)
{
if (asm == null)
@ -141,7 +143,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -141,7 +143,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
return null;
}
public AssemblyTreeNode FindAssemblyNode(LoadedAssembly asm)
{
if (asm == null)
@ -153,7 +155,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -153,7 +155,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
return null;
}
/// <summary>
/// Looks up the type node corresponding to the type definition.
/// Returns null if no matching node is found.
@ -176,12 +178,12 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -176,12 +178,12 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
return null;
}
/// <summary>
/// Looks up the method node corresponding to the method definition.
/// Returns null if no matching node is found.
/// </summary>
public MethodTreeNode FindMethodNode(MethodDefinition def)
public SharpTreeNode FindMethodNode(MethodDefinition def)
{
if (def == null)
return null;
@ -195,16 +197,25 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -195,16 +197,25 @@ namespace ICSharpCode.ILSpy.TreeNodes
foreach (var p in typeNode.Children.OfType<ILSpyTreeNode>()) {
if (p.IsHidden)
continue;
// method might be a child or a property or events
p.EnsureLazyChildren();
methodNode = p.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition == def && !m.IsHidden);
if (methodNode != null)
return methodNode;
// method might be a child of a property or event
if (p is PropertyTreeNode || p is EventTreeNode) {
p.EnsureLazyChildren();
methodNode = p.Children.OfType<MethodTreeNode>().FirstOrDefault(m => m.MethodDefinition == def);
if (methodNode != null) {
/// If the requested method is a property or event accessor, and accessors are
/// hidden in the UI, then return the owning property or event.
if (methodNode.IsHidden)
return p;
else
return methodNode;
}
}
}
return null;
}
/// <summary>
/// Looks up the field node corresponding to the field definition.
/// Returns null if no matching node is found.
@ -219,7 +230,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -219,7 +230,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<FieldTreeNode>().FirstOrDefault(m => m.FieldDefinition == def && !m.IsHidden);
}
/// <summary>
/// Looks up the property node corresponding to the property definition.
/// Returns null if no matching node is found.
@ -234,7 +245,7 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -234,7 +245,7 @@ namespace ICSharpCode.ILSpy.TreeNodes
typeNode.EnsureLazyChildren();
return typeNode.Children.OfType<PropertyTreeNode>().FirstOrDefault(m => m.PropertyDefinition == def && !m.IsHidden);
}
/// <summary>
/// Looks up the event node corresponding to the event definition.
/// Returns null if no matching node is found.

27
ILSpy/TreeNodes/EventTreeNode.cs

@ -47,17 +47,33 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -47,17 +47,33 @@ namespace ICSharpCode.ILSpy.TreeNodes
}
}
public EventDefinition EventDefinition {
public EventDefinition EventDefinition
{
get { return ev; }
}
public override object Text {
get { return HighlightSearchMatch(ev.Name, " : " + this.Language.TypeToString(ev.EventType, false, ev)); }
public override object Text
{
get { return GetText(ev, this.Language); }
}
public static object GetText(EventDefinition eventDef, Language language)
{
return HighlightSearchMatch(eventDef.Name, " : " + language.TypeToString(eventDef.EventType, false, eventDef));
}
public override object Icon
{
get { return Images.GetIcon(MemberIcon.Event, GetOverlayIcon(ev.AddMethod.Attributes), ev.AddMethod.IsStatic); }
get { return GetIcon(ev); }
}
public static object GetIcon(EventDefinition eventDef)
{
MethodDefinition accessor = eventDef.AddMethod ?? eventDef.RemoveMethod;
if (accessor != null)
return Images.GetIcon(MemberIcon.Event, GetOverlayIcon(eventDef.AddMethod.Attributes), eventDef.AddMethod.IsStatic);
else
return Images.GetIcon(MemberIcon.Event, AccessOverlayIcon.Public, false);
}
private static AccessOverlayIcon GetOverlayIcon(MethodAttributes methodAttributes)
@ -91,7 +107,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -91,7 +107,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
language.DecompileEvent(ev, output, options);
}
MemberReference IMemberTreeNode.Member {
MemberReference IMemberTreeNode.Member
{
get { return ev; }
}
}

Loading…
Cancel
Save