• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import * as ts from "typescript";
17import {
18    ExternalType, PrimitiveType, TypeSummary, userDefinedTypeStartIndex
19} from "./base/typeSystem";
20import * as jshelpers from "./jshelpers";
21import { ModuleStmt } from "./modules";
22import { TypeChecker } from "./typeChecker";
23
24export class TypeRecorder {
25    private static instance: TypeRecorder;
26    private type2Index: Map<ts.Node, number> = new Map<ts.Node, number>();
27    private variable2Type: Map<ts.Node, number> = new Map<ts.Node, number>();
28    private userDefinedTypeSet: Set<number> = new Set<number>();;
29    private typeSummary: TypeSummary = new TypeSummary();
30    private class2InstanceMap: Map<number, number> = new Map<number, number>();
31    private builtinContainer2InstanceMap: Map<object, number> = new Map<object, number>();
32    private arrayTypeMap: Map<number, number> = new Map<number, number>();
33    private unionTypeMap: Map<string, number> = new Map<string, number>();
34    private exportedType: Map<string, number> = new Map<string, number>();
35    private declaredType: Map<string, number> = new Map<string, number>();
36    // namespace mapping: namepace -> filepath (import * as sth from "...")
37    // In PropertyAccessExpression we'll need this to map the symbol to filepath
38    private namespaceMap: Map<string, string> = new Map<string, string>();
39    // (export * from "..."), if the symbol isn't in the reExportedType map, search here.
40    private anonymousReExport: Array<string> = new Array<string>();
41
42    private constructor() { }
43
44    public static getInstance(): TypeRecorder {
45        return TypeRecorder.instance;
46    }
47
48    public static createInstance(): TypeRecorder {
49        TypeRecorder.instance = new TypeRecorder();
50        return TypeRecorder.instance;
51    }
52
53    public setTypeSummary() {
54        this.typeSummary.setInfo(this.countUserDefinedTypeSet(), this.anonymousReExport);
55    }
56
57    public getTypeSummaryIndex() {
58        return this.typeSummary.getPreservedIndex();
59    }
60
61    public addUserDefinedTypeSet(index: number) {
62        if (index > userDefinedTypeStartIndex) {
63            this.userDefinedTypeSet.add(index);
64        }
65    }
66
67    public countUserDefinedTypeSet(): number {
68        return this.userDefinedTypeSet.size;
69    }
70
71    public addType2Index(typeNode: ts.Node, index: number) {
72        this.type2Index.set(typeNode, index);
73        this.addUserDefinedTypeSet(index);
74    }
75
76    public setVariable2Type(variableNode: ts.Node, index: number) {
77        this.variable2Type.set(variableNode, index);
78        this.addUserDefinedTypeSet(index);
79    }
80
81    public hasType(typeNode: ts.Node): boolean {
82        return this.type2Index.has(typeNode);
83    }
84
85    public tryGetTypeIndex(typeNode: ts.Node): number {
86        if (this.type2Index.has(typeNode)) {
87            return this.type2Index.get(typeNode)!;
88        } else {
89            return PrimitiveType.ANY;
90        }
91    }
92
93    public tryGetVariable2Type(variableNode: ts.Node): number {
94        if (this.variable2Type.has(variableNode)) {
95            return this.variable2Type.get(variableNode)!;
96        } else {
97            return PrimitiveType.ANY;
98        }
99    }
100
101    public setArrayTypeMap(contentTypeIndex: number, arrayTypeIndex: number) {
102        this.arrayTypeMap.set(contentTypeIndex, arrayTypeIndex)
103    }
104
105    public hasArrayTypeMapping(contentTypeIndex: number) {
106        return this.arrayTypeMap.has(contentTypeIndex);
107    }
108
109    public getFromArrayTypeMap(contentTypeIndex: number) {
110        return this.arrayTypeMap.get(contentTypeIndex);
111    }
112
113    public setUnionTypeMap(unionStr: string, unionTypeIndex: number) {
114        this.unionTypeMap.set(unionStr, unionTypeIndex)
115    }
116
117    public hasUnionTypeMapping(unionStr: string) {
118        return this.unionTypeMap.has(unionStr);
119    }
120
121    public getFromUnionTypeMap(unionStr: string) {
122        return this.unionTypeMap.get(unionStr);
123    }
124
125    public setClass2InstanceMap(classIndex: number, instanceIndex: number) {
126        this.class2InstanceMap.set(classIndex, instanceIndex)
127    }
128
129    public hasClass2InstanceMap(classIndex: number) {
130        return this.class2InstanceMap.has(classIndex);
131    }
132
133    public getClass2InstanceMap(classIndex: number) {
134        return this.class2InstanceMap.get(classIndex);
135    }
136
137    public setBuiltinContainer2InstanceMap(builtinContainer: object, instanceIndex: number) {
138        this.builtinContainer2InstanceMap.set(builtinContainer, instanceIndex)
139    }
140
141    public hasBuiltinContainer2InstanceMap(builtinContainer: object) {
142        return this.builtinContainer2InstanceMap.has(builtinContainer);
143    }
144
145    public getBuiltinContainer2InstanceMap(builtinContainer: object) {
146        return this.builtinContainer2InstanceMap.get(builtinContainer);
147    }
148
149    // exported/imported
150    public addImportedType(moduleStmt: ModuleStmt) {
151        moduleStmt.getBindingNodeMap().forEach((externalNode, localNode) => {
152            let externalName = jshelpers.getTextOfIdentifierOrLiteral(externalNode);
153            let importDeclNode = TypeChecker.getInstance().getTypeDeclForIdentifier(localNode);
154            let externalType = new ExternalType(externalName, moduleStmt.getModuleRequest());
155            this.addType2Index(importDeclNode, externalType.shiftedTypeIndex);
156            this.setVariable2Type(localNode, externalType.shiftedTypeIndex);
157        });
158
159        if (moduleStmt.getNameSpace() != "") {
160            this.setNamespaceMap(moduleStmt.getNameSpace(), moduleStmt.getModuleRequest());
161            let externalType = new ExternalType("*", moduleStmt.getNameSpace());
162            let ImportTypeIndex = externalType.shiftedTypeIndex;
163            this.addUserDefinedTypeSet(ImportTypeIndex);
164        }
165    }
166
167    public addExportedType(moduleStmt: ModuleStmt) {
168        if (moduleStmt.getModuleRequest() != "") {
169            // re-export, no need to search in typeRecord cause it must not be there
170            if (moduleStmt.getNameSpace() != "") {
171                // re-export * as namespace
172                let externalType = new ExternalType("*", moduleStmt.getModuleRequest());
173                let typeIndex = externalType.shiftedTypeIndex;
174                this.setExportedType(moduleStmt.getNameSpace(), typeIndex);
175                this.addUserDefinedTypeSet(typeIndex);
176            } else if (moduleStmt.getBindingNameMap().size != 0) {
177                // re-export via clause
178                moduleStmt.getBindingNameMap().forEach((originalName, exportedName) => {
179                    let externalType = new ExternalType(originalName, moduleStmt.getModuleRequest());
180                    let typeIndex = externalType.shiftedTypeIndex;
181                    this.setExportedType(exportedName, typeIndex);
182                    this.addUserDefinedTypeSet(typeIndex);
183                });
184            } else {
185                // re-export * with anonymuse namespace
186                this.addAnonymousReExport(moduleStmt.getModuleRequest());
187            }
188        } else {
189            // named export via clause, could came from imported or local
190            moduleStmt.getBindingNodeMap().forEach((localNode, externalNode) => {
191                let exportedName = jshelpers.getTextOfIdentifierOrLiteral(externalNode);
192                let nodeType = TypeChecker.getInstance().getTypeAtLocation(localNode);
193                let typeNode = nodeType?.getSymbol()?.valueDeclaration;
194                if (typeNode) {
195                    this.addNonReExportedType(exportedName, typeNode!, localNode);
196                }
197            });
198        }
199    }
200
201    public addNonReExportedType(exportedName: string, typeNode: ts.Node, localNode: ts.Node) {
202        // Check if type of localName was already stroed in typeRecord
203        // Imported type should already be stored in typeRecord by design
204        let typeIndexForType = this.tryGetTypeIndex(typeNode);
205        let typeIndexForVariable = this.tryGetVariable2Type(typeNode);
206        if (typeIndexForType != PrimitiveType.ANY) {
207            this.setExportedType(exportedName, typeIndexForType);
208        } else if (typeIndexForVariable != PrimitiveType.ANY) {
209            this.setExportedType(exportedName, typeIndexForVariable);
210        } else {
211            // not found in typeRecord. Need to create the type and
212            // add to typeRecord with its localName and to exportedType with its exportedName
213            let typeIndex = TypeChecker.getInstance().getTypeFromDecl(typeNode, localNode.kind == ts.SyntaxKind.NewExpression);
214            this.setExportedType(exportedName, typeIndex);
215        }
216    }
217
218    public setExportedType(exportedName: string, typeIndex: number) {
219        this.exportedType.set(exportedName, typeIndex);
220    }
221
222    public setDeclaredType(exportedName: string, typeIndex: number) {
223        this.declaredType.set(exportedName, typeIndex);
224    }
225
226    public addAnonymousReExport(redirectName: string) {
227        this.anonymousReExport.push(redirectName);
228    }
229
230    public setNamespaceMap(namespace: string, filePath: string) {
231        this.namespaceMap.set(namespace, filePath);
232    }
233
234    public inNampespaceMap(targetName: string) {
235        return this.namespaceMap.has(targetName);
236    }
237
238    public getPathForNamespace(targetName: string) {
239        return this.namespaceMap.get(targetName);
240    }
241
242    // for log
243    public getType2Index(): Map<ts.Node, number> {
244        return this.type2Index;
245    }
246
247    public getVariable2Type(): Map<ts.Node, number> {
248        return this.variable2Type;
249    }
250
251    public getTypeSet() {
252        return this.userDefinedTypeSet;
253    }
254
255    public getExportedType() {
256        return this.exportedType;
257    }
258
259    public getDeclaredType() {
260        return this.declaredType;
261    }
262
263    public getAnonymousReExport() {
264        return this.anonymousReExport;
265    }
266
267    public getNamespaceMap() {
268        return this.namespaceMap;
269    }
270
271    public printNodeMap(map: Map<ts.Node, number>) {
272        map.forEach((value, key) => {
273            console.log(jshelpers.getTextOfNode(key) + ": " + value);
274        });
275    }
276
277    public printExportMap(map: Map<string, number>) {
278        map.forEach((value, key) => {
279            console.log(key + " : " + value);
280        });
281    }
282
283    public printReExportMap(map: Map<string, string>) {
284        map.forEach((value, key) => {
285            console.log(key + " : " + value);
286        });
287    }
288
289    public getLog() {
290        console.log("type2Index: ");
291        console.log(this.printNodeMap(this.getType2Index()));
292        console.log("variable2Type: ");
293        console.log(this.printNodeMap(this.getVariable2Type()));
294        console.log("getTypeSet: ");
295        console.log(this.getTypeSet());
296        console.log("class instance Map:");
297        console.log(this.class2InstanceMap);
298        console.log("builtinContainer instance Map:");
299        console.log(this.builtinContainer2InstanceMap);
300        console.log("exportedType:");
301        console.log(this.printExportMap(this.getExportedType()));
302        console.log("AnoymousRedirect:");
303        console.log(this.getAnonymousReExport());
304        console.log("namespace Map:");
305        console.log(this.getNamespaceMap());
306    }
307}
308