Skip to content

Commit 3552746

Browse files
authored
feat: improve type inference model (#169)
Creates a state storage plugin for saving type information. Type system improvements: * disallow anonymous ids in relations/elements * ensure function ids are included in function definition relations * map function calls correctly to the original function definition * remove notion of 'index' in array type * differentiate between strong and weak relations between type models * removed type enum from statement constructors * always keep typeid in statements
1 parent 109a757 commit 3552746

File tree

63 files changed

+1458
-698
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1458
-698
lines changed

libraries/analysis-javascript/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,4 +59,5 @@ export * from "./lib/type/resolving/InferenceTypeModelFactory";
5959

6060
export * from "./lib/utils/fileSystem";
6161

62+
export * from "./lib/Events";
6263
export * from "./lib/RootContext";
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/*
2+
* Copyright 2020-2023 Delft University of Technology and SynTest contributors
3+
*
4+
* This file is part of SynTest Framework - SynTest JavaScript.
5+
*
6+
* Licensed under the Apache License, Version 2.0 (the "License");
7+
* you may not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
import { RootContext } from "./RootContext";
20+
21+
export type Events = {
22+
exportExtractionStart: (rootContext: RootContext, filepath: string) => void;
23+
exportExtractionComplete: (
24+
rootContext: RootContext,
25+
filepath: string
26+
) => void;
27+
elementExtractionStart: (rootContext: RootContext, filepath: string) => void;
28+
elementExtractionComplete: (
29+
rootContext: RootContext,
30+
filepath: string
31+
) => void;
32+
relationExtractionStart: (rootContext: RootContext, filepath: string) => void;
33+
relationExtractionComplete: (
34+
rootContext: RootContext,
35+
filepath: string
36+
) => void;
37+
objectTypeExtractionStart: (
38+
rootContext: RootContext,
39+
filepath: string
40+
) => void;
41+
objectTypeExtractionComplete: (
42+
rootContext: RootContext,
43+
filepath: string
44+
) => void;
45+
46+
typeResolvingStart: (rootContext: RootContext) => void;
47+
typeResolvingComplete: (rootContext: RootContext) => void;
48+
};

libraries/analysis-javascript/lib/RootContext.ts

Lines changed: 144 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,21 @@ import { Element } from "./type/discovery/element/Element";
3535
import { DiscoveredObjectType } from "./type/discovery/object/DiscoveredType";
3636
import { Relation } from "./type/discovery/relation/Relation";
3737
import { TypePool } from "./type/resolving/TypePool";
38+
import TypedEmitter from "typed-emitter";
39+
import { Events } from "./Events";
3840

3941
export class RootContext extends CoreRootContext<t.Node> {
4042
protected _exportFactory: ExportFactory;
4143
protected _typeExtractor: TypeExtractor;
4244
protected _typeResolver: TypeModelFactory;
4345

4446
protected _files: string[];
45-
protected _elementMap: Map<string, Element>;
46-
protected _relationMap: Map<string, Relation>;
47-
protected _objectMap: Map<string, DiscoveredObjectType>;
47+
// filepath -> id -> element
48+
protected _elementMap: Map<string, Map<string, Element>>;
49+
// filepath -> id -> relation
50+
protected _relationMap: Map<string, Map<string, Relation>>;
51+
// filepath -> id -> object
52+
protected _objectMap: Map<string, Map<string, DiscoveredObjectType>>;
4853

4954
protected _typeModel: TypeModel;
5055
protected _typePool: TypePool;
@@ -126,13 +131,26 @@ export class RootContext extends CoreRootContext<t.Node> {
126131
return this._sources.get(absoluteTargetPath);
127132
}
128133

129-
private getExports(filePath: string): Export[] {
134+
getExports(filePath: string): Export[] {
130135
const absolutePath = this.resolvePath(filePath);
131136

132137
if (!this._exportMap.has(absolutePath)) {
133-
return this._exportFactory.extract(
138+
(<TypedEmitter<Events>>process).emit(
139+
"exportExtractionStart",
140+
this,
141+
absolutePath
142+
);
143+
this._exportMap.set(
134144
absolutePath,
135-
this.getAbstractSyntaxTree(absolutePath)
145+
this._exportFactory.extract(
146+
absolutePath,
147+
this.getAbstractSyntaxTree(absolutePath)
148+
)
149+
);
150+
(<TypedEmitter<Events>>process).emit(
151+
"exportExtractionComplete",
152+
this,
153+
absolutePath
136154
);
137155
}
138156

@@ -150,26 +168,137 @@ export class RootContext extends CoreRootContext<t.Node> {
150168
return this._exportMap;
151169
}
152170

153-
extractTypes(): void {
154-
if (!this._elementMap || !this._relationMap || !this._objectMap) {
155-
this._typeExtractor.extractAll(this);
156-
this._elementMap = this._typeExtractor.elementMap;
157-
this._relationMap = this._typeExtractor.relationMap;
158-
this._objectMap = this._typeExtractor.objectMap;
171+
getElements(filepath: string) {
172+
const absolutePath = this.resolvePath(filepath);
173+
174+
if (!this._elementMap.has(absolutePath)) {
175+
(<TypedEmitter<Events>>process).emit(
176+
"elementExtractionStart",
177+
this,
178+
absolutePath
179+
);
180+
const elementMap = this._typeExtractor.extractElements(
181+
absolutePath,
182+
this.getAbstractSyntaxTree(absolutePath)
183+
);
184+
185+
this._elementMap.set(absolutePath, elementMap);
186+
(<TypedEmitter<Events>>process).emit(
187+
"elementExtractionComplete",
188+
this,
189+
absolutePath
190+
);
191+
}
192+
193+
return this._elementMap.get(absolutePath);
194+
}
195+
196+
getAllElements() {
197+
if (!this._elementMap) {
198+
this._elementMap = new Map();
199+
200+
for (const filepath of this.getFiles()) {
201+
this._elementMap.set(filepath, this.getElements(filepath));
202+
}
203+
}
204+
return this._elementMap;
205+
}
206+
207+
getRelations(filepath: string) {
208+
const absolutePath = this.resolvePath(filepath);
209+
210+
if (!this._relationMap.has(absolutePath)) {
211+
(<TypedEmitter<Events>>process).emit(
212+
"relationExtractionStart",
213+
this,
214+
absolutePath
215+
);
216+
const relationsMap = this._typeExtractor.extractRelations(
217+
absolutePath,
218+
this.getAbstractSyntaxTree(absolutePath)
219+
);
220+
221+
this._relationMap.set(absolutePath, relationsMap);
222+
(<TypedEmitter<Events>>process).emit(
223+
"relationExtractionComplete",
224+
this,
225+
absolutePath
226+
);
227+
}
228+
229+
return this._relationMap.get(absolutePath);
230+
}
231+
232+
getAllRelations() {
233+
if (!this._relationMap) {
234+
this._relationMap = new Map();
235+
236+
for (const filepath of this.getFiles()) {
237+
this._relationMap.set(filepath, this.getRelations(filepath));
238+
}
239+
}
240+
return this._relationMap;
241+
}
242+
243+
getObjectTypes(filepath: string) {
244+
const absolutePath = this.resolvePath(filepath);
245+
246+
if (!this._objectMap.has(absolutePath)) {
247+
(<TypedEmitter<Events>>process).emit(
248+
"objectTypeExtractionStart",
249+
this,
250+
absolutePath
251+
);
252+
const objectsMap = this._typeExtractor.extractObjectTypes(
253+
absolutePath,
254+
this.getAbstractSyntaxTree(absolutePath)
255+
);
256+
257+
this._objectMap.set(absolutePath, objectsMap);
258+
(<TypedEmitter<Events>>process).emit(
259+
"objectTypeExtractionComplete",
260+
this,
261+
absolutePath
262+
);
263+
}
264+
265+
return this._objectMap.get(absolutePath);
266+
}
267+
268+
getAllObjectTypes() {
269+
if (!this._objectMap) {
270+
this._objectMap = new Map();
271+
272+
for (const filepath of this.getFiles()) {
273+
this._objectMap.set(filepath, this.getObjectTypes(filepath));
274+
}
159275
}
276+
return this._objectMap;
160277
}
161278

162279
resolveTypes(): void {
163-
if (!this._elementMap || !this._relationMap || !this._objectMap) {
164-
this.extractTypes();
280+
// TODO allow sub selections of files (do not consider entire context)
281+
if (!this._elementMap) {
282+
this.getAllElements();
283+
}
284+
if (!this._relationMap) {
285+
this.getAllRelations();
286+
}
287+
if (!this._objectMap) {
288+
this.getAllObjectTypes();
289+
}
290+
if (!this._exportMap) {
291+
this.getAllExports();
165292
}
166293

167294
if (!this._typeModel) {
295+
(<TypedEmitter<Events>>process).emit("typeResolvingStart", this);
168296
this._typeModel = this._typeResolver.resolveTypes(
169297
this._elementMap,
170298
this._relationMap
171299
);
172-
this._typePool = new TypePool(this._objectMap, this.getAllExports());
300+
this._typePool = new TypePool(this._objectMap, this._exportMap);
301+
(<TypedEmitter<Events>>process).emit("typeResolvingComplete", this);
173302
}
174303
}
175304

@@ -188,25 +317,4 @@ export class RootContext extends CoreRootContext<t.Node> {
188317

189318
return this._typePool;
190319
}
191-
192-
getElement(id: string): Element {
193-
if (!this._elementMap || !this._elementMap.has(id)) {
194-
this.extractTypes();
195-
}
196-
return this._elementMap.get(id);
197-
}
198-
199-
getRelation(id: string): Relation {
200-
if (!this._relationMap || !this._relationMap.has(id)) {
201-
this.extractTypes();
202-
}
203-
return this._relationMap.get(id);
204-
}
205-
206-
getObject(id: string): DiscoveredObjectType {
207-
if (!this._objectMap || !this._objectMap.has(id)) {
208-
this.extractTypes();
209-
}
210-
return this._objectMap.get(id);
211-
}
212320
}

libraries/analysis-javascript/lib/target/TargetVisitor.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -349,10 +349,34 @@ export class TargetVisitor extends AbstractSyntaxTreeVisitor {
349349
// only thing left where these can be found is:
350350
// call(() => {})
351351
const targetName = this._getTargetNameOfExpression(path);
352-
const id = this._getNodeId(path);
353-
const export_ = this._getExport(id);
354352

355-
this._extractFromFunction(path, id, id, targetName, export_, false, false);
353+
if (path.parentPath.isVariableDeclarator()) {
354+
const id = this._getNodeId(path);
355+
const export_ = this._getExport(id);
356+
357+
this._extractFromFunction(
358+
path,
359+
id,
360+
id,
361+
targetName,
362+
export_,
363+
false,
364+
false
365+
);
366+
} else {
367+
const id = this._getNodeId(path);
368+
const export_ = this._getExport(id);
369+
370+
this._extractFromFunction(
371+
path,
372+
id,
373+
id,
374+
targetName,
375+
export_,
376+
false,
377+
false
378+
);
379+
}
356380

357381
path.skip();
358382
};

0 commit comments

Comments
 (0)