diff --git a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs index 3e553db6e..0affa19aa 100644 --- a/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs +++ b/ICSharpCode.Decompiler/ILAst/ILAstOptimizer.cs @@ -586,16 +586,37 @@ namespace ICSharpCode.Decompiler.ILAst // This ensures that a single IL variable is a single C# variable (gets assigned only one name) // The DeclareVariables transformation might then split up the C# variable again if it is used indendently in two separate scopes. Dictionary dict = new Dictionary(); - foreach (ILExpression expr in method.GetSelfAndChildrenRecursive()) { - ILVariable v = expr.Operand as ILVariable; - if (v != null && v.OriginalVariable != null) { + ReplaceVariables( + method, + delegate(ILVariable v) { + if (v.OriginalVariable == null) + return v; ILVariable combinedVariable; if (!dict.TryGetValue(v.OriginalVariable, out combinedVariable)) { dict.Add(v.OriginalVariable, v); combinedVariable = v; } - expr.Operand = combinedVariable; + return combinedVariable; + }); + } + + public static void ReplaceVariables(ILNode node, Func variableMapping) + { + ILExpression expr = node as ILExpression; + if (expr != null) { + ILVariable v = expr.Operand as ILVariable; + if (v != null) + expr.Operand = variableMapping(v); + foreach (ILExpression child in expr.Arguments) + ReplaceVariables(child, variableMapping); + } else { + var catchBlock = node as ILTryCatchBlock.CatchBlock; + if (catchBlock != null && catchBlock.ExceptionVariable != null) { + catchBlock.ExceptionVariable = variableMapping(catchBlock.ExceptionVariable); } + + foreach (ILNode child in node.GetChildren()) + ReplaceVariables(child, variableMapping); } } diff --git a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs index 3825012d0..5596fe24b 100644 --- a/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs +++ b/ICSharpCode.Decompiler/ILAst/PeepholeTransform.cs @@ -523,8 +523,8 @@ namespace ICSharpCode.Decompiler.ILAst if (recombineVariable) { // Split local variable, unsplit these two instances - foreach (var ilExpression in method.GetSelfAndChildrenRecursive(expression => expression.Operand == nextExpr.Operand)) - ilExpression.Operand = exprInit.Operand; + // replace nextExpr.Operand with exprInit.Operand + ReplaceVariables(method, oldVar => oldVar == nextExpr.Operand ? (ILVariable)exprInit.Operand : oldVar); } switch (loadInstruction) { diff --git a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs index 45b5d72ca..0db35e357 100644 --- a/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs +++ b/ICSharpCode.Decompiler/Tests/ExceptionHandling.cs @@ -105,4 +105,24 @@ public class ExceptionHandling cancellationTokenSource = new CancellationTokenSource(); } } + + public void TwoCatchBlocksWithSameVariable() + { + try + { + Console.WriteLine("Try1"); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + try + { + Console.WriteLine("Try2"); + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + } } diff --git a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj index 6f4ffcb22..6514c632b 100644 --- a/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj +++ b/ILSpy.BamlDecompiler/ILSpy.BamlDecompiler.csproj @@ -14,6 +14,7 @@ False 4 false + OnBuildSuccess x86 @@ -130,5 +131,8 @@ False + + + \ No newline at end of file diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BAML format.txt b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BAML format.txt new file mode 100644 index 000000000..8cd23cb29 --- /dev/null +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BAML format.txt @@ -0,0 +1,22 @@ +BAML format description + +A BAML stream consists of a header and sequential records that contain the data. + +The Header + +The header is 28 bytes long. The first four bytes are a little endian integer containing the length of the preamble in bytes. The preamble is the UTF-16 string "MSBAML", followed by three integers with the value 0x60000. + +Record format: + +In general a record consists of a type byte and its content. Some records also have a field, following the type byte, containing the remaining length of the record, encoded as 7-bit encoded integer. + +Record Types: + +DocumentStart (= 0x01): + +The document start (after the type byte) is 6 Bytes long. Usually 00 FF FF FF FF 00. These bytes can be safely ignored. + +AssemblyInfo (= 0xC1): + +The length field is followed by a 2 byte long ID. The ID is followed by a string containing the assembly name. + diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs index 50ca01ddb..dd53eff6c 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/BamlRecordType.cs @@ -9,64 +9,63 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { internal enum BamlRecordType : byte { - AssemblyInfo = 0x1c, - AttributeInfo = 0x1f, - ClrEvent = 0x13, - Comment = 0x17, - ConnectionId = 0x2d, - ConstructorParametersEnd = 0x2b, - ConstructorParametersStart = 0x2a, - ConstructorParameterType = 0x2c, - ContentProperty = 0x2e, - DefAttribute = 0x19, - DefAttributeKeyString = 0x26, - DefAttributeKeyType = 0x27, - DeferableContentStart = 0x25, - DefTag = 0x18, - DocumentEnd = 2, - DocumentStart = 1, - ElementEnd = 4, - ElementStart = 3, - EndAttributes = 0x1a, - KeyElementEnd = 0x29, - KeyElementStart = 40, - LastRecordType = 0x39, - LineNumberAndPosition = 0x35, - LinePosition = 0x36, - LiteralContent = 15, - NamedElementStart = 0x2f, - OptimizedStaticResource = 0x37, - PIMapping = 0x1b, - PresentationOptionsAttribute = 0x34, - ProcessingInstruction = 0x16, - Property = 5, - PropertyArrayEnd = 10, - PropertyArrayStart = 9, - PropertyComplexEnd = 8, - PropertyComplexStart = 7, - PropertyCustom = 6, - PropertyDictionaryEnd = 14, - PropertyDictionaryStart = 13, - PropertyListEnd = 12, - PropertyListStart = 11, - PropertyStringReference = 0x21, - PropertyTypeReference = 0x22, - PropertyWithConverter = 0x24, - PropertyWithExtension = 0x23, - PropertyWithStaticResourceId = 0x38, - RoutedEvent = 0x12, - StaticResourceEnd = 0x31, - StaticResourceId = 50, - StaticResourceStart = 0x30, - StringInfo = 0x20, - Text = 0x10, - TextWithConverter = 0x11, - TextWithId = 0x33, - TypeInfo = 0x1d, - TypeSerializerInfo = 30, - Unknown = 0, - XmlAttribute = 0x15, - XmlnsProperty = 20 + Unknown, + DocumentStart, + DocumentEnd, + ElementStart, + ElementEnd, + Property, + PropertyCustom, + PropertyComplexStart, + PropertyComplexEnd, + PropertyArrayStart, + PropertyArrayEnd, + PropertyListStart, + PropertyListEnd, + PropertyDictionaryStart, + PropertyDictionaryEnd, + LiteralContent, + Text, + TextWithConverter, + RoutedEvent, + ClrEvent, + XmlnsProperty, + XmlAttribute, + ProcessingInstruction, + Comment, + DefTag, + DefAttribute, + EndAttributes, + PIMapping, + AssemblyInfo, + TypeInfo, + TypeSerializerInfo, + AttributeInfo, + StringInfo, + PropertyStringReference, + PropertyTypeReference, + PropertyWithExtension, + PropertyWithConverter, + DeferableContentStart, + DefAttributeKeyString, + DefAttributeKeyType, + KeyElementStart, + KeyElementEnd, + ConstructorParametersStart, + ConstructorParametersEnd, + ConstructorParameterType, + ConnectionId, + ContentProperty, + NamedElementStart, + StaticResourceStart, + StaticResourceEnd, + StaticResourceId, + TextWithId, + PresentationOptionsAttribute, + LineNumberAndPosition, + LinePosition, + OptimizedStaticResource, + PropertyWithStaticResourceId, + LastRecordType } - } diff --git a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs index 615c02d4d..93ed72c66 100644 --- a/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs +++ b/ILSpy.BamlDecompiler/Ricciolo.StylesExplorer.MarkupReflection/XmlBamlReader.cs @@ -21,10 +21,10 @@ namespace Ricciolo.StylesExplorer.MarkupReflection #region Variables private BamlBinaryReader reader; - private Hashtable assemblyTable = new Hashtable(); - private Hashtable stringTable = new Hashtable(); - private Hashtable typeTable = new Hashtable(); - private Hashtable propertyTable = new Hashtable(); + private Dictionary assemblyTable = new Dictionary(); + private Dictionary stringTable = new Dictionary(); + private Dictionary typeTable = new Dictionary(); + private Dictionary propertyTable = new Dictionary(); private readonly ITypeResolver _resolver; @@ -247,9 +247,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { do { - currentType = (BamlRecordType)reader.ReadByte(); - //Debug.WriteLine(currentType); - if (currentType == BamlRecordType.DocumentEnd) break; + ReadRecordType(); + if (currentType == BamlRecordType.DocumentEnd) + break; long position = reader.BaseStream.Position; @@ -257,9 +257,8 @@ namespace Ricciolo.StylesExplorer.MarkupReflection ProcessNext(); if (bytesToSkip > 0) - { + // jump to the end of the record reader.BaseStream.Position = position + bytesToSkip; - } } //while (currentType != BamlRecordType.DocumentEnd); while (nodes.Count == 0 || (currentType != BamlRecordType.ElementEnd) || complexPropertyOpened > 0); @@ -277,6 +276,23 @@ namespace Ricciolo.StylesExplorer.MarkupReflection } } + void ReadRecordType() + { + byte type = reader.ReadByte(); + if (type < 0) + currentType = BamlRecordType.DocumentEnd; + else + currentType = (BamlRecordType)type; + + if (currentType.ToString().EndsWith("End")) + Debug.Unindent(); + + Debug.WriteLine(currentType); + + if (currentType.ToString().StartsWith("Start")) + Debug.Indent(); + } + private bool SetNextNode() { while (nodes.Count > 0) @@ -305,12 +321,8 @@ namespace Ricciolo.StylesExplorer.MarkupReflection switch (currentType) { case BamlRecordType.DocumentStart: - { - reader.ReadBoolean(); - reader.ReadInt32(); - reader.ReadBoolean(); - break; - } + reader.ReadBytes(6); + break; case BamlRecordType.DocumentEnd: break; case BamlRecordType.ElementStart: @@ -407,6 +419,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection case BamlRecordType.TextWithConverter: this.ReadTextWithConverter(); break; + case BamlRecordType.TextWithId: + this.ReadTextWithId(); + break; case BamlRecordType.PropertyWithStaticResourceId: this.ReadPropertyWithStaticResourceIdentifier(); break; @@ -435,9 +450,17 @@ namespace Ricciolo.StylesExplorer.MarkupReflection this.ReadPresentationOptionsAttribute(); break; default: + throw new NotImplementedException("UnsupportedNode: " + currentType); break; } } + + void ReadTextWithId() + { + short textId = reader.ReadInt16(); + string text = stringTable[textId]; + nodes.Enqueue(new XmlBamlText(text)); + } private void ComputeBytesToSkip() { @@ -450,6 +473,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection case BamlRecordType.Property: case BamlRecordType.PropertyCustom: case BamlRecordType.Text: + case BamlRecordType.TextWithId: case BamlRecordType.TextWithConverter: case BamlRecordType.XmlnsProperty: case BamlRecordType.DefAttribute: @@ -586,7 +610,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection PropertyDeclaration declaration; if (identifier >= 0) { - declaration = (PropertyDeclaration)this.propertyTable[identifier]; + declaration = this.propertyTable[identifier]; } else { @@ -603,7 +627,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { if (identifier >= 0) { - PropertyDeclaration declaration = (PropertyDeclaration)this.propertyTable[identifier]; + PropertyDeclaration declaration = this.propertyTable[identifier]; return declaration; } else @@ -752,35 +776,23 @@ namespace Ricciolo.StylesExplorer.MarkupReflection throw new ArgumentException(); Int32Collection ints = new Int32Collection(capacity); - switch (type) - { + switch (type) { case IntegerCollectionType.Byte: for (int i = 0; i < capacity; i++) - { ints.Add(reader.ReadByte()); - } return ints; - case IntegerCollectionType.UShort: for (int j = 0; j < capacity; j++) - { ints.Add(reader.ReadUInt16()); - } return ints; - case IntegerCollectionType.Integer: for (int k = 0; k < capacity; k++) - { - int num7 = reader.ReadInt32(); - ints.Add(num7); - } + ints.Add(reader.ReadInt32()); return ints; - case IntegerCollectionType.Consecutive: - for (int m = reader.ReadInt32(); m < capacity; m++) - { + int start = reader.ReadInt32(); + for (int m = start; m < capacity + start; m++) ints.Add(m); - } return ints; } throw new ArgumentException(); @@ -906,7 +918,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection pd = new PropertyDeclaration("Name", XamlTypeDeclaration); break; default: - string recordName = (string)this.stringTable[identifier]; + string recordName = this.stringTable[identifier]; if (recordName != "Key") throw new NotSupportedException(recordName); pd = new PropertyDeclaration(recordName, XamlTypeDeclaration); @@ -926,7 +938,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection int position = reader.ReadInt32(); //bool shared = reader.ReadBoolean(); //bool sharedSet = reader.ReadBoolean(); - string text = (string)this.stringTable[num]; + string text = this.stringTable[num]; if (text == null) throw new NotSupportedException(); @@ -963,7 +975,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection string[] textArray = new string[(uint)reader.ReadInt16()]; for (int i = 0; i < textArray.Length; i++) { - textArray[i] = (string)this.assemblyTable[reader.ReadInt16()]; + textArray[i] = this.assemblyTable[reader.ReadInt16()]; } XmlNamespaceCollection namespaces = elements.Peek().Namespaces; @@ -1045,6 +1057,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection private void ReadPropertyComplexEnd() { + if (!(elements.Peek() is XmlBamlPropertyElement)) + throw new InvalidCastException(); + XmlBamlPropertyElement propertyElement = (XmlBamlPropertyElement) elements.Peek(); CloseElement(); @@ -1157,7 +1172,9 @@ namespace Ricciolo.StylesExplorer.MarkupReflection private void ReadElementStart() { short identifier = reader.ReadInt16(); - reader.ReadByte(); + sbyte flags = reader.ReadSByte(); + if (flags < 0 || flags > 3) + throw new NotImplementedException(); TypeDeclaration declaration = GetTypeDeclaration(identifier); XmlBamlElement element; @@ -1546,7 +1563,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection { TypeDeclaration declaration; if (identifier >= 0) - declaration = (TypeDeclaration)this.typeTable[identifier]; + declaration = this.typeTable[identifier]; else declaration = KnownInfo.KnownTypeTable[-identifier]; @@ -1558,7 +1575,7 @@ namespace Ricciolo.StylesExplorer.MarkupReflection internal string GetAssembly(short identifier) { - return this.assemblyTable[identifier].ToString(); + return this.assemblyTable[identifier]; } private XmlBamlNode CurrentNode diff --git a/ILSpy/ILSpy.csproj b/ILSpy/ILSpy.csproj index 3eaca83ce..4e99f674b 100644 --- a/ILSpy/ILSpy.csproj +++ b/ILSpy/ILSpy.csproj @@ -148,6 +148,7 @@ + diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs new file mode 100644 index 000000000..02320cc09 --- /dev/null +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventFiredByTreeNode.cs @@ -0,0 +1,153 @@ +// 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.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using ICSharpCode.TreeView; +using Mono.Cecil; +using Mono.Cecil.Cil; + +namespace ICSharpCode.ILSpy.TreeNodes.Analyzer +{ + internal sealed class AnalyzedEventFiredByTreeNode : AnalyzerTreeNode + { + private readonly EventDefinition analyzedEvent; + private readonly FieldDefinition eventBackingField; + private readonly MethodDefinition eventFiringMethod; + + private readonly ThreadingSupport threading; + private ConcurrentDictionary foundMethods; + + public AnalyzedEventFiredByTreeNode(EventDefinition analyzedEvent) + { + if (analyzedEvent == null) + throw new ArgumentNullException("analyzedEvent"); + + this.analyzedEvent = analyzedEvent; + this.threading = new ThreadingSupport(); + this.LazyLoading = true; + + this.eventBackingField = GetBackingField(analyzedEvent); + this.eventFiringMethod = analyzedEvent.EventType.Resolve().Methods.First(md => md.Name == "Invoke"); + } + + public override object Text + { + get { return "Raised 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(); + } + } + + private IEnumerable FetchChildren(CancellationToken ct) + { + foundMethods = new ConcurrentDictionary(); + + foreach (var child in FindReferencesInType(analyzedEvent.DeclaringType)) { + yield return child; + } + + foundMethods = null; + } + + private IEnumerable FindReferencesInType(TypeDefinition type) + { + // HACK: in lieu of proper flow analysis, I'm going to use a simple heuristic + // If the method accesses the event's backing field, and calls invoke on a delegate + // with the same signature, then it is (most likely) raise the given event. + + foreach (MethodDefinition method in type.Methods) { + bool readBackingField = false; + bool found = false; + if (!method.HasBody) + continue; + foreach (Instruction instr in method.Body.Instructions) { + Code code = instr.OpCode.Code; + if (code == Code.Ldfld || code == Code.Ldflda) { + FieldReference fr = instr.Operand as FieldReference; + if (fr != null && fr.Name == eventBackingField.Name && fr == eventBackingField) { + readBackingField = true; + } + } + if (readBackingField && (code == Code.Callvirt || code == Code.Call)) { + MethodReference mr = instr.Operand as MethodReference; + if (mr != null && mr.Name == eventFiringMethod.Name && mr.Resolve() == eventFiringMethod) { + found = true; + break; + } + } + } + + method.Body = null; + + if (found) { + MethodDefinition codeLocation = this.Language.GetOriginalCodeLocation(method) as MethodDefinition; + if (codeLocation != null && !HasAlreadyBeenFound(codeLocation)) { + yield return new AnalyzedMethodTreeNode(codeLocation); + } + } + } + } + + private bool HasAlreadyBeenFound(MethodDefinition method) + { + return !foundMethods.TryAdd(method, 0); + } + + // HACK: we should probably examine add/remove methods to determine this + private static FieldDefinition GetBackingField(EventDefinition ev) + { + var fieldName = ev.Name; + var vbStyleFieldName = fieldName + "Event"; + var fieldType = ev.EventType; + + foreach (var fd in ev.DeclaringType.Fields) { + if (fd.Name == fieldName || fd.Name == vbStyleFieldName) + if (fd.FieldType.FullName == fieldType.FullName) + return fd; + } + + return null; + } + + + public static bool CanShow(EventDefinition ev) + { + return GetBackingField(ev) != null; + } + } +} diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs index 89903abd4..84a937942 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedEventTreeNode.cs @@ -59,13 +59,19 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { 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)); + if (AnalyzedEventFiredByTreeNode.CanShow(analyzedEvent)) + this.Children.Add(new AnalyzedEventFiredByTreeNode(analyzedEvent)); + if (AnalyzedEventOverridesTreeNode.CanShow(analyzedEvent)) this.Children.Add(new AnalyzedEventOverridesTreeNode(analyzedEvent)); + if (AnalyzedInterfaceEventImplementedByTreeNode.CanShow(analyzedEvent)) this.Children.Add(new AnalyzedInterfaceEventImplementedByTreeNode(analyzedEvent)); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs index 993ef36d7..26f5e4c65 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedVirtualMethodUsedByTreeNode.cs @@ -89,7 +89,8 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer var BaseMethods = TypesHierarchyHelpers.FindBaseMethods(analyzedMethod).ToArray(); if (BaseMethods.Length > 0) { baseMethod = BaseMethods[BaseMethods.Length - 1]; - } + } else + baseMethod = analyzedMethod; possibleTypes = new List(); @@ -119,15 +120,24 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer MethodReference mr = instr.Operand as MethodReference; if (mr != null && mr.Name == name) { // explicit call to the requested method - if (Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == analyzedMethod) { + if (instr.OpCode.Code == Code.Call + && Helpers.IsReferencedBy(analyzedMethod.DeclaringType, mr.DeclaringType) + && mr.Resolve() == analyzedMethod) { found = true; prefix = "(as base) "; break; } // virtual call to base method - if (instr.OpCode.Code == Code.Callvirt && Helpers.IsReferencedBy(baseMethod.DeclaringType, mr.DeclaringType) && mr.Resolve() == baseMethod) { - found = true; - break; + if (instr.OpCode.Code == Code.Callvirt) { + MethodDefinition md = mr.Resolve(); + if (md == null) { + // cannot resolve the operand, so ignore this method + break; + } + if (md == baseMethod) { + found = true; + break; + } } } } diff --git a/ILSpy/TreeNodes/Analyzer/Helpers.cs b/ILSpy/TreeNodes/Analyzer/Helpers.cs index bef4e486a..b773ad875 100644 --- a/ILSpy/TreeNodes/Analyzer/Helpers.cs +++ b/ILSpy/TreeNodes/Analyzer/Helpers.cs @@ -67,11 +67,11 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return FindMethodUsageInType(method.DeclaringType, method) ?? method; } - var typeUsage = GetOriginalCodeLocation(method.DeclaringType, method); + var typeUsage = GetOriginalCodeLocation(method.DeclaringType); return typeUsage ?? method; } - public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type, MethodDefinition method) + public static MethodDefinition GetOriginalCodeLocation(TypeDefinition type) { if (type != null && type.DeclaringType != null && type.IsCompilerGenerated()) { MethodDefinition constructor = GetTypeConstructor(type);