-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathTypeSolver.fs
114 lines (99 loc) · 4.89 KB
/
TypeSolver.fs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
namespace VSharp.Fuzzer
open VSharp
open VSharp.Core
type internal SolvingResult = {
concreteClassParams: System.Type[]
mockedClassParams: Mocking.Type option[]
concreteMethodParams: System.Type[]
mockedMethodParams: Mocking.Type option[]
}
type internal TypeSolver() =
let dynamicTypeBuilder = Mocking.Mocker()
let mockCache = System.Collections.Generic.Dictionary<System.Type list, ITypeMock>()
let mockTypeCache = System.Collections.Generic.Dictionary<ITypeMock, Mocking.Type>()
let systemTypeCache = System.Collections.Generic.Dictionary<Mocking.Type, System.Type>()
let defaultClausesCount = 10
let mockMethod (freshMock: Mocking.Type) (generate: System.Type -> obj) (method: Mocking.Method) =
let baseMethod = method.BaseMethod
let implementation =
if baseMethod.ReturnType = typeof<System.Void> then
Array.empty
else
Array.init defaultClausesCount (fun _ -> generate baseMethod.ReturnType)
let outParams = baseMethod.GetParameters() |> Array.filter (fun p -> p.IsOut)
let outImplementations =
if Array.isEmpty outParams then Array.empty
else
let types = outParams |> Array.map (fun p -> p.ParameterType.GetElementType())
Array.init defaultClausesCount (fun _ -> Array.map generate types)
freshMock.AddMethod(baseMethod, implementation, outImplementations)
let encodeMock (mock: ITypeMock) (generate: System.Type -> obj) =
Logger.traceTypeSolving $"Encode mock {mock.Name}"
match mockTypeCache.TryGetValue(mock) with
| true, value ->
Logger.traceTypeSolving $"{mock.Name} got from cache"
value
| _ ->
Logger.traceTypeSolving $"{mock.Name} new Mocking.Type"
let freshMock = Mocking.Type(mock.Name)
mock.SuperTypes |> Seq.iter freshMock.AddSuperType
freshMock.MethodMocks |> Seq.iter (mockMethod freshMock generate)
mockTypeCache.Add(mock, freshMock)
freshMock
let mockToType (mock: Mocking.Type) =
Logger.traceTypeSolving $"Build mock {mock.Id}"
match systemTypeCache.TryGetValue(mock) with
| true, value ->
Logger.traceTypeSolving $"{mock.Id} got from cache"
value
| _ ->
Logger.traceTypeSolving $"{mock.Id} new System.Type"
let dynamicType = dynamicTypeBuilder.BuildDynamicType mock
systemTypeCache.Add(mock, dynamicType)
dynamicType
let mockTypes (ts: System.Type list) (generate: System.Type -> obj) =
Logger.traceTypeSolving $"Mock type {ts}"
let mock =
match mockCache.TryGetValue ts with
| true, v ->
Logger.traceTypeSolving $"{ts} got from cache"
v
| false, _ ->
Logger.traceTypeSolving $"{ts} new TypeMock"
let mock = TypeMock(ts)
mockCache.Add(ts, mock)
mock
let encodedMock = encodeMock mock generate
let typ = mockToType encodedMock
mock, typ
member this.MockType (t: System.Type) (generate: System.Type -> obj) =
mockTypes [t] generate
member this.GetMocks () = mockTypeCache
member this.SolveGenericMethodParameters (method: Method) (generate: System.Type -> obj) =
// TODO: Receive type parameters substitution from master process
Logger.traceTypeSolving $"Solve generics for {method.Name}"
let mockedGenerics = System.Collections.Generic.Dictionary<System.Type, ITypeMock>()
let substituteGenerics classParams methodParams =
let getConcreteType =
function
| ConcreteType t -> t
| MockType m ->
let typeMock, systemType = mockTypes (m.SuperTypes |> Seq.toList) generate
if mockedGenerics.ContainsKey(systemType) |> not then
mockedGenerics.Add(systemType, typeMock)
systemType
let methodBase = (method :> IMethod).MethodBase
let classParams = classParams |> Array.map getConcreteType
let methodParams = methodParams |> Array.map getConcreteType
let declaringType = Reflection.concretizeTypeParameters methodBase.DeclaringType classParams
let method = Reflection.concretizeMethodParameters declaringType methodBase methodParams
method
let typeStorage = typeStorage()
match SolveGenericMethodParameters typeStorage method with
| Some(classParams, methodParams) ->
let method = substituteGenerics classParams methodParams
Logger.traceTypeSolving $"Solved generics for {method.Name}"
Some (method, typeStorage, mockedGenerics)
| _ ->
Logger.traceTypeSolving $"Failed solve generics for {method.Name}"
None