Browse Source

Add PropertyAndEventBackingFieldLookup to improve performance of MemberIsHidden.

master
Siegfried Pammer 1 week ago
parent
commit
978c31ca5e
  1. 53
      ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs
  2. 44
      ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
  3. 1
      ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
  4. 12
      ICSharpCode.Decompiler/Metadata/MetadataFile.cs
  5. 93
      ICSharpCode.Decompiler/Metadata/PropertyAndEventBackingFieldLookup.cs

53
ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs

@ -23,7 +23,6 @@ using System.IO; @@ -23,7 +23,6 @@ using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;
using System.Text.RegularExpressions;
using System.Threading;
using ICSharpCode.Decompiler;
@ -340,24 +339,13 @@ namespace ICSharpCode.Decompiler.CSharp @@ -340,24 +339,13 @@ namespace ICSharpCode.Decompiler.CSharp
return true;
if (settings.UsePrimaryConstructorSyntaxForNonRecordTypes && IsPrimaryConstructorParameterBackingField(field, metadata))
return true;
if (settings.AutomaticProperties && IsAutomaticPropertyBackingField(field, metadata, out var propertyName))
if (settings.AutomaticProperties && module.PropertyAndEventBackingFieldLookup.IsPropertyBackingField(fieldHandle, out var propertyHandle))
{
if (!settings.GetterOnlyAutomaticProperties && IsGetterOnlyProperty(propertyName))
return false;
bool IsGetterOnlyProperty(string propertyName)
if (!settings.GetterOnlyAutomaticProperties)
{
var properties = metadata.GetTypeDefinition(field.GetDeclaringType()).GetProperties();
foreach (var p in properties)
{
var pd = metadata.GetPropertyDefinition(p);
string name = metadata.GetString(pd.Name);
if (!metadata.StringComparer.Equals(pd.Name, propertyName))
continue;
PropertyAccessors accessors = pd.GetAccessors();
return !accessors.Getter.IsNil && accessors.Setter.IsNil;
}
return false;
PropertyAccessors accessors = metadata.GetPropertyDefinition(propertyHandle).GetAccessors();
if (!accessors.Getter.IsNil && accessors.Setter.IsNil)
return false;
}
return true;
@ -367,15 +355,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -367,15 +355,9 @@ namespace ICSharpCode.Decompiler.CSharp
return true;
}
// event-fields are not [CompilerGenerated]
if (settings.AutomaticEvents)
if (settings.AutomaticEvents && module.PropertyAndEventBackingFieldLookup.IsEventBackingField(fieldHandle, out _))
{
foreach (var ev in metadata.GetTypeDefinition(field.GetDeclaringType()).GetEvents())
{
var eventName = metadata.GetString(metadata.GetEventDefinition(ev).Name);
var fieldName = metadata.GetString(field.Name);
if (IsEventBackingFieldName(fieldName, eventName, out _))
return true;
}
return true;
}
if (settings.ArrayInitializers && metadata.GetString(metadata.GetTypeDefinition(field.GetDeclaringType()).Name).StartsWith("<PrivateImplementationDetails>", StringComparison.Ordinal))
{
@ -404,27 +386,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -404,27 +386,6 @@ namespace ICSharpCode.Decompiler.CSharp
return metadata.GetString(field.Name).StartsWith("<>f__switch", StringComparison.Ordinal);
}
static readonly Regex automaticPropertyBackingFieldRegex = new Regex(@"^<(.*)>k__BackingField$",
RegexOptions.Compiled | RegexOptions.CultureInvariant);
static bool IsAutomaticPropertyBackingField(FieldDefinition field, MetadataReader metadata, out string propertyName)
{
propertyName = null;
var name = metadata.GetString(field.Name);
var m = automaticPropertyBackingFieldRegex.Match(name);
if (m.Success)
{
propertyName = m.Groups[1].Value;
return true;
}
if (name.StartsWith("_", StringComparison.Ordinal))
{
propertyName = name.Substring(1);
return field.GetCustomAttributes().HasKnownAttribute(metadata, KnownAttribute.CompilerGenerated);
}
return false;
}
internal static bool IsEventBackingFieldName(string fieldName, string eventName, out int suffixLength)
{
suffixLength = 0;

44
ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs

@ -20,6 +20,7 @@ using System; @@ -20,6 +20,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using ICSharpCode.Decompiler.CSharp.Syntax;
using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching;
@ -760,18 +761,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -760,18 +761,19 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
{
var parent = identifier.Parent;
var mrr = parent.Annotation<MemberResolveResult>();
var field = mrr?.Member as IField;
if (field == null || field.Accessibility != Accessibility.Private)
if (mrr?.Member is not IField field || field.Accessibility != Accessibility.Private)
return null;
foreach (var ev in field.DeclaringType.GetEvents(null, GetMemberOptions.IgnoreInheritedMembers))
var module = field.ParentModule as MetadataModule;
if (module == null)
return null;
if (module.MetadataFile.PropertyAndEventBackingFieldLookup.IsEventBackingField((FieldDefinitionHandle)field.MetadataToken, out var eventHandle))
{
if (CSharpDecompiler.IsEventBackingFieldName(field.Name, ev.Name, out int suffixLength) &&
currentMethod.AccessorOwner != ev)
var eventDef = module.ResolveEntity(eventHandle) as IEvent;
if (eventDef != null && currentMethod.AccessorOwner != eventDef)
{
parent.RemoveAnnotations<MemberResolveResult>();
parent.AddAnnotation(new MemberResolveResult(mrr.TargetResult, ev));
if (suffixLength != 0)
identifier.Name = identifier.Name.Substring(0, identifier.Name.Length - suffixLength);
parent.AddAnnotation(new MemberResolveResult(mrr.TargetResult, eventDef));
identifier.Name = eventDef.Name;
return identifier;
}
}
@ -912,20 +914,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -912,20 +914,14 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
if (!m.Success)
return false;
Expression fieldExpression = m.Get<Expression>("field").Single();
// field name must match event name
switch (fieldExpression)
{
case IdentifierExpression identifier:
if (!CSharpDecompiler.IsEventBackingFieldName(identifier.Identifier, ev.Name, out _))
return false;
break;
case MemberReferenceExpression memberRef:
if (!CSharpDecompiler.IsEventBackingFieldName(memberRef.MemberName, ev.Name, out _))
return false;
break;
default:
return false;
}
IField eventField = fieldExpression.GetSymbol() as IField;
if (eventField == null)
return false;
var module = eventField.ParentModule as MetadataModule;
if (module == null)
return false;
if (!module.MetadataFile.PropertyAndEventBackingFieldLookup.IsEventBackingField((FieldDefinitionHandle)eventField.MetadataToken, out _))
return false;
var returnType = ev.ReturnType.GetResolveResult().Type;
var eventType = m.Get<AstType>("type").Single().GetResolveResult().Type;
// ignore tuple element names, dynamic and nullability
@ -1042,9 +1038,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -1042,9 +1038,11 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
return false;
if (fd.GetSymbol() is not IField f)
return false;
if (f.ParentModule is not MetadataModule module)
return false;
return f.Accessibility == Accessibility.Private
&& symbol.ReturnType.Equals(f.ReturnType)
&& CSharpDecompiler.IsEventBackingFieldName(f.Name, ev.Name, out _);
&& module.MetadataFile.PropertyAndEventBackingFieldLookup.IsEventBackingField((FieldDefinitionHandle)f.MetadataToken, out _);
}
}
#endregion

