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 ArrayType, 19 BuiltinContainerType, 20 BuiltinType, 21 ClassInstType, 22 ClassType, 23 ExternalType, 24 FunctionType, 25 InterfaceType, 26 ObjectType, 27 PrimitiveType, 28 UnionType 29} from "./base/typeSystem"; 30import * as jshelpers from "./jshelpers"; 31import { LOGD } from "./log"; 32import { ModuleStmt } from "./modules"; 33import { isGlobalDeclare } from "./strictMode"; 34import { TypeRecorder } from "./typeRecorder"; 35 36export class TypeChecker { 37 private static instance: TypeChecker; 38 private compiledTypeChecker: any = null; 39 // After ets runtime adapt to parse BuiltinContainerType, set the flag as true. 40 private needRecordBuiltinContainer: boolean = false; 41 private constructor() { } 42 43 public static getInstance(): TypeChecker { 44 if (!TypeChecker.instance) { 45 TypeChecker.instance = new TypeChecker(); 46 } 47 return TypeChecker.instance; 48 } 49 50 public setTypeChecker(typeChecker: ts.TypeChecker) { 51 this.compiledTypeChecker = typeChecker; 52 } 53 54 public getTypeChecker(): ts.TypeChecker { 55 return this.compiledTypeChecker; 56 } 57 58 public getTypeAtLocation(node: ts.Node) { 59 if (!node) { 60 return undefined; 61 } 62 try { 63 return this.compiledTypeChecker.getTypeAtLocation(node); 64 } catch { 65 LOGD("Get getTypeAtLocation filed for : " + node.getFullText()); 66 return undefined; 67 } 68 } 69 70 public getTypeDeclForIdentifier(node: ts.Node) { 71 if (!node) { 72 return undefined; 73 } 74 let symbol; 75 try { 76 symbol = this.compiledTypeChecker.getSymbolAtLocation(node); 77 } catch { 78 LOGD("Get getSymbolAtLocation filed for : " + node.getFullText()); 79 return undefined; 80 } 81 if (symbol && symbol.declarations) { 82 return symbol.declarations[0]; 83 } 84 return undefined; 85 } 86 87 public hasExportKeyword(node: ts.Node): boolean { 88 if (node.modifiers) { 89 for (let modifier of node.modifiers) { 90 if (modifier.kind === ts.SyntaxKind.ExportKeyword) { 91 return true; 92 } 93 } 94 } 95 return false; 96 } 97 98 public hasDeclareKeyword(node: ts.Node): boolean { 99 if (node.modifiers) { 100 for (let modifier of node.modifiers) { 101 if (modifier.kind === ts.SyntaxKind.DeclareKeyword) { 102 return true; 103 } 104 } 105 } 106 return false; 107 } 108 109 public getDeclNodeForInitializer(initializer: ts.Node) { 110 switch (initializer.kind) { 111 case ts.SyntaxKind.Identifier: 112 return this.getTypeDeclForIdentifier(initializer); 113 case ts.SyntaxKind.NewExpression: 114 let initializerExpression = <ts.NewExpression>initializer; 115 let expression = initializerExpression.expression; 116 if (expression.kind == ts.SyntaxKind.ClassExpression) { 117 return expression; 118 } 119 return this.getTypeDeclForIdentifier(expression); 120 case ts.SyntaxKind.ClassExpression: 121 return initializer; 122 case ts.SyntaxKind.PropertyAccessExpression: 123 return initializer; 124 default: 125 return undefined; 126 } 127 } 128 129 public getTypeForClassDeclOrExp(typeDeclNode: ts.Node, getTypeForInstace: boolean): number { 130 if (this.isFromDefaultLib(typeDeclNode)) { 131 return PrimitiveType.ANY; 132 } 133 let classTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode); 134 if (classTypeIndex == PrimitiveType.ANY) { 135 let classDeclNode = <ts.ClassDeclaration>typeDeclNode; 136 let className = ""; 137 let classNameNode = classDeclNode.name; 138 if (classNameNode) { 139 className = jshelpers.getTextOfIdentifierOrLiteral(classNameNode).replace(/\s/g, ""); 140 } 141 let classType = new ClassType(classDeclNode, BuiltinType[className]); 142 classTypeIndex = classType.shiftedTypeIndex; 143 } 144 if (getTypeForInstace) { 145 classTypeIndex = this.getOrCreateInstanceType(classTypeIndex); 146 } 147 return classTypeIndex; 148 } 149 150 public getTypeForPropertyAccessExpression(typeDeclNode: ts.Node) { 151 let propertyAccessExpression = <ts.PropertyAccessExpression>typeDeclNode; 152 let localName = jshelpers.getTextOfIdentifierOrLiteral(propertyAccessExpression.expression); 153 let externalName = jshelpers.getTextOfIdentifierOrLiteral(propertyAccessExpression.name); 154 if (TypeRecorder.getInstance().inNampespaceMap(localName)) { 155 let redirectPath = TypeRecorder.getInstance().getPathForNamespace(localName)!; 156 let externalType = new ExternalType(externalName, redirectPath); 157 let ImportTypeIndex = externalType.shiftedTypeIndex; 158 return ImportTypeIndex; 159 } 160 return PrimitiveType.ANY; 161 } 162 163 public getInterfaceDeclaration(typeDeclNode: ts.Node) { 164 if (this.isFromDefaultLib(typeDeclNode)) { 165 return PrimitiveType.ANY; 166 } 167 let interfaceTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode); 168 if (interfaceTypeIndex == PrimitiveType.ANY) { 169 let interefaceType = new InterfaceType(<ts.InterfaceDeclaration>typeDeclNode); 170 interfaceTypeIndex = interefaceType.shiftedTypeIndex; 171 } 172 return interfaceTypeIndex; 173 } 174 175 public getTypeFromDecl(typeDeclNode: ts.Node, getTypeForInstace: boolean): number { 176 if (!typeDeclNode) { 177 return PrimitiveType.ANY; 178 } 179 switch (typeDeclNode.kind) { 180 // Type found to be defined a classDeclaration or classExpression 181 case ts.SyntaxKind.ClassDeclaration: 182 case ts.SyntaxKind.ClassExpression: 183 return this.getTypeForClassDeclOrExp(typeDeclNode, getTypeForInstace); 184 case ts.SyntaxKind.ImportSpecifier: 185 case ts.SyntaxKind.ImportClause: 186 let ImportTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(typeDeclNode); 187 if (ImportTypeIndex != PrimitiveType.ANY) { 188 return ImportTypeIndex; 189 } 190 return PrimitiveType.ANY; 191 case ts.SyntaxKind.PropertyAccessExpression: 192 return this.getTypeForPropertyAccessExpression(typeDeclNode); 193 case ts.SyntaxKind.InterfaceDeclaration: 194 return this.getInterfaceDeclaration(typeDeclNode); 195 default: 196 return PrimitiveType.ANY; 197 } 198 } 199 200 public getTypeForLiteralTypeNode(node: ts.Node) { 201 switch (node.kind) { 202 case ts.SyntaxKind.NumericLiteral: 203 return PrimitiveType.NUMBER; 204 case ts.SyntaxKind.TrueKeyword: 205 case ts.SyntaxKind.FalseKeyword: 206 return PrimitiveType.BOOLEAN; 207 case ts.SyntaxKind.StringLiteral: 208 return PrimitiveType.STRING; 209 case ts.SyntaxKind.NullKeyword: 210 return PrimitiveType.NULL; 211 default: 212 return PrimitiveType.ANY; 213 } 214 } 215 216 public getTypeFromAnotation(typeNode: ts.TypeNode | undefined) { 217 if (!typeNode) { 218 return PrimitiveType.ANY; 219 } 220 switch (typeNode.kind) { 221 case ts.SyntaxKind.StringKeyword: 222 case ts.SyntaxKind.NumberKeyword: 223 case ts.SyntaxKind.BooleanKeyword: 224 case ts.SyntaxKind.SymbolKeyword: 225 case ts.SyntaxKind.UndefinedKeyword: 226 case ts.SyntaxKind.VoidKeyword: 227 let typeName = typeNode.getText().toUpperCase(); 228 let typeIndex = PrimitiveType.ANY; 229 if (typeName && typeName in PrimitiveType) { 230 typeIndex = PrimitiveType[typeName as keyof typeof PrimitiveType]; 231 } 232 return typeIndex; 233 case ts.SyntaxKind.LiteralType: 234 let literalType = (<ts.LiteralTypeNode>typeNode).literal; 235 return this.getTypeForLiteralTypeNode(literalType); 236 case ts.SyntaxKind.UnionType: 237 let unionType = new UnionType(typeNode); 238 return unionType.shiftedTypeIndex; 239 case ts.SyntaxKind.ArrayType: 240 let arrayType = new ArrayType(typeNode); 241 return arrayType.shiftedTypeIndex; 242 case ts.SyntaxKind.ParenthesizedType: 243 let subType = (<ts.ParenthesizedTypeNode>typeNode).type 244 if (subType.kind == ts.SyntaxKind.UnionType) { 245 let unionType = new UnionType(subType); 246 return unionType.shiftedTypeIndex; 247 } 248 return PrimitiveType.ANY; 249 case ts.SyntaxKind.TypeLiteral: 250 let objectType = new ObjectType(<ts.TypeLiteralNode>typeNode); 251 return objectType.shiftedTypeIndex; 252 case ts.SyntaxKind.TypeReference: 253 let typeIdentifier = (<ts.TypeReferenceNode>typeNode).typeName; 254 let typeIdentifierName = jshelpers.getTextOfIdentifierOrLiteral(typeIdentifier); 255 if (BuiltinType[typeIdentifierName]) { 256 let declNode = this.getDeclNodeForInitializer(typeIdentifier); 257 if (declNode && (ts.isClassLike(declNode) || declNode.kind == ts.SyntaxKind.InterfaceDeclaration)) { 258 return this.getBuiltinTypeIndex(<ts.TypeReferenceNode>typeNode, typeIdentifierName); 259 } else { 260 return BuiltinType[typeIdentifierName]; 261 } 262 } 263 default: 264 return PrimitiveType.ANY; 265 } 266 } 267 268 isBuiltinType(expr: ts.NewExpression) { 269 let name = expr.expression.getFullText().replace(/\s/g, ""); 270 return name in BuiltinType; 271 } 272 273 isFromDefaultLib(node: ts.Node) { 274 return node.getSourceFile().hasNoDefaultLib; 275 } 276 277 getOrCreateInstanceType(classTypeIdx: number) { 278 let typeRec = TypeRecorder.getInstance(); 279 if (typeRec.hasClass2InstanceMap(classTypeIdx)) { 280 return typeRec.getClass2InstanceMap(classTypeIdx); 281 } 282 let instanceType = new ClassInstType(classTypeIdx); 283 return instanceType.shiftedTypeIndex; 284 } 285 286 getOrCreateInstanceTypeForBuiltinContainer(builtinContainerSignature: object) { 287 let typeRec = TypeRecorder.getInstance(); 288 if (typeRec.hasBuiltinContainer2InstanceMap(builtinContainerSignature)) { 289 return typeRec.getBuiltinContainer2InstanceMap(builtinContainerSignature); 290 } 291 let builtinContainerType = new BuiltinContainerType(builtinContainerSignature); 292 let builtinContainerTypeIdx = builtinContainerType.shiftedTypeIndex; 293 return this.getOrCreateInstanceType(builtinContainerTypeIdx); 294 } 295 296 getBuiltinTypeIndex(node: ts.NewExpression | ts.TypeReferenceNode, name: string) { 297 let typeArguments = node.typeArguments; 298 if (typeArguments && this.needRecordBuiltinContainer) { 299 let typeArgIdxs = new Array<number>(); 300 for(let typeArg of typeArguments) { 301 let typeArgIdx = this.getOrCreateRecordForTypeNode(typeArg); 302 typeArgIdxs.push(typeArgIdx); 303 } 304 let builtinContainerSignature = { 305 "typeIndex": BuiltinType[name], 306 "typeArgIdxs": typeArgIdxs 307 } 308 return this.getOrCreateInstanceTypeForBuiltinContainer(builtinContainerSignature); 309 } 310 return this.getOrCreateInstanceType(BuiltinType[name]); 311 } 312 313 getBuiltinTypeIndexForExpr(expr: ts.NewExpression) { 314 let origExprNode = <ts.NewExpression>ts.getOriginalNode(expr); 315 let name = origExprNode.expression.getFullText().replace(/\s/g, ""); 316 return this.getBuiltinTypeIndex(origExprNode, name); 317 } 318 319 public getOrCreateRecordForDeclNode(initializer: ts.Node | undefined, variableNode?: ts.Node) { 320 if (!initializer) { 321 return PrimitiveType.ANY; 322 } 323 324 let typeIndex = PrimitiveType.ANY; 325 if (initializer.kind == ts.SyntaxKind.NewExpression && this.isBuiltinType(<ts.NewExpression>initializer)) { 326 typeIndex = this.getBuiltinTypeIndexForExpr(<ts.NewExpression>initializer); 327 } else { 328 let declNode = this.getDeclNodeForInitializer(initializer); 329 typeIndex = this.getTypeFromDecl(declNode, initializer.kind == ts.SyntaxKind.NewExpression); 330 } 331 332 if (variableNode) { 333 TypeRecorder.getInstance().setVariable2Type(variableNode, typeIndex); 334 } 335 return typeIndex; 336 } 337 338 public getOrCreateRecordForTypeNode(typeNode: ts.TypeNode | undefined, variableNode?: ts.Node) { 339 if (!typeNode) { 340 return PrimitiveType.ANY; 341 } 342 let typeIndex = PrimitiveType.ANY; 343 typeIndex = this.getTypeFromAnotation(typeNode); 344 if (typeIndex == PrimitiveType.ANY && typeNode.kind == ts.SyntaxKind.TypeReference) { 345 let typeName = typeNode.getChildAt(0); 346 let typeDecl = this.getDeclNodeForInitializer(typeName); 347 typeIndex = this.getTypeFromDecl(typeDecl, true); 348 } 349 if (variableNode) { 350 TypeRecorder.getInstance().setVariable2Type(variableNode, typeIndex); 351 } 352 return typeIndex; 353 } 354 355 public formatVariableStatement(variableStatementNode: ts.VariableStatement) { 356 let decList = variableStatementNode.declarationList; 357 decList.declarations.forEach(declaration => { 358 let variableNode = declaration.name; 359 let typeNode = declaration.type; 360 let initializer = declaration.initializer; 361 let typeIndex = this.getOrCreateRecordForTypeNode(typeNode, variableNode); 362 if (typeIndex == PrimitiveType.ANY) { 363 typeIndex = this.getOrCreateRecordForDeclNode(initializer, variableNode); 364 } 365 if (this.hasExportKeyword(variableStatementNode) && typeIndex != PrimitiveType.ANY) { 366 let exportedName = jshelpers.getTextOfIdentifierOrLiteral(variableNode); 367 TypeRecorder.getInstance().setExportedType(exportedName, typeIndex); 368 } 369 }); 370 } 371 372 public formatClassDeclaration(classDeclNode: ts.ClassDeclaration) { 373 if (this.isFromDefaultLib(classDeclNode)) { 374 return; 375 } 376 let classNameNode = classDeclNode.name; 377 let className = "default"; 378 if (classNameNode) { 379 className = jshelpers.getTextOfIdentifierOrLiteral(classNameNode).replace(/\s/g, ""); 380 } 381 382 let typeIndex = TypeRecorder.getInstance().tryGetTypeIndex(classDeclNode); 383 if (typeIndex == PrimitiveType.ANY) { 384 let classType = new ClassType(classDeclNode, BuiltinType[className]); 385 typeIndex = classType.shiftedTypeIndex; 386 } 387 388 if (this.hasExportKeyword(classDeclNode)) { 389 TypeRecorder.getInstance().setExportedType(className, typeIndex); 390 } else if (this.hasDeclareKeyword(classDeclNode) && isGlobalDeclare()) { 391 TypeRecorder.getInstance().setDeclaredType(className, typeIndex); 392 } 393 } 394 395 // Entry for type recording 396 public formatNodeType(node: ts.Node, importOrExportStmt?: ModuleStmt) { 397 if (this.compiledTypeChecker === null) { 398 return; 399 } 400 switch (node.kind) { 401 case ts.SyntaxKind.VariableStatement: 402 let variableStatementNode = <ts.VariableStatement>ts.getOriginalNode(node); 403 if (variableStatementNode.kind == ts.SyntaxKind.VariableStatement) { 404 this.formatVariableStatement(variableStatementNode); 405 } 406 break; 407 case ts.SyntaxKind.FunctionDeclaration: 408 let functionDeclNode = <ts.FunctionDeclaration>ts.getOriginalNode(node); 409 if (this.isFromDefaultLib(functionDeclNode)) { 410 break; 411 } 412 let functionName = functionDeclNode.name ? functionDeclNode.name : undefined; 413 let funcTypeIndex = TypeRecorder.getInstance().tryGetTypeIndex(functionDeclNode); 414 if (funcTypeIndex == PrimitiveType.ANY) { 415 let functionnameText = ""; 416 if (functionName) { 417 functionnameText = jshelpers.getTextOfIdentifierOrLiteral(functionName); 418 } 419 let funcType = new FunctionType(functionDeclNode, BuiltinType[functionnameText]); 420 funcTypeIndex = funcType.shiftedTypeIndex; 421 } 422 if (functionName) { 423 TypeRecorder.getInstance().setVariable2Type(functionName, funcTypeIndex); 424 } 425 break; 426 case ts.SyntaxKind.ClassDeclaration: 427 // Create the type if it is exported or declared. If not, also create type in case there's 428 // static property access of this class. 429 let classDeclNode = <ts.ClassDeclaration>ts.getOriginalNode(node); 430 this.formatClassDeclaration(classDeclNode); 431 break; 432 case ts.SyntaxKind.InterfaceDeclaration: 433 if (isGlobalDeclare()) { 434 let interfaceDeclNode = <ts.InterfaceDeclaration>ts.getOriginalNode(node); 435 if (this.isFromDefaultLib(interfaceDeclNode)) { 436 break; 437 } 438 let interfaceType = new InterfaceType(interfaceDeclNode); 439 let interfaceName = interfaceDeclNode.name; 440 if (interfaceName) { 441 let name = jshelpers.getTextOfIdentifierOrLiteral(interfaceName); 442 TypeRecorder.getInstance().setDeclaredType(name, interfaceType.shiftedTypeIndex); 443 } 444 } 445 break; 446 case ts.SyntaxKind.ExportDeclaration: 447 if (importOrExportStmt) { 448 TypeRecorder.getInstance().addExportedType(importOrExportStmt); 449 } 450 break; 451 case ts.SyntaxKind.ImportDeclaration: 452 if (importOrExportStmt) { 453 TypeRecorder.getInstance().addImportedType(importOrExportStmt); 454 } 455 break; 456 case ts.SyntaxKind.ExportAssignment: 457 let expression = (<ts.ExportAssignment>node).expression; 458 let exportedName = "default"; 459 let expressionType = this.getTypeAtLocation(expression); 460 if (expressionType) { 461 let typeNode = expressionType.getSymbol()?.valueDeclaration; 462 TypeRecorder.getInstance().addNonReExportedType(exportedName, typeNode, expression); 463 } 464 break; 465 default: 466 break; 467 } 468 } 469 470 471} 472