ts2famix 2.1.0-beta.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eslintrc.json +24 -24
- package/LICENSE +24 -24
- package/README.md +78 -78
- package/dist/analyze.js +20 -10
- package/dist/analyze_functions/process_functions.js +109 -69
- package/dist/famix_functions/EntityDictionary.js +691 -606
- package/dist/famix_functions/helpers_creation.js +35 -19
- package/dist/fqn.js +351 -18
- package/dist/lib/famix/famix_JSON_exporter.js +1 -1
- package/dist/lib/famix/famix_base_element.js +1 -1
- package/dist/lib/famix/famix_repository.js +31 -12
- package/dist/lib/famix/index.js +18 -8
- package/dist/lib/famix/model/famix/access.js +1 -1
- package/dist/lib/famix/model/famix/accessor.js +1 -1
- package/dist/lib/famix/model/famix/alias.js +1 -1
- package/dist/lib/famix/model/famix/arrow_function.js +1 -1
- package/dist/lib/famix/model/famix/behavioral_entity.js +1 -1
- package/dist/lib/famix/model/famix/class.js +1 -1
- package/dist/lib/famix/model/famix/comment.js +1 -1
- package/dist/lib/famix/model/famix/concretisation.js +1 -1
- package/dist/lib/famix/model/famix/container_entity.js +1 -1
- package/dist/lib/famix/model/famix/decorator.js +1 -1
- package/dist/lib/famix/model/famix/entity.js +1 -1
- package/dist/lib/famix/model/famix/enum.js +1 -1
- package/dist/lib/famix/model/famix/enum_value.js +1 -1
- package/dist/lib/famix/model/famix/function.js +1 -1
- package/dist/lib/famix/model/famix/import_clause.js +1 -1
- package/dist/lib/famix/model/famix/index.js +1 -1
- package/dist/lib/famix/model/famix/indexed_file_anchor.js +1 -1
- package/dist/lib/famix/model/famix/inheritance.js +1 -1
- package/dist/lib/famix/model/famix/interface.js +1 -1
- package/dist/lib/famix/model/famix/invocation.js +1 -1
- package/dist/lib/famix/model/famix/method.js +1 -1
- package/dist/lib/famix/model/famix/module.js +2 -2
- package/dist/lib/famix/model/famix/named_entity.js +1 -1
- package/dist/lib/famix/model/famix/parameter.js +1 -1
- package/dist/lib/famix/model/famix/parameter_concretisation.js +1 -1
- package/dist/lib/famix/model/famix/parameter_type.js +1 -1
- package/dist/lib/famix/model/famix/parametric_arrow_function.js +1 -1
- package/dist/lib/famix/model/famix/parametric_class.js +1 -1
- package/dist/lib/famix/model/famix/parametric_function.js +1 -1
- package/dist/lib/famix/model/famix/parametric_interface.js +1 -1
- package/dist/lib/famix/model/famix/parametric_method.js +1 -1
- package/dist/lib/famix/model/famix/primitive_type.js +1 -1
- package/dist/lib/famix/model/famix/property.js +1 -1
- package/dist/lib/famix/model/famix/reference.js +1 -1
- package/dist/lib/famix/model/famix/scoping_entity.js +1 -1
- package/dist/lib/famix/model/famix/script_entity.js +1 -1
- package/dist/lib/famix/model/famix/source_anchor.js +1 -1
- package/dist/lib/famix/model/famix/source_language.js +1 -1
- package/dist/lib/famix/model/famix/sourced_entity.js +1 -1
- package/dist/lib/famix/model/famix/structural_entity.js +1 -1
- package/dist/lib/famix/model/famix/type.js +1 -1
- package/dist/lib/famix/model/famix/variable.js +1 -1
- package/dist/lib/ts-complex/cyclomatic-service.js +1 -1
- package/dist/refactorer/refactor-getter-setter.js +18 -8
- package/dist/ts2famix-cli-wrapper.js +18 -8
- package/dist/ts2famix-cli.js +18 -8
- package/dist/ts2famix-tsconfig.js +18 -8
- package/doc-uml/famix-typescript-model.puml +619 -607
- package/doc-uml/famix-typescript-model.svg +1 -1
- package/eslint.config.mjs +28 -28
- package/jest.config.json +1 -1
- package/package.json +70 -70
- package/src/analyze.ts +120 -120
- package/src/analyze_functions/process_functions.ts +1069 -1040
- package/src/famix_functions/EntityDictionary.ts +2061 -2016
- package/src/famix_functions/helpers_creation.ts +143 -135
- package/src/fqn.ts +416 -50
- package/src/generate_uml.sh +20 -20
- package/src/lib/famix/License.md +22 -22
- package/src/lib/famix/famix_JSON_exporter.ts +56 -56
- package/src/lib/famix/famix_base_element.ts +22 -22
- package/src/lib/famix/famix_repository.ts +288 -278
- package/src/lib/famix/index.ts +8 -8
- package/src/lib/famix/model/famix/access.ts +50 -50
- package/src/lib/famix/model/famix/accessor.ts +15 -15
- package/src/lib/famix/model/famix/alias.ts +39 -39
- package/src/lib/famix/model/famix/arrow_function.ts +15 -15
- package/src/lib/famix/model/famix/behavioral_entity.ts +97 -97
- package/src/lib/famix/model/famix/class.ts +85 -85
- package/src/lib/famix/model/famix/comment.ts +47 -47
- package/src/lib/famix/model/famix/concretisation.ts +40 -40
- package/src/lib/famix/model/famix/container_entity.ts +160 -160
- package/src/lib/famix/model/famix/decorator.ts +37 -37
- package/src/lib/famix/model/famix/entity.ts +15 -15
- package/src/lib/famix/model/famix/enum.ts +30 -30
- package/src/lib/famix/model/famix/enum_value.ts +28 -28
- package/src/lib/famix/model/famix/function.ts +15 -15
- package/src/lib/famix/model/famix/import_clause.ts +51 -51
- package/src/lib/famix/model/famix/index.ts +41 -41
- package/src/lib/famix/model/famix/indexed_file_anchor.ts +46 -46
- package/src/lib/famix/model/famix/inheritance.ts +40 -40
- package/src/lib/famix/model/famix/interface.ts +75 -75
- package/src/lib/famix/model/famix/invocation.ts +65 -65
- package/src/lib/famix/model/famix/method.ts +89 -89
- package/src/lib/famix/model/famix/module.ts +71 -71
- package/src/lib/famix/model/famix/named_entity.ts +95 -95
- package/src/lib/famix/model/famix/parameter.ts +28 -28
- package/src/lib/famix/model/famix/parameter_concretisation.ts +51 -51
- package/src/lib/famix/model/famix/parameter_type.ts +58 -58
- package/src/lib/famix/model/famix/parametric_arrow_function.ts +32 -32
- package/src/lib/famix/model/famix/parametric_class.ts +49 -49
- package/src/lib/famix/model/famix/parametric_function.ts +32 -32
- package/src/lib/famix/model/famix/parametric_interface.ts +49 -49
- package/src/lib/famix/model/famix/parametric_method.ts +32 -32
- package/src/lib/famix/model/famix/primitive_type.ts +15 -15
- package/src/lib/famix/model/famix/property.ts +94 -94
- package/src/lib/famix/model/famix/reference.ts +40 -40
- package/src/lib/famix/model/famix/scoping_entity.ts +35 -35
- package/src/lib/famix/model/famix/script_entity.ts +34 -34
- package/src/lib/famix/model/famix/source_anchor.ts +30 -30
- package/src/lib/famix/model/famix/source_language.ts +35 -35
- package/src/lib/famix/model/famix/sourced_entity.ts +70 -70
- package/src/lib/famix/model/famix/structural_entity.ts +43 -43
- package/src/lib/famix/model/famix/type.ts +87 -87
- package/src/lib/famix/model/famix/variable.ts +27 -27
- package/src/lib/famix/package.json +28 -28
- package/src/lib/ts-complex/cyclomatic-service.ts +83 -83
- package/src/refactorer/refactor-getter-setter.ts +140 -140
- package/src/ts2famix-cli-wrapper.ts +21 -21
- package/src/ts2famix-cli.ts +60 -60
- package/tsconfig.check-tests.json +14 -14
- package/tsconfig.json +73 -72
- package/.vscode/settings.json +0 -4
- package/TODO +0 -1
- package/arwea-fix.json +0 -1
- package/bogus.ts +0 -3
- package/class-diagram.puml +0 -792
- package/debug.txt +0 -13332
- package/debuglog.txt +0 -12073
- package/dist/famix2puml.js +0 -126
- package/dist/getClasses-arrow-body.js +0 -43
- package/dist/lib/famix/src/famix_JSON_exporter.js +0 -55
- package/dist/lib/famix/src/famix_base_element.js +0 -18
- package/dist/lib/famix/src/famix_repository.js +0 -224
- package/dist/lib/famix/src/index.js +0 -31
- package/dist/lib/famix/src/model/famix/access.js +0 -40
- package/dist/lib/famix/src/model/famix/accessor.js +0 -17
- package/dist/lib/famix/src/model/famix/alias.js +0 -33
- package/dist/lib/famix/src/model/famix/arrowFunction.js +0 -17
- package/dist/lib/famix/src/model/famix/arrow_function.js +0 -17
- package/dist/lib/famix/src/model/famix/behavioral_entity.js +0 -79
- package/dist/lib/famix/src/model/famix/class.js +0 -71
- package/dist/lib/famix/src/model/famix/comment.js +0 -39
- package/dist/lib/famix/src/model/famix/concretisation.js +0 -31
- package/dist/lib/famix/src/model/famix/container_entity.js +0 -126
- package/dist/lib/famix/src/model/famix/decorator.js +0 -32
- package/dist/lib/famix/src/model/famix/entity.js +0 -17
- package/dist/lib/famix/src/model/famix/enum.js +0 -31
- package/dist/lib/famix/src/model/famix/enum_value.js +0 -25
- package/dist/lib/famix/src/model/famix/function.js +0 -17
- package/dist/lib/famix/src/model/famix/implicit_variable.js +0 -17
- package/dist/lib/famix/src/model/famix/import_clause.js +0 -41
- package/dist/lib/famix/src/model/famix/index.js +0 -86
- package/dist/lib/famix/src/model/famix/indexed_file_anchor.js +0 -38
- package/dist/lib/famix/src/model/famix/inheritance.js +0 -33
- package/dist/lib/famix/src/model/famix/interface.js +0 -64
- package/dist/lib/famix/src/model/famix/invocation.js +0 -54
- package/dist/lib/famix/src/model/famix/method.js +0 -67
- package/dist/lib/famix/src/model/famix/module.js +0 -60
- package/dist/lib/famix/src/model/famix/named_entity.js +0 -78
- package/dist/lib/famix/src/model/famix/parameter.js +0 -25
- package/dist/lib/famix/src/model/famix/parameterConcretisation.js +0 -44
- package/dist/lib/famix/src/model/famix/parameter_concretisation.js +0 -44
- package/dist/lib/famix/src/model/famix/parameter_type.js +0 -45
- package/dist/lib/famix/src/model/famix/parametricArrowFunction.js +0 -29
- package/dist/lib/famix/src/model/famix/parametric_arrow_function.js +0 -31
- package/dist/lib/famix/src/model/famix/parametric_class.js +0 -44
- package/dist/lib/famix/src/model/famix/parametric_function.js +0 -31
- package/dist/lib/famix/src/model/famix/parametric_interface.js +0 -44
- package/dist/lib/famix/src/model/famix/parametric_method.js +0 -31
- package/dist/lib/famix/src/model/famix/primitive_type.js +0 -17
- package/dist/lib/famix/src/model/famix/property.js +0 -73
- package/dist/lib/famix/src/model/famix/reference.js +0 -33
- package/dist/lib/famix/src/model/famix/scoping_entity.js +0 -36
- package/dist/lib/famix/src/model/famix/script_entity.js +0 -29
- package/dist/lib/famix/src/model/famix/source_anchor.js +0 -27
- package/dist/lib/famix/src/model/famix/source_language.js +0 -35
- package/dist/lib/famix/src/model/famix/sourced_entity.js +0 -60
- package/dist/lib/famix/src/model/famix/structural_entity.js +0 -39
- package/dist/lib/famix/src/model/famix/text_anchor.js +0 -38
- package/dist/lib/famix/src/model/famix/type.js +0 -73
- package/dist/lib/famix/src/model/famix/variable.js +0 -24
- package/fqn-model.json +0 -1
- package/iterateGenericTypes.ts +0 -69
- package/out/class-diagram/class-diagram.svg +0 -1
- package/sample.json +0 -1
- package/sample.ts +0 -1
- package/stats.txt +0 -3091
- package/tabby-debug-output.txt +0 -19433
- package/ts2famix.log +0 -22656
- package/validate-references.js +0 -103
|
@@ -1,2016 +1,2061 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* a function getOrCreateXType takes arguments name: string and element: ts-morph-type and returns a Famix.Type
|
|
3
|
-
* The goal is to keep track of the types (e.g., a method's definedType), for the model.
|
|
4
|
-
* The name doesn't need to be fully qualified (it's the name used in the source code, or the Famix model).
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
import { ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, Identifier, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, PropertyDeclaration, PropertySignature, SourceFile, TypeParameterDeclaration, VariableDeclaration, ParameterDeclaration, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ImportSpecifier, CommentRange, EnumDeclaration, EnumMember, TypeAliasDeclaration, FunctionExpression,
|
|
9
|
-
import { isAmbient, isNamespace } from "../analyze_functions/process_functions";
|
|
10
|
-
import * as Famix from "../lib/famix/model/famix";
|
|
11
|
-
import { FamixRepository } from "../lib/famix/famix_repository";
|
|
12
|
-
import { logger, config } from "../analyze";
|
|
13
|
-
//
|
|
14
|
-
|
|
15
|
-
import
|
|
16
|
-
import * as
|
|
17
|
-
import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export type
|
|
22
|
-
|
|
23
|
-
export type
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
private
|
|
37
|
-
private
|
|
38
|
-
private
|
|
39
|
-
private
|
|
40
|
-
private
|
|
41
|
-
private
|
|
42
|
-
private
|
|
43
|
-
private
|
|
44
|
-
private
|
|
45
|
-
private
|
|
46
|
-
private
|
|
47
|
-
private
|
|
48
|
-
private
|
|
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
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
sourceAnchor
|
|
108
|
-
sourceAnchor
|
|
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
|
-
const
|
|
138
|
-
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
pathInProject =
|
|
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
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const
|
|
188
|
-
const
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
//
|
|
204
|
-
|
|
205
|
-
//
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
fmxIndexFileAnchor.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
*
|
|
219
|
-
* @
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
fmxFile.
|
|
237
|
-
|
|
238
|
-
fmxFile
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
this.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
*
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
fmxModule.
|
|
273
|
-
fmxModule.
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
this.
|
|
279
|
-
|
|
280
|
-
this.
|
|
281
|
-
|
|
282
|
-
this.
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
*
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
this.
|
|
309
|
-
|
|
310
|
-
this.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
*
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
const
|
|
330
|
-
const
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
fmxClass.
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
this.
|
|
348
|
-
|
|
349
|
-
this.
|
|
350
|
-
|
|
351
|
-
this.
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
*
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
this.
|
|
385
|
-
|
|
386
|
-
this.
|
|
387
|
-
|
|
388
|
-
this.
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
*
|
|
401
|
-
* @param
|
|
402
|
-
* @
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
*
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
fmxProperty
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
break;
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
this.
|
|
518
|
-
|
|
519
|
-
this.
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
*
|
|
528
|
-
* @
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
if (
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
fmxMethod.
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
this.
|
|
724
|
-
|
|
725
|
-
this.
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
fmxVariable
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
fmxDecorator
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
if (
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
}
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
//
|
|
1088
|
-
|
|
1089
|
-
//
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
//
|
|
1098
|
-
//
|
|
1099
|
-
//
|
|
1100
|
-
//
|
|
1101
|
-
//
|
|
1102
|
-
//
|
|
1103
|
-
|
|
1104
|
-
//
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
this.
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
if (
|
|
1292
|
-
|
|
1293
|
-
}
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
this.
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
fmxInheritance
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
//
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
this.
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
fmxArrowFunction =
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
function
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
//
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
1
|
+
/**
|
|
2
|
+
* a function getOrCreateXType takes arguments name: string and element: ts-morph-type and returns a Famix.Type
|
|
3
|
+
* The goal is to keep track of the types (e.g., a method's definedType), for the model.
|
|
4
|
+
* The name doesn't need to be fully qualified (it's the name used in the source code, or the Famix model).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
import { ClassDeclaration, ConstructorDeclaration, FunctionDeclaration, Identifier, InterfaceDeclaration, MethodDeclaration, MethodSignature, ModuleDeclaration, PropertyDeclaration, PropertySignature, SourceFile, TypeParameterDeclaration, VariableDeclaration, ParameterDeclaration, Decorator, GetAccessorDeclaration, SetAccessorDeclaration, ImportSpecifier, CommentRange, EnumDeclaration, EnumMember, TypeAliasDeclaration, FunctionExpression, ImportDeclaration, ImportEqualsDeclaration, SyntaxKind, Expression, TypeNode, Scope, ArrowFunction, ExpressionWithTypeArguments, HeritageClause, ts, Type } from "ts-morph";
|
|
9
|
+
import { isAmbient, isNamespace } from "../analyze_functions/process_functions";
|
|
10
|
+
import * as Famix from "../lib/famix/model/famix";
|
|
11
|
+
import { FamixRepository } from "../lib/famix/famix_repository";
|
|
12
|
+
import { logger, config } from "../analyze";
|
|
13
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
14
|
+
import GraphemeSplitter = require('grapheme-splitter');
|
|
15
|
+
import * as Helpers from "./helpers_creation";
|
|
16
|
+
import * as FQNFunctions from "../fqn";
|
|
17
|
+
import path from "path";
|
|
18
|
+
|
|
19
|
+
export type TSMorphObjectType = ImportDeclaration | ImportEqualsDeclaration | SourceFile | ModuleDeclaration | ClassDeclaration | InterfaceDeclaration | MethodDeclaration | ConstructorDeclaration | MethodSignature | FunctionDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | PropertyDeclaration | PropertySignature | TypeParameterDeclaration | Identifier | Decorator | GetAccessorDeclaration | SetAccessorDeclaration | ImportSpecifier | CommentRange | EnumDeclaration | EnumMember | TypeAliasDeclaration | ExpressionWithTypeArguments | TSMorphParametricType;
|
|
20
|
+
|
|
21
|
+
export type TSMorphTypeDeclaration = TypeAliasDeclaration | PropertyDeclaration | PropertySignature | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration | FunctionExpression | ParameterDeclaration | VariableDeclaration | EnumMember | ImportEqualsDeclaration | TSMorphParametricType | TypeParameterDeclaration ;
|
|
22
|
+
|
|
23
|
+
export type TSMorphParametricType = ClassDeclaration | InterfaceDeclaration | FunctionDeclaration | MethodDeclaration | ArrowFunction;
|
|
24
|
+
|
|
25
|
+
type ParametricVariantType = Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod;
|
|
26
|
+
|
|
27
|
+
type ConcreteElementTSMorphType = ClassDeclaration | InterfaceDeclaration | FunctionDeclaration | MethodDeclaration;
|
|
28
|
+
|
|
29
|
+
export type InvocableType = MethodDeclaration | ConstructorDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | FunctionDeclaration | FunctionExpression | ArrowFunction;
|
|
30
|
+
|
|
31
|
+
export class EntityDictionary {
|
|
32
|
+
|
|
33
|
+
public famixRep = new FamixRepository();
|
|
34
|
+
private fmxAliasMap = new Map<string, Famix.Alias>(); // Maps the alias names to their Famix model
|
|
35
|
+
private fmxClassMap = new Map<string, Famix.Class | Famix.ParametricClass>(); // Maps the fully qualified class names to their Famix model
|
|
36
|
+
private fmxInterfaceMap = new Map<string, Famix.Interface | Famix.ParametricInterface>(); // Maps the interface names to their Famix model
|
|
37
|
+
private fmxModuleMap = new Map<ModuleDeclaration, Famix.Module>(); // Maps the namespace names to their Famix model
|
|
38
|
+
private fmxFileMap = new Map<string, Famix.ScriptEntity | Famix.Module>(); // Maps the source file names to their Famix model
|
|
39
|
+
private fmxTypeMap = new Map<TSMorphTypeDeclaration, Famix.Type | Famix.ParameterType>(); // Maps the types declarations to their Famix model
|
|
40
|
+
private fmxPrimitiveTypeMap = new Map<string, Famix.PrimitiveType>(); // Maps the primitive type names to their Famix model
|
|
41
|
+
private fmxFunctionAndMethodMap = new Map<string, Famix.Function | Famix.ParametricFunction | Famix.Method | Famix.ParametricMethod>; // Maps the function names to their Famix model
|
|
42
|
+
private fmxArrowFunctionMap = new Map<string, Famix.ArrowFunction>; // Maps the function names to their Famix model
|
|
43
|
+
private fmxParameterMap = new Map<ParameterDeclaration, Famix.Parameter>(); // Maps the parameters to their Famix model
|
|
44
|
+
private fmxVariableMap = new Map<VariableDeclaration, Famix.Variable>(); // Maps the variables to their Famix model
|
|
45
|
+
private fmxImportClauseMap = new Map<ImportDeclaration | ImportEqualsDeclaration, Famix.ImportClause>(); // Maps the import clauses to their Famix model
|
|
46
|
+
private fmxEnumMap = new Map<EnumDeclaration, Famix.Enum>(); // Maps the enum names to their Famix model
|
|
47
|
+
private fmxInheritanceMap = new Map<string, Famix.Inheritance>(); // Maps the inheritance names to their Famix model
|
|
48
|
+
private UNKNOWN_VALUE = '(unknown due to parsing error)'; // The value to use when a name is not usable
|
|
49
|
+
public fmxElementObjectMap = new Map<Famix.Entity,TSMorphObjectType>();
|
|
50
|
+
public tsMorphElementObjectMap = new Map<TSMorphObjectType,Famix.Entity>();
|
|
51
|
+
|
|
52
|
+
constructor() {
|
|
53
|
+
this.famixRep.setFmxElementObjectMap(this.fmxElementObjectMap);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
public addSourceAnchor(fmx: Famix.SourcedEntity, node: TSMorphObjectType): Famix.IndexedFileAnchor {
|
|
57
|
+
const sourceAnchor: Famix.IndexedFileAnchor = new Famix.IndexedFileAnchor();
|
|
58
|
+
let sourceStart, sourceEnd: number;
|
|
59
|
+
if (fmx && node) {
|
|
60
|
+
// find the start and end positions of the source element
|
|
61
|
+
if (!(node instanceof CommentRange)) {
|
|
62
|
+
sourceStart = node.getStart();
|
|
63
|
+
sourceEnd = node.getEnd();
|
|
64
|
+
} else {
|
|
65
|
+
sourceStart = node.getPos();
|
|
66
|
+
sourceEnd = node.getEnd();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (config.expectGraphemes) {
|
|
70
|
+
/**
|
|
71
|
+
* The following logic handles the case of multi-code point characters (e.g. emoji) in the source text.
|
|
72
|
+
* This is needed because Pharo/Smalltalk treats multi-code point characters as a single character,
|
|
73
|
+
* but JavaScript treats them as multiple characters. This means that the start and end positions
|
|
74
|
+
* of a source element in Pharo/Smalltalk will be different than the start and end positions of the
|
|
75
|
+
* same source element in JavaScript. This logic finds the start and end positions of the source
|
|
76
|
+
* element in JavaScript and then uses those positions to set the start and end positions of the
|
|
77
|
+
* Famix index file anchor.
|
|
78
|
+
* It depends on code in the 'grapheme-splitter' package in npm.
|
|
79
|
+
*/
|
|
80
|
+
const splitter = new GraphemeSplitter();
|
|
81
|
+
const sourceFileText = node.getSourceFile().getFullText();
|
|
82
|
+
const hasGraphemeClusters = splitter.countGraphemes(sourceFileText) > 1;
|
|
83
|
+
if (hasGraphemeClusters) {
|
|
84
|
+
const sourceElementText = sourceFileText.substring(sourceStart, sourceEnd);
|
|
85
|
+
const sourceElementTextGraphemes = splitter.splitGraphemes(sourceElementText);
|
|
86
|
+
const sourceFileTextGraphemes = splitter.splitGraphemes(sourceFileText);
|
|
87
|
+
const numberOfGraphemeClustersBeforeStart = splitter.countGraphemes(sourceFileText.substring(0, sourceStart));
|
|
88
|
+
|
|
89
|
+
// find the start of the sourceElementTextGraphemes array in the sourceFileTextGraphemes array
|
|
90
|
+
sourceStart = Helpers.indexOfSplitArray({searchArray: sourceFileTextGraphemes,
|
|
91
|
+
targetArray: sourceElementTextGraphemes,
|
|
92
|
+
start: sourceStart - numberOfGraphemeClustersBeforeStart});
|
|
93
|
+
sourceEnd = sourceStart + sourceElementTextGraphemes.length;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// The +1 is because the source anchor (Pharo) is 1-based, but ts-morph is 0-based
|
|
98
|
+
sourceAnchor.startPos = sourceStart + 1;
|
|
99
|
+
sourceAnchor.endPos = sourceEnd + 1;
|
|
100
|
+
|
|
101
|
+
let fileName = node.getSourceFile().getFilePath() as string;
|
|
102
|
+
if (fileName.startsWith("/")) {
|
|
103
|
+
fileName = fileName.substring(1);
|
|
104
|
+
}
|
|
105
|
+
sourceAnchor.element = fmx;
|
|
106
|
+
sourceAnchor.fileName = fileName;
|
|
107
|
+
fmx.sourceAnchor = sourceAnchor;
|
|
108
|
+
this.famixRep.addElement(sourceAnchor);
|
|
109
|
+
|
|
110
|
+
}
|
|
111
|
+
return sourceAnchor;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Makes a Famix index file anchor
|
|
116
|
+
* @param sourceElement A source element
|
|
117
|
+
* @param famixElement The Famix model of the source element
|
|
118
|
+
*/
|
|
119
|
+
public makeFamixIndexFileAnchor(sourceElement: TSMorphObjectType, famixElement: Famix.SourcedEntity): void {
|
|
120
|
+
// Famix.Comment is not a named entity (does not have a fullyQualifiedName)
|
|
121
|
+
if (!(famixElement instanceof Famix.Comment)) { // must be a named entity
|
|
122
|
+
// insanity check: named entities should have fullyQualifiedName
|
|
123
|
+
const fullyQualifiedName = (famixElement as Famix.NamedEntity).fullyQualifiedName;
|
|
124
|
+
if (!fullyQualifiedName || fullyQualifiedName === this.UNKNOWN_VALUE) {
|
|
125
|
+
throw new Error(`Famix element ${famixElement.constructor.name} has no valid fullyQualifiedName.`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
logger.debug("making index file anchor for '" + sourceElement?.getText() + "' with famixElement " + famixElement.getJSON());
|
|
130
|
+
const fmxIndexFileAnchor = new Famix.IndexedFileAnchor();
|
|
131
|
+
fmxIndexFileAnchor.element = famixElement;
|
|
132
|
+
this.fmxElementObjectMap.set(famixElement, sourceElement);
|
|
133
|
+
|
|
134
|
+
if (sourceElement !== null) {
|
|
135
|
+
const absolutePathProject = this.famixRep.getAbsolutePath();
|
|
136
|
+
|
|
137
|
+
const absolutePath = path.normalize(sourceElement.getSourceFile().getFilePath());
|
|
138
|
+
|
|
139
|
+
const positionNodeModules = absolutePath.indexOf('node_modules');
|
|
140
|
+
|
|
141
|
+
let pathInProject: string = "";
|
|
142
|
+
|
|
143
|
+
if (positionNodeModules !== -1) {
|
|
144
|
+
const pathFromNodeModules = absolutePath.substring(positionNodeModules);
|
|
145
|
+
pathInProject = pathFromNodeModules;
|
|
146
|
+
} else {
|
|
147
|
+
pathInProject = this.convertToRelativePath(absolutePath, absolutePathProject);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// revert any backslashes to forward slashes (path.normalize on windows introduces them)
|
|
151
|
+
pathInProject = pathInProject.replace(/\\/g, "/");
|
|
152
|
+
|
|
153
|
+
if (pathInProject.startsWith("/")) {
|
|
154
|
+
pathInProject = pathInProject.substring(1);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
fmxIndexFileAnchor.fileName = pathInProject;
|
|
158
|
+
let sourceStart, sourceEnd
|
|
159
|
+
// ,sourceLineStart, sourceLineEnd
|
|
160
|
+
: number;
|
|
161
|
+
if (!(sourceElement instanceof CommentRange)) {
|
|
162
|
+
sourceStart = sourceElement.getStart();
|
|
163
|
+
sourceEnd = sourceElement.getEnd();
|
|
164
|
+
// sourceLineStart = sourceElement.getStartLineNumber();
|
|
165
|
+
// sourceLineEnd = sourceElement.getEndLineNumber();
|
|
166
|
+
} else {
|
|
167
|
+
sourceStart = sourceElement.getPos();
|
|
168
|
+
sourceEnd = sourceElement.getEnd();
|
|
169
|
+
}
|
|
170
|
+
if (config.expectGraphemes) {
|
|
171
|
+
/**
|
|
172
|
+
* The following logic handles the case of multi-code point characters (e.g. emoji) in the source text.
|
|
173
|
+
* This is needed because Pharo/Smalltalk treats multi-code point characters as a single character,
|
|
174
|
+
* but JavaScript treats them as multiple characters. This means that the start and end positions
|
|
175
|
+
* of a source element in Pharo/Smalltalk will be different than the start and end positions of the
|
|
176
|
+
* same source element in JavaScript. This logic finds the start and end positions of the source
|
|
177
|
+
* element in JavaScript and then uses those positions to set the start and end positions of the
|
|
178
|
+
* Famix index file anchor.
|
|
179
|
+
* It depends on code in the 'grapheme-splitter' package in npm.
|
|
180
|
+
*/
|
|
181
|
+
const splitter = new GraphemeSplitter();
|
|
182
|
+
const sourceFileText = sourceElement.getSourceFile().getFullText();
|
|
183
|
+
const hasGraphemeClusters = splitter.countGraphemes(sourceFileText) > 1;
|
|
184
|
+
if (hasGraphemeClusters) {
|
|
185
|
+
const sourceElementText = sourceFileText.substring(sourceStart, sourceEnd);
|
|
186
|
+
const sourceElementTextGraphemes = splitter.splitGraphemes(sourceElementText);
|
|
187
|
+
const sourceFileTextGraphemes = splitter.splitGraphemes(sourceFileText);
|
|
188
|
+
const numberOfGraphemeClustersBeforeStart = splitter.countGraphemes(sourceFileText.substring(0, sourceStart));
|
|
189
|
+
|
|
190
|
+
// find the start of the sourceElementTextGraphemes array in the sourceFileTextGraphemes array
|
|
191
|
+
sourceStart = Helpers.indexOfSplitArray({searchArray: sourceFileTextGraphemes,
|
|
192
|
+
targetArray: sourceElementTextGraphemes,
|
|
193
|
+
start: sourceStart - numberOfGraphemeClustersBeforeStart});
|
|
194
|
+
sourceEnd = sourceStart + sourceElementTextGraphemes.length;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
// note: the +1 is because the source anchor is 1-based, but ts-morph is 0-based
|
|
198
|
+
fmxIndexFileAnchor.startPos = sourceStart + 1;
|
|
199
|
+
fmxIndexFileAnchor.endPos = sourceEnd + 1;
|
|
200
|
+
|
|
201
|
+
// if (!(famixElement instanceof Famix.ImportClause || famixElement instanceof Famix.Access || famixElement instanceof Famix.Reference || famixElement instanceof Famix.Invocation || famixElement instanceof Famix.Inheritance) && !(famixElement instanceof Famix.Comment) && !(sourceElement instanceof CommentRange) && !(sourceElement instanceof Identifier) && !(sourceElement instanceof ImportSpecifier) && !(sourceElement instanceof ExpressionWithTypeArguments)) {
|
|
202
|
+
// initFQN(sourceElement, famixElement);
|
|
203
|
+
// }
|
|
204
|
+
} else {
|
|
205
|
+
// sourceElement is null
|
|
206
|
+
logger.warn("sourceElement is null for famixElement " + famixElement.getJSON());
|
|
207
|
+
fmxIndexFileAnchor.fileName = "unknown";
|
|
208
|
+
fmxIndexFileAnchor.startPos = 0;
|
|
209
|
+
fmxIndexFileAnchor.endPos = 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
this.famixRep.addElement(fmxIndexFileAnchor);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Creates or gets a Famix script entity or module
|
|
217
|
+
* @param f A source file
|
|
218
|
+
* @param isModule A boolean indicating if the source file is a module
|
|
219
|
+
* @returns The Famix model of the source file
|
|
220
|
+
*/
|
|
221
|
+
public createOrGetFamixFile(f: SourceFile, isModule: boolean): Famix.ScriptEntity | Famix.Module {
|
|
222
|
+
let fmxFile: Famix.ScriptEntity; // | Famix.Module;
|
|
223
|
+
|
|
224
|
+
const fileName = f.getBaseName();
|
|
225
|
+
const fullyQualifiedFilename = f.getFilePath();
|
|
226
|
+
const foundFileName = this.fmxFileMap.get(fullyQualifiedFilename);
|
|
227
|
+
if (!foundFileName) {
|
|
228
|
+
if (isModule) {
|
|
229
|
+
fmxFile = new Famix.Module();
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
fmxFile = new Famix.ScriptEntity();
|
|
233
|
+
}
|
|
234
|
+
fmxFile.name = fileName;
|
|
235
|
+
fmxFile.numberOfLinesOfText = f.getEndLineNumber() - f.getStartLineNumber();
|
|
236
|
+
fmxFile.numberOfCharacters = f.getFullText().length;
|
|
237
|
+
|
|
238
|
+
initFQN(f, fmxFile);
|
|
239
|
+
|
|
240
|
+
this.makeFamixIndexFileAnchor(f, fmxFile);
|
|
241
|
+
|
|
242
|
+
this.fmxFileMap.set(fullyQualifiedFilename, fmxFile);
|
|
243
|
+
this.famixRep.addElement(fmxFile);
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
fmxFile = foundFileName;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this.fmxElementObjectMap.set(fmxFile,f);
|
|
250
|
+
return fmxFile;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Creates or gets a Famix Module
|
|
255
|
+
* @param moduleDeclaration A module
|
|
256
|
+
* @returns The Famix model of the module
|
|
257
|
+
*/
|
|
258
|
+
public createOrGetFamixModule(moduleDeclaration: ModuleDeclaration): Famix.Module {
|
|
259
|
+
if (this.fmxModuleMap.has(moduleDeclaration)) {
|
|
260
|
+
const rModule = this.fmxModuleMap.get(moduleDeclaration);
|
|
261
|
+
if (rModule) {
|
|
262
|
+
return rModule;
|
|
263
|
+
} else {
|
|
264
|
+
throw new Error(`Famix module ${moduleDeclaration.getName()} is not found in the module map.`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const fmxModule = new Famix.Module();
|
|
269
|
+
const moduleName = moduleDeclaration.getName();
|
|
270
|
+
fmxModule.name = moduleName;
|
|
271
|
+
fmxModule.isAmbient = isAmbient(moduleDeclaration);
|
|
272
|
+
fmxModule.isNamespace = isNamespace(moduleDeclaration);
|
|
273
|
+
fmxModule.isModule = !fmxModule.isNamespace && !fmxModule.isAmbient;
|
|
274
|
+
|
|
275
|
+
initFQN(moduleDeclaration, fmxModule);
|
|
276
|
+
this.makeFamixIndexFileAnchor(moduleDeclaration, fmxModule);
|
|
277
|
+
|
|
278
|
+
this.fmxModuleMap.set(moduleDeclaration, fmxModule);
|
|
279
|
+
|
|
280
|
+
this.famixRep.addElement(fmxModule);
|
|
281
|
+
|
|
282
|
+
this.fmxElementObjectMap.set(fmxModule,moduleDeclaration);
|
|
283
|
+
return fmxModule;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Creates a Famix alias
|
|
288
|
+
* @param typeAliasDeclaration An alias
|
|
289
|
+
* @returns The Famix model of the alias
|
|
290
|
+
*/
|
|
291
|
+
public createFamixAlias(typeAliasDeclaration: TypeAliasDeclaration): Famix.Alias {
|
|
292
|
+
let fmxAlias: Famix.Alias;
|
|
293
|
+
const aliasName = typeAliasDeclaration.getName();
|
|
294
|
+
//const aliasFullyQualifiedName = a.getType().getText(); // FQNFunctions.getFQN(a);
|
|
295
|
+
const aliasFullyQualifiedName = FQNFunctions.getFQN(typeAliasDeclaration);
|
|
296
|
+
const foundAlias = this.fmxAliasMap.get(aliasFullyQualifiedName);
|
|
297
|
+
if (!foundAlias) {
|
|
298
|
+
fmxAlias = new Famix.Alias();
|
|
299
|
+
fmxAlias.name = typeAliasDeclaration.getName();
|
|
300
|
+
const aliasNameWithGenerics = aliasName + (typeAliasDeclaration.getTypeParameters().length ? ("<" + typeAliasDeclaration.getTypeParameters().map(tp => tp.getName()).join(", ") + ">") : "");
|
|
301
|
+
logger.debug(`> NOTE: alias ${aliasName} has fully qualified name ${aliasFullyQualifiedName} and name with generics ${aliasNameWithGenerics}.`);
|
|
302
|
+
|
|
303
|
+
const fmxType = this.createOrGetFamixType(aliasNameWithGenerics, typeAliasDeclaration.getType(), typeAliasDeclaration);
|
|
304
|
+
fmxAlias.aliasedEntity = fmxType;
|
|
305
|
+
initFQN(typeAliasDeclaration, fmxAlias);
|
|
306
|
+
this.makeFamixIndexFileAnchor(typeAliasDeclaration, fmxAlias);
|
|
307
|
+
|
|
308
|
+
this.fmxAliasMap.set(aliasFullyQualifiedName, fmxAlias);
|
|
309
|
+
|
|
310
|
+
this.famixRep.addElement(fmxAlias);
|
|
311
|
+
}
|
|
312
|
+
else {
|
|
313
|
+
fmxAlias = foundAlias;
|
|
314
|
+
}
|
|
315
|
+
this.fmxElementObjectMap.set(fmxAlias,typeAliasDeclaration);
|
|
316
|
+
|
|
317
|
+
return fmxAlias;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Creates or gets a Famix class or parameterizable class
|
|
322
|
+
* @param cls A class
|
|
323
|
+
* @returns The Famix model of the class
|
|
324
|
+
*/
|
|
325
|
+
public createOrGetFamixClass(cls: ClassDeclaration): Famix.Class | Famix.ParametricClass {
|
|
326
|
+
let fmxClass: Famix.Class | Famix.ParametricClass;
|
|
327
|
+
const isAbstract = cls.isAbstract();
|
|
328
|
+
const classFullyQualifiedName = FQNFunctions.getFQN(cls);
|
|
329
|
+
const clsName = cls.getName() || this.UNKNOWN_VALUE;
|
|
330
|
+
const isGeneric = cls.getTypeParameters().length;
|
|
331
|
+
const foundClass = this.fmxClassMap.get(classFullyQualifiedName);
|
|
332
|
+
if (!foundClass) {
|
|
333
|
+
if (isGeneric) {
|
|
334
|
+
fmxClass = new Famix.ParametricClass();
|
|
335
|
+
}
|
|
336
|
+
else {
|
|
337
|
+
fmxClass = new Famix.Class();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
fmxClass.name = clsName;
|
|
341
|
+
initFQN(cls, fmxClass);
|
|
342
|
+
// fmxClass.fullyQualifiedName = classFullyQualifiedName;
|
|
343
|
+
fmxClass.isAbstract = isAbstract;
|
|
344
|
+
|
|
345
|
+
this.makeFamixIndexFileAnchor(cls, fmxClass);
|
|
346
|
+
|
|
347
|
+
this.fmxClassMap.set(classFullyQualifiedName, fmxClass);
|
|
348
|
+
|
|
349
|
+
this.famixRep.addElement(fmxClass);
|
|
350
|
+
|
|
351
|
+
this.fmxElementObjectMap.set(fmxClass,cls);
|
|
352
|
+
}
|
|
353
|
+
else {
|
|
354
|
+
fmxClass = foundClass;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return fmxClass;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Creates or gets a Famix interface or parameterizable interface
|
|
362
|
+
* @param inter An interface
|
|
363
|
+
* @returns The Famix model of the interface
|
|
364
|
+
*/
|
|
365
|
+
public createOrGetFamixInterface(inter: InterfaceDeclaration): Famix.Interface | Famix.ParametricInterface {
|
|
366
|
+
|
|
367
|
+
let fmxInterface: Famix.Interface | Famix.ParametricInterface;
|
|
368
|
+
const interName = inter.getName();
|
|
369
|
+
const interFullyQualifiedName = FQNFunctions.getFQN(inter);
|
|
370
|
+
const foundInterface = this.fmxInterfaceMap.get(interFullyQualifiedName);
|
|
371
|
+
if (!foundInterface) {
|
|
372
|
+
const isGeneric = inter.getTypeParameters().length;
|
|
373
|
+
if (isGeneric) {
|
|
374
|
+
fmxInterface = new Famix.ParametricInterface();
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
fmxInterface = new Famix.Interface();
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
fmxInterface.name = interName;
|
|
381
|
+
initFQN(inter, fmxInterface);
|
|
382
|
+
this.makeFamixIndexFileAnchor(inter, fmxInterface);
|
|
383
|
+
|
|
384
|
+
this.fmxInterfaceMap.set(interFullyQualifiedName, fmxInterface);
|
|
385
|
+
|
|
386
|
+
this.famixRep.addElement(fmxInterface);
|
|
387
|
+
|
|
388
|
+
this.fmxElementObjectMap.set(fmxInterface,inter);
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
fmxInterface = foundInterface;
|
|
392
|
+
}
|
|
393
|
+
return fmxInterface;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Creates or gets a Famix concrete element
|
|
399
|
+
* @param concreteElement A parametric Element
|
|
400
|
+
* @param concreteElementDeclaration the element declaration
|
|
401
|
+
* @param concreteArguments concrete arguments
|
|
402
|
+
* @returns A parametric Element
|
|
403
|
+
*/
|
|
404
|
+
public createOrGetFamixConcreteElement(concreteElement : ParametricVariantType,
|
|
405
|
+
concreteElementDeclaration : ConcreteElementTSMorphType,
|
|
406
|
+
concreteArguments: TypeNode[]): ParametricVariantType {
|
|
407
|
+
|
|
408
|
+
let fullyQualifiedFilename = concreteElement.fullyQualifiedName;
|
|
409
|
+
let params = "";
|
|
410
|
+
|
|
411
|
+
concreteArguments.map((param) => {
|
|
412
|
+
params = params+param.getText()+',';
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
params = params.substring(0, params.length - 1);
|
|
416
|
+
|
|
417
|
+
fullyQualifiedFilename = Helpers.replaceLastBetweenTags(fullyQualifiedFilename,params);
|
|
418
|
+
|
|
419
|
+
let concElement: ParametricVariantType;
|
|
420
|
+
|
|
421
|
+
if (!this.fmxInterfaceMap.has(fullyQualifiedFilename) &&
|
|
422
|
+
!this.fmxClassMap.has(fullyQualifiedFilename) &&
|
|
423
|
+
!this.fmxFunctionAndMethodMap.has(fullyQualifiedFilename)){
|
|
424
|
+
concElement = _.cloneDeep(concreteElement);
|
|
425
|
+
concElement.fullyQualifiedName = fullyQualifiedFilename;
|
|
426
|
+
concElement.clearGenericParameters();
|
|
427
|
+
concreteArguments.map((param) => {
|
|
428
|
+
if (param instanceof TypeParameterDeclaration) {
|
|
429
|
+
const parameter = this.createOrGetFamixType(param.getText(),param.getType(), param);
|
|
430
|
+
concElement.addConcreteParameter(parameter);
|
|
431
|
+
} else {
|
|
432
|
+
logger.warn(`> WARNING: concrete argument ${param.getText()} is not a TypeParameterDeclaration. It is a ${param.getKindName()}.`);
|
|
433
|
+
}
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
if (concreteElement instanceof Famix.ParametricClass) {
|
|
437
|
+
this.fmxClassMap.set(fullyQualifiedFilename, concElement as Famix.ParametricClass);
|
|
438
|
+
} else if (concreteElement instanceof Famix.ParametricInterface) {
|
|
439
|
+
this.fmxInterfaceMap.set(fullyQualifiedFilename, concElement as Famix.ParametricInterface);
|
|
440
|
+
} else if (concreteElement instanceof Famix.ParametricFunction) {
|
|
441
|
+
this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricFunction);
|
|
442
|
+
} else { // if (concreteElement instanceof Famix.ParametricMethod) {
|
|
443
|
+
this.fmxFunctionAndMethodMap.set(fullyQualifiedFilename, concElement as Famix.ParametricMethod);
|
|
444
|
+
}
|
|
445
|
+
this.famixRep.addElement(concElement);
|
|
446
|
+
this.fmxElementObjectMap.set(concElement,concreteElementDeclaration);
|
|
447
|
+
} else {
|
|
448
|
+
if (concreteElement instanceof Famix.ParametricClass) {
|
|
449
|
+
concElement = this.fmxClassMap.get(fullyQualifiedFilename) as Famix.ParametricClass;
|
|
450
|
+
} else if (concreteElement instanceof Famix.ParametricInterface) {
|
|
451
|
+
concElement = this.fmxInterfaceMap.get(fullyQualifiedFilename) as Famix.ParametricInterface;
|
|
452
|
+
} else if (concreteElement instanceof Famix.ParametricFunction) {
|
|
453
|
+
concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename) as Famix.ParametricFunction;
|
|
454
|
+
} else { // if (concreteElement instanceof Famix.ParametricMethod) {
|
|
455
|
+
concElement = this.fmxFunctionAndMethodMap.get(fullyQualifiedFilename) as Famix.ParametricMethod;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return concElement;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Creates a Famix property
|
|
463
|
+
* @param property A property
|
|
464
|
+
* @returns The Famix model of the property
|
|
465
|
+
*/
|
|
466
|
+
public createFamixProperty(property: PropertyDeclaration | PropertySignature): Famix.Property {
|
|
467
|
+
const fmxProperty = new Famix.Property();
|
|
468
|
+
const isSignature = property instanceof PropertySignature;
|
|
469
|
+
fmxProperty.name = property.getName();
|
|
470
|
+
|
|
471
|
+
let propTypeName = this.UNKNOWN_VALUE;
|
|
472
|
+
try {
|
|
473
|
+
propTypeName = property.getType().getText().trim();
|
|
474
|
+
} catch (error) {
|
|
475
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for property: ${property.getName()}. Continuing...`);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
const fmxType = this.createOrGetFamixType(propTypeName, property.getType(), property);
|
|
479
|
+
fmxProperty.declaredType = fmxType;
|
|
480
|
+
|
|
481
|
+
// add the visibility (public, private, etc.) to the fmxProperty
|
|
482
|
+
fmxProperty.visibility = "";
|
|
483
|
+
|
|
484
|
+
property.getModifiers().forEach(m => {
|
|
485
|
+
switch (m.getText()) {
|
|
486
|
+
case Scope.Public:
|
|
487
|
+
fmxProperty.visibility = "public";
|
|
488
|
+
break;
|
|
489
|
+
case Scope.Protected:
|
|
490
|
+
fmxProperty.visibility = "protected";
|
|
491
|
+
break;
|
|
492
|
+
case Scope.Private:
|
|
493
|
+
fmxProperty.visibility = "private";
|
|
494
|
+
break;
|
|
495
|
+
case "static":
|
|
496
|
+
fmxProperty.isClassSide = true;
|
|
497
|
+
break;
|
|
498
|
+
case "readonly":
|
|
499
|
+
fmxProperty.readOnly = true;
|
|
500
|
+
break;
|
|
501
|
+
default:
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
if (!isSignature && property.getExclamationTokenNode()) {
|
|
507
|
+
fmxProperty.isDefinitelyAssigned = true;
|
|
508
|
+
}
|
|
509
|
+
if (property.getQuestionTokenNode()) {
|
|
510
|
+
fmxProperty.isOptional = true;
|
|
511
|
+
}
|
|
512
|
+
if (property.getName().substring(0, 1) === "#") {
|
|
513
|
+
fmxProperty.isJavaScriptPrivate = true;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
initFQN(property, fmxProperty);
|
|
517
|
+
this.makeFamixIndexFileAnchor(property, fmxProperty);
|
|
518
|
+
|
|
519
|
+
this.famixRep.addElement(fmxProperty);
|
|
520
|
+
|
|
521
|
+
this.fmxElementObjectMap.set(fmxProperty,property);
|
|
522
|
+
|
|
523
|
+
return fmxProperty;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Creates a Famix method or accessor
|
|
528
|
+
* @param method A method or an accessor
|
|
529
|
+
* @param currentCC The cyclomatic complexity metrics of the current source file
|
|
530
|
+
* @returns The Famix model of the method or the accessor
|
|
531
|
+
*/
|
|
532
|
+
public createOrGetFamixMethod(
|
|
533
|
+
method: MethodDeclaration | ConstructorDeclaration | MethodSignature | GetAccessorDeclaration | SetAccessorDeclaration,
|
|
534
|
+
currentCC: { [key: string]: number }
|
|
535
|
+
): Famix.Method | Famix.Accessor | Famix.ParametricMethod {
|
|
536
|
+
// console.log(`\n=== Creating/Getting Method ===`);
|
|
537
|
+
// console.log(`Method kind: ${method.getKindName()}`);
|
|
538
|
+
// console.log(`Method text: ${method.getText().slice(0, 50)}...`);
|
|
539
|
+
const fqn = FQNFunctions.getFQN(method);
|
|
540
|
+
// console.log(`Method FQN: ${fqn}`);
|
|
541
|
+
logger.debug(`Processing method ${fqn}`);
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
let fmxMethod = this.fmxFunctionAndMethodMap.get(fqn) as Famix.Method | Famix.Accessor | Famix.ParametricMethod;
|
|
545
|
+
if (!fmxMethod) {
|
|
546
|
+
// console.log('Method not found in map, creating new');
|
|
547
|
+
const isGeneric = method.getTypeParameters().length > 0;
|
|
548
|
+
if (method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
|
|
549
|
+
fmxMethod = new Famix.Accessor();
|
|
550
|
+
const isGetter = method instanceof GetAccessorDeclaration;
|
|
551
|
+
const isSetter = method instanceof SetAccessorDeclaration;
|
|
552
|
+
if (isGetter) { (fmxMethod as Famix.Accessor).kind = "getter"; }
|
|
553
|
+
if (isSetter) { (fmxMethod as Famix.Accessor).kind = "setter"; }
|
|
554
|
+
} else {
|
|
555
|
+
fmxMethod = isGeneric ? new Famix.ParametricMethod() : new Famix.Method();
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const isConstructor = method instanceof ConstructorDeclaration;
|
|
559
|
+
const isSignature = method instanceof MethodSignature;
|
|
560
|
+
let isAbstract = false;
|
|
561
|
+
let isStatic = false;
|
|
562
|
+
if (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration) {
|
|
563
|
+
isAbstract = method.isAbstract();
|
|
564
|
+
isStatic = method.isStatic();
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (isConstructor) { (fmxMethod as Famix.Accessor).kind = "constructor"; }
|
|
568
|
+
fmxMethod.isAbstract = isAbstract;
|
|
569
|
+
fmxMethod.isClassSide = isStatic;
|
|
570
|
+
fmxMethod.isPrivate = (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration)
|
|
571
|
+
? !!method.getModifiers().find(x => x.getText() === 'private') : false;
|
|
572
|
+
fmxMethod.isProtected = (method instanceof MethodDeclaration || method instanceof GetAccessorDeclaration || method instanceof SetAccessorDeclaration)
|
|
573
|
+
? !!method.getModifiers().find(x => x.getText() === 'protected') : false;
|
|
574
|
+
fmxMethod.signature = Helpers.computeSignature(method.getText());
|
|
575
|
+
|
|
576
|
+
const methodName = isConstructor ? "constructor" : method.getName();
|
|
577
|
+
fmxMethod.name = methodName;
|
|
578
|
+
|
|
579
|
+
if (!isConstructor && methodName.startsWith("#")) {
|
|
580
|
+
fmxMethod.isPrivate = true;
|
|
581
|
+
}
|
|
582
|
+
fmxMethod.isPublic = !fmxMethod.isPrivate && !fmxMethod.isProtected;
|
|
583
|
+
|
|
584
|
+
fmxMethod.cyclomaticComplexity = isSignature ? 0 : (currentCC[methodName] || 0);
|
|
585
|
+
let methodTypeName = this.UNKNOWN_VALUE;
|
|
586
|
+
try {
|
|
587
|
+
methodTypeName = method.getReturnType().getText().trim();
|
|
588
|
+
logger.debug(`Method return type: ${methodTypeName}`);
|
|
589
|
+
} catch (error) {
|
|
590
|
+
logger.error(`Failed to get return type for ${fqn}: ${error}`);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
const fmxType = this.createOrGetFamixType(methodTypeName, method.getType(), method);
|
|
594
|
+
// console.log(`Created/retrieved return type with FQN: ${fmxType.fullyQualifiedName}`);
|
|
595
|
+
fmxMethod.declaredType = fmxType;
|
|
596
|
+
fmxMethod.numberOfLinesOfCode = method.getEndLineNumber() - method.getStartLineNumber();
|
|
597
|
+
fmxMethod.numberOfParameters = method.getParameters().length;
|
|
598
|
+
fmxMethod.numberOfStatements = isSignature ? 0 : method.getStatements().length;
|
|
599
|
+
|
|
600
|
+
// Add to famixRep
|
|
601
|
+
initFQN(method, fmxMethod);
|
|
602
|
+
this.famixRep.addElement(fmxMethod);
|
|
603
|
+
this.makeFamixIndexFileAnchor(method, fmxMethod);
|
|
604
|
+
this.fmxFunctionAndMethodMap.set(fqn, fmxMethod);
|
|
605
|
+
logger.debug(`Added method ${fqn} to famixRep`);
|
|
606
|
+
} else {
|
|
607
|
+
logger.debug(`Method ${fqn} already exists`);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
this.fmxElementObjectMap.set(fmxMethod, method);
|
|
611
|
+
return fmxMethod;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/**
|
|
615
|
+
* Creates a Famix function
|
|
616
|
+
* @param func A function
|
|
617
|
+
* @param currentCC The cyclomatic complexity metrics of the current source file
|
|
618
|
+
* @returns The Famix model of the function
|
|
619
|
+
*/
|
|
620
|
+
public createOrGetFamixFunction(func: FunctionDeclaration | FunctionExpression, currentCC: { [key: string]: number }): Famix.Function | Famix.ParametricFunction {
|
|
621
|
+
let fmxFunction: Famix.Function | Famix.ParametricFunction;
|
|
622
|
+
const isGeneric = func.getTypeParameters().length > 0;
|
|
623
|
+
const functionFullyQualifiedName = FQNFunctions.getFQN(func);
|
|
624
|
+
if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
|
|
625
|
+
if (isGeneric) {
|
|
626
|
+
fmxFunction = new Famix.ParametricFunction();
|
|
627
|
+
}
|
|
628
|
+
else {
|
|
629
|
+
fmxFunction = new Famix.Function();
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
const name = func.getName();
|
|
633
|
+
if (name) {
|
|
634
|
+
fmxFunction.name = name;
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
fmxFunction.name = "anonymous";
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
fmxFunction.signature = Helpers.computeSignature(func.getText());
|
|
641
|
+
fmxFunction.cyclomaticComplexity = currentCC[fmxFunction.name];
|
|
642
|
+
initFQN(func, fmxFunction);
|
|
643
|
+
// fmxFunction.fullyQualifiedName = functionFullyQualifiedName;
|
|
644
|
+
|
|
645
|
+
let functionTypeName = this.UNKNOWN_VALUE;
|
|
646
|
+
try {
|
|
647
|
+
functionTypeName = func.getReturnType().getText().trim();
|
|
648
|
+
} catch (error) {
|
|
649
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${func.getName()}. Continuing...`);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
const fmxType = this.createOrGetFamixType(functionTypeName, func.getType(), func);
|
|
653
|
+
fmxFunction.declaredType = fmxType;
|
|
654
|
+
fmxFunction.numberOfLinesOfCode = func.getEndLineNumber() - func.getStartLineNumber();
|
|
655
|
+
const parameters = func.getParameters();
|
|
656
|
+
fmxFunction.numberOfParameters = parameters.length;
|
|
657
|
+
fmxFunction.numberOfStatements = func.getStatements().length;
|
|
658
|
+
this.makeFamixIndexFileAnchor(func, fmxFunction);
|
|
659
|
+
|
|
660
|
+
this.famixRep.addElement(fmxFunction);
|
|
661
|
+
|
|
662
|
+
this.fmxElementObjectMap.set(fmxFunction,func);
|
|
663
|
+
|
|
664
|
+
this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxFunction);
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
fmxFunction = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as (Famix.Function | Famix.ParametricFunction);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
return fmxFunction;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Creates a Famix parameter
|
|
675
|
+
* @param param A parameter
|
|
676
|
+
* @returns The Famix model of the parameter
|
|
677
|
+
*/
|
|
678
|
+
public createOrGetFamixParameter(param: ParameterDeclaration): Famix.Parameter {
|
|
679
|
+
if (this.fmxParameterMap.has(param)) {
|
|
680
|
+
const rParameter = this.fmxParameterMap.get(param);
|
|
681
|
+
if (rParameter) {
|
|
682
|
+
return rParameter;
|
|
683
|
+
} else {
|
|
684
|
+
throw new Error(`Famix parameter ${param.getName()} is not found in the parameter map.`);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
const fmxParam = new Famix.Parameter();
|
|
689
|
+
|
|
690
|
+
let paramTypeName = this.UNKNOWN_VALUE;
|
|
691
|
+
try {
|
|
692
|
+
paramTypeName = param.getType().getText().trim();
|
|
693
|
+
} catch (error) {
|
|
694
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for parameter: ${param.getName()}. Continuing...`);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
const fmxType = this.createOrGetFamixType(paramTypeName, param.getType(), param);
|
|
698
|
+
fmxParam.declaredType = fmxType;
|
|
699
|
+
fmxParam.name = param.getName();
|
|
700
|
+
|
|
701
|
+
initFQN(param, fmxParam);
|
|
702
|
+
this.makeFamixIndexFileAnchor(param, fmxParam);
|
|
703
|
+
|
|
704
|
+
this.famixRep.addElement(fmxParam);
|
|
705
|
+
|
|
706
|
+
this.fmxElementObjectMap.set(fmxParam, param);
|
|
707
|
+
this.fmxParameterMap.set(param, fmxParam);
|
|
708
|
+
|
|
709
|
+
return fmxParam;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Creates a Famix type parameter
|
|
714
|
+
* @param tp A type parameter
|
|
715
|
+
* @returns The Famix model of the type parameter
|
|
716
|
+
*/
|
|
717
|
+
public createFamixParameterType(tp: TypeParameterDeclaration): Famix.ParameterType {
|
|
718
|
+
|
|
719
|
+
const fmxParameterType = new Famix.ParameterType();
|
|
720
|
+
|
|
721
|
+
fmxParameterType.name = tp.getName();
|
|
722
|
+
initFQN(tp, fmxParameterType);
|
|
723
|
+
this.makeFamixIndexFileAnchor(tp, fmxParameterType);
|
|
724
|
+
|
|
725
|
+
this.famixRep.addElement(fmxParameterType);
|
|
726
|
+
|
|
727
|
+
this.fmxElementObjectMap.set(fmxParameterType,tp);
|
|
728
|
+
|
|
729
|
+
return fmxParameterType;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
// /**
|
|
734
|
+
// * Creates a Famix type in the context of concretizations
|
|
735
|
+
// * @param typeName A type name
|
|
736
|
+
// * @param element An element
|
|
737
|
+
// * @returns The Famix model of the type
|
|
738
|
+
// */
|
|
739
|
+
// public createOrGetFamixConcreteType(element: TypeNode):
|
|
740
|
+
// Famix.ParameterType | Famix.PrimitiveType | Famix.Class | Famix.Interface {
|
|
741
|
+
// if (this.fmxTypeMap.has(element)) {
|
|
742
|
+
// const rType = this.fmxTypeMap.get(element);
|
|
743
|
+
// if (rType) {
|
|
744
|
+
// return rType;
|
|
745
|
+
// } else {
|
|
746
|
+
// throw new Error(`Famix type ${element.getText()} is not found in the type map.`);
|
|
747
|
+
// }
|
|
748
|
+
// }
|
|
749
|
+
|
|
750
|
+
// const typeParameterDeclaration = element.getSymbol()?.getDeclarations()[0] as TypeParameterDeclaration;
|
|
751
|
+
// // const parameterTypeName : string = element.getText();
|
|
752
|
+
// const parameterTypeName = getPrimitiveTypeName(element.getType()) || element.getText();
|
|
753
|
+
// let fmxParameterType: Famix.Type | Famix.Class | Famix.Interface | undefined = undefined;
|
|
754
|
+
|
|
755
|
+
// // get a TypeReference from a TypeNode
|
|
756
|
+
// const typeReference = element.getType();
|
|
757
|
+
// // get a TypeDeclaration from a TypeReference
|
|
758
|
+
// const typeDeclaration = typeReference.getSymbol()?.getDeclarations()[0] as TSMorphTypeDeclaration;
|
|
759
|
+
|
|
760
|
+
// let isClassOrInterface = false;
|
|
761
|
+
// if (this.fmxClassMap.has(parameterTypeName)){
|
|
762
|
+
// this.fmxClassMap.forEach((obj, name) => {
|
|
763
|
+
// if(obj instanceof Famix.ParametricClass){
|
|
764
|
+
// if (name === element.getText() && obj.genericParameters.size>0) {
|
|
765
|
+
// fmxParameterType = obj;
|
|
766
|
+
// isClassOrInterface = true;
|
|
767
|
+
// }
|
|
768
|
+
// } else {
|
|
769
|
+
// if (name === element.getText()) {
|
|
770
|
+
// fmxParameterType = obj;
|
|
771
|
+
// isClassOrInterface = true;
|
|
772
|
+
// }
|
|
773
|
+
// }
|
|
774
|
+
// });
|
|
775
|
+
// }
|
|
776
|
+
|
|
777
|
+
// if (this.fmxInterfaceMap.has(parameterTypeName)){
|
|
778
|
+
// this.fmxInterfaceMap.forEach((obj, name) => {
|
|
779
|
+
// if(obj instanceof Famix.ParametricInterface){
|
|
780
|
+
// if (name === element.getText() && obj.genericParameters.size>0) {
|
|
781
|
+
// fmxParameterType = obj;
|
|
782
|
+
// isClassOrInterface = true;
|
|
783
|
+
// }
|
|
784
|
+
// } else {
|
|
785
|
+
// if (name === element.getText()) {
|
|
786
|
+
// fmxParameterType = obj;
|
|
787
|
+
// isClassOrInterface = true;
|
|
788
|
+
// }
|
|
789
|
+
// }
|
|
790
|
+
// });
|
|
791
|
+
// }
|
|
792
|
+
|
|
793
|
+
// if(!isClassOrInterface){
|
|
794
|
+
// if (!this.fmxTypeMap.has(typeDeclaration)) {
|
|
795
|
+
// // TODO refactor
|
|
796
|
+
// if (isPrimitiveType(parameterTypeName)) {
|
|
797
|
+
// fmxParameterType = this.createOrGetFamixPrimitiveType(parameterTypeName);
|
|
798
|
+
// } else {
|
|
799
|
+
// fmxParameterType = new Famix.ParameterType();
|
|
800
|
+
// }
|
|
801
|
+
|
|
802
|
+
// fmxParameterType.name = parameterTypeName;
|
|
803
|
+
// this.famixRep.addElement(fmxParameterType);
|
|
804
|
+
// this.fmxTypeMap.set(typeDeclaration, fmxParameterType);
|
|
805
|
+
// this.fmxElementObjectMap.set(fmxParameterType,typeParameterDeclaration);
|
|
806
|
+
// }
|
|
807
|
+
// else {
|
|
808
|
+
// const result = this.fmxTypeMap.get(typeDeclaration);
|
|
809
|
+
// if (result) {
|
|
810
|
+
// fmxParameterType = result;
|
|
811
|
+
// } else {
|
|
812
|
+
// throw new Error(`Famix type ${typeDeclaration} is not found in the Type map.`);
|
|
813
|
+
// }
|
|
814
|
+
// }
|
|
815
|
+
// }
|
|
816
|
+
// if (!fmxParameterType) {
|
|
817
|
+
// throw new Error(`fmxParameterType was undefined for parameterTypeName ${parameterTypeName}`);
|
|
818
|
+
// }
|
|
819
|
+
// return fmxParameterType;
|
|
820
|
+
// }
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
* Creates a Famix variable
|
|
824
|
+
* @param variable A variable
|
|
825
|
+
* @returns The Famix model of the variable
|
|
826
|
+
*/
|
|
827
|
+
public createOrGetFamixVariable(variable: VariableDeclaration): Famix.Variable {
|
|
828
|
+
if (this.fmxVariableMap.has(variable)) {
|
|
829
|
+
const rVariable = this.fmxVariableMap.get(variable);
|
|
830
|
+
if (rVariable) {
|
|
831
|
+
return rVariable;
|
|
832
|
+
} else {
|
|
833
|
+
throw new Error(`Famix parameter ${variable.getName()} is not found in the variable map.`);
|
|
834
|
+
}
|
|
835
|
+
}
|
|
836
|
+
const fmxVariable = new Famix.Variable();
|
|
837
|
+
|
|
838
|
+
let variableTypeName = this.UNKNOWN_VALUE;
|
|
839
|
+
try {
|
|
840
|
+
variableTypeName = variable.getType().getText().trim();
|
|
841
|
+
} catch (error) {
|
|
842
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for variable: ${variable.getName()}. Continuing...`);
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const fmxType = this.createOrGetFamixType(variableTypeName, variable.getType(), variable);
|
|
846
|
+
fmxVariable.declaredType = fmxType;
|
|
847
|
+
fmxVariable.name = variable.getName();
|
|
848
|
+
initFQN(variable, fmxVariable);
|
|
849
|
+
this.makeFamixIndexFileAnchor(variable, fmxVariable);
|
|
850
|
+
|
|
851
|
+
this.famixRep.addElement(fmxVariable);
|
|
852
|
+
|
|
853
|
+
this.fmxElementObjectMap.set(fmxVariable,variable);
|
|
854
|
+
this.fmxVariableMap.set(variable, fmxVariable);
|
|
855
|
+
|
|
856
|
+
return fmxVariable;
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
/**
|
|
860
|
+
* Creates a Famix enum
|
|
861
|
+
* @param enumEntity An enum
|
|
862
|
+
* @returns The Famix model of the enum
|
|
863
|
+
*/
|
|
864
|
+
public createOrGetFamixEnum(enumEntity: EnumDeclaration): Famix.Enum {
|
|
865
|
+
if (this.fmxEnumMap.has(enumEntity)) {
|
|
866
|
+
const rEnum = this.fmxEnumMap.get(enumEntity);
|
|
867
|
+
if (rEnum) {
|
|
868
|
+
return rEnum;
|
|
869
|
+
} else {
|
|
870
|
+
throw new Error(`Famix enum ${enumEntity.getName()} is not found in the enum map.`);
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
const fmxEnum = new Famix.Enum();
|
|
874
|
+
fmxEnum.name = enumEntity.getName();
|
|
875
|
+
initFQN(enumEntity, fmxEnum);
|
|
876
|
+
this.makeFamixIndexFileAnchor(enumEntity, fmxEnum);
|
|
877
|
+
|
|
878
|
+
this.famixRep.addElement(fmxEnum);
|
|
879
|
+
|
|
880
|
+
this.fmxElementObjectMap.set(fmxEnum,enumEntity);
|
|
881
|
+
this.fmxEnumMap.set(enumEntity, fmxEnum);
|
|
882
|
+
|
|
883
|
+
return fmxEnum;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Creates a Famix enum value
|
|
888
|
+
* @param enumMember An enum member
|
|
889
|
+
* @returns The Famix model of the enum member
|
|
890
|
+
*/
|
|
891
|
+
public createFamixEnumValue(enumMember: EnumMember): Famix.EnumValue {
|
|
892
|
+
const fmxEnumValue = new Famix.EnumValue();
|
|
893
|
+
|
|
894
|
+
let enumValueTypeName = this.UNKNOWN_VALUE;
|
|
895
|
+
try {
|
|
896
|
+
enumValueTypeName = enumMember.getType().getText().trim();
|
|
897
|
+
} catch (error) {
|
|
898
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for enum value: ${enumMember.getName()}. Continuing...`);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
const fmxType = this.createOrGetFamixType(enumValueTypeName, enumMember.getType(), enumMember);
|
|
902
|
+
fmxEnumValue.declaredType = fmxType;
|
|
903
|
+
fmxEnumValue.name = enumMember.getName();
|
|
904
|
+
initFQN(enumMember, fmxEnumValue);
|
|
905
|
+
this.makeFamixIndexFileAnchor(enumMember, fmxEnumValue);
|
|
906
|
+
|
|
907
|
+
this.famixRep.addElement(fmxEnumValue);
|
|
908
|
+
|
|
909
|
+
this.fmxElementObjectMap.set(fmxEnumValue,enumMember);
|
|
910
|
+
|
|
911
|
+
return fmxEnumValue;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
/**
|
|
915
|
+
* Creates or gets a Famix decorator
|
|
916
|
+
* @param decorator A decorator
|
|
917
|
+
* @param decoratedEntity A class, a method, a parameter or a property
|
|
918
|
+
* @returns The Famix model of the decorator
|
|
919
|
+
*/
|
|
920
|
+
public createOrGetFamixDecorator(decorator: Decorator, decoratedEntity: ClassDeclaration | MethodDeclaration | GetAccessorDeclaration | SetAccessorDeclaration | ParameterDeclaration | PropertyDeclaration): Famix.Decorator {
|
|
921
|
+
const fmxDecorator = new Famix.Decorator();
|
|
922
|
+
const decoratorName = "@" + decorator.getName();
|
|
923
|
+
const decoratorExpression = decorator.getText().substring(1);
|
|
924
|
+
|
|
925
|
+
fmxDecorator.name = decoratorName;
|
|
926
|
+
fmxDecorator.decoratorExpression = decoratorExpression;
|
|
927
|
+
const decoratedEntityFullyQualifiedName = FQNFunctions.getFQN(decoratedEntity);
|
|
928
|
+
const fmxDecoratedEntity = this.famixRep.getFamixEntityByFullyQualifiedName(decoratedEntityFullyQualifiedName) as Famix.NamedEntity;
|
|
929
|
+
fmxDecorator.decoratedEntity = fmxDecoratedEntity;
|
|
930
|
+
initFQN(decorator, fmxDecorator);
|
|
931
|
+
this.makeFamixIndexFileAnchor(decorator, fmxDecorator);
|
|
932
|
+
|
|
933
|
+
this.famixRep.addElement(fmxDecorator);
|
|
934
|
+
|
|
935
|
+
this.fmxElementObjectMap.set(fmxDecorator,decorator);
|
|
936
|
+
|
|
937
|
+
return fmxDecorator;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
/**
|
|
941
|
+
* Creates a Famix comment
|
|
942
|
+
* @param comment A comment
|
|
943
|
+
* @param fmxScope The Famix model of the comment's container
|
|
944
|
+
* @param isJSDoc A boolean indicating if the comment is a JSDoc
|
|
945
|
+
* @returns The Famix model of the comment
|
|
946
|
+
*/
|
|
947
|
+
public createFamixComment(comment: CommentRange, fmxScope: Famix.NamedEntity, isJSDoc: boolean): Famix.Comment {
|
|
948
|
+
logger.debug(`> NOTE: creating comment ${comment.getText()} in scope ${fmxScope.name}.`);
|
|
949
|
+
const fmxComment = new Famix.Comment();
|
|
950
|
+
fmxComment.container = fmxScope; // adds comment to the container's comments collection
|
|
951
|
+
fmxComment.isJSDoc = isJSDoc;
|
|
952
|
+
|
|
953
|
+
this.makeFamixIndexFileAnchor(comment, fmxComment);
|
|
954
|
+
|
|
955
|
+
this.famixRep.addElement(fmxComment);
|
|
956
|
+
|
|
957
|
+
this.fmxElementObjectMap.set(fmxComment,comment);
|
|
958
|
+
|
|
959
|
+
return fmxComment;
|
|
960
|
+
}
|
|
961
|
+
|
|
962
|
+
/**
|
|
963
|
+
* Creates or gets a Famix type
|
|
964
|
+
* @param typeName A type name
|
|
965
|
+
* @param element A ts-morph element
|
|
966
|
+
* @returns The Famix model of the type
|
|
967
|
+
*/
|
|
968
|
+
public createOrGetFamixType(typeNameArg: string, tsMorphType: Type | undefined, element: TSMorphTypeDeclaration): Famix.Type {
|
|
969
|
+
logger.debug(`Creating (or getting) type: '${tsMorphType?.getText() || "undefined"}' of element: '${element?.getText().slice(0, 50)}...' of kind: ${element?.getKindName()}`);
|
|
970
|
+
|
|
971
|
+
// convert type to correct primitive name (workaround for unique symbole primitive type)
|
|
972
|
+
// don't convert to primitive if it's a generic type
|
|
973
|
+
const typeName = !typeNameArg.includes("<") && tsMorphType && getPrimitiveTypeName(tsMorphType) || typeNameArg;
|
|
974
|
+
|
|
975
|
+
if (isPrimitiveType(typeName)) {
|
|
976
|
+
return this.createOrGetFamixPrimitiveType(typeName);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
if (element.isKind(SyntaxKind.MethodSignature) || element.isKind(SyntaxKind.MethodDeclaration)) {
|
|
980
|
+
const methodFQN = FQNFunctions.getFQN(element);
|
|
981
|
+
const returnTypeFQN = `${methodFQN.replace(/\[Method(Signature|Declaration)\]$/, '')}[ReturnType]`;
|
|
982
|
+
|
|
983
|
+
// Check if we already have this return type in the repository
|
|
984
|
+
const existingType = this.famixRep.getFamixEntityByFullyQualifiedName(returnTypeFQN);
|
|
985
|
+
if (existingType) {
|
|
986
|
+
// console.log(`Found existing return type with FQN: ${returnTypeFQN}`);
|
|
987
|
+
return existingType as Famix.Type;
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// console.log(`Creating return type with distinct FQN: ${returnTypeFQN}`);
|
|
991
|
+
const fmxType = new Famix.Type();
|
|
992
|
+
fmxType.name = typeName;
|
|
993
|
+
fmxType.fullyQualifiedName = returnTypeFQN;
|
|
994
|
+
|
|
995
|
+
// Set container (same as method's container)
|
|
996
|
+
const methodAncestor = Helpers.findTypeAncestor(element);
|
|
997
|
+
if (methodAncestor) {
|
|
998
|
+
const ancestorFQN = FQNFunctions.getFQN(methodAncestor);
|
|
999
|
+
const ancestor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFQN) as Famix.ContainerEntity;
|
|
1000
|
+
if (ancestor) {
|
|
1001
|
+
fmxType.container = ancestor;
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
this.famixRep.addElement(fmxType);
|
|
1006
|
+
this.fmxTypeMap.set(element, fmxType);
|
|
1007
|
+
this.fmxElementObjectMap.set(fmxType, element);
|
|
1008
|
+
return fmxType;
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
const isParametricType =
|
|
1012
|
+
(element instanceof ClassDeclaration && element.getTypeParameters().length > 0) ||
|
|
1013
|
+
(element instanceof InterfaceDeclaration && element.getTypeParameters().length > 0);
|
|
1014
|
+
|
|
1015
|
+
if (isParametricType) {
|
|
1016
|
+
return this.createOrGetFamixParametricType(typeName, element as TSMorphParametricType);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
if (!this.fmxTypeMap.has(element)) {
|
|
1020
|
+
// console.log('Type not found in map, creating new');
|
|
1021
|
+
let ancestor: Famix.ContainerEntity | undefined;
|
|
1022
|
+
if (element) {
|
|
1023
|
+
const typeAncestor = Helpers.findTypeAncestor(element);
|
|
1024
|
+
// console.log(`Type ancestor found: ${typeAncestor?.getKindName()}`);
|
|
1025
|
+
|
|
1026
|
+
if (typeAncestor) {
|
|
1027
|
+
const ancestorFullyQualifiedName = FQNFunctions.getFQN(typeAncestor);
|
|
1028
|
+
// console.log(`Ancestor FQN: ${ancestorFullyQualifiedName}`);
|
|
1029
|
+
ancestor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
|
|
1030
|
+
if (!ancestor) {
|
|
1031
|
+
ancestor = this.createOrGetFamixType(typeAncestor.getText(), typeAncestor.getType(), typeAncestor as TSMorphTypeDeclaration);
|
|
1032
|
+
// console.log('Ancestor not found in repo, creating it');
|
|
1033
|
+
} else {
|
|
1034
|
+
console.log(`Found ancestor in famixRep: ${ancestor.fullyQualifiedName}`);
|
|
1035
|
+
}
|
|
1036
|
+
} else {
|
|
1037
|
+
console.log(`No type ancestor found for ${typeName} - proceeding without container`);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
const fmxType = new Famix.Type();
|
|
1042
|
+
fmxType.name = typeName;
|
|
1043
|
+
// console.log(`Created new type with name: ${typeName}`);
|
|
1044
|
+
if (ancestor) {
|
|
1045
|
+
fmxType.container = ancestor;
|
|
1046
|
+
} else {
|
|
1047
|
+
throw new Error(`Ancestor not found for type ${typeName}.`);
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
initFQN(element, fmxType);
|
|
1051
|
+
// console.log(`Type FQN after init: ${fmxType.fullyQualifiedName}`);
|
|
1052
|
+
this.makeFamixIndexFileAnchor(element, fmxType);
|
|
1053
|
+
this.famixRep.addElement(fmxType);
|
|
1054
|
+
// console.log('Added type to repository');
|
|
1055
|
+
this.fmxTypeMap.set(element, fmxType);
|
|
1056
|
+
} else {
|
|
1057
|
+
const fmxType = this.fmxTypeMap.get(element);
|
|
1058
|
+
if (!fmxType) {
|
|
1059
|
+
throw new Error(`Famix type ${typeName} is not found in the Type map.`);
|
|
1060
|
+
}
|
|
1061
|
+
return fmxType;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
const fmxType = this.fmxTypeMap.get(element)!;
|
|
1065
|
+
this.fmxElementObjectMap.set(fmxType, element);
|
|
1066
|
+
return fmxType;
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
/**
|
|
1070
|
+
* Creates or gets a Famix type that is parametric
|
|
1071
|
+
* @param typeName A type name
|
|
1072
|
+
* @param element A ts-morph element
|
|
1073
|
+
* @returns The Famix model of the parameter type
|
|
1074
|
+
*/
|
|
1075
|
+
createOrGetFamixParametricType(typeName: string, element: TSMorphParametricType): Famix.Type {
|
|
1076
|
+
|
|
1077
|
+
if (this.fmxTypeMap.has(element) === true) {
|
|
1078
|
+
const result = this.fmxTypeMap.get(element);
|
|
1079
|
+
if (result) {
|
|
1080
|
+
return result;
|
|
1081
|
+
} else {
|
|
1082
|
+
throw new Error(`Famix type ${typeName} is not found (undefined) in the Type map.`);
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
// A parametric type is a type that has type parameters, e.g., List<T>
|
|
1087
|
+
// In TS it can be a class, an interface, a function, an arrow function, or a method
|
|
1088
|
+
|
|
1089
|
+
// create the Famix Parametric Type (maybe it's just an Interface, etc.)
|
|
1090
|
+
let fmxType: Famix.Type;
|
|
1091
|
+
|
|
1092
|
+
if (element instanceof ClassDeclaration) {
|
|
1093
|
+
fmxType = new Famix.ParametricClass();
|
|
1094
|
+
} else if (element instanceof InterfaceDeclaration) {
|
|
1095
|
+
fmxType = new Famix.ParametricInterface();
|
|
1096
|
+
}
|
|
1097
|
+
// functions and methods are not types
|
|
1098
|
+
// else if (element instanceof FunctionDeclaration) {
|
|
1099
|
+
// fmxType = new Famix.ParametricFunction();
|
|
1100
|
+
// } else if (element instanceof ArrowFunction) {
|
|
1101
|
+
// fmxType = new Famix.ParametricArrowFunction();
|
|
1102
|
+
// } else if (element instanceof MethodDeclaration) {
|
|
1103
|
+
// fmxType = new Famix.ParametricMethod();
|
|
1104
|
+
// }
|
|
1105
|
+
else {
|
|
1106
|
+
throw new Error(`Element is not a class, interface, function, arrow function, or method.`);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// const parameters = element.getTypeParameters();
|
|
1110
|
+
|
|
1111
|
+
// // for each parameter, getOrCreate the FamixParameterType
|
|
1112
|
+
// for (const parameter of parameters) {
|
|
1113
|
+
// this.createOrGetFamixParameterType(parameter.getName(), parameter);
|
|
1114
|
+
// }
|
|
1115
|
+
|
|
1116
|
+
// // TODO: the following code is not correct, it is just a placeholder
|
|
1117
|
+
// const parameterTypeNames = typeName.substring(typeName.indexOf("<") + 1, typeName.indexOf(">"))
|
|
1118
|
+
// .split(",").map(s => s.trim());
|
|
1119
|
+
// const baseTypeName = typeName.substring(0, typeName.indexOf("<")).trim();
|
|
1120
|
+
// parameterTypeNames.forEach(parameterTypeName => {
|
|
1121
|
+
// const fmxParameterType = this.createOrGetFamixParameterType(parameterTypeName, element);
|
|
1122
|
+
// (fmxType as Famix.ParameterType).addArgument(fmxParameterType);
|
|
1123
|
+
// });
|
|
1124
|
+
// const fmxBaseType = this.createOrGetFamixType(baseTypeName, element);
|
|
1125
|
+
|
|
1126
|
+
// (fmxType as Famix.ParameterType).baseType = fmxBaseType;
|
|
1127
|
+
|
|
1128
|
+
fmxType.name = typeName;
|
|
1129
|
+
initFQN(element, fmxType);
|
|
1130
|
+
this.famixRep.addElement(fmxType);
|
|
1131
|
+
this.fmxTypeMap.set(element, fmxType);
|
|
1132
|
+
return fmxType;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
/**
|
|
1136
|
+
* Creates a type for a parameter in a parametric type, e.g., T in List<T>
|
|
1137
|
+
* @param parameterTypeName
|
|
1138
|
+
* @param element the TypeScript element (TSMorphParametricType) that the type is associated with
|
|
1139
|
+
* @returns
|
|
1140
|
+
*/
|
|
1141
|
+
// createOrGetFamixParameterType(parameterTypeName: string, element: ParameterDeclaration) {
|
|
1142
|
+
// if (this.fmxTypeMap.has(element)) {
|
|
1143
|
+
// return this.fmxTypeMap.get(element) as Famix.ParameterType;
|
|
1144
|
+
// }
|
|
1145
|
+
|
|
1146
|
+
// // determine if element is a
|
|
1147
|
+
// const fmxType = new Famix.ParameterType();
|
|
1148
|
+
// // const parameterTypeNames = typeName.substring(typeName.indexOf("<") + 1, typeName.indexOf(">"))
|
|
1149
|
+
// // .split(",").map(s => s.trim());
|
|
1150
|
+
// // const baseTypeName = typeName.substring(0, typeName.indexOf("<")).trim();
|
|
1151
|
+
// // parameterTypeNames.forEach(parameterTypeName => {
|
|
1152
|
+
// // const fmxParameterType = this.createOrGetFamixParameterType(parameterTypeName, element);
|
|
1153
|
+
// // (fmxType as Famix.ParameterType).addArgument(fmxParameterType);
|
|
1154
|
+
// // });
|
|
1155
|
+
// const fmxBaseType = this.createOrGetFamixType(baseTypeName, element);
|
|
1156
|
+
// (fmxType as Famix.ParameterType).baseType = fmxBaseType;
|
|
1157
|
+
// initFQN(element, fmxType);
|
|
1158
|
+
// this.famixRep.addElement(fmxType);
|
|
1159
|
+
// this.fmxTypeMap.set(element, fmxType);
|
|
1160
|
+
// return fmxType;
|
|
1161
|
+
// }
|
|
1162
|
+
|
|
1163
|
+
/**
|
|
1164
|
+
* Creates or gets a Famix primitive type
|
|
1165
|
+
* @param typeName A type name
|
|
1166
|
+
* @returns The Famix model of the primitive type
|
|
1167
|
+
*/
|
|
1168
|
+
createOrGetFamixPrimitiveType(typeName: string): Famix.PrimitiveType {
|
|
1169
|
+
let fmxType: Famix.PrimitiveType = new Famix.PrimitiveType();
|
|
1170
|
+
if (!this.fmxPrimitiveTypeMap.has(typeName)) {
|
|
1171
|
+
fmxType = new Famix.PrimitiveType();
|
|
1172
|
+
fmxType.isStub = true;
|
|
1173
|
+
fmxType.name = typeName;
|
|
1174
|
+
fmxType.fullyQualifiedName = typeName + "[PrimitiveType]";
|
|
1175
|
+
this.fmxPrimitiveTypeMap.set(typeName, fmxType);
|
|
1176
|
+
this.famixRep.addElement(fmxType);
|
|
1177
|
+
} else {
|
|
1178
|
+
fmxType = this.fmxPrimitiveTypeMap.get(typeName) as Famix.PrimitiveType;
|
|
1179
|
+
}
|
|
1180
|
+
return fmxType;
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
/**
|
|
1184
|
+
* Creates a Famix access
|
|
1185
|
+
* @param node A node
|
|
1186
|
+
* @param id An id of a parameter, a variable, a property or an enum member
|
|
1187
|
+
*/
|
|
1188
|
+
public createFamixAccess(node: Identifier, id: number): void {
|
|
1189
|
+
const fmxVar = this.famixRep.getFamixEntityById(id) as Famix.StructuralEntity;
|
|
1190
|
+
if (!fmxVar) {
|
|
1191
|
+
throw new Error(`Famix entity with id ${id} not found, for node ${node.getText()} in ${node.getSourceFile().getBaseName()} at line ${node.getStartLineNumber()}.`);
|
|
1192
|
+
}
|
|
1193
|
+
|
|
1194
|
+
logger.debug(`Creating FamixAccess. Node: [${node.getKindName()}] '${node.getText()}' at line ${node.getStartLineNumber()} in ${node.getSourceFile().getBaseName()}, id: ${id} refers to fmxVar '${fmxVar.fullyQualifiedName}'.`);
|
|
1195
|
+
|
|
1196
|
+
const nodeReferenceAncestor = Helpers.findAncestor(node);
|
|
1197
|
+
if (!nodeReferenceAncestor) {
|
|
1198
|
+
logger.error(`No ancestor found for node '${node.getText()}'`);
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1202
|
+
const ancestorFullyQualifiedName = FQNFunctions.getFQN(nodeReferenceAncestor);
|
|
1203
|
+
const accessor = this.famixRep.getFamixEntityByFullyQualifiedName(ancestorFullyQualifiedName) as Famix.ContainerEntity;
|
|
1204
|
+
if (!accessor) {
|
|
1205
|
+
logger.error(`Ancestor ${ancestorFullyQualifiedName} of kind ${nodeReferenceAncestor.getKindName()} not found.`);
|
|
1206
|
+
return; // Bail out for now
|
|
1207
|
+
} else {
|
|
1208
|
+
logger.debug(`Found accessor to be ${accessor.fullyQualifiedName}.`);
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1211
|
+
// Ensure accessor is a method, function, script, or module
|
|
1212
|
+
if (!(accessor instanceof Famix.Method) && !(accessor instanceof Famix.ArrowFunction) && !(accessor instanceof Famix.Function) && !(accessor instanceof Famix.ScriptEntity) && !(accessor instanceof Famix.Module)) {
|
|
1213
|
+
logger.error(`Accessor ${accessor.fullyQualifiedName} is not a method, function, etc.`);
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// Avoid duplicates
|
|
1218
|
+
const foundAccess = this.famixRep.getFamixAccessByAccessorAndVariable(accessor, fmxVar);
|
|
1219
|
+
if (foundAccess) {
|
|
1220
|
+
logger.debug(`FamixAccess already exists for accessor ${accessor.fullyQualifiedName} and variable ${fmxVar.fullyQualifiedName}.`);
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
const fmxAccess = new Famix.Access();
|
|
1225
|
+
fmxAccess.accessor = accessor;
|
|
1226
|
+
fmxAccess.variable = fmxVar;
|
|
1227
|
+
this.famixRep.addElement(fmxAccess);
|
|
1228
|
+
this.fmxElementObjectMap.set(fmxAccess, node);
|
|
1229
|
+
logger.debug(`Created access: ${accessor.fullyQualifiedName} -> ${fmxVar.fullyQualifiedName}`);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
/**
|
|
1233
|
+
* Creates a Famix invocation
|
|
1234
|
+
* @param nodeReferringToInvocable A node
|
|
1235
|
+
* @param invocable A method or a function
|
|
1236
|
+
* @param id The id of the method or the function
|
|
1237
|
+
*/
|
|
1238
|
+
public createFamixInvocation(nodeReferringToInvocable: Identifier, invocable: InvocableType, id: number): void {
|
|
1239
|
+
const fmxInvocable = this.famixRep.getFamixEntityById(id) as Famix.BehavioralEntity;
|
|
1240
|
+
// since the node is in the AST, we need to find the ancestor that is in the Famix model
|
|
1241
|
+
const containerOfNode = Helpers.findAncestor(nodeReferringToInvocable);
|
|
1242
|
+
logger.debug(`Found container (ancestor) ${containerOfNode.getKindName()} for AST node ${nodeReferringToInvocable.getText()}.`);
|
|
1243
|
+
const containerFQN = FQNFunctions.getFQN(containerOfNode);
|
|
1244
|
+
logger.debug(`Found containerFQN ${containerFQN}.`);
|
|
1245
|
+
let sender = this.famixRep.getFamixEntityByFullyQualifiedName(containerFQN) as Famix.ContainerEntity;
|
|
1246
|
+
logger.debug(`Found a sender that matches ${sender.fullyQualifiedName}.`);
|
|
1247
|
+
if (sender instanceof Famix.Type) {
|
|
1248
|
+
// TODO this might be an error in getFamixEntityByFullyQualifiedName
|
|
1249
|
+
logger.debug(`Oops! Sender is a type, which is not valid for an Invocation. Trying to find a container for ${sender.fullyQualifiedName}.`);
|
|
1250
|
+
const senderContainer = sender.container;
|
|
1251
|
+
if (senderContainer) {
|
|
1252
|
+
sender = senderContainer;
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
const receiverFullyQualifiedName = FQNFunctions.getFQN(invocable.getParent());
|
|
1256
|
+
const receiver = this.famixRep.getFamixEntityByFullyQualifiedName(receiverFullyQualifiedName) as Famix.NamedEntity;
|
|
1257
|
+
|
|
1258
|
+
const fmxInvocation = new Famix.Invocation();
|
|
1259
|
+
fmxInvocation.sender = sender;
|
|
1260
|
+
fmxInvocation.receiver = receiver;
|
|
1261
|
+
fmxInvocation.addCandidate(fmxInvocable);
|
|
1262
|
+
fmxInvocation.signature = fmxInvocable.signature;
|
|
1263
|
+
|
|
1264
|
+
this.famixRep.addElement(fmxInvocation);
|
|
1265
|
+
|
|
1266
|
+
this.fmxElementObjectMap.set(fmxInvocation,nodeReferringToInvocable);
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
/**
|
|
1270
|
+
* Creates a Famix inheritance
|
|
1271
|
+
* @param baseClassOrInterface A class or an interface (subclass)
|
|
1272
|
+
* @param inheritedClassOrInterface The inherited class or interface (superclass)
|
|
1273
|
+
*/
|
|
1274
|
+
public createOrGetFamixInheritance(baseClassOrInterface: ClassDeclaration | InterfaceDeclaration, inheritedClassOrInterface: ClassDeclaration | InterfaceDeclaration | ExpressionWithTypeArguments): void {
|
|
1275
|
+
logger.debug(`Creating FamixInheritance for ${baseClassOrInterface.getText()} and ${inheritedClassOrInterface.getText()} [${inheritedClassOrInterface.constructor.name}].`);
|
|
1276
|
+
const fmxInheritance = new Famix.Inheritance();
|
|
1277
|
+
|
|
1278
|
+
let subClass: Famix.Class | Famix.Interface | undefined;
|
|
1279
|
+
if (baseClassOrInterface instanceof ClassDeclaration) {
|
|
1280
|
+
subClass = this.createOrGetFamixClass(baseClassOrInterface);
|
|
1281
|
+
} else {
|
|
1282
|
+
subClass = this.createOrGetFamixInterface(baseClassOrInterface);
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
if (!subClass) {
|
|
1286
|
+
throw new Error(`Subclass ${baseClassOrInterface} not found in Class or Interface maps.`);
|
|
1287
|
+
}
|
|
1288
|
+
|
|
1289
|
+
let superClass: Famix.Class | Famix.Interface | undefined;
|
|
1290
|
+
|
|
1291
|
+
if (inheritedClassOrInterface instanceof ClassDeclaration) {
|
|
1292
|
+
superClass = this.createOrGetFamixClass(inheritedClassOrInterface);
|
|
1293
|
+
} else if (inheritedClassOrInterface instanceof InterfaceDeclaration) {
|
|
1294
|
+
superClass = this.createOrGetFamixInterface(inheritedClassOrInterface);
|
|
1295
|
+
} else {
|
|
1296
|
+
// inheritedClassOrInterface instanceof ExpressionWithTypeArguments
|
|
1297
|
+
// must determine if inheritedClassOrInterface is a class or an interface
|
|
1298
|
+
// then find the declaration, else it's a stub
|
|
1299
|
+
|
|
1300
|
+
const heritageClause = inheritedClassOrInterface.getParent();
|
|
1301
|
+
if (heritageClause instanceof HeritageClause) {
|
|
1302
|
+
// cases: 1) class extends class, 2) class implements interface, 3) interface extends interface
|
|
1303
|
+
|
|
1304
|
+
// class extends class
|
|
1305
|
+
if (heritageClause.getText().startsWith("extends") && baseClassOrInterface instanceof ClassDeclaration) {
|
|
1306
|
+
const classDeclaration = getInterfaceOrClassDeclarationFromExpression(inheritedClassOrInterface);
|
|
1307
|
+
if (classDeclaration !== undefined && classDeclaration instanceof ClassDeclaration) {
|
|
1308
|
+
superClass = this.createOrGetFamixClass(classDeclaration);
|
|
1309
|
+
} else {
|
|
1310
|
+
logger.error(`Class declaration not found for ${inheritedClassOrInterface.getText()}.`);
|
|
1311
|
+
superClass = this.createOrGetFamixClassStub(inheritedClassOrInterface);
|
|
1312
|
+
}
|
|
1313
|
+
}
|
|
1314
|
+
else if (heritageClause.getText().startsWith("implements") && baseClassOrInterface instanceof ClassDeclaration // class implements interface
|
|
1315
|
+
|| (heritageClause.getText().startsWith("extends") && baseClassOrInterface instanceof InterfaceDeclaration)) { // interface extends interface
|
|
1316
|
+
|
|
1317
|
+
const interfaceOrClassDeclaration = getInterfaceOrClassDeclarationFromExpression(inheritedClassOrInterface);
|
|
1318
|
+
if (interfaceOrClassDeclaration !== undefined && interfaceOrClassDeclaration instanceof InterfaceDeclaration) {
|
|
1319
|
+
superClass = this.createOrGetFamixInterface(interfaceOrClassDeclaration);
|
|
1320
|
+
} else {
|
|
1321
|
+
logger.error(`Interface declaration not found for ${inheritedClassOrInterface.getText()}.`);
|
|
1322
|
+
superClass = this.createOrGetFamixInterfaceStub(inheritedClassOrInterface);
|
|
1323
|
+
}
|
|
1324
|
+
} else {
|
|
1325
|
+
// throw new Error(`Parent of ${inheritedClassOrInterface.getText()} is not a class or an interface.`);
|
|
1326
|
+
logger.error(`Parent of ${inheritedClassOrInterface.getText()} is not a class or an interface.`);
|
|
1327
|
+
superClass = this.createOrGetFamixInterfaceStub(inheritedClassOrInterface);
|
|
1328
|
+
}
|
|
1329
|
+
} else {
|
|
1330
|
+
throw new Error(`Heritage clause not found for ${inheritedClassOrInterface.getText()}.`);
|
|
1331
|
+
}
|
|
1332
|
+
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
this.fmxElementObjectMap.set(superClass, inheritedClassOrInterface);
|
|
1336
|
+
|
|
1337
|
+
this.makeFamixIndexFileAnchor(inheritedClassOrInterface, superClass);
|
|
1338
|
+
|
|
1339
|
+
this.famixRep.addElement(superClass);
|
|
1340
|
+
|
|
1341
|
+
fmxInheritance.subclass = subClass;
|
|
1342
|
+
fmxInheritance.superclass = superClass;
|
|
1343
|
+
|
|
1344
|
+
this.famixRep.addElement(fmxInheritance);
|
|
1345
|
+
// no FQN for inheritance
|
|
1346
|
+
|
|
1347
|
+
// We don't map inheritance to the source code element because there are two elements (super, sub)
|
|
1348
|
+
// this.fmxElementObjectMap.set(fmxInheritance, null);
|
|
1349
|
+
|
|
1350
|
+
}
|
|
1351
|
+
createOrGetFamixClassStub(unresolvedInheritedClass: ExpressionWithTypeArguments): Famix.Class {
|
|
1352
|
+
// make a FQN for the stub
|
|
1353
|
+
const fqn = FQNFunctions.getFQNUnresolvedInheritedClassOrInterface(unresolvedInheritedClass);
|
|
1354
|
+
logger.debug(`createOrGetFamixClassStub: fqn: ${fqn}`);
|
|
1355
|
+
const fmxClass = this.famixRep.getFamixEntityByFullyQualifiedName(fqn) as Famix.Class;
|
|
1356
|
+
if (fmxClass) {
|
|
1357
|
+
return fmxClass;
|
|
1358
|
+
} else {
|
|
1359
|
+
const stub = new Famix.Class();
|
|
1360
|
+
stub.name = unresolvedInheritedClass.getText();
|
|
1361
|
+
stub.isStub = true;
|
|
1362
|
+
stub.fullyQualifiedName = fqn;
|
|
1363
|
+
this.famixRep.addElement(stub);
|
|
1364
|
+
this.fmxElementObjectMap.set(stub, unresolvedInheritedClass);
|
|
1365
|
+
return stub;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1369
|
+
createOrGetFamixInterfaceStub(unresolvedInheritedInterface: ExpressionWithTypeArguments): Famix.Interface {
|
|
1370
|
+
// make a FQN for the stub
|
|
1371
|
+
const fqn = FQNFunctions.getFQNUnresolvedInheritedClassOrInterface(unresolvedInheritedInterface);
|
|
1372
|
+
logger.debug(`createOrGetFamixInterfaceStub: fqn: ${fqn}`);
|
|
1373
|
+
const fmxInterface = this.famixRep.getFamixEntityByFullyQualifiedName(fqn) as Famix.Interface;
|
|
1374
|
+
if (fmxInterface) {
|
|
1375
|
+
return fmxInterface;
|
|
1376
|
+
} else {
|
|
1377
|
+
const stub = new Famix.Interface();
|
|
1378
|
+
stub.name = unresolvedInheritedInterface.getText();
|
|
1379
|
+
stub.isStub = true;
|
|
1380
|
+
stub.fullyQualifiedName = fqn;
|
|
1381
|
+
this.famixRep.addElement(stub);
|
|
1382
|
+
this.fmxElementObjectMap.set(stub, unresolvedInheritedInterface);
|
|
1383
|
+
return stub;
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
public createFamixImportClause(importedEntity: Famix.NamedEntity, importingEntity: Famix.Module) {
|
|
1388
|
+
const fmxImportClause = new Famix.ImportClause();
|
|
1389
|
+
fmxImportClause.importedEntity = importedEntity;
|
|
1390
|
+
fmxImportClause.importingEntity = importingEntity;
|
|
1391
|
+
importingEntity.addOutgoingImport(fmxImportClause);
|
|
1392
|
+
this.famixRep.addElement(fmxImportClause);
|
|
1393
|
+
}
|
|
1394
|
+
|
|
1395
|
+
/**
|
|
1396
|
+
* Creates a Famix import clause
|
|
1397
|
+
* @param importClauseInfo The information needed to create a Famix import clause
|
|
1398
|
+
* @param importDeclaration The import declaration
|
|
1399
|
+
* @param importer A source file which is a module
|
|
1400
|
+
* @param moduleSpecifierFilePath The path of the module where the export declaration is
|
|
1401
|
+
* @param importElement The imported entity
|
|
1402
|
+
* @param isInExports A boolean indicating if the imported entity is in the exports
|
|
1403
|
+
* @param isDefaultExport A boolean indicating if the imported entity is a default export
|
|
1404
|
+
*/
|
|
1405
|
+
public oldCreateOrGetFamixImportClause(importClauseInfo: {importDeclaration?: ImportDeclaration | ImportEqualsDeclaration, importerSourceFile: SourceFile, moduleSpecifierFilePath: string, importElement: ImportSpecifier | Identifier, isInExports: boolean, isDefaultExport: boolean}): void {
|
|
1406
|
+
const {importDeclaration, importerSourceFile: importer, moduleSpecifierFilePath, importElement, isInExports, isDefaultExport} = importClauseInfo;
|
|
1407
|
+
if (importDeclaration && this.fmxImportClauseMap.has(importDeclaration)) {
|
|
1408
|
+
const rImportClause = this.fmxImportClauseMap.get(importDeclaration);
|
|
1409
|
+
if (rImportClause) {
|
|
1410
|
+
logger.debug(`Import clause ${importElement.getText()} already exists in map, skipping.`);
|
|
1411
|
+
return;
|
|
1412
|
+
} else {
|
|
1413
|
+
throw new Error(`Import clause ${importElement.getText()} is not found in the import clause map.`);
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
logger.info(`creating a new FamixImportClause for ${importDeclaration?.getText()} in ${importer.getBaseName()}.`);
|
|
1418
|
+
const fmxImportClause = new Famix.ImportClause();
|
|
1419
|
+
|
|
1420
|
+
let importedEntity: Famix.NamedEntity | Famix.StructuralEntity | undefined = undefined;
|
|
1421
|
+
let importedEntityName: string;
|
|
1422
|
+
|
|
1423
|
+
const absolutePathProject = this.famixRep.getAbsolutePath();
|
|
1424
|
+
|
|
1425
|
+
const absolutePath = path.normalize(moduleSpecifierFilePath);
|
|
1426
|
+
logger.debug(`createFamixImportClause: absolutePath: ${absolutePath}`);
|
|
1427
|
+
logger.debug(`createFamixImportClause: convertToRelativePath: ${this.convertToRelativePath(absolutePath, absolutePathProject)}`);
|
|
1428
|
+
const pathInProject: string = this.convertToRelativePath(absolutePath, absolutePathProject).replace(/\\/g, "/");
|
|
1429
|
+
logger.debug(`createFamixImportClause: pathInProject: ${pathInProject}`);
|
|
1430
|
+
let pathName = "{" + pathInProject + "}.";
|
|
1431
|
+
logger.debug(`createFamixImportClause: pathName: ${pathName}`);
|
|
1432
|
+
|
|
1433
|
+
if (importDeclaration instanceof ImportDeclaration
|
|
1434
|
+
&& importElement instanceof ImportSpecifier) {
|
|
1435
|
+
importedEntityName = importElement.getName();
|
|
1436
|
+
pathName = pathName + importedEntityName;
|
|
1437
|
+
if (isInExports) {
|
|
1438
|
+
importedEntity = this.famixRep.getFamixEntityByFullyQualifiedName(pathName) as Famix.NamedEntity;
|
|
1439
|
+
logger.debug(`Found exported entity: ${pathName}`);
|
|
1440
|
+
}
|
|
1441
|
+
if (importedEntity === undefined) {
|
|
1442
|
+
importedEntity = new Famix.NamedEntity();
|
|
1443
|
+
importedEntity.name = importedEntityName;
|
|
1444
|
+
if (!isInExports) {
|
|
1445
|
+
importedEntity.isStub = true;
|
|
1446
|
+
}
|
|
1447
|
+
logger.debug(`Creating named entity ${importedEntityName} for ImportSpecifier ${importElement.getText()}`);
|
|
1448
|
+
initFQN(importElement, importedEntity);
|
|
1449
|
+
logger.debug(`Assigned FQN to entity: ${importedEntity.fullyQualifiedName}`);
|
|
1450
|
+
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
1451
|
+
this.famixRep.addElement(importedEntity);
|
|
1452
|
+
logger.debug(`Added entity to repository: ${importedEntity.fullyQualifiedName}`);
|
|
1453
|
+
}
|
|
1454
|
+
}
|
|
1455
|
+
else if (importDeclaration instanceof ImportEqualsDeclaration) {
|
|
1456
|
+
importedEntityName = importDeclaration?.getName();
|
|
1457
|
+
pathName = pathName + importedEntityName;
|
|
1458
|
+
importedEntity = new Famix.StructuralEntity();
|
|
1459
|
+
importedEntity.name = importedEntityName;
|
|
1460
|
+
initFQN(importDeclaration, importedEntity);
|
|
1461
|
+
logger.debug(`Assigned FQN to ImportEquals entity: ${importedEntity.fullyQualifiedName}`);
|
|
1462
|
+
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
1463
|
+
const anyType = this.createOrGetFamixType('any', undefined, importDeclaration);
|
|
1464
|
+
(importedEntity as Famix.StructuralEntity).declaredType = anyType;
|
|
1465
|
+
} else {
|
|
1466
|
+
importedEntityName = importElement.getText();
|
|
1467
|
+
pathName = pathName + (isDefaultExport ? "defaultExport" : "namespaceExport");
|
|
1468
|
+
importedEntity = new Famix.NamedEntity();
|
|
1469
|
+
importedEntity.name = importedEntityName;
|
|
1470
|
+
initFQN(importElement, importedEntity);
|
|
1471
|
+
logger.debug(`Assigned FQN to default/namespace entity: ${importedEntity.fullyQualifiedName}`);
|
|
1472
|
+
this.makeFamixIndexFileAnchor(importElement, importedEntity);
|
|
1473
|
+
}
|
|
1474
|
+
if (!isInExports) {
|
|
1475
|
+
this.famixRep.addElement(importedEntity);
|
|
1476
|
+
logger.debug(`Added non-exported entity to repository: ${importedEntity.fullyQualifiedName}`);
|
|
1477
|
+
}
|
|
1478
|
+
const importerFullyQualifiedName = FQNFunctions.getFQN(importer);
|
|
1479
|
+
const fmxImporter = this.famixRep.getFamixEntityByFullyQualifiedName(importerFullyQualifiedName) as Famix.Module;
|
|
1480
|
+
fmxImportClause.importingEntity = fmxImporter;
|
|
1481
|
+
fmxImportClause.importedEntity = importedEntity;
|
|
1482
|
+
if (importDeclaration instanceof ImportEqualsDeclaration) {
|
|
1483
|
+
fmxImportClause.moduleSpecifier = importDeclaration?.getModuleReference().getText() as string;
|
|
1484
|
+
} else {
|
|
1485
|
+
fmxImportClause.moduleSpecifier = importDeclaration?.getModuleSpecifierValue() as string;
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
logger.debug(`ImportClause: ${fmxImportClause.importedEntity?.name} (type=${Helpers.getSubTypeName(fmxImportClause.importedEntity)}) imported by ${fmxImportClause.importingEntity?.name}`);
|
|
1489
|
+
|
|
1490
|
+
fmxImporter.addOutgoingImport(fmxImportClause);
|
|
1491
|
+
this.famixRep.addElement(fmxImportClause);
|
|
1492
|
+
|
|
1493
|
+
if (importDeclaration) {
|
|
1494
|
+
this.fmxElementObjectMap.set(fmxImportClause, importDeclaration);
|
|
1495
|
+
this.fmxImportClauseMap.set(importDeclaration, fmxImportClause);
|
|
1496
|
+
}
|
|
1497
|
+
}
|
|
1498
|
+
|
|
1499
|
+
/**
|
|
1500
|
+
* Creates a Famix Arrow Function
|
|
1501
|
+
* @param arrowExpression An Expression
|
|
1502
|
+
* @returns The Famix model of the variable
|
|
1503
|
+
*/
|
|
1504
|
+
public createOrGetFamixArrowFunction(arrowExpression: Expression, currentCC: { [key: string]: number } ): Famix.ArrowFunction | Famix.ParametricArrowFunction {
|
|
1505
|
+
|
|
1506
|
+
let fmxArrowFunction: Famix.ArrowFunction | Famix.ParametricArrowFunction;
|
|
1507
|
+
const functionFullyQualifiedName = FQNFunctions.getFQN(arrowExpression);
|
|
1508
|
+
|
|
1509
|
+
if (!this.fmxFunctionAndMethodMap.has(functionFullyQualifiedName)) {
|
|
1510
|
+
|
|
1511
|
+
const arrowFunction = arrowExpression.asKindOrThrow(SyntaxKind.ArrowFunction);
|
|
1512
|
+
|
|
1513
|
+
const isGeneric = arrowFunction.getTypeParameters().length > 0;
|
|
1514
|
+
|
|
1515
|
+
if (isGeneric) {
|
|
1516
|
+
fmxArrowFunction = new Famix.ParametricArrowFunction();
|
|
1517
|
+
}
|
|
1518
|
+
else {
|
|
1519
|
+
fmxArrowFunction = new Famix.ArrowFunction();
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
// Get the parent of the arrow function (the variable declaration)
|
|
1523
|
+
const parent = arrowFunction.getParentIfKind(SyntaxKind.VariableDeclaration);
|
|
1524
|
+
let functionName = '(NO_NAME)';
|
|
1525
|
+
|
|
1526
|
+
if (parent && parent instanceof VariableDeclaration) {
|
|
1527
|
+
// Get the name of the variable
|
|
1528
|
+
functionName = parent.getName();
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
if (functionName) {
|
|
1532
|
+
fmxArrowFunction.name = functionName;
|
|
1533
|
+
}
|
|
1534
|
+
else {
|
|
1535
|
+
fmxArrowFunction.name = "anonymous";
|
|
1536
|
+
}
|
|
1537
|
+
|
|
1538
|
+
// Signature of an arrow function is (parameters) => return_type
|
|
1539
|
+
const parametersSignature = arrowFunction.getParameters().map(p => p.getText()).join(", ");
|
|
1540
|
+
const returnTypeSignature = arrowFunction.getReturnType().getText();
|
|
1541
|
+
fmxArrowFunction.signature = `(${parametersSignature}) => ${returnTypeSignature}`;
|
|
1542
|
+
fmxArrowFunction.cyclomaticComplexity = currentCC[fmxArrowFunction.name];
|
|
1543
|
+
|
|
1544
|
+
let functionTypeName = this.UNKNOWN_VALUE;
|
|
1545
|
+
try {
|
|
1546
|
+
functionTypeName = arrowFunction.getReturnType().getText().trim();
|
|
1547
|
+
} catch (error) {
|
|
1548
|
+
logger.error(`> WARNING: got exception ${error}. Failed to get usable name for return type of function: ${functionName}. Continuing...`);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
const fmxType = this.createOrGetFamixType(functionTypeName, arrowFunction.getReturnType(), arrowFunction as unknown as FunctionDeclaration);
|
|
1552
|
+
fmxArrowFunction.declaredType = fmxType;
|
|
1553
|
+
fmxArrowFunction.numberOfLinesOfCode = arrowFunction.getEndLineNumber() - arrowFunction.getStartLineNumber();
|
|
1554
|
+
const parameters = arrowFunction.getParameters();
|
|
1555
|
+
fmxArrowFunction.numberOfParameters = parameters.length;
|
|
1556
|
+
fmxArrowFunction.numberOfStatements = arrowFunction.getStatements().length;
|
|
1557
|
+
initFQN(arrowExpression as unknown as TSMorphObjectType, fmxArrowFunction);
|
|
1558
|
+
this.makeFamixIndexFileAnchor(arrowExpression as unknown as TSMorphObjectType, fmxArrowFunction);
|
|
1559
|
+
this.famixRep.addElement(fmxArrowFunction);
|
|
1560
|
+
this.fmxElementObjectMap.set(fmxArrowFunction,arrowFunction as unknown as TSMorphObjectType);
|
|
1561
|
+
this.fmxFunctionAndMethodMap.set(functionFullyQualifiedName, fmxArrowFunction);
|
|
1562
|
+
} else {
|
|
1563
|
+
fmxArrowFunction = this.fmxFunctionAndMethodMap.get(functionFullyQualifiedName) as Famix.ArrowFunction;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
return fmxArrowFunction;
|
|
1567
|
+
}
|
|
1568
|
+
|
|
1569
|
+
/**
|
|
1570
|
+
* Creates a Famix concretisation
|
|
1571
|
+
* @param cls A class
|
|
1572
|
+
* @returns The Famix model of the concretisation
|
|
1573
|
+
*/
|
|
1574
|
+
public createFamixConcretisation(conEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod ,genEntity : Famix.ParametricClass | Famix.ParametricInterface | Famix.ParametricFunction | Famix.ParametricMethod): Famix.Concretisation {
|
|
1575
|
+
|
|
1576
|
+
const fmxConcretisation : Famix.Concretisation = new Famix.Concretisation();
|
|
1577
|
+
|
|
1578
|
+
fmxConcretisation.concreteEntity = conEntity;
|
|
1579
|
+
fmxConcretisation.genericEntity = genEntity;
|
|
1580
|
+
// this.fmxElementObjectMap.set(fmxConcretisation,null);
|
|
1581
|
+
this.famixRep.addElement(fmxConcretisation);
|
|
1582
|
+
// const parameterConcretisation = this.createFamixParameterConcretisation(fmxConcretisation);
|
|
1583
|
+
|
|
1584
|
+
return fmxConcretisation;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* Creates a Famix concretisation
|
|
1589
|
+
* @param concretisation A FamixConcretisation
|
|
1590
|
+
* @returns The Famix model of the ParameterConcrestisation
|
|
1591
|
+
*/
|
|
1592
|
+
public createFamixParameterConcretisation(concretisation: Famix.Concretisation): Famix.ParameterConcretisation | undefined{
|
|
1593
|
+
const conClass = concretisation.concreteEntity;
|
|
1594
|
+
const genClass = concretisation.genericEntity;
|
|
1595
|
+
logger.debug(`Creating parameter concretisation between ${conClass.fullyQualifiedName} and ${genClass.fullyQualifiedName}`);
|
|
1596
|
+
const parameterConcretisations = this.famixRep._getAllEntitiesWithType("ParameterConcretisation") as Set<Famix.ParameterConcretisation>;
|
|
1597
|
+
const concreteParameters = conClass.concreteParameters;
|
|
1598
|
+
const genericParameters = genClass.genericParameters;
|
|
1599
|
+
|
|
1600
|
+
const conClassTypeParametersIterator = concreteParameters.values();
|
|
1601
|
+
const genClassTypeParametersIterator = genericParameters.values();
|
|
1602
|
+
let fmxParameterConcretisation : Famix.ParameterConcretisation | undefined = undefined;
|
|
1603
|
+
|
|
1604
|
+
for (let i = 0; i < genericParameters.size; i++) {
|
|
1605
|
+
const conClassTypeParameter = conClassTypeParametersIterator.next().value as Famix.ParameterType;
|
|
1606
|
+
const genClassTypeParameter = genClassTypeParametersIterator.next().value as Famix.ParameterType;
|
|
1607
|
+
let createParameterConcretisation : boolean = true;
|
|
1608
|
+
if(conClassTypeParameter && genClassTypeParameter && conClassTypeParameter.name != genClassTypeParameter.name){
|
|
1609
|
+
parameterConcretisations.forEach((param : Famix.ParameterConcretisation) => {
|
|
1610
|
+
if (conClassTypeParameter.name == param.concreteParameter.name && genClassTypeParameter.name == param.genericParameter.name) {
|
|
1611
|
+
createParameterConcretisation = false;
|
|
1612
|
+
fmxParameterConcretisation = param;
|
|
1613
|
+
}
|
|
1614
|
+
});
|
|
1615
|
+
if (createParameterConcretisation) {
|
|
1616
|
+
fmxParameterConcretisation = new Famix.ParameterConcretisation();
|
|
1617
|
+
fmxParameterConcretisation.genericParameter = genClassTypeParameter;
|
|
1618
|
+
fmxParameterConcretisation.concreteParameter = conClassTypeParameter;
|
|
1619
|
+
fmxParameterConcretisation.addConcretisation(concretisation);
|
|
1620
|
+
// this.fmxElementObjectMap.set(fmxParameterConcretisation,null);
|
|
1621
|
+
} else {
|
|
1622
|
+
if (!fmxParameterConcretisation) {
|
|
1623
|
+
throw new Error(`fmxParameterConcretisation was undefined for concretisation with generic parameter ${genClassTypeParameter.name} and concrete parameter ${conClassTypeParameter.name}`);
|
|
1624
|
+
}
|
|
1625
|
+
fmxParameterConcretisation.addConcretisation(concretisation);
|
|
1626
|
+
}
|
|
1627
|
+
this.famixRep.addElement(fmxParameterConcretisation);
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
if (!fmxParameterConcretisation) {
|
|
1631
|
+
logger.error(`fmxParameterConcretisation was undefined for concretisation with concrete entity ${conClass.fullyQualifiedName} and generic entity ${genClass.fullyQualifiedName}`);
|
|
1632
|
+
}
|
|
1633
|
+
return fmxParameterConcretisation;
|
|
1634
|
+
|
|
1635
|
+
}
|
|
1636
|
+
|
|
1637
|
+
/**
|
|
1638
|
+
* Creates a Famix concretisation between two classes or two interfaces
|
|
1639
|
+
* @param element A class or an Interface
|
|
1640
|
+
*/
|
|
1641
|
+
public createFamixConcretisationClassOrInterfaceSpecialisation(element: ClassDeclaration | InterfaceDeclaration){
|
|
1642
|
+
|
|
1643
|
+
const superEntity = element.getExtends();
|
|
1644
|
+
let superEntityArray;
|
|
1645
|
+
if (superEntity){
|
|
1646
|
+
superEntityArray = Array.isArray(superEntity) ? superEntity : [superEntity];
|
|
1647
|
+
}
|
|
1648
|
+
if (superEntityArray && superEntityArray.length > 0) {
|
|
1649
|
+
superEntityArray.forEach(entity => {
|
|
1650
|
+
let entityIsGeneric;
|
|
1651
|
+
const superEntitySymbol = entity.getExpression().getSymbolOrThrow();
|
|
1652
|
+
let superEntityDeclaration;
|
|
1653
|
+
if (superEntity instanceof ExpressionWithTypeArguments) {
|
|
1654
|
+
superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.ClassDeclaration);
|
|
1655
|
+
} else {
|
|
1656
|
+
superEntityDeclaration = superEntitySymbol.getDeclarations()[0].asKind(ts.SyntaxKind.InterfaceDeclaration);
|
|
1657
|
+
}
|
|
1658
|
+
if (superEntityDeclaration) {
|
|
1659
|
+
entityIsGeneric = superEntityDeclaration.getTypeParameters().length > 0;
|
|
1660
|
+
}
|
|
1661
|
+
if (entityIsGeneric) {
|
|
1662
|
+
let EntityDeclaration;
|
|
1663
|
+
let genEntity;
|
|
1664
|
+
if (superEntity instanceof ExpressionWithTypeArguments) {
|
|
1665
|
+
EntityDeclaration = entity.getExpression().getSymbol()?.getDeclarations()[0] as ClassDeclaration;
|
|
1666
|
+
genEntity = this.createOrGetFamixClass(EntityDeclaration) as Famix.ParametricClass;
|
|
1667
|
+
} else {
|
|
1668
|
+
EntityDeclaration = entity.getExpression().getSymbol()?.getDeclarations()[0] as InterfaceDeclaration;
|
|
1669
|
+
genEntity = this.createOrGetFamixInterface(EntityDeclaration) as Famix.ParametricInterface;
|
|
1670
|
+
}
|
|
1671
|
+
const genParams = EntityDeclaration.getTypeParameters().map((param) => param.getText());
|
|
1672
|
+
const args = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments();
|
|
1673
|
+
const conParams = element.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
|
|
1674
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1675
|
+
const conEntity = this.createOrGetFamixConcreteElement(genEntity,EntityDeclaration,args);
|
|
1676
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1677
|
+
let createConcretisation : boolean = true;
|
|
1678
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1679
|
+
if (genEntity.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conEntity.fullyQualifiedName){
|
|
1680
|
+
createConcretisation = false;
|
|
1681
|
+
}
|
|
1682
|
+
});
|
|
1683
|
+
|
|
1684
|
+
if (createConcretisation) {
|
|
1685
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
|
|
1686
|
+
}
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
});
|
|
1690
|
+
}
|
|
1691
|
+
// TODO: This function seems unfinished
|
|
1692
|
+
}
|
|
1693
|
+
|
|
1694
|
+
|
|
1695
|
+
/**
|
|
1696
|
+
* Creates a Famix concretisation between a class and its instanciations
|
|
1697
|
+
* @param cls A class
|
|
1698
|
+
*/
|
|
1699
|
+
public createFamixConcretisationGenericInstantiation(cls: ClassDeclaration){
|
|
1700
|
+
|
|
1701
|
+
const isGeneric = cls.getTypeParameters().length > 0;
|
|
1702
|
+
if (isGeneric) {
|
|
1703
|
+
const instances = cls.getSourceFile().getDescendantsOfKind(ts.SyntaxKind.NewExpression)
|
|
1704
|
+
.filter(newExpr => {
|
|
1705
|
+
const expression = newExpr.getExpression();
|
|
1706
|
+
return expression.getText() === cls.getName();
|
|
1707
|
+
});
|
|
1708
|
+
|
|
1709
|
+
instances.forEach(instance => {
|
|
1710
|
+
const instanceIsGeneric = instance.getTypeArguments().length > 0;
|
|
1711
|
+
if (instanceIsGeneric) {
|
|
1712
|
+
const conParams = instance.getTypeArguments().map((param) => param.getText());
|
|
1713
|
+
const genEntity = this.createOrGetFamixClass(cls) as Famix.ParametricClass;
|
|
1714
|
+
const genParams = cls.getTypeParameters().map((param) => param.getText());
|
|
1715
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1716
|
+
const conEntity = this.createOrGetFamixConcreteElement(genEntity,cls,instance.getTypeArguments());
|
|
1717
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1718
|
+
let createConcretisation : boolean = true;
|
|
1719
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1720
|
+
if (genEntity.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conEntity.fullyQualifiedName){
|
|
1721
|
+
createConcretisation = false;
|
|
1722
|
+
}
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
if (createConcretisation) {
|
|
1726
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conEntity,genEntity);
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
// TODO: This function seems unfinished
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
/**
|
|
1736
|
+
* Creates a Famix concretisation between a class and its instanciations
|
|
1737
|
+
* @param func A function
|
|
1738
|
+
*/
|
|
1739
|
+
public createFamixConcretisationFunctionInstantiation(element: FunctionDeclaration | MethodDeclaration){
|
|
1740
|
+
const isGeneric = element.getTypeParameters().length > 0;
|
|
1741
|
+
if (isGeneric) {
|
|
1742
|
+
const genParams = element.getTypeParameters().map(param => param.getText());
|
|
1743
|
+
const uses = element.findReferencesAsNodes();
|
|
1744
|
+
uses.forEach(usage => {
|
|
1745
|
+
let currentNode: TsMorphNode | undefined = usage;
|
|
1746
|
+
|
|
1747
|
+
while (currentNode) {
|
|
1748
|
+
if (currentNode.getKind() === SyntaxKind.CallExpression) {
|
|
1749
|
+
const callExpression = currentNode.asKind(SyntaxKind.CallExpression);
|
|
1750
|
+
if (!callExpression) {
|
|
1751
|
+
throw new Error(`CallExpression not found for ${currentNode.getText()}`);
|
|
1752
|
+
}
|
|
1753
|
+
const instanceIsGeneric = callExpression.getTypeArguments().length > 0;
|
|
1754
|
+
if (instanceIsGeneric) {
|
|
1755
|
+
const args = callExpression.getTypeArguments();
|
|
1756
|
+
const conParams = callExpression.getTypeArguments().map(param => param.getText());
|
|
1757
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1758
|
+
let genElement;
|
|
1759
|
+
if(element instanceof FunctionDeclaration){
|
|
1760
|
+
genElement = this.createOrGetFamixFunction(element, {}) as Famix.ParametricFunction;
|
|
1761
|
+
} else {
|
|
1762
|
+
genElement = this.createOrGetFamixMethod(element, {}) as Famix.ParametricMethod;
|
|
1763
|
+
}
|
|
1764
|
+
const concElement = this.createOrGetFamixConcreteElement(genElement,element,args);
|
|
1765
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1766
|
+
let createConcretisation : boolean = true;
|
|
1767
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1768
|
+
if (genElement.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == concElement.fullyQualifiedName){
|
|
1769
|
+
createConcretisation = false;
|
|
1770
|
+
}
|
|
1771
|
+
});
|
|
1772
|
+
|
|
1773
|
+
if (createConcretisation) {
|
|
1774
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(concElement,genElement);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
break;
|
|
1779
|
+
}
|
|
1780
|
+
// Remonter à l'élément parent (utile si le nœud de référence est un enfant)
|
|
1781
|
+
currentNode = currentNode.getParent();
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
/**
|
|
1788
|
+
* Creates a Famix concretisation between a class and an interface
|
|
1789
|
+
* @param cls A class
|
|
1790
|
+
*/
|
|
1791
|
+
public createFamixConcretisationInterfaceClass(cls: ClassDeclaration){
|
|
1792
|
+
|
|
1793
|
+
const superInterfaces = cls.getImplements();
|
|
1794
|
+
superInterfaces.forEach(interfaceType => {
|
|
1795
|
+
const interfaceIsGeneric = interfaceType.getTypeArguments().length>0;
|
|
1796
|
+
if (interfaceIsGeneric) {
|
|
1797
|
+
const interfaceDeclaration = interfaceType.getExpression().getSymbol()?.getDeclarations()[0] as InterfaceDeclaration;
|
|
1798
|
+
const genParams = interfaceDeclaration.getTypeParameters().map((param) => param.getText());
|
|
1799
|
+
const conParams = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments().map((param) => param.getText());
|
|
1800
|
+
const args = cls.getHeritageClauses()[0].getTypeNodes()[0].getTypeArguments();
|
|
1801
|
+
if (!Helpers.arraysAreEqual(conParams,genParams)) {
|
|
1802
|
+
const genInterface = this.createOrGetFamixInterface(interfaceDeclaration) as Famix.ParametricInterface;
|
|
1803
|
+
const conInterface = this.createOrGetFamixConcreteElement(genInterface,interfaceDeclaration,args);
|
|
1804
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1805
|
+
let createConcretisation : boolean = true;
|
|
1806
|
+
concretisations.forEach((conc : Famix.Concretisation) => {
|
|
1807
|
+
if (genInterface.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == conInterface.fullyQualifiedName){
|
|
1808
|
+
createConcretisation = false;
|
|
1809
|
+
}
|
|
1810
|
+
});
|
|
1811
|
+
|
|
1812
|
+
if (createConcretisation) {
|
|
1813
|
+
const fmxConcretisation : Famix.Concretisation = this.createFamixConcretisation(conInterface,genInterface);
|
|
1814
|
+
}
|
|
1815
|
+
}
|
|
1816
|
+
}
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* Creates a Famix concretisation between an interface and a Type
|
|
1822
|
+
* @param element A variable or a function
|
|
1823
|
+
* @param inter An interface
|
|
1824
|
+
*/
|
|
1825
|
+
public createFamixConcretisationTypeInstanciation(element: InterfaceDeclaration | ClassDeclaration) {
|
|
1826
|
+
|
|
1827
|
+
const isGeneric = element.getTypeParameters().length > 0;
|
|
1828
|
+
if (isGeneric) {
|
|
1829
|
+
const genParams = element.getTypeParameters().map(param => param.getText());
|
|
1830
|
+
const uses = element.findReferencesAsNodes();
|
|
1831
|
+
uses.forEach(use => {
|
|
1832
|
+
let parentNode = use.getParent();
|
|
1833
|
+
while (parentNode) {
|
|
1834
|
+
if (parentNode.getKind() === SyntaxKind.TypeReference) {
|
|
1835
|
+
const typeReferenceNode = parentNode.asKind(SyntaxKind.TypeReference);
|
|
1836
|
+
if (!typeReferenceNode) {
|
|
1837
|
+
throw new Error(`TypeReferenceNode not found for ${parentNode.getText()}`);
|
|
1838
|
+
}
|
|
1839
|
+
const typeReferenceNodeIsGeneric = typeReferenceNode.getTypeArguments().length > 0;
|
|
1840
|
+
if (typeReferenceNodeIsGeneric) { }
|
|
1841
|
+
const args = typeReferenceNode.getTypeArguments();
|
|
1842
|
+
const conParams = typeReferenceNode.getTypeArguments().map(param => param.getText());
|
|
1843
|
+
if (!Helpers.arraysAreEqual(conParams, genParams)) {
|
|
1844
|
+
let genElement;
|
|
1845
|
+
if (element instanceof ClassDeclaration) {
|
|
1846
|
+
genElement = this.createOrGetFamixClass(element) as Famix.ParametricClass;
|
|
1847
|
+
} else {
|
|
1848
|
+
genElement = this.createOrGetFamixInterface(element) as Famix.ParametricInterface;
|
|
1849
|
+
}
|
|
1850
|
+
const concElement = this.createOrGetFamixConcreteElement(genElement, element, args);
|
|
1851
|
+
const concretisations = this.famixRep._getAllEntitiesWithType("Concretisation") as Set<Famix.Concretisation>;
|
|
1852
|
+
let createConcretisation: boolean = true;
|
|
1853
|
+
concretisations.forEach((conc: Famix.Concretisation) => {
|
|
1854
|
+
if (genElement.fullyQualifiedName == conc.genericEntity.fullyQualifiedName && conc.concreteEntity.fullyQualifiedName == concElement.fullyQualifiedName) {
|
|
1855
|
+
createConcretisation = false;
|
|
1856
|
+
}
|
|
1857
|
+
});
|
|
1858
|
+
|
|
1859
|
+
if (createConcretisation) {
|
|
1860
|
+
const fmxConcretisation: Famix.Concretisation = this.createFamixConcretisation(concElement, genElement);
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
break;
|
|
1864
|
+
}
|
|
1865
|
+
parentNode = parentNode.getParent();
|
|
1866
|
+
}
|
|
1867
|
+
});
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
public convertToRelativePath(absolutePath: string, absolutePathProject: string) {
|
|
1872
|
+
logger.debug(`convertToRelativePath: absolutePath: '${absolutePath}', absolutePathProject: '${absolutePathProject}'`);
|
|
1873
|
+
if (absolutePath.startsWith(absolutePathProject)) {
|
|
1874
|
+
return absolutePath.replace(absolutePathProject, "").slice(1);
|
|
1875
|
+
} else if (absolutePath.startsWith("/")) {
|
|
1876
|
+
return absolutePath.slice(1);
|
|
1877
|
+
} else {
|
|
1878
|
+
return absolutePath;
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
export function isPrimitiveType(typeName: string) {
|
|
1884
|
+
return typeName === "number" ||
|
|
1885
|
+
typeName === "string" ||
|
|
1886
|
+
typeName === "boolean" ||
|
|
1887
|
+
typeName === "bigint" ||
|
|
1888
|
+
typeName === "symbol" ||
|
|
1889
|
+
typeName === "unique symbol" ||
|
|
1890
|
+
typeName === "undefined" ||
|
|
1891
|
+
typeName === "null" ||
|
|
1892
|
+
typeName === "any" ||
|
|
1893
|
+
typeName === "unknown" ||
|
|
1894
|
+
typeName === "never" ||
|
|
1895
|
+
typeName === "void";
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
function initFQN(sourceElement: TSMorphObjectType, famixElement: Famix.SourcedEntity) {
|
|
1899
|
+
// handle special cases where an element is a Type -- need to change its name
|
|
1900
|
+
if (famixElement instanceof Famix.Type && !(sourceElement instanceof CommentRange) && isTypeContext(sourceElement)) {
|
|
1901
|
+
let fqn = FQNFunctions.getFQN(sourceElement);
|
|
1902
|
+
// using regex, replace [blah] with [blahType]
|
|
1903
|
+
fqn = fqn.replace(/\[([^\]]+)\]/g, "[$1Type]");
|
|
1904
|
+
logger.debug("Setting fully qualified name for " + famixElement.getJSON() + " to " + fqn);
|
|
1905
|
+
famixElement.fullyQualifiedName = fqn;
|
|
1906
|
+
return;
|
|
1907
|
+
}
|
|
1908
|
+
// catch all (except comments)
|
|
1909
|
+
if (!(sourceElement instanceof CommentRange)) {
|
|
1910
|
+
const fqn = FQNFunctions.getFQN(sourceElement);
|
|
1911
|
+
logger.debug("Setting fully qualified name for " + famixElement.getJSON() + " to " + fqn);
|
|
1912
|
+
(famixElement as Famix.NamedEntity).fullyQualifiedName = fqn;
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
|
|
1916
|
+
|
|
1917
|
+
function isTypeContext(sourceElement: TSMorphObjectType): boolean {
|
|
1918
|
+
// Just keep the existing SyntaxKind set as it is
|
|
1919
|
+
const typeContextKinds = new Set([
|
|
1920
|
+
SyntaxKind.Constructor,
|
|
1921
|
+
SyntaxKind.MethodDeclaration,
|
|
1922
|
+
SyntaxKind.FunctionDeclaration,
|
|
1923
|
+
SyntaxKind.FunctionExpression,
|
|
1924
|
+
SyntaxKind.ArrowFunction,
|
|
1925
|
+
SyntaxKind.Parameter,
|
|
1926
|
+
SyntaxKind.VariableDeclaration,
|
|
1927
|
+
SyntaxKind.PropertyDeclaration,
|
|
1928
|
+
SyntaxKind.PropertySignature,
|
|
1929
|
+
SyntaxKind.TypeParameter,
|
|
1930
|
+
SyntaxKind.Identifier,
|
|
1931
|
+
SyntaxKind.Decorator,
|
|
1932
|
+
SyntaxKind.GetAccessor,
|
|
1933
|
+
SyntaxKind.SetAccessor,
|
|
1934
|
+
SyntaxKind.ImportSpecifier,
|
|
1935
|
+
SyntaxKind.EnumDeclaration,
|
|
1936
|
+
SyntaxKind.EnumMember,
|
|
1937
|
+
SyntaxKind.TypeAliasDeclaration,
|
|
1938
|
+
SyntaxKind.ImportDeclaration,
|
|
1939
|
+
SyntaxKind.ExpressionWithTypeArguments
|
|
1940
|
+
]);
|
|
1941
|
+
|
|
1942
|
+
return typeContextKinds.has(sourceElement.getKind());
|
|
1943
|
+
}
|
|
1944
|
+
|
|
1945
|
+
function getInterfaceOrClassDeclarationFromExpression(expression: ExpressionWithTypeArguments): InterfaceDeclaration | ClassDeclaration | undefined {
|
|
1946
|
+
// Step 1: Get the type of the expression
|
|
1947
|
+
const type = expression.getType();
|
|
1948
|
+
|
|
1949
|
+
// Step 2: Get the symbol associated with the type
|
|
1950
|
+
let symbol = type.getSymbol();
|
|
1951
|
+
|
|
1952
|
+
if (!symbol) {
|
|
1953
|
+
// If symbol is not found, try to get the symbol from the identifier
|
|
1954
|
+
const identifier = expression.getFirstDescendantByKind(SyntaxKind.Identifier);
|
|
1955
|
+
if (!identifier) {
|
|
1956
|
+
throw new Error(`Identifier not found for ${expression.getText()}.`);
|
|
1957
|
+
}
|
|
1958
|
+
symbol = identifier.getSymbol();
|
|
1959
|
+
if (!symbol) {
|
|
1960
|
+
throw new Error(`Symbol not found for ${identifier.getText()}.`);
|
|
1961
|
+
}
|
|
1962
|
+
}
|
|
1963
|
+
|
|
1964
|
+
// Step 3: Resolve the symbol to find the actual declaration
|
|
1965
|
+
const interfaceDeclaration = resolveSymbolToInterfaceOrClassDeclaration(symbol);
|
|
1966
|
+
|
|
1967
|
+
if (!interfaceDeclaration) {
|
|
1968
|
+
logger.error(`Interface declaration not found for ${expression.getText()}.`);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
return interfaceDeclaration;
|
|
1972
|
+
}
|
|
1973
|
+
|
|
1974
|
+
import { Symbol as TSMorphSymbol, Node as TsMorphNode } from "ts-morph";
|
|
1975
|
+
import _ from "lodash";
|
|
1976
|
+
|
|
1977
|
+
function resolveSymbolToInterfaceOrClassDeclaration(symbol: TSMorphSymbol): InterfaceDeclaration | ClassDeclaration | undefined {
|
|
1978
|
+
// Get the declarations associated with the symbol
|
|
1979
|
+
const declarations = symbol.getDeclarations();
|
|
1980
|
+
|
|
1981
|
+
// Filter for InterfaceDeclaration or ClassDeclaration
|
|
1982
|
+
const interfaceOrClassDeclaration = declarations.find(
|
|
1983
|
+
declaration =>
|
|
1984
|
+
declaration instanceof InterfaceDeclaration ||
|
|
1985
|
+
declaration instanceof ClassDeclaration) as InterfaceDeclaration | ClassDeclaration | undefined;
|
|
1986
|
+
|
|
1987
|
+
if (interfaceOrClassDeclaration) {
|
|
1988
|
+
return interfaceOrClassDeclaration;
|
|
1989
|
+
}
|
|
1990
|
+
|
|
1991
|
+
// Handle imports: If the symbol is imported, resolve the import to find the actual declaration
|
|
1992
|
+
for (const declaration of declarations) {
|
|
1993
|
+
if (declaration.getKind() === SyntaxKind.ImportSpecifier) {
|
|
1994
|
+
const importSpecifier = declaration as ImportSpecifier;
|
|
1995
|
+
const importDeclaration = importSpecifier.getImportDeclaration();
|
|
1996
|
+
const moduleSpecifier = importDeclaration.getModuleSpecifierSourceFile();
|
|
1997
|
+
|
|
1998
|
+
if (moduleSpecifier) {
|
|
1999
|
+
const exportedSymbols = moduleSpecifier.getExportSymbols();
|
|
2000
|
+
const exportedSymbol = exportedSymbols.find(symbol => symbol.getName() === importSpecifier.getName());
|
|
2001
|
+
if (exportedSymbol) {
|
|
2002
|
+
return resolveSymbolToInterfaceOrClassDeclaration(exportedSymbol);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
return undefined;
|
|
2008
|
+
}
|
|
2009
|
+
|
|
2010
|
+
|
|
2011
|
+
export function getPrimitiveTypeName(type: Type): string | undefined {
|
|
2012
|
+
const flags = type.compilerType.flags;
|
|
2013
|
+
|
|
2014
|
+
if (flags & ts.TypeFlags.String) return "string";
|
|
2015
|
+
if (flags & ts.TypeFlags.Number) return "number";
|
|
2016
|
+
if (flags & ts.TypeFlags.Boolean) return "boolean";
|
|
2017
|
+
if (flags & ts.TypeFlags.BigInt) return "bigint";
|
|
2018
|
+
if (flags & ts.TypeFlags.UniqueESSymbol) return "unique symbol";
|
|
2019
|
+
if (flags & ts.TypeFlags.ESSymbol) return "symbol";
|
|
2020
|
+
if (flags & ts.TypeFlags.Undefined) return "undefined";
|
|
2021
|
+
if (flags & ts.TypeFlags.Null) return "null";
|
|
2022
|
+
if (flags & ts.TypeFlags.Void) return "void";
|
|
2023
|
+
if (flags & ts.TypeFlags.Never) return "never";
|
|
2024
|
+
if (flags & ts.TypeFlags.Any) return "any";
|
|
2025
|
+
if (flags & ts.TypeFlags.Unknown) return "unknown";
|
|
2026
|
+
|
|
2027
|
+
return undefined;
|
|
2028
|
+
}
|
|
2029
|
+
|
|
2030
|
+
// function oldGetInterfaceDeclarationFromExpression(expression: ExpressionWithTypeArguments): InterfaceDeclaration | undefined {
|
|
2031
|
+
// // Two cases:
|
|
2032
|
+
// // class A implements ImportedInterface, DeclaredInterface {}
|
|
2033
|
+
// const type = expression.getType();
|
|
2034
|
+
|
|
2035
|
+
// // ImportedInterface: type will a symbol
|
|
2036
|
+
// let symbol = type.getAliasSymbol(); // will be defined for imported interfaces
|
|
2037
|
+
|
|
2038
|
+
// if (!symbol) {
|
|
2039
|
+
// // DeclaredInterface: type will be an InterfaceDeclaration on Identifier node that is the child of the ExpressionWithTypeArguments
|
|
2040
|
+
// const identifier = expression.getFirstDescendantByKind(SyntaxKind.Identifier);
|
|
2041
|
+
// if (!identifier) {
|
|
2042
|
+
// throw new Error(`Identifier not found for ${expression.getText()}.`);
|
|
2043
|
+
// }
|
|
2044
|
+
// symbol = identifier.getSymbol();
|
|
2045
|
+
// if (!symbol) {
|
|
2046
|
+
// throw new Error(`Symbol not found for ${identifier.getText()}.`);
|
|
2047
|
+
// }
|
|
2048
|
+
// }
|
|
2049
|
+
|
|
2050
|
+
// // Step 3: Get the declarations associated with the symbol
|
|
2051
|
+
// const declarations = symbol.getDeclarations();
|
|
2052
|
+
|
|
2053
|
+
// // Step 4: Filter for InterfaceDeclaration
|
|
2054
|
+
// const interfaceDeclaration = declarations.find(declaration => declaration instanceof InterfaceDeclaration) as InterfaceDeclaration | undefined;
|
|
2055
|
+
|
|
2056
|
+
// if (!interfaceDeclaration) {
|
|
2057
|
+
// throw new Error(`Interface declaration not found for ${expression.getText()}.`);
|
|
2058
|
+
// }
|
|
2059
|
+
|
|
2060
|
+
// return interfaceDeclaration;
|
|
2061
|
+
// }
|