diff --git a/ClosedGenericInterceptor.cs b/ClosedGenericInterceptor.cs index 643ad55..637ed32 100644 --- a/ClosedGenericInterceptor.cs +++ b/ClosedGenericInterceptor.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; -using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -12,7 +11,7 @@ internal sealed class ClosedGenericInterceptor : IEquatable InterceptedMethodReferences { get; } + public ImmutableArray InterceptedMethodReferences { get; } public MethodDescriptor InterceptsMethod { get; } public string SourceText { get; } @@ -30,7 +29,7 @@ IReadOnlyList methodReferences { ImplementationClass = implementationClass; InterceptsMethod = interceptsMethod; - InterceptedMethodReferences = [.. methodReferences.Select(x => new InterceptedMethodReference(x, this))]; + InterceptedMethodReferences = [.. methodReferences]; SourceText = GetSourceText(); hashCode = SourceText.GetHashCode(); } @@ -42,9 +41,9 @@ IReadOnlyList methodReferences private string GetSourceText() { var sb = new StringBuilder($"{Constants.NewLineIndent2}"); - foreach (var reference in InterceptedMethodReferences.Select(x => x.MethodReference)) + foreach (var reference in InterceptedMethodReferences) { - _ = sb.Append(reference.InterceptorAttributeSourceText).Append($"{Constants.NewLineIndent2}"); + _ = sb.Append(reference.Location.AttributeSourceText).Append($"{Constants.NewLineIndent2}"); } var method = InterceptsMethod; var typeParameters = Constants.InterceptorTypeParameters[method.ContainingInterface.Arity]; diff --git a/ImplementationClass.cs b/ImplementationClass.cs index 114207b..cb7526b 100644 --- a/ImplementationClass.cs +++ b/ImplementationClass.cs @@ -23,6 +23,7 @@ public ImplementationClass bool isInterfaceOrMethodOpenGeneric, MarshalInfo marshalInfo, int invocationArgumentCount, + InterceptedLocation location, IReadOnlyList methodReferences ) { @@ -37,7 +38,7 @@ IReadOnlyList methodReferences } else { - openGenericInterceptorsBuilder.Add(this, methodReferences); + openGenericInterceptorsBuilder.Add(this, location, methodReferences); } SourceText = GetSourceText(); hashCode = SourceText.GetHashCode(); diff --git a/ImplementationClassCollection.Key.cs b/ImplementationClassCollection.Key.cs index b85f730..e1c88c2 100644 --- a/ImplementationClassCollection.Key.cs +++ b/ImplementationClassCollection.Key.cs @@ -19,18 +19,11 @@ internal sealed partial class ImplementationClassCollection public Key(MethodReference methodReference) { MethodReference = methodReference; - hashCode = Hash.Combine - ( - MethodReference.Method, - MethodReference.InvocationArgumentCount, - MethodReference.MarshalInfo - ); + hashCode = MethodReference.GetHashCode(); } public override bool Equals(object? obj) => obj is Key other && Equals(other); - public bool Equals(Key other) => (MethodReference.Method == other.MethodReference.Method) && - (MethodReference.InvocationArgumentCount == other.MethodReference.InvocationArgumentCount) && - (MethodReference.MarshalInfo == other.MethodReference.MarshalInfo); + public bool Equals(Key other) => MethodReference == other.MethodReference; public override int GetHashCode() => hashCode; } } diff --git a/ImplementationClassCollection.cs b/ImplementationClassCollection.cs index 507848a..87d46d0 100644 --- a/ImplementationClassCollection.cs +++ b/ImplementationClassCollection.cs @@ -47,6 +47,7 @@ .. dictionary.Select x.Key.MethodReference.IsInterfaceOrMethodOpenGeneric, x.Key.MethodReference.MarshalInfo, x.Key.MethodReference.InvocationArgumentCount, + x.Key.MethodReference.Location, x.Value.AsReadOnly() ) ) diff --git a/InterceptedLocation.cs b/InterceptedLocation.cs new file mode 100644 index 0000000..3fbcc84 --- /dev/null +++ b/InterceptedLocation.cs @@ -0,0 +1,34 @@ +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; + +namespace Monkeymoto.NativeGenericDelegates +{ + internal readonly struct InterceptedLocation : IEquatable + { + private readonly int hashCode; + + public string AttributeSourceText { get; } + public int Character { get; } + public string FilePath { get; } + public int Line { get; } + + public static bool operator ==(InterceptedLocation left, InterceptedLocation right) => left.Equals(right); + public static bool operator !=(InterceptedLocation left, InterceptedLocation right) => !(left == right); + + public InterceptedLocation(InvocationExpressionSyntax invocationExpression) + { + var methodNode = ((MemberAccessExpressionSyntax)invocationExpression.Expression).Name; + var linePosition = methodNode.GetLocation().GetLineSpan().Span.Start; + Character = linePosition.Character + 1; + FilePath = invocationExpression.SyntaxTree.FilePath; + Line = linePosition.Line + 1; + AttributeSourceText = $"[InterceptsLocation(@\"{FilePath}\", {Line}, {Character})]"; + hashCode = Hash.Combine(Character, FilePath, Line); + } + + public override bool Equals(object? obj) => obj is InterceptedLocation other && Equals(other); + public bool Equals(InterceptedLocation other) => (Character == other.Character) && + (FilePath == other.FilePath) && (Line == other.Line); + public override int GetHashCode() => hashCode; + } +} diff --git a/InterceptedMethodReference.cs b/InterceptedMethodReference.cs deleted file mode 100644 index 4aae0ed..0000000 --- a/InterceptedMethodReference.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace Monkeymoto.NativeGenericDelegates -{ - internal sealed class InterceptedMethodReference : IEquatable - { - private readonly int hashCode; - - public ClosedGenericInterceptor Interceptor { get; } - public MethodReference MethodReference { get; } - - public static bool operator ==(InterceptedMethodReference? left, InterceptedMethodReference? right) => - left?.Equals(right) ?? right is null; - public static bool operator !=(InterceptedMethodReference? left, InterceptedMethodReference? right) => - !(left == right); - - public InterceptedMethodReference(MethodReference methodReference, ClosedGenericInterceptor interceptor) - { - Interceptor = interceptor; - MethodReference = methodReference; - hashCode = Hash.Combine(MethodReference.Method, MethodReference.MarshalInfo); - } - - public override bool Equals(object? obj) => obj is InterceptedMethodReference other && Equals(other); - public bool Equals(InterceptedMethodReference? other) => (other is not null) && - (MethodReference.Method == other.MethodReference.Method) && - (MethodReference.MarshalInfo == other.MethodReference.MarshalInfo); - public override int GetHashCode() => hashCode; - } -} diff --git a/MethodReference.cs b/MethodReference.cs index d12b97e..6645102 100644 --- a/MethodReference.cs +++ b/MethodReference.cs @@ -14,13 +14,10 @@ internal sealed class MethodReference : IEquatable { private readonly int hashCode; - public int Character { get; } - public string FilePath { get; } - public string InterceptorAttributeSourceText { get; } public InterfaceDescriptor Interface { get; } public int InvocationArgumentCount { get; } public bool IsInterfaceOrMethodOpenGeneric { get; } - public int Line { get; } + public InterceptedLocation Location { get; } public MarshalInfo MarshalInfo { get; } public MethodDescriptor Method { get; } @@ -50,6 +47,7 @@ CancellationToken cancellationToken } var methodSymbol = invocation.TargetMethod; var marshallers = new HashSet(SymbolEqualityComparer.Default); + var isOpenGenericMethod = false; if (methodSymbol.IsGenericMethod) { if (methodSymbol.TypeArguments.First() is INamedTypeSymbol namedMarshaller) @@ -58,6 +56,7 @@ CancellationToken cancellationToken } else { + isOpenGenericMethod = true; foreach ( var marshaller in getGenericMethodReferences(methodSymbol, invocationExpression) @@ -91,7 +90,7 @@ MethodReference GetReference(INamedTypeSymbol? marshaller) methodDescriptor, invocationExpression, marshalInfo, - !interfaceReference.IsSyntaxReferenceClosedTypeOrMethod, + !interfaceReference.IsSyntaxReferenceClosedTypeOrMethod || isOpenGenericMethod, invocationArgumentCount ); } @@ -120,23 +119,19 @@ private MethodReference int invocationArgumentCount ) { - var methodNode = ((MemberAccessExpressionSyntax)invocationExpression.Expression).Name; - var linePosition = methodNode.GetLocation().GetLineSpan().Span.Start; - Character = linePosition.Character + 1; - FilePath = invocationExpression.SyntaxTree.FilePath; Interface = interfaceDescriptor; + InvocationArgumentCount = invocationArgumentCount; IsInterfaceOrMethodOpenGeneric = isInterfaceOrMethodOpenGeneric; - Line = linePosition.Line + 1; + Location = new InterceptedLocation(invocationExpression); MarshalInfo = marshalInfo; Method = methodDescriptor; - InterceptorAttributeSourceText = $"[InterceptsLocation(@\"{FilePath}\", {Line}, {Character})]"; - InvocationArgumentCount = invocationArgumentCount; - hashCode = Hash.Combine(Character, FilePath, Line); + hashCode = Hash.Combine(Location, Method, InvocationArgumentCount, MarshalInfo); } public override bool Equals(object? obj) => obj is MethodReference other && Equals(other); - public bool Equals(MethodReference? other) => (other is not null) && (Character == other.Character) && - (FilePath == other.FilePath) && (Line == other.Line); + public bool Equals(MethodReference? other) => (other is not null) && (Location == other.Location) && + (Method == other.Method) && (InvocationArgumentCount == other.InvocationArgumentCount) && + (MarshalInfo == other.MarshalInfo); public override int GetHashCode() => hashCode; } } diff --git a/OpenGenericInterceptors.Builder.cs b/OpenGenericInterceptors.Builder.cs index 1844ccc..e15d484 100644 --- a/OpenGenericInterceptors.Builder.cs +++ b/OpenGenericInterceptors.Builder.cs @@ -14,20 +14,21 @@ public sealed class Builder public void Add ( ImplementationClass implementationClass, + InterceptedLocation location, IReadOnlyList methodReferences ) { var attributesHashSet = attributes.GetOrCreate ( - new Key(implementationClass), + new Key(location), ImmutableHashSet.CreateBuilder ); var implementationClassesList = implementationClasses.GetOrCreate ( - new Key(implementationClass), + new Key(location), ImmutableList.CreateBuilder ); - attributesHashSet!.UnionWith(methodReferences.Select(static x => x.InterceptorAttributeSourceText)); + attributesHashSet!.UnionWith(methodReferences.Select(static x => x.Location.AttributeSourceText)); implementationClassesList!.Add(implementationClass); } diff --git a/OpenGenericInterceptors.Key.cs b/OpenGenericInterceptors.Key.cs index 3c22bc8..09f80e3 100644 --- a/OpenGenericInterceptors.Key.cs +++ b/OpenGenericInterceptors.Key.cs @@ -4,15 +4,16 @@ namespace Monkeymoto.NativeGenericDelegates { internal sealed partial class OpenGenericInterceptors { - private readonly struct Key(ImplementationClass implementationClass) : IEquatable + private readonly struct Key(InterceptedLocation location) : IEquatable { - private readonly int hashCode = implementationClass.ID.GetHashCode(); + private readonly int hashCode = location.GetHashCode(); + private readonly InterceptedLocation location = location; public static bool operator ==(Key left, Key right) => left.Equals(right); public static bool operator !=(Key left, Key right) => !(left == right); public override bool Equals(object? obj) => obj is Key other && Equals(other); - public bool Equals(Key other) => hashCode == other.hashCode; + public bool Equals(Key other) => location == other.location; public override int GetHashCode() => hashCode; } }