Skip to content

Commit

Permalink
Optimized interface method implementation lookup. Should now work wit…
Browse files Browse the repository at this point in the history
…h .NET 8.
  • Loading branch information
sajagi committed Nov 8, 2023
1 parent 4142302 commit 8b1bc4f
Show file tree
Hide file tree
Showing 24 changed files with 691 additions and 239 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module DeriverTests
module DeriveDownTests

open Mono.Cecil
open NUnit.Framework
Expand All @@ -8,7 +8,7 @@ open System.Collections.Generic
open Jumble
open Jumble.Analysis

type DeriverTests () as this =
type DeriveDownTests () as this =
inherit CecilTestsBase()
let ftd = this.LH.FindTypeDef
let type_IComplexGeneric = ftd typedefof<LibA.Complex.IComplexGeneric<_,_>>
Expand Down Expand Up @@ -38,15 +38,15 @@ type DeriverTests () as this =
[<Test>]
member _.``deriveParameter - simple specific (string MethodA() -> same)`` () =
let parameter = method_IA_MethodA_string.Parameters[0].ParameterType
let derivedParameter = Deriver.deriveType type_CA1.Interfaces[0].InterfaceType parameter
let derivedParameter = DeriveDown.deriveType type_CA1.Interfaces[0].InterfaceType parameter

let dp = TypeReference.safeResolve derivedParameter
Assert.AreEqual(type_string, dp)

[<Test>]
member _.``deriveParameter - IEn<IColl<string>> MethodComplexParameter(IEn<IColl<string>> _) -> same`` () =
let parameter = method_IA_MethodComplexParameter.Parameters[0].ParameterType
let derivedParameter = Deriver.deriveType type_CA1.Interfaces[0].InterfaceType parameter
let derivedParameter = DeriveDown.deriveType type_CA1.Interfaces[0].InterfaceType parameter

derivedParameter =?= type_IEnumerable_T

Expand All @@ -62,7 +62,7 @@ type DeriverTests () as this =
|> Seq.filter(fun i -> i.InterfaceType.Name = type_IComplexGeneric.Name
&& (i.InterfaceType :?> GenericInstanceType).GenericArguments[1].Name = type_float.Name)
|> Seq.exactlyOne
let derivedParameter = Deriver.deriveType deriveByRef.InterfaceType parameter
let derivedParameter = DeriveDown.deriveType deriveByRef.InterfaceType parameter

derivedParameter =?= type_int

Expand All @@ -73,7 +73,7 @@ type DeriverTests () as this =
|> Seq.filter(fun i -> i.InterfaceType.Name = type_IComplexGeneric.Name)
|> Seq.exactlyOne

let derivedParameter = Deriver.deriveType deriveByRef.InterfaceType parameter
let derivedParameter = DeriveDown.deriveType deriveByRef.InterfaceType parameter

derivedParameter =?= type_CComplexGeneric_T.GenericParameters[0]

Expand All @@ -86,7 +86,7 @@ type DeriverTests () as this =
|> Seq.exactlyOne

// IDictionary<T,U> -> IDictionary<int, float>
let derivedParameter = Deriver.deriveType deriveByRef.InterfaceType parameter
let derivedParameter = DeriveDown.deriveType deriveByRef.InterfaceType parameter

derivedParameter =?= type_IDictionary_T_U
let getGenericArg n = (derivedParameter :?> GenericInstanceType).GenericArguments[n]
Expand All @@ -104,7 +104,7 @@ type DeriverTests () as this =
|> Seq.exactlyOne

// IDictionary<T,U> -> IDictionary<int, float>
let derivedMethod = Deriver.deriveMethod type_CComplexGenericDouble deriveByRef.InterfaceType method
let derivedMethod = DeriveDown.deriveMethodWithTarget deriveByRef.InterfaceType method type_CComplexGenericDouble

let resolved = MethodReference.safeResolve derivedMethod
let targetMethod = this.LH.FindMethodDefs typedefof<LibA.Complex.CComplexGenericDouble> "MethodComplex"
Expand All @@ -117,7 +117,7 @@ type DeriverTests () as this =
let method = method_IA_MethodGeneric
let deriveByRef = type_CA1.Interfaces |> Seq.exactlyOne
// string GenericMethod<T>() -> string GenericMethod<T>()
let derivedMethod = Deriver.deriveMethod type_CA1 deriveByRef.InterfaceType method
let derivedMethod = DeriveDown.deriveMethodWithTarget deriveByRef.InterfaceType method type_CA1

