-
Notifications
You must be signed in to change notification settings - Fork 326
Expand file tree
/
Copy pathOverloadSuffix.fs
More file actions
208 lines (185 loc) · 7.31 KB
/
Copy pathOverloadSuffix.fs
File metadata and controls
208 lines (185 loc) · 7.31 KB
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
[<RequireQualifiedAccess>]
module rec Fable.Transforms.OverloadSuffix
open Fable
open Fable.AST
open System.Collections.Generic
type ParamTypes = Fable.Type list
let private tryFindAttributeArgs fullName (atts: Fable.Attribute seq) =
atts
|> Seq.tryPick (fun att ->
if att.Entity.FullName = fullName then
Some att.ConstructorArgs
else
None
)
// -------- End of helper functions
let private hashToString (i: int) =
if i < 0 then
"Z" + (abs i).ToString("X")
else
i.ToString("X")
// Not perfect but hopefully covers most of the cases
// Using only common constrains from https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/generics/constraints
let private getConstraintHash genParams =
function
// TODO: Full member signature hash?
| Fable.Constraint.HasMember(name, isStatic) ->
(if isStatic then
"static "
else
"")
+ "member "
+ name
| Fable.Constraint.CoercesTo t -> ":>" + (getTypeFastFullName genParams t)
| Fable.Constraint.IsNullable -> "null"
| Fable.Constraint.IsNotNullable -> "not null"
| Fable.Constraint.IsValueType -> "struct"
| Fable.Constraint.IsReferenceType -> "not struct"
| Fable.Constraint.HasDefaultConstructor -> "new"
| Fable.Constraint.HasAllowsRefStruct -> "allows ref struct"
| Fable.Constraint.HasComparison -> "comparison"
| Fable.Constraint.HasEquality -> "equality"
| Fable.Constraint.IsUnmanaged -> "unmanaged"
| Fable.Constraint.IsDelegate(at, rt) ->
"delegate"
+ ([ at; rt ] |> List.map (getTypeFastFullName genParams) |> String.concat "|")
| Fable.Constraint.IsEnum t -> "enum" + (getTypeFastFullName genParams t)
| Fable.Constraint.SimpleChoice types ->
"simple choice"
+ (types |> List.map (getTypeFastFullName genParams) |> String.concat "|")
let rec private getTypeFastFullName (genParams: IDictionary<_, _>) (t: Fable.Type) =
match t with
| Fable.Nullable(genArg, isStruct) ->
let typeName = getTypeFastFullName genParams genArg
if isStruct then
$"System.Nullable<%s{typeName}>"
else
// there is no overload distinction for nullable reference types,
// so the name and overload suffix are the same as the inner type
typeName
| Fable.Measure fullname -> fullname
| Fable.GenericParam(name, isMeasure, constraints) ->
if isMeasure then
"measure"
else
match genParams.TryGetValue(name) with
| true, i -> i
| false, _ -> constraints |> List.map (getConstraintHash genParams) |> String.concat ","
| Fable.Tuple(genArgs, isStruct) ->
let genArgs =
genArgs |> Seq.map (getTypeFastFullName genParams) |> String.concat " * "
if isStruct then
"struct " + genArgs
else
genArgs
| Fable.Array(genArg, kind) ->
let name =
match kind with
| Fable.ResizeArray -> "array"
| Fable.MutableArray -> "resizearray"
| Fable.ImmutableArray -> "immutablearray"
getTypeFastFullName genParams genArg + " " + name
| Fable.List genArg -> getTypeFastFullName genParams genArg + " list"
| Fable.Option(genArg, isStruct) ->
(if isStruct then
"struct "
else
"")
+ (getTypeFastFullName genParams genArg)
+ " option"
| Fable.LambdaType(argType, returnType) ->
[ argType; returnType ]
|> List.map (getTypeFastFullName genParams)
|> String.concat " -> "
// TODO: Use Func` instead?
| Fable.DelegateType(argTypes, returnType) ->
argTypes @ [ returnType ]
|> List.map (getTypeFastFullName genParams)
|> String.concat " -> "
| Fable.AnonymousRecordType(fieldNames, genArgs, isStruct) ->
let fields =
Seq.zip fieldNames genArgs
|> Seq.map (fun (key, typ) -> key + " : " + getTypeFastFullName genParams typ)
|> String.concat "; "
(if isStruct then
"struct "
else
"")
+ "{|"
+ fields
+ "|}"
| Fable.DeclaredType(tdef, genArgs) ->
let genArgs = genArgs |> Seq.mapToList (getTypeFastFullName genParams)
// Not sure why, but when precompiling F# changes measure types to MeasureProduct<'M, MeasureOne>
match tdef.FullName, genArgs with
| Types.measureProduct2, [ measure; Types.measureOne ] ->
// TODO: generalize it to support aggregate units such as <m/s> or more complex
measure
| _ ->
let genArgs = String.concat "," genArgs
let genArgs =
if System.String.IsNullOrEmpty(genArgs) then
""
else
"[" + genArgs + "]"
tdef.FullName + genArgs
| Fable.MetaType -> Types.type_
| Fable.Any -> Types.object
| Fable.Unit -> Types.unit
| Fable.Boolean -> Types.bool
| Fable.Char -> Types.char
| Fable.String -> Types.string
| Fable.Regex -> Types.regex
| Fable.Number(kind, detail) -> getNumberFullName false kind detail
// From https://stackoverflow.com/a/37449594
let private combineHashCodes (hashes: int seq) =
let hashes = Seq.toArray hashes
if hashes.Length = 0 then
0
else
hashes |> Array.reduce (fun h1 h2 -> ((h1 <<< 5) + h1) ^^^ h2)
// F# hash function gives different results in different runs
// Taken from fable-library-ts/Util.ts. Possible variant in https://stackoverflow.com/a/1660613
let private stringHash (s: string) =
let mutable h = 5381
for i = 0 to s.Length - 1 do
h <- (h * 33) ^^^ (int s[i])
h
let private getHashPrivate (paramTypes: ParamTypes) genParams =
paramTypes
|> List.map (getTypeFastFullName genParams >> stringHash)
|> combineHashCodes
|> hashToString
let hasEmptyOverloadSuffix (curriedParamTypes: ParamTypes) =
// Don't use overload suffix for members without arguments
match curriedParamTypes with
| [] -> true
| [ Fable.Unit ] -> true
| _ -> false
let getHash (entityGenericParams: string list) (curriedParamTypeGroups: Fable.Type list list) =
match curriedParamTypeGroups with
| [ paramTypes ] ->
if hasEmptyOverloadSuffix paramTypes then
""
else
// Generics can have different names in signature
// and implementation files, use the position instead
let genParams =
entityGenericParams |> List.mapi (fun i p -> p, string<int> i) |> dict
getHashPrivate paramTypes genParams
// Members with curried params cannot be overloaded in F#
// TODO: Also private methods defined with `let` cannot be overloaded
// but I don't know how to identify them in the AST
| _ -> ""
/// Used for extension members
let getExtensionHash (curriedParamTypeGroups: Fable.Type list list) =
match curriedParamTypeGroups with
| [ paramTypes ] ->
if hasEmptyOverloadSuffix paramTypes then
""
else
// Type resolution in extension member seems to be different
// and doesn't take generics into account
dict [] |> getHashPrivate paramTypes
// Members with curried params cannot be overloaded in F#
| _ -> ""