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