diff --git a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs index 6ea1a92d6..d08c4972a 100644 --- a/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstMethodBodyBuilder.cs @@ -349,9 +349,8 @@ namespace Decompiler case Code.Ldelem_R4: case Code.Ldelem_R8: case Code.Ldelem_Ref: - return arg1.Indexer(arg2); case Code.Ldelem_Any: - return InlineAssembly(byteCode, args); + return arg1.Indexer(arg2); case Code.Ldelema: return MakeRef(arg1.Indexer(arg2)); @@ -638,7 +637,7 @@ namespace Decompiler } if (target is ThisReferenceExpression && !isVirtual) { // a non-virtual call on "this" might be a "base"-call. - if (cecilMethod.DeclaringType != methodDef.DeclaringType) { + if ((cecilMethod.DeclaringType.IsGenericInstance ? cecilMethod.DeclaringType.GetElementType() : cecilMethod.DeclaringType) != methodDef.DeclaringType) { // If we're not calling a method in the current class; we must be calling one in the base class. target = new BaseReferenceExpression(); } diff --git a/ICSharpCode.Decompiler/ILAst/ILInlining.cs b/ICSharpCode.Decompiler/ILAst/ILInlining.cs index 6337eb032..6ca8d90db 100644 --- a/ICSharpCode.Decompiler/ILAst/ILInlining.cs +++ b/ICSharpCode.Decompiler/ILAst/ILInlining.cs @@ -50,7 +50,7 @@ namespace Decompiler { if (expr.Code != ILCode.Stloc) throw new ArgumentException("expr must be stloc"); - // ensure the variable is only accessed only a single time + // ensure the variable is accessed only a single time if (method.GetSelfAndChildrenRecursive().Count(e => e != expr && e.Operand == expr.Operand) != 1) return false; ILExpression parent; diff --git a/ICSharpCode.Decompiler/Tests/Generics.cs b/ICSharpCode.Decompiler/Tests/Generics.cs new file mode 100644 index 000000000..636eb708c --- /dev/null +++ b/ICSharpCode.Decompiler/Tests/Generics.cs @@ -0,0 +1,30 @@ +// 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; + +public static class Generics +{ + class MyArray + { + private T[] arr; + + public MyArray(int capacity) + { + this.arr = new T[capacity]; + } + + public void Size(int capacity) + { + Array.Resize(ref this.arr, capacity); + } + + public void Grow(int capacity) + { + if (capacity >= this.arr.Length) + { + this.Size(capacity); + } + } + } +} diff --git a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj index ba85e099c..e210d308b 100644 --- a/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj +++ b/ICSharpCode.Decompiler/Tests/ICSharpCode.Decompiler.Tests.csproj @@ -51,6 +51,7 @@ + diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index acd8aa6a3..aae49e8c0 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -136,8 +136,11 @@ + + + diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs new file mode 100644 index 000000000..cfad11418 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs @@ -0,0 +1,109 @@ +// 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 System.Threading; + +using ICSharpCode.NRefactory.Utils; +using ICSharpCode.TreeView; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + class AnalyzedFieldAccessNode : AnalyzerTreeNode + { + 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 { + get { return showWrites ? "Assigned By" : "Read 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 FetchChildren(CancellationToken ct) + { + return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); + } + + IEnumerable 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 FindReferences(LoadedAssembly asm, CancellationToken ct) + { + string name = analyzedField.Name; + string declTypeName = analyzedField.DeclaringType.FullName; + 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 && fr.DeclaringType.FullName == declTypeName && fr.Resolve() == analyzedField) { + found = true; + break; + } + } + } + if (found) + yield return new AnalyzedMethodTreeNode(method); + } + } + } + + bool CanBeReference(Code code) + { + switch (code) { + case Code.Ldfld: + case Code.Ldsfld: + return !showWrites; + case Code.Stfld: + case Code.Stsfld: + return showWrites; + case Code.Ldflda: + case Code.Ldsflda: + return true; // always show address-loading + default: + return false; + } + } + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldNode.cs new file mode 100644 index 000000000..ce15a16fa --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldNode.cs @@ -0,0 +1,45 @@ +// 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 Mono.Cecil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + class AnalyzedFieldNode : AnalyzerTreeNode + { + FieldDefinition analyzedField; + + public AnalyzedFieldNode(FieldDefinition analyzedField) + { + if (analyzedField == null) + throw new ArgumentNullException("analyzedField"); + this.analyzedField = analyzedField; + this.LazyLoading = true; + } + + public override object Icon { + get { return FieldTreeNode.GetIcon(analyzedField); } + } + + public override object Text { + get { + return Language.TypeToString(analyzedField.DeclaringType, true) + + "." + analyzedField.Name + " : " + this.Language.TypeToString(analyzedField.FieldType, false, analyzedField); + } + } + + public override void ActivateItem(System.Windows.RoutedEventArgs e) + { + e.Handled = true; + MainWindow.Instance.JumpToReference(analyzedField); + } + + protected override void LoadChildren() + { + this.Children.Add(new AnalyzedFieldAccessNode(analyzedField, false)); + if (!analyzedField.IsLiteral) + this.Children.Add(new AnalyzedFieldAccessNode(analyzedField, true)); + } + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs index 5287fee95..f577e4a85 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodTreeNode.cs @@ -49,6 +49,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer protected override void LoadChildren() { + if (analyzedMethod.HasBody) + this.Children.Add(new AnalyzedMethodUsesNode(analyzedMethod)); this.Children.Add(new AnalyzedMethodUsedByTreeNode(analyzedMethod)); } } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsesNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsesNode.cs new file mode 100644 index 000000000..65b1706c4 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsesNode.cs @@ -0,0 +1,85 @@ +// Copyright (c) 2011 AlphaSierraPapa for the SharpDevelop Team +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of this +// software and associated documentation files (the "Software"), to deal in the Software +// without restriction, including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons +// to whom the Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all copies or +// substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR +// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE +// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +using System; +using System.Collections.Generic; +using System.Linq; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + /// + /// Shows the methods that are used by this method. + /// + sealed class AnalyzedMethodUsesNode : AnalyzerTreeNode + { + MethodDefinition analyzedMethod; + + public AnalyzedMethodUsesNode(MethodDefinition analyzedMethod) + { + if (analyzedMethod == null) + throw new ArgumentNullException("analyzedMethod"); + + this.analyzedMethod = analyzedMethod; + this.LazyLoading = true; + } + + public override object Text { + get { return "Uses"; } + } + + public override object Icon { + get { return Images.Search; } + } + + protected override void LoadChildren() + { + foreach (var f in GetUsedFields().Distinct()) { + this.Children.Add(new AnalyzedFieldNode(f)); + } + foreach (var m in GetUsedMethods().Distinct()) { + this.Children.Add(new AnalyzedMethodTreeNode(m)); + } + } + + IEnumerable GetUsedMethods() + { + foreach (Instruction instr in analyzedMethod.Body.Instructions) { + MethodReference mr = instr.Operand as MethodReference; + if (mr != null) { + MethodDefinition def = mr.Resolve(); + if (def != null) + yield return def; + } + } + } + + IEnumerable GetUsedFields() + { + foreach (Instruction instr in analyzedMethod.Body.Instructions) { + FieldReference fr = instr.Operand as FieldReference; + if (fr != null) { + FieldDefinition def = fr.Resolve(); + if (def != null) + yield return def; + } + } + } + } +} diff --git a/ILSpy/TreeNodes/FieldTreeNode.cs b/ILSpy/TreeNodes/FieldTreeNode.cs index 3838c0522..d3becb0ce 100644 --- a/ILSpy/TreeNodes/FieldTreeNode.cs +++ b/ILSpy/TreeNodes/FieldTreeNode.cs @@ -17,7 +17,9 @@ // DEALINGS IN THE SOFTWARE. using System; +using System.Windows.Controls; using ICSharpCode.Decompiler; +using ICSharpCode.ILSpy.TreeNodes.Analyzer; using Mono.Cecil; namespace ICSharpCode.ILSpy.TreeNodes @@ -45,12 +47,15 @@ namespace ICSharpCode.ILSpy.TreeNodes } public override object Icon { - get { - if (field.IsLiteral) - return Images.Literal; - else - return Images.Field; - } + get { return GetIcon(field); } + } + + public static object GetIcon(FieldDefinition field) + { + if (field.IsLiteral) + return Images.Literal; + else + return Images.Field; } public override FilterResult Filter(FilterSettings settings) @@ -65,5 +70,16 @@ namespace ICSharpCode.ILSpy.TreeNodes { language.DecompileField(field, output, options); } + + public override System.Windows.Controls.ContextMenu GetContextMenu() + { + ContextMenu menu = new ContextMenu(); + MenuItem item = new MenuItem() { Header = "Analyze", Icon = new Image() { Source = Images.Search } }; + item.Click += delegate { MainWindow.Instance.AddToAnalyzer(new AnalyzedFieldNode(field)); }; + + menu.Items.Add(item); + + return menu; + } } }