1
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj

@ -110,6 +110,7 @@ @@ -110,6 +110,7 @@
<Compile Include="IL\Transforms\RemoveUnconstrainedGenericReferenceTypeCheck.cs" />
<Compile Include="Metadata\MetadataFile.cs" />
<Compile Include="Metadata\ModuleReferenceMetadata.cs" />
<Compile Include="Metadata\PropertyAndEventBackingFieldLookup.cs" />
<Compile Include="NRTAttributes.cs" />
<Compile Include="PartialTypeInfo.cs" />
<Compile Include="CSharp\ProjectDecompiler\IProjectFileWriter.cs" />

12
ICSharpCode.Decompiler/Metadata/MetadataFile.cs

@ -227,6 +227,18 @@ namespace ICSharpCode.Decompiler.Metadata @@ -227,6 +227,18 @@ namespace ICSharpCode.Decompiler.Metadata
}
}
PropertyAndEventBackingFieldLookup? propertyAndEventBackingFieldLookup;
internal PropertyAndEventBackingFieldLookup PropertyAndEventBackingFieldLookup {
get {
var r = LazyInit.VolatileRead(ref propertyAndEventBackingFieldLookup);
if (r != null)
return r;
else
return LazyInit.GetOrSet(ref propertyAndEventBackingFieldLookup, new PropertyAndEventBackingFieldLookup(Metadata));
}
}
public MetadataFile(MetadataFileKind kind, string fileName, MetadataReaderProvider metadata, MetadataReaderOptions metadataOptions = MetadataReaderOptions.Default, int metadataOffset = 0, bool isEmbedded = false)
{
this.Kind = kind;

93
ICSharpCode.Decompiler/Metadata/PropertyAndEventBackingFieldLookup.cs

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
// Copyright (c) 2025 Siegfried Pammer
//
// 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.Collections.Generic;
using System.Reflection.Metadata;
namespace ICSharpCode.Decompiler.Metadata
{
class PropertyAndEventBackingFieldLookup
{
private readonly MetadataReader metadata;
private readonly Dictionary<FieldDefinitionHandle, PropertyDefinitionHandle> propertyLookup
= new();
private readonly Dictionary<FieldDefinitionHandle, EventDefinitionHandle> eventLookup
= new();
public PropertyAndEventBackingFieldLookup(MetadataReader metadata)
{
this.metadata = metadata;
var nameToFieldMap = new Dictionary<string, FieldDefinitionHandle>();
foreach (var tdh in metadata.TypeDefinitions)
{
var type = metadata.GetTypeDefinition(tdh);
foreach (var fdh in type.GetFields())
{
var field = metadata.GetFieldDefinition(fdh);
var name = metadata.GetString(field.Name);
nameToFieldMap.Add(name, fdh);
}
foreach (var pdh in type.GetProperties())
{
var property = metadata.GetPropertyDefinition(pdh);
var name = metadata.GetString(property.Name);
// default C# property backing field name is "<PropertyName>k__BackingField"
if (nameToFieldMap.TryGetValue($"<{name}>k__BackingField", out var fieldHandle))
{
propertyLookup[fieldHandle] = pdh;
}
else if (nameToFieldMap.TryGetValue($"_{name}", out fieldHandle)
&& fieldHandle.IsCompilerGenerated(metadata))
{
propertyLookup[fieldHandle] = pdh;
}
}
foreach (var edh in type.GetEvents())
{
var ev = metadata.GetEventDefinition(edh);
var name = metadata.GetString(ev.Name);
if (nameToFieldMap.TryGetValue(name, out var fieldHandle))
{
eventLookup[fieldHandle] = edh;
}
else if (nameToFieldMap.TryGetValue($"{name}Event", out fieldHandle))
{
eventLookup[fieldHandle] = edh;
}
}
nameToFieldMap.Clear();
}
}
public bool IsPropertyBackingField(FieldDefinitionHandle field, out PropertyDefinitionHandle handle)
{
return propertyLookup.TryGetValue(field, out handle);
}
public bool IsEventBackingField(FieldDefinitionHandle field, out EventDefinitionHandle handle)
{
return eventLookup.TryGetValue(field, out handle);
}
}
}
Loading…
Cancel
Save