1/* 2 * Copyright (c) 2024-2025 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 ts, { HeritageClause, ParameterDeclaration, TypeNode, TypeParameterDeclaration } from 'ohos-typescript'; 17import { 18 AliasType, 19 ArrayType, 20 ClassType, 21 FunctionType, 22 GenericType, 23 IntersectionType, 24 TupleType, 25 Type, 26 UnclearReferenceType, 27 UnionType, 28 UnknownType, 29} from '../../base/Type'; 30import { TypeInference } from '../../common/TypeInference'; 31import { ArkField } from '../ArkField'; 32import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger'; 33import { ArkClass } from '../ArkClass'; 34import { ArkMethod } from '../ArkMethod'; 35import { Decorator } from '../../base/Decorator'; 36import { ArrayBindingPatternParameter, buildArkMethodFromArkClass, MethodParameter, ObjectBindingPatternParameter } from './ArkMethodBuilder'; 37import { buildNormalArkClassFromArkMethod } from './ArkClassBuilder'; 38import { Builtin } from '../../common/Builtin'; 39import { modifierKind2Enum } from '../ArkBaseModel'; 40import { ArkValueTransformer } from '../../common/ArkValueTransformer'; 41import { KeyofTypeExpr, TypeQueryExpr } from '../../base/TypeExpr'; 42import { 43 ANY_KEYWORD, 44 BIGINT_KEYWORD, 45 BOOLEAN_KEYWORD, 46 NEVER_KEYWORD, 47 NULL_KEYWORD, 48 NUMBER_KEYWORD, 49 STRING_KEYWORD, 50 THIS_NAME, 51 UNDEFINED_KEYWORD, 52 VOID_KEYWORD, 53} from '../../common/TSConst'; 54import { ArkSignatureBuilder } from './ArkSignatureBuilder'; 55import { ArkInstanceFieldRef } from '../../base/Ref'; 56import { Local } from '../../base/Local'; 57import { Value } from '../../base/Value'; 58 59const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'builderUtils'); 60 61export function handleQualifiedName(node: ts.QualifiedName): string { 62 let right = (node.right as ts.Identifier).text; 63 let left: string = ''; 64 if (node.left.kind === ts.SyntaxKind.Identifier) { 65 left = (node.left as ts.Identifier).text; 66 } else if (node.left.kind === ts.SyntaxKind.QualifiedName) { 67 left = handleQualifiedName(node.left as ts.QualifiedName); 68 } 69 let qualifiedName = left + '.' + right; 70 return qualifiedName; 71} 72 73export function handlePropertyAccessExpression(node: ts.PropertyAccessExpression): string { 74 let right = (node.name as ts.Identifier).text; 75 let left: string = ''; 76 if (ts.SyntaxKind[node.expression.kind] === 'Identifier') { 77 left = (node.expression as ts.Identifier).text; 78 } else if (ts.isStringLiteral(node.expression)) { 79 left = node.expression.text; 80 } else if (ts.isPropertyAccessExpression(node.expression)) { 81 left = handlePropertyAccessExpression(node.expression as ts.PropertyAccessExpression); 82 } 83 let propertyAccessExpressionName = left + '.' + right; 84 return propertyAccessExpressionName; 85} 86 87export function buildDecorators(node: ts.Node, sourceFile: ts.SourceFile): Set<Decorator> { 88 let decorators: Set<Decorator> = new Set(); 89 ts.getAllDecorators(node).forEach(decoratorNode => { 90 let decorator = parseDecorator(decoratorNode); 91 if (decorator) { 92 decorator.setContent(decoratorNode.expression.getText(sourceFile)); 93 decorators.add(decorator); 94 } 95 }); 96 return decorators; 97} 98 99function parseDecorator(node: ts.Decorator): Decorator | undefined { 100 if (!node.expression) { 101 return undefined; 102 } 103 104 let expression = node.expression; 105 if (ts.isIdentifier(expression)) { 106 return new Decorator(expression.text); 107 } 108 if (!ts.isCallExpression(expression) || !ts.isIdentifier(expression.expression)) { 109 return undefined; 110 } 111 112 let decorator = new Decorator(expression.expression.text); 113 114 if (expression.arguments.length > 0) { 115 const arg = expression.arguments[0]; 116 if (ts.isArrowFunction(arg) && ts.isIdentifier(arg.body)) { 117 decorator.setParam(arg.body.text); 118 } 119 } 120 121 return decorator; 122} 123 124export function buildModifiers(node: ts.Node): number { 125 let modifiers: number = 0; 126 127 if (ts.canHaveModifiers(node)) { 128 ts.getModifiers(node)?.forEach(modifier => { 129 modifiers |= modifierKind2Enum(modifier.kind); 130 }); 131 } 132 133 return modifiers; 134} 135 136export function buildHeritageClauses(heritageClauses?: ts.NodeArray<HeritageClause>): Map<string, string> { 137 let heritageClausesMap: Map<string, string> = new Map<string, string>(); 138 heritageClauses?.forEach(heritageClause => { 139 heritageClause.types.forEach(type => { 140 let heritageClauseName: string = ''; 141 if (type.typeArguments) { 142 heritageClauseName = type.getText(); 143 } else if (ts.isIdentifier(type.expression)) { 144 heritageClauseName = (type.expression as ts.Identifier).text; 145 } else if (ts.isPropertyAccessExpression(type.expression)) { 146 heritageClauseName = handlePropertyAccessExpression(type.expression); 147 } else { 148 heritageClauseName = type.getText(); 149 } 150 heritageClausesMap.set(heritageClauseName, ts.SyntaxKind[heritageClause.token]); 151 }); 152 }); 153 return heritageClausesMap; 154} 155 156export function buildTypeParameters( 157 typeParameters: ts.NodeArray<TypeParameterDeclaration>, 158 sourceFile: ts.SourceFile, 159 arkInstance: ArkMethod | ArkClass 160): GenericType[] { 161 const genericTypes: GenericType[] = []; 162 let index = 0; 163 if (arkInstance instanceof ArkMethod) { 164 const len = arkInstance.getDeclaringArkClass().getGenericsTypes()?.length; 165 if (len) { 166 index = len; 167 } 168 } 169 typeParameters.forEach(typeParameter => { 170 const genericType = tsNode2Type(typeParameter, sourceFile, arkInstance); 171 if (genericType instanceof GenericType) { 172 genericType.setIndex(index++); 173 genericTypes.push(genericType); 174 } 175 176 if (typeParameter.modifiers) { 177 logger.warn('This typeparameter has modifiers.'); 178 } 179 180 if (typeParameter.expression) { 181 logger.warn('This typeparameter has expression.'); 182 } 183 }); 184 return genericTypes; 185} 186 187function buildObjectBindingPatternParam(methodParameter: MethodParameter, paramNameNode: ts.ObjectBindingPattern): void { 188 methodParameter.setName('ObjectBindingPattern'); 189 let elements: ObjectBindingPatternParameter[] = []; 190 paramNameNode.elements.forEach(element => { 191 let paraElement = new ObjectBindingPatternParameter(); 192 if (element.propertyName) { 193 if (ts.isIdentifier(element.propertyName)) { 194 paraElement.setPropertyName(element.propertyName.text); 195 } else { 196 logger.warn('New propertyName of ObjectBindingPattern found, please contact developers to support this!'); 197 } 198 } 199 200 if (element.name) { 201 if (ts.isIdentifier(element.name)) { 202 paraElement.setName(element.name.text); 203 } else { 204 logger.warn('New name of ObjectBindingPattern found, please contact developers to support this!'); 205 } 206 } 207 208 if (element.initializer) { 209 logger.warn('TODO: support ObjectBindingPattern initializer.'); 210 } 211 212 if (element.dotDotDotToken) { 213 paraElement.setOptional(true); 214 } 215 elements.push(paraElement); 216 }); 217 methodParameter.setObjElements(elements); 218} 219 220function buildBindingElementOfBindingPatternParam(element: ts.BindingElement, paraElement: ArrayBindingPatternParameter): void { 221 if (element.propertyName) { 222 if (ts.isIdentifier(element.propertyName)) { 223 paraElement.setPropertyName(element.propertyName.text); 224 } else { 225 logger.warn('New propertyName of ArrayBindingPattern found, please contact developers to support this!'); 226 } 227 } 228 229 if (element.name) { 230 if (ts.isIdentifier(element.name)) { 231 paraElement.setName(element.name.text); 232 } else { 233 logger.warn('New name of ArrayBindingPattern found, please contact developers to support this!'); 234 } 235 } 236 237 if (element.initializer) { 238 logger.warn('TODO: support ArrayBindingPattern initializer.'); 239 } 240 241 if (element.dotDotDotToken) { 242 paraElement.setOptional(true); 243 } 244} 245 246function buildArrayBindingPatternParam(methodParameter: MethodParameter, paramNameNode: ts.ArrayBindingPattern): void { 247 methodParameter.setName('ArrayBindingPattern'); 248 let elements: ArrayBindingPatternParameter[] = []; 249 paramNameNode.elements.forEach(element => { 250 let paraElement = new ArrayBindingPatternParameter(); 251 if (ts.isBindingElement(element)) { 252 buildBindingElementOfBindingPatternParam(element, paraElement); 253 } else if (ts.isOmittedExpression(element)) { 254 logger.warn('TODO: support OmittedExpression for ArrayBindingPattern parameter name.'); 255 } 256 elements.push(paraElement); 257 }); 258 methodParameter.setArrayElements(elements); 259} 260 261export function buildParameters(params: ts.NodeArray<ParameterDeclaration>, arkInstance: ArkMethod | ArkField, sourceFile: ts.SourceFile): MethodParameter[] { 262 let parameters: MethodParameter[] = []; 263 params.forEach(parameter => { 264 let methodParameter = new MethodParameter(); 265 266 // name 267 if (ts.isIdentifier(parameter.name)) { 268 methodParameter.setName(parameter.name.text); 269 } else if (ts.isObjectBindingPattern(parameter.name)) { 270 buildObjectBindingPatternParam(methodParameter, parameter.name); 271 } else if (ts.isArrayBindingPattern(parameter.name)) { 272 buildArrayBindingPatternParam(methodParameter, parameter.name); 273 } else { 274 logger.warn('Parameter name is not identifier, ObjectBindingPattern nor ArrayBindingPattern, please contact developers to support this!'); 275 } 276 277 // questionToken 278 if (parameter.questionToken) { 279 methodParameter.setOptional(true); 280 } 281 282 // type 283 if (parameter.type) { 284 methodParameter.setType(buildGenericType(tsNode2Type(parameter.type, sourceFile, arkInstance), arkInstance)); 285 } else { 286 methodParameter.setType(UnknownType.getInstance()); 287 } 288 289 // initializer 290 if (parameter.initializer) { 291 //TODO? 292 } 293 294 // dotDotDotToken 295 if (parameter.dotDotDotToken) { 296 methodParameter.setDotDotDotToken(true); 297 } 298 299 // modifiers 300 if (parameter.modifiers) { 301 // 302 } 303 304 parameters.push(methodParameter); 305 }); 306 return parameters; 307} 308 309export function buildGenericType(type: Type, arkInstance: ArkMethod | ArkField | AliasType): Type { 310 function replace(urType: UnclearReferenceType): Type { 311 const typeName = urType.getName(); 312 let gType; 313 if (arkInstance instanceof AliasType) { 314 gType = arkInstance.getGenericTypes()?.find(f => f.getName() === typeName); 315 } else { 316 if (arkInstance instanceof ArkMethod) { 317 gType = arkInstance.getGenericTypes()?.find(f => f.getName() === typeName); 318 } 319 if (!gType) { 320 gType = arkInstance 321 .getDeclaringArkClass() 322 .getGenericsTypes() 323 ?.find(f => f.getName() === typeName); 324 } 325 } 326 if (gType) { 327 return gType; 328 } 329 const types = urType.getGenericTypes(); 330 for (let i = 0; i < types.length; i++) { 331 const mayType = types[i]; 332 if (mayType instanceof UnclearReferenceType) { 333 types[i] = replace(mayType); 334 } 335 } 336 return urType; 337 } 338 339 if (type instanceof UnclearReferenceType) { 340 return replace(type); 341 } else if (type instanceof ClassType && arkInstance instanceof AliasType) { 342 type.setRealGenericTypes(arkInstance.getGenericTypes()); 343 } else if (type instanceof UnionType || type instanceof TupleType) { 344 const types = type.getTypes(); 345 for (let i = 0; i < types.length; i++) { 346 const mayType = types[i]; 347 if (mayType instanceof UnclearReferenceType) { 348 types[i] = replace(mayType); 349 } 350 } 351 } else if (type instanceof ArrayType) { 352 const baseType = type.getBaseType(); 353 if (baseType instanceof UnclearReferenceType) { 354 type.setBaseType(replace(baseType)); 355 } 356 } else if (type instanceof FunctionType) { 357 const returnType = type.getMethodSignature().getType(); 358 if (returnType instanceof UnclearReferenceType) { 359 type.getMethodSignature().getMethodSubSignature().setReturnType(replace(returnType)); 360 } 361 } 362 return type; 363} 364 365export function buildReturnType(node: TypeNode, sourceFile: ts.SourceFile, method: ArkMethod): Type { 366 if (node) { 367 return tsNode2Type(node, sourceFile, method); 368 } else { 369 return UnknownType.getInstance(); 370 } 371} 372 373export function tsNode2Type( 374 typeNode: ts.TypeNode | ts.TypeParameterDeclaration, 375 sourceFile: ts.SourceFile, 376 arkInstance: ArkMethod | ArkClass | ArkField 377): Type { 378 if (ts.isTypeReferenceNode(typeNode)) { 379 const genericTypes: Type[] = []; 380 if (typeNode.typeArguments) { 381 for (const typeArgument of typeNode.typeArguments) { 382 genericTypes.push(tsNode2Type(typeArgument, sourceFile, arkInstance)); 383 } 384 } 385 let referenceNodeName = typeNode.typeName; 386 if (ts.isQualifiedName(referenceNodeName)) { 387 let parameterTypeStr = handleQualifiedName(referenceNodeName as ts.QualifiedName); 388 return new UnclearReferenceType(parameterTypeStr, genericTypes); 389 } else { 390 let parameterTypeStr = referenceNodeName.text; 391 if (parameterTypeStr === Builtin.OBJECT) { 392 return Builtin.OBJECT_CLASS_TYPE; 393 } 394 return new UnclearReferenceType(parameterTypeStr, genericTypes); 395 } 396 } else if (ts.isUnionTypeNode(typeNode) || ts.isIntersectionTypeNode(typeNode)) { 397 let multipleTypePara: Type[] = []; 398 typeNode.types.forEach(tmpType => { 399 multipleTypePara.push(tsNode2Type(tmpType, sourceFile, arkInstance)); 400 }); 401 if (ts.isUnionTypeNode(typeNode)) { 402 return new UnionType(multipleTypePara); 403 } else { 404 return new IntersectionType(multipleTypePara); 405 } 406 } else if (ts.isLiteralTypeNode(typeNode)) { 407 return ArkValueTransformer.resolveLiteralTypeNode(typeNode, sourceFile); 408 } else if (ts.isTypeLiteralNode(typeNode)) { 409 let cls: ArkClass = new ArkClass(); 410 let declaringClass: ArkClass; 411 412 if (arkInstance instanceof ArkMethod) { 413 declaringClass = arkInstance.getDeclaringArkClass(); 414 } else if (arkInstance instanceof ArkField) { 415 declaringClass = arkInstance.getDeclaringArkClass(); 416 } else { 417 declaringClass = arkInstance; 418 } 419 if (declaringClass.getDeclaringArkNamespace()) { 420 cls.setDeclaringArkNamespace(declaringClass.getDeclaringArkNamespace()); 421 cls.setDeclaringArkFile(declaringClass.getDeclaringArkFile()); 422 } else { 423 cls.setDeclaringArkFile(declaringClass.getDeclaringArkFile()); 424 } 425 buildNormalArkClassFromArkMethod(typeNode, cls, sourceFile); 426 427 return new ClassType(cls.getSignature()); 428 } else if (ts.isFunctionTypeNode(typeNode)) { 429 let mtd: ArkMethod = new ArkMethod(); 430 let cls: ArkClass; 431 if (arkInstance instanceof ArkMethod) { 432 cls = arkInstance.getDeclaringArkClass(); 433 } else if (arkInstance instanceof ArkClass) { 434 cls = arkInstance; 435 } else { 436 cls = arkInstance.getDeclaringArkClass(); 437 } 438 buildArkMethodFromArkClass(typeNode, cls, mtd, sourceFile); 439 return new FunctionType(mtd.getSignature()); 440 } else if (ts.isTypeParameterDeclaration(typeNode)) { 441 const name = typeNode.name.text; 442 let defaultType; 443 if (typeNode.default) { 444 defaultType = tsNode2Type(typeNode.default, sourceFile, arkInstance); 445 } 446 let constraint; 447 if (typeNode.constraint) { 448 constraint = tsNode2Type(typeNode.constraint, sourceFile, arkInstance); 449 } 450 return new GenericType(name, defaultType, constraint); 451 } else if (ts.isTupleTypeNode(typeNode)) { 452 const types: Type[] = []; 453 typeNode.elements.forEach(element => { 454 types.push(tsNode2Type(element, sourceFile, arkInstance)); 455 }); 456 return new TupleType(types); 457 } else if (ts.isArrayTypeNode(typeNode)) { 458 return new ArrayType(tsNode2Type((typeNode as ts.ArrayTypeNode).elementType, sourceFile, arkInstance), 1); 459 } else if (ts.isParenthesizedTypeNode(typeNode)) { 460 return tsNode2Type(typeNode.type, sourceFile, arkInstance); 461 } else if (ts.isTypeOperatorNode(typeNode)) { 462 return buildTypeFromTypeOperator(typeNode as ts.TypeOperatorNode, sourceFile, arkInstance); 463 } else if (ts.isTypeQueryNode(typeNode)) { 464 return buildTypeFromTypeQuery(typeNode as ts.TypeQueryNode, sourceFile, arkInstance); 465 } else if (typeNode.kind === ts.SyntaxKind.ObjectKeyword) { 466 // TODO: type object which is different from Object is needed to support, such as let a: object = {} 467 return new UnclearReferenceType('object'); 468 } else { 469 return buildTypeFromPreStr(ts.SyntaxKind[typeNode.kind]); 470 } 471} 472 473export function buildTypeFromPreStr(preStr: string): Type { 474 let postStr = ''; 475 switch (preStr) { 476 case 'BooleanKeyword': 477 postStr = BOOLEAN_KEYWORD; 478 break; 479 case 'FalseKeyword': 480 postStr = BOOLEAN_KEYWORD; 481 break; 482 case 'TrueKeyword': 483 postStr = BOOLEAN_KEYWORD; 484 break; 485 case 'NumberKeyword': 486 postStr = NUMBER_KEYWORD; 487 break; 488 case 'NumericLiteral': 489 postStr = NUMBER_KEYWORD; 490 break; 491 case 'FirstLiteralToken': 492 postStr = NUMBER_KEYWORD; 493 break; 494 case 'StringKeyword': 495 postStr = STRING_KEYWORD; 496 break; 497 case 'StringLiteral': 498 postStr = STRING_KEYWORD; 499 break; 500 case 'UndefinedKeyword': 501 postStr = UNDEFINED_KEYWORD; 502 break; 503 case 'NullKeyword': 504 postStr = NULL_KEYWORD; 505 break; 506 case 'AnyKeyword': 507 postStr = ANY_KEYWORD; 508 break; 509 case 'VoidKeyword': 510 postStr = VOID_KEYWORD; 511 break; 512 case 'NeverKeyword': 513 postStr = NEVER_KEYWORD; 514 break; 515 case 'BigIntKeyword': 516 postStr = BIGINT_KEYWORD; 517 break; 518 default: 519 postStr = preStr; 520 } 521 return TypeInference.buildTypeFromStr(postStr); 522} 523 524function buildTypeFromTypeOperator(typeOperatorNode: ts.TypeOperatorNode, sourceFile: ts.SourceFile, arkInstance: ArkMethod | ArkClass | ArkField): Type { 525 const typeNode = typeOperatorNode.type; 526 let type = tsNode2Type(typeNode, sourceFile, arkInstance); 527 528 switch (typeOperatorNode.operator) { 529 case ts.SyntaxKind.ReadonlyKeyword: { 530 if (type instanceof ArrayType || type instanceof TupleType) { 531 type.setReadonlyFlag(true); 532 } 533 return type; 534 } 535 case ts.SyntaxKind.KeyOfKeyword: 536 return new KeyofTypeExpr(type); 537 case ts.SyntaxKind.UniqueKeyword: 538 return UnknownType.getInstance(); 539 default: 540 return UnknownType.getInstance(); 541 } 542} 543 544function buildTypeFromTypeQuery(typeQueryNode: ts.TypeQueryNode, sourceFile: ts.SourceFile, arkInstance: ArkMethod | ArkClass | ArkField): Type { 545 const exprNameNode = typeQueryNode.exprName; 546 let opValue: Value; 547 if (ts.isQualifiedName(exprNameNode)) { 548 if (exprNameNode.left.getText(sourceFile) === THIS_NAME) { 549 const fieldName = exprNameNode.right.getText(sourceFile); 550 if (arkInstance instanceof ArkMethod) { 551 const fieldSignature = 552 arkInstance.getDeclaringArkClass().getFieldWithName(fieldName)?.getSignature() ?? 553 ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName); 554 const baseLocal = 555 arkInstance.getBody()?.getLocals().get(THIS_NAME) ?? 556 new Local( 557 THIS_NAME, 558 new ClassType(arkInstance.getDeclaringArkClass().getSignature(), arkInstance.getDeclaringArkClass().getGenericsTypes()) 559 ); 560 opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature); 561 } else if (arkInstance instanceof ArkClass) { 562 const fieldSignature = 563 arkInstance.getFieldWithName(fieldName)?.getSignature() ?? ArkSignatureBuilder.buildFieldSignatureFromFieldName(fieldName); 564 const baseLocal = new Local(THIS_NAME, new ClassType(arkInstance.getSignature(), arkInstance.getGenericsTypes())); 565 opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature); 566 } else { 567 const fieldSignature = arkInstance.getSignature(); 568 const baseLocal = new Local( 569 THIS_NAME, 570 new ClassType(arkInstance.getDeclaringArkClass().getSignature(), arkInstance.getDeclaringArkClass().getGenericsTypes()) 571 ); 572 opValue = new ArkInstanceFieldRef(baseLocal, fieldSignature); 573 } 574 } else { 575 const exprName = exprNameNode.getText(sourceFile); 576 opValue = new Local(exprName, UnknownType.getInstance()); 577 } 578 } else { 579 const exprName = exprNameNode.escapedText.toString(); 580 opValue = new Local(exprName, UnknownType.getInstance()); 581 } 582 583 let expr = new TypeQueryExpr(opValue); 584 if (typeQueryNode.typeArguments) { 585 for (const typeArgument of typeQueryNode.typeArguments) { 586 expr.addGenericType(tsNode2Type(typeArgument, sourceFile, arkInstance)); 587 } 588 } 589 return expr; 590} 591