Files
2026-08-27 10:56:38 -06:00

155 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp;
internal sealed class DelegateCacheRewriter
{
private readonly SyntheticBoundNodeFactory _factory;
private readonly int _topLevelMethodOrdinal;
private Dictionary<MethodSymbol, DelegateCacheContainer>? _genericCacheContainers;
private static readonly Func<TypeSymbol, HashSet<TypeParameterSymbol>, bool, bool> s_typeParameterSymbolCollector = delegate(TypeSymbol typeSymbol, HashSet<TypeParameterSymbol> result, bool _)
{
if (typeSymbol is TypeParameterSymbol item)
{
result.Add(item);
}
return false;
};
internal DelegateCacheRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal)
{
_factory = factory;
_topLevelMethodOrdinal = topLevelMethodOrdinal;
}
internal static bool CanRewrite(BoundDelegateCreationExpression boundDelegateCreation)
{
if (boundDelegateCreation.MethodOpt.IsStatic)
{
return !boundDelegateCreation.IsExtensionMethod;
}
return false;
}
internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation)
{
SyntaxNode syntax = _factory.Syntax;
_factory.Syntax = boundDelegateCreation.Syntax;
FieldSymbol orAddCacheField = GetOrAddCacheContainer(boundDelegateCreation).GetOrAddCacheField(_factory, boundDelegateCreation);
BoundFieldAccess left = _factory.Field(null, orAddCacheField);
BoundExpression result = _factory.Coalesce(left, _factory.AssignmentExpression(left, boundDelegateCreation));
_factory.Syntax = syntax;
return result;
}
private DelegateCacheContainer GetOrAddCacheContainer(BoundDelegateCreationExpression boundDelegateCreation)
{
int currentGenerationOrdinal = ((CommonPEModuleBuilder)_factory.ModuleBuilderOpt).CurrentGenerationOrdinal;
DelegateCacheContainer concreteDelegateCacheContainer;
if (!TryGetOwnerFunction(_factory.CurrentFunction, boundDelegateCreation, out MethodSymbol ownerFunction))
{
TypeCompilationState compilationState = _factory.CompilationState;
concreteDelegateCacheContainer = compilationState.ConcreteDelegateCacheContainer;
if ((object)concreteDelegateCacheContainer != null)
{
return concreteDelegateCacheContainer;
}
concreteDelegateCacheContainer = (compilationState.ConcreteDelegateCacheContainer = new DelegateCacheContainer(compilationState.Type, currentGenerationOrdinal));
}
else
{
Dictionary<MethodSymbol, DelegateCacheContainer> dictionary = _genericCacheContainers ?? (_genericCacheContainers = new Dictionary<MethodSymbol, DelegateCacheContainer>((IEqualityComparer<MethodSymbol>?)ReferenceEqualityComparer.Instance));
if (dictionary.TryGetValue(ownerFunction, out concreteDelegateCacheContainer))
{
return concreteDelegateCacheContainer;
}
concreteDelegateCacheContainer = new DelegateCacheContainer(ownerFunction, _topLevelMethodOrdinal, dictionary.Count, currentGenerationOrdinal);
dictionary.Add(ownerFunction, concreteDelegateCacheContainer);
}
_factory.AddNestedType(concreteDelegateCacheContainer);
return concreteDelegateCacheContainer;
}
private static bool TryGetOwnerFunction(MethodSymbol currentFunction, BoundDelegateCreationExpression boundDelegateCreation, [NotNullWhen(true)] out MethodSymbol? ownerFunction)
{
//IL_0008: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Invalid comparison between Unknown and I4
MethodSymbol methodOpt = boundDelegateCreation.MethodOpt;
if ((int)methodOpt.MethodKind == 17)
{
for (Symbol symbol = currentFunction; symbol is MethodSymbol methodSymbol; symbol = symbol.ContainingSymbol)
{
if (methodSymbol.Arity > 0)
{
ownerFunction = methodSymbol;
return true;
}
}
ownerFunction = null;
return false;
}
PooledHashSet<TypeParameterSymbol> instance = PooledHashSet<TypeParameterSymbol>.GetInstance();
try
{
if ((methodOpt.IsAbstract || methodOpt.IsVirtual) && boundDelegateCreation.Argument is BoundTypeExpression boundTypeExpression)
{
FindTypeParameters(boundTypeExpression.Type, (HashSet<TypeParameterSymbol>)(object)instance);
}
FindTypeParameters(boundDelegateCreation.Type, (HashSet<TypeParameterSymbol>)(object)instance);
FindTypeParameters(methodOpt, (HashSet<TypeParameterSymbol>)(object)instance);
for (Symbol symbol2 = currentFunction; symbol2 is MethodSymbol methodSymbol2; symbol2 = symbol2.ContainingSymbol)
{
if (usedTypeParametersContains((HashSet<TypeParameterSymbol>)(object)instance, methodSymbol2.TypeParameters))
{
ownerFunction = methodSymbol2;
return true;
}
}
ownerFunction = null;
return false;
}
finally
{
instance.Free();
}
static bool usedTypeParametersContains(HashSet<TypeParameterSymbol> used, ImmutableArray<TypeParameterSymbol> typeParameters)
{
ImmutableArray<TypeParameterSymbol>.Enumerator enumerator = typeParameters.GetEnumerator();
while (enumerator.MoveNext())
{
TypeParameterSymbol current = enumerator.Current;
if (used.Contains(current))
{
return true;
}
}
return false;
}
}
private static void FindTypeParameters(TypeSymbol type, HashSet<TypeParameterSymbol> result)
{
type.VisitType<HashSet<TypeParameterSymbol>>(s_typeParameterSymbolCollector, result, canDigThroughNullable: false, visitCustomModifiers: true);
}
private static void FindTypeParameters(MethodSymbol method, HashSet<TypeParameterSymbol> result)
{
FindTypeParameters(method.ContainingType, result);
ImmutableArray<TypeWithAnnotations>.Enumerator enumerator = method.TypeArgumentsWithAnnotations.GetEnumerator();
while (enumerator.MoveNext())
{
enumerator.Current.VisitType<HashSet<TypeParameterSymbol>>(null, null, s_typeParameterSymbolCollector, result, canDigThroughNullable: false, useDefaultType: false, visitCustomModifiers: true);
}
}
}