diff --git a/ICSharpCode.Decompiler/Ast/AstBuilder.cs b/ICSharpCode.Decompiler/Ast/AstBuilder.cs index 7178b6e50..8991b246a 100644 --- a/ICSharpCode.Decompiler/Ast/AstBuilder.cs +++ b/ICSharpCode.Decompiler/Ast/AstBuilder.cs @@ -168,7 +168,7 @@ namespace ICSharpCode.Decompiler.Ast AstNode node = method.IsConstructor ? (AstNode)CreateConstructor(method) : CreateMethod(method); astCompileUnit.AddChild(node, CompilationUnit.MemberRole); } - + public void AddProperty(PropertyDefinition property) { astCompileUnit.AddChild(CreateProperty(property), CompilationUnit.MemberRole); @@ -269,10 +269,9 @@ namespace ICSharpCode.Decompiler.Ast foreach (var i in typeDef.Interfaces) astType.AddChild(ConvertType(i), TypeDeclaration.BaseTypeRole); - AddTypeMembers(astType, typeDef); - - if (astType.Members.Any(m => m is IndexerDeclaration)) { + + if (astType.Members.OfType().Any(idx => idx.PrivateImplementationType.IsNull)) { // Remove the [DefaultMember] attribute if the class contains indexers foreach (AttributeSection section in astType.Attributes) { foreach (Ast.Attribute attr in section.Attributes) { @@ -775,39 +774,12 @@ namespace ICSharpCode.Decompiler.Ast } ConvertCustomAttributes(astProp, propDef); - // Check whether the property is an indexer: - if (propDef.HasParameters) { - PropertyDefinition basePropDef = propDef; - if (accessor.HasOverrides) { - // if the property is explicitly implementing an interface, look up the property in the interface: - MethodDefinition baseAccessor = accessor.Overrides.First().Resolve(); - if (baseAccessor != null) { - foreach (PropertyDefinition baseProp in baseAccessor.DeclaringType.Properties) { - if (baseProp.GetMethod == baseAccessor || baseProp.SetMethod == baseAccessor) { - basePropDef = baseProp; - break; - } - } - } - } - // figure out the name of the indexer: - string defaultMemberName = null; - var defaultMemberAttribute = basePropDef.DeclaringType.CustomAttributes.FirstOrDefault(IsDefaultMemberAttribute); - if (defaultMemberAttribute != null && defaultMemberAttribute.ConstructorArguments.Count == 1) { - defaultMemberName = defaultMemberAttribute.ConstructorArguments[0].Value as string; - } - if (basePropDef.Name == defaultMemberName) { - return ConvertPropertyToIndexer(astProp, propDef); - } - } - return astProp; + if(propDef.IsIndexer()) + return ConvertPropertyToIndexer(astProp, propDef); + else + return astProp; } - bool IsDefaultMemberAttribute(CustomAttribute ca) - { - return ca.AttributeType.Name == "DefaultMemberAttribute" && ca.AttributeType.Namespace == "System.Reflection"; - } - IndexerDeclaration ConvertPropertyToIndexer(PropertyDeclaration astProp, PropertyDefinition propDef) { var astIndexer = new IndexerDeclaration(); @@ -1313,18 +1285,5 @@ namespace ICSharpCode.Decompiler.Ast return type.CustomAttributes.Any(attr => attr.AttributeType.FullName == "System.FlagsAttribute"); } - - /// - /// Gets the name of the default member of the type pointed by the attribute. - /// - /// The type definition. - /// The name of the default member or null if no attribute has been found. - private static Tuple GetDefaultMember(TypeDefinition type) - { - foreach (CustomAttribute ca in type.CustomAttributes) - if (ca.Constructor.FullName == "System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") - return Tuple.Create(ca.ConstructorArguments.Single().Value as string, ca); - return new Tuple(null, null); - } } } diff --git a/ICSharpCode.Decompiler/CecilExtensions.cs b/ICSharpCode.Decompiler/CecilExtensions.cs index 8b31a2701..5e806e7af 100644 --- a/ICSharpCode.Decompiler/CecilExtensions.cs +++ b/ICSharpCode.Decompiler/CecilExtensions.cs @@ -18,6 +18,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Mono.Cecil; using Mono.Cecil.Cil; @@ -234,5 +235,59 @@ namespace ICSharpCode.Decompiler else return false; } + + public static string GetDefaultMemberName(this TypeDefinition type) + { + CustomAttribute attr; + return type.GetDefaultMemberName(out attr); + } + + public static string GetDefaultMemberName(this TypeDefinition type, out CustomAttribute defaultMemberAttribute) + { + if (type.HasCustomAttributes) + foreach (CustomAttribute ca in type.CustomAttributes) + if (ca.Constructor.DeclaringType.Name == "DefaultMemberAttribute" && ca.Constructor.DeclaringType.Namespace == "System.Reflection" + && ca.Constructor.FullName == @"System.Void System.Reflection.DefaultMemberAttribute::.ctor(System.String)") { + defaultMemberAttribute = ca; + return ca.ConstructorArguments[0].Value as string; + } + defaultMemberAttribute = null; + return null; + } + + public static bool IsIndexer(this PropertyDefinition property) + { + CustomAttribute attr; + return property.IsIndexer(out attr); + } + + public static bool IsIndexer(this PropertyDefinition property, out CustomAttribute defaultMemberAttribute) + { + defaultMemberAttribute = null; + if (property.HasParameters) { + var accessor = property.GetMethod ?? property.SetMethod; + PropertyDefinition basePropDef = property; + if (accessor.HasOverrides) { + // if the property is explicitly implementing an interface, look up the property in the interface: + MethodDefinition baseAccessor = accessor.Overrides.First().Resolve(); + if (baseAccessor != null) { + foreach (PropertyDefinition baseProp in baseAccessor.DeclaringType.Properties) { + if (baseProp.GetMethod == baseAccessor || baseProp.SetMethod == baseAccessor) { + basePropDef = baseProp; + break; + } + } + } else + return false; + } + CustomAttribute attr; + var defaultMemberName = basePropDef.DeclaringType.GetDefaultMemberName(out attr); + if (defaultMemberName == basePropDef.Name) { + defaultMemberAttribute = attr; + return true; + } + } + return false; + } } } diff --git a/ILSpy/CSharpLanguage.cs b/ILSpy/CSharpLanguage.cs index 057d0d532..002c67d66 100644 --- a/ILSpy/CSharpLanguage.cs +++ b/ILSpy/CSharpLanguage.cs @@ -418,7 +418,38 @@ namespace ICSharpCode.ILSpy astType.AcceptVisitor(new OutputVisitor(w, new CSharpFormattingPolicy()), null); return w.ToString(); } - + + public override string FormatPropertyName(PropertyDefinition property, bool? isIndexer) + { + if (property == null) + throw new ArgumentNullException("property"); + + if (!isIndexer.HasValue) { + isIndexer = property.IsIndexer(); + } + if (isIndexer.Value) { + var buffer = new System.Text.StringBuilder(); + var accessor = property.GetMethod ?? property.SetMethod; + if (accessor.HasOverrides) { + var declaringType = accessor.Overrides.First().DeclaringType; + buffer.Append(TypeToString(declaringType, includeNamespace: true)); + buffer.Append(@"."); + } + buffer.Append(@"this["); + bool addSeparator = false; + foreach (var p in property.Parameters) { + if (addSeparator) + buffer.Append(@", "); + else + addSeparator = true; + buffer.Append(TypeToString(p.ParameterType, includeNamespace: true)); + } + buffer.Append(@"]"); + return buffer.ToString(); + } else + return property.Name; + } + public override bool ShowMember(MemberReference member) { return showAllMembers || !AstBuilder.MemberIsHidden(member, new DecompilationOptions().DecompilerSettings); diff --git a/ILSpy/Language.cs b/ILSpy/Language.cs index 238e87e63..fcde9c686 100644 --- a/ILSpy/Language.cs +++ b/ILSpy/Language.cs @@ -105,6 +105,13 @@ namespace ICSharpCode.ILSpy else return type.Name; } + + public virtual string FormatPropertyName(PropertyDefinition property, bool? isIndexer = null) + { + if (property == null) + throw new ArgumentNullException("property"); + return property.Name; + } /// /// Used for WPF keyboard navigation. diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs index cfad11418..200f8e693 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedFieldAccessNode.cs @@ -57,8 +57,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); } - IEnumerable FindReferences(LoadedAssembly[] assemblies, CancellationToken ct) + IEnumerable FindReferences(IEnumerable 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)); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs index 28bdce99f..4c7900c12 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedMethodUsedByTreeNode.cs @@ -70,8 +70,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); } - IEnumerable FindReferences(LoadedAssembly[] assemblies, CancellationToken ct) + IEnumerable FindReferences(IEnumerable 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)); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs index b3e21b6e1..cb7a810a8 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyOverridesTreeNode.cs @@ -54,8 +54,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); } - IEnumerable FindReferences(LoadedAssembly[] assemblies, CancellationToken ct) + IEnumerable FindReferences(IEnumerable 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)); } diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs index fc3871f97..84598997a 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzedPropertyTreeNode.cs @@ -18,31 +18,34 @@ using System; using Mono.Cecil; +using ICSharpCode.Decompiler; namespace ICSharpCode.ILSpy.TreeNodes.Analyzer { class AnalyzedPropertyTreeNode : AnalyzerTreeNode { PropertyDefinition analyzedProperty; + bool isIndexer; string prefix; public AnalyzedPropertyTreeNode(PropertyDefinition analyzedProperty, string prefix = "") { if (analyzedProperty == null) throw new ArgumentNullException("analyzedMethod"); - + this.isIndexer = analyzedProperty.IsIndexer(); this.analyzedProperty = analyzedProperty; this.prefix = prefix; this.LazyLoading = true; } public override object Icon { - get { return PropertyTreeNode.GetIcon(analyzedProperty); } + get { return PropertyTreeNode.GetIcon(analyzedProperty, isIndexer); } } public override object Text { get { - return prefix + Language.TypeToString(analyzedProperty.DeclaringType, true) + "." + PropertyTreeNode.GetText(analyzedProperty, Language); } + // TODO: This way of formatting is not suitable for properties which explicitly implement interfaces. + return prefix + Language.TypeToString(analyzedProperty.DeclaringType, true) + "." + PropertyTreeNode.GetText(analyzedProperty, Language, isIndexer); } } public override void ActivateItem(System.Windows.RoutedEventArgs e) diff --git a/ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs b/ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs index 69151630b..753f02b9e 100644 --- a/ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs +++ b/ILSpy/TreeNodes/Analyzer/AnalyzerMethodOverridesTreeNode.cs @@ -58,8 +58,9 @@ namespace ICSharpCode.ILSpy.TreeNodes.Analyzer return FindReferences(MainWindow.Instance.AssemblyList.GetAssemblies(), ct); } - IEnumerable FindReferences(LoadedAssembly[] assemblies, CancellationToken ct) + IEnumerable FindReferences(IEnumerable 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)); } diff --git a/ILSpy/TreeNodes/PropertyTreeNode.cs b/ILSpy/TreeNodes/PropertyTreeNode.cs index 0559e0582..24a12f0c7 100644 --- a/ILSpy/TreeNodes/PropertyTreeNode.cs +++ b/ILSpy/TreeNodes/PropertyTreeNode.cs @@ -31,12 +31,12 @@ namespace ICSharpCode.ILSpy.TreeNodes readonly PropertyDefinition property; readonly bool isIndexer; - public PropertyTreeNode(PropertyDefinition property, bool isIndexer) + public PropertyTreeNode(PropertyDefinition property) { if (property == null) throw new ArgumentNullException("property"); this.property = property; - this.isIndexer = isIndexer; + this.isIndexer = property.IsIndexer(); if (property.GetMethod != null) this.Children.Add(new MethodTreeNode(property.GetMethod)); @@ -55,12 +55,12 @@ namespace ICSharpCode.ILSpy.TreeNodes public override object Text { - get { return GetText(property, Language); } + get { return GetText(property, Language, isIndexer); } } - public static object GetText(PropertyDefinition property, Language language) + public static object GetText(PropertyDefinition property, Language language, bool? isIndexer = null) { - return HighlightSearchMatch(property.Name, " : " + language.TypeToString(property.PropertyType, false, property)); + return HighlightSearchMatch(language.FormatPropertyName(property, isIndexer), " : " + language.TypeToString(property.PropertyType, false, property)); } public override object Icon diff --git a/ILSpy/TreeNodes/TypeTreeNode.cs b/ILSpy/TreeNodes/TypeTreeNode.cs index 8905ae1fc..23bce5df7 100644 --- a/ILSpy/TreeNodes/TypeTreeNode.cs +++ b/ILSpy/TreeNodes/TypeTreeNode.cs @@ -103,16 +103,8 @@ namespace ICSharpCode.ILSpy.TreeNodes this.Children.Add(new FieldTreeNode(field)); } - // figure out the name of the indexer: - string defaultMemberName = null; - var defaultMemberAttribute = type.CustomAttributes.FirstOrDefault( - a => a.AttributeType.FullName == typeof(System.Reflection.DefaultMemberAttribute).FullName); - if (defaultMemberAttribute != null && defaultMemberAttribute.ConstructorArguments.Count == 1) { - defaultMemberName = defaultMemberAttribute.ConstructorArguments[0].Value as string; - } - foreach (PropertyDefinition property in type.Properties.OrderBy(m => m.Name)) { - this.Children.Add(new PropertyTreeNode(property, property.Name == defaultMemberName)); + this.Children.Add(new PropertyTreeNode(property)); } foreach (EventDefinition ev in type.Events.OrderBy(m => m.Name)) { this.Children.Add(new EventTreeNode(ev));