Assert.IsNotNull(derivedMethod)
let resolved = derivedMethod.Resolve()
Expand Down
174 changes: 174 additions & 0 deletions src/Jumble.Tests/Analysis/FindInterfaceMethodImplementationsTests.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
module Jumble.Tests.Analysis.FindInterfaceMethodImplementationsTests

open FsUnit
open NUnit.Framework

open Jumble.Analysis
open Jumble
open Jumble.Tests

type InterfaceImplLookupTests () as this =
inherit CecilTestsBase()

let ftd = this.LH.FindTypeDef

let test expected actual =
actual |> should equivalent expected

let f = InterfaceMethodImplSearch.findInterfaceMethodImplementations

[<Test>]
member _.``Implicit on same class``() =
let i = ftd typeof<LibA.FindInterfaceMethodImpls.DirectImplicit.I>
let c = ftd typeof<LibA.FindInterfaceMethodImpls.DirectImplicit.C>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Name = "Method" && m.Parameters.Count = 0) |> Seq.exactlyOne
f im c |> should equal [expected]

[<Test>]
member _.``Implicit on parent class``() =
let i = ftd typeof<LibA.FindInterfaceMethodImpls.ParentImplicit.I>
let c1 = ftd typeof<LibA.FindInterfaceMethodImpls.ParentImplicit.C1>
let c2 = ftd typeof<LibA.FindInterfaceMethodImpls.ParentImplicit.C2>
let im = i.Methods |> Seq.exactlyOne
let expected = TypeDefinition.findMethodSingle c1 (nameof(Unchecked.defaultof<LibA.FindInterfaceMethodImpls.ParentImplicit.C1>.Method))
f im c2 |> should equal [expected]

[<Test>]
member _.``Explicit on same class``() =
let i = ftd typeof<LibA.FindInterfaceMethodImpls.DirectExplicit.I>
let c = ftd typeof<LibA.FindInterfaceMethodImpls.DirectExplicit.C>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
f im c |> should equivalent [expected]

[<Test>]
member _.``Explicit on parent class``() =
let i = ftd typeof<LibA.FindInterfaceMethodImpls.ParentExplicit.I>
let c1 = ftd typeof<LibA.FindInterfaceMethodImpls.ParentExplicit.C1>
let c2 = ftd typeof<LibA.FindInterfaceMethodImpls.ParentExplicit.C2>
let im = i.Methods |> Seq.exactlyOne
let expected = c1.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
f im c2 |> should equivalent [expected]

[<Test>]
member _.``Implicit generic with single argument on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericOneArg.I<_>>
let c = ftd typeof<LibA.FindInterfaceMethodImpls.GenericOneArg.CImpl>
let im = i.Methods |> Seq.exactlyOne
let expected = TypeDefinition.findMethodSingle c (nameof(Unchecked.defaultof<LibA.FindInterfaceMethodImpls.GenericOneArg.CImpl>.Method))
f im c |> should equivalent [expected]

[<Test>]
member _.``Implicit generic with single argument on same generic class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericOneArg.I<_>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericOneArg.CGenImpl<_>>
let im = i.Methods |> Seq.exactlyOne
let expected = TypeDefinition.findMethodSingle c (nameof(Unchecked.defaultof<LibA.FindInterfaceMethodImpls.GenericOneArg.CGenImpl<_>>.Method))
f im c |> should equivalent [expected]

[<Test>]
member _.``Explicit generic with single argument on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericOneArg.I<_>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericOneArg.CExpl>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
f im c |> should equivalent [expected]

[<Test>]
member _.``Explicit generic with single argument on same generic class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericOneArg.I<_>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericOneArg.CGenExpl<_>>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
f im c |> should equivalent [expected]

[<Test>]
member _.``Implicit generic with two arguments on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.I<_, _>>
let c = ftd typeof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.CImpl>
let im = i.Methods |> Seq.exactlyOne
let expected = TypeDefinition.findMethodSingle c (nameof(Unchecked.defaultof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.CImpl>.Method))
f im c |> should equivalent [expected]

[<Test>]
member _.``Implicit generic with two arguments on same generic class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.I<_, _>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.CGenImpl<_, _>>
let im = i.Methods |> Seq.exactlyOne
let expected = TypeDefinition.findMethodSingle c (nameof(Unchecked.defaultof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.CGenImpl<_,_>>.Method))
f im c |> should equivalent [expected]

[<Test>]
member _.``Explicit generic with two arguments on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.I<_, _>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.CExpl>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
f im c |> should equivalent [expected]

[<Test>]
member _.``Explicit generic with two arguments on same generic class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.I<_, _>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.GenericMoreArgs.CGenExpl<_, _>>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
f im c |> should equivalent [expected]

[<Test>]
member _.``Two explicit implementations of generic interface on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.MultipleImplementation.I<_>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.MultipleImplementation.CExplExpl>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.toList

expected |> List.length |> should equal 2
f im c |> should equivalent expected

[<Test>]
member _.``One explicit and one implicit implementations of generic interface on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.MultipleImplementation.I<_>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.MultipleImplementation.CImplExpl>
let im = i.Methods |> Seq.exactlyOne
let explMethod = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
let implMethod = c.Methods |> Seq.filter (fun m -> m.Name = "Method" && m.Parameters[0].ParameterType.Resolve() = ftd typedefof<int>) |> Seq.exactlyOne
let expected = [explMethod; implMethod]
f im c |> should equivalent expected

[<Test>]
member _.``Two implicit implementations of generic interface on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.MultipleImplementation.I<_>>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.MultipleImplementation.CImplImpl>
let im = i.Methods |> Seq.exactlyOne

let expected = c.Methods
|> Seq.filter (fun m ->
m.Name = "Method" && m.Parameters[0].ParameterType.Resolve() <> ftd typedefof<char>)
|> Seq.toList

expected |> Seq.length |> should equal 2
f im c |> should equivalent expected

[<Test>]
member _.``Explicit method-level generic on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.MethodLevelGeneric.I>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.MethodLevelGeneric.CExpl>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.Overrides.Count > 0) |> Seq.exactlyOne
f im c |> should equivalent [expected]

[<Test>]
member _.``Implicit method-level generic on same class``() =
let i = ftd typedefof<LibA.FindInterfaceMethodImpls.MethodLevelGeneric.I>
let c = ftd typedefof<LibA.FindInterfaceMethodImpls.MethodLevelGeneric.CImpl>
let im = i.Methods |> Seq.exactlyOne
let expected = c.Methods |> Seq.filter (fun m -> m.HasGenericParameters) |> Seq.exactlyOne
f im c |> should equivalent [expected]

[<Test>]
member _.``Default implementation on child interface``() =
let i1 = ftd typedefof<LibA.FindInterfaceMethodImpls.InterfaceDefault.I1>
let i2 = ftd typedefof<LibA.FindInterfaceMethodImpls.InterfaceDefault.I2>
let im = i1.Methods |> Seq.exactlyOne
let expected = i2.Methods |> Seq.exactlyOne
f im i2 |> should equivalent [expected]
67 changes: 0 additions & 67 deletions src/Jumble.Tests/Analysis/MethodLookupTests.fs

This file was deleted.

5 changes: 3 additions & 2 deletions src/Jumble.Tests/Jumble.Tests.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
<Compile Include="Analysis\Parsers\PParameterTests.fs" />
<Compile Include="Analysis\Parsers\PStacktraceTests.fs" />
<Compile Include="Analysis\TypeTreeTests.fs" />
<Compile Include="Analysis\MethodLookupTests.fs" />
<Compile Include="Analysis\DeriverTests.fs" />
<Compile Include="Analysis\DeriveDownTests.fs" />
<Compile Include="Analysis\GroupingTests.fs" />
<Compile Include="Analysis\FrameworkVersionTests.fs" />
<Compile Include="Analysis\VersionTests.fs" />
<Compile Include="Analysis\FindInterfaceMethodImplementationsTests.fs" />
<Compile Include="ModLoaderTests.fs" />
<Compile Include="E2E\E2ETestsBase.fs" />
<Compile Include="E2E\E2EPrivateOnly.fs" />
Expand All @@ -32,6 +32,7 @@
</Content>
<Compile Include="ManualPerfTests.fs" />
<Compile Include="Export\CSharpExportTests.fs" />
<Compile Include="SanityTests.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Jumble.Console\Jumble.Console.fsproj" />
Expand Down
Loading

0 comments on commit 8b1bc4f

Please sign in to comment.