155 lines
5.9 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|