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 Logger, { LOG_MODULE_TYPE } from '../../utils/logger'; 17import { AbstractExpr, ArkInstanceInvokeExpr, ArkPtrInvokeExpr, ArkStaticInvokeExpr } from '../base/Expr'; 18import { Local } from '../base/Local'; 19import { 20 AbstractFieldRef, 21 AbstractRef, 22 ArkArrayRef, 23 ArkInstanceFieldRef, 24 ArkParameterRef, 25 ArkStaticFieldRef, GlobalRef 26} from '../base/Ref'; 27import { ArkAliasTypeDefineStmt, ArkAssignStmt, ArkReturnStmt, Stmt } from '../base/Stmt'; 28import { 29 AliasType, 30 AnnotationNamespaceType, 31 AnyType, 32 ArrayType, 33 BigIntType, 34 BooleanType, 35 ClassType, 36 EnumValueType, 37 FunctionType, 38 GenericType, 39 IntersectionType, 40 NeverType, 41 NullType, 42 NumberType, 43 StringType, 44 TupleType, 45 Type, 46 UnclearReferenceType, 47 UndefinedType, 48 UnionType, 49 UnknownType, 50 VoidType, 51} from '../base/Type'; 52import { ArkMethod } from '../model/ArkMethod'; 53import { ArkExport } from '../model/ArkExport'; 54import { ArkClass, ClassCategory } from '../model/ArkClass'; 55import { ArkField } from '../model/ArkField'; 56import { Value } from '../base/Value'; 57import { Constant } from '../base/Constant'; 58import { ArkNamespace } from '../model/ArkNamespace'; 59import { 60 ALL, 61 ANY_KEYWORD, 62 BIGINT_KEYWORD, 63 BOOLEAN_KEYWORD, 64 CONSTRUCTOR_NAME, DEFAULT, 65 GLOBAL_THIS_NAME, 66 NEVER_KEYWORD, 67 NULL_KEYWORD, 68 NUMBER_KEYWORD, 69 PROMISE, 70 STRING_KEYWORD, 71 SUPER_NAME, 72 THIS_NAME, 73 UNDEFINED_KEYWORD, 74 VOID_KEYWORD, 75} from './TSConst'; 76import { ModelUtils } from './ModelUtils'; 77import { Builtin } from './Builtin'; 78import { MethodSignature, MethodSubSignature, NamespaceSignature } from '../model/ArkSignature'; 79import { INSTANCE_INIT_METHOD_NAME, LEXICAL_ENV_NAME_PREFIX, UNKNOWN_FILE_NAME } from './Const'; 80import { EMPTY_STRING } from './ValueUtil'; 81import { ImportInfo } from '../model/ArkImport'; 82import { MethodParameter } from '../model/builder/ArkMethodBuilder'; 83import { IRInference } from './IRInference'; 84import { AbstractTypeExpr, KeyofTypeExpr, TypeQueryExpr } from '../base/TypeExpr'; 85import { SdkUtils } from './SdkUtils'; 86import { ModifierType } from '../model/ArkBaseModel'; 87 88const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'TypeInference'); 89 90export class TypeInference { 91 public static inferTypeInArkField(arkField: ArkField): void { 92 const arkClass = arkField.getDeclaringArkClass(); 93 const stmts = arkField.getInitializer(); 94 const method = arkClass.getMethodWithName(INSTANCE_INIT_METHOD_NAME) ?? arkClass.getMethodWithName(CONSTRUCTOR_NAME); 95 for (const stmt of stmts) { 96 if (method) { 97 this.resolveStmt(stmt, method); 98 } 99 } 100 const beforeType = arkField.getType(); 101 if (!this.isUnclearType(beforeType)) { 102 return; 103 } 104 let rightType: Type | undefined; 105 let fieldRef: ArkInstanceFieldRef | undefined; 106 const lastStmt = stmts[stmts.length - 1]; 107 if (lastStmt instanceof ArkAssignStmt) { 108 rightType = lastStmt.getRightOp().getType(); 109 if (lastStmt.getLeftOp() instanceof ArkInstanceFieldRef) { 110 fieldRef = lastStmt.getLeftOp() as ArkInstanceFieldRef; 111 } 112 } 113 let fieldType; 114 if (beforeType) { 115 fieldType = this.inferUnclearedType(beforeType, arkClass); 116 } 117 if (fieldType) { 118 arkField.getSignature().setType(fieldType); 119 fieldRef?.setFieldSignature(arkField.getSignature()); 120 } else if (rightType && this.isUnclearType(beforeType) && !this.isUnclearType(rightType)) { 121 arkField.getSignature().setType(rightType); 122 fieldRef?.setFieldSignature(arkField.getSignature()); 123 } 124 } 125 126 /** 127 * Infer type for a given unclear type. 128 * It returns an array with 2 items, original object and original type. 129 * The original object is null if there is no object, or it failed to find the object. 130 * The original type is null if failed to infer the type. 131 * @param leftOpType 132 * @param declaringArkClass 133 * @param visited 134 * @returns 135 */ 136 public static inferUnclearedType(leftOpType: Type, declaringArkClass: ArkClass, visited: Set<Type> = new Set()): Type | null | undefined { 137 if (visited.has(leftOpType)) { 138 return leftOpType; 139 } else { 140 visited.add(leftOpType); 141 } 142 let type; 143 if (leftOpType instanceof ClassType && leftOpType.getClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME) { 144 type = TypeInference.inferUnclearRefName(leftOpType.getClassSignature().getClassName(), declaringArkClass); 145 } else if (leftOpType instanceof UnionType || leftOpType instanceof IntersectionType || leftOpType instanceof TupleType) { 146 let types = leftOpType.getTypes(); 147 for (let i = 0; i < types.length; i++) { 148 let newType = this.inferUnclearedType(types[i], declaringArkClass, visited); 149 if (newType) { 150 types[i] = newType; 151 } 152 } 153 type = leftOpType; 154 } else if (leftOpType instanceof ArrayType) { 155 let baseType = this.inferUnclearedType(leftOpType.getBaseType(), declaringArkClass, visited); 156 if (baseType) { 157 leftOpType.setBaseType(baseType); 158 type = leftOpType; 159 } 160 } else if (leftOpType instanceof AliasType) { 161 let baseType = this.inferUnclearedType(leftOpType.getOriginalType(), declaringArkClass, visited); 162 if (baseType) { 163 leftOpType.setOriginalType(baseType); 164 type = leftOpType; 165 } 166 } else if (leftOpType instanceof AnnotationNamespaceType) { 167 type = this.inferBaseType(leftOpType.getOriginType(), declaringArkClass); 168 } else if (leftOpType instanceof UnclearReferenceType) { 169 type = this.inferUnclearRefType(leftOpType, declaringArkClass); 170 } 171 return type; 172 } 173 174 public static inferTypeInMethod(arkMethod: ArkMethod): void { 175 const arkClass = arkMethod.getDeclaringArkClass(); 176 this.inferGenericType(arkMethod.getGenericTypes(), arkClass); 177 const signatures: MethodSignature[] = []; 178 arkMethod.getDeclareSignatures()?.forEach(m => signatures.push(m)); 179 const impl = arkMethod.getImplementationSignature(); 180 if (impl) { 181 signatures.push(impl); 182 } 183 signatures.forEach(s => { 184 s.getMethodSubSignature() 185 .getParameters() 186 .forEach(p => { 187 this.inferParameterType(p, arkMethod); 188 }); 189 }); 190 const body = arkMethod.getBody(); 191 if (!body) { 192 signatures.forEach(s => this.inferSignatureReturnType(s, arkMethod)); 193 return; 194 } 195 body.getUsedGlobals()?.forEach((value, key) => { 196 if (value instanceof GlobalRef && !value.getRef()) { 197 const arkExport = ModelUtils.findGlobalRef(key, arkMethod); 198 if (arkExport instanceof Local) { 199 arkExport.getUsedStmts().push(...value.getUsedStmts()); 200 value.setRef(arkExport); 201 } 202 } 203 }); 204 const cfg = body.getCfg(); 205 for (const block of cfg.getBlocks()) { 206 for (const stmt of block.getStmts()) { 207 this.resolveStmt(stmt, arkMethod); 208 } 209 } 210 signatures.forEach(s => this.inferSignatureReturnType(s, arkMethod)); 211 } 212 213 private static resolveStmt(stmt: Stmt, arkMethod: ArkMethod): void { 214 try { 215 this.resolveTypeExprsInStmt(stmt, arkMethod); 216 this.resolveExprsInStmt(stmt, arkMethod); 217 this.resolveFieldRefsInStmt(stmt, arkMethod); 218 this.resolveArkAssignStmt(stmt, arkMethod); 219 this.resolveArkReturnStmt(stmt, arkMethod); 220 } catch (e) { 221 logger.warn('stmt is not correct: ' + stmt.toString()); 222 } 223 } 224 225 /** 226 * @Deprecated 227 * @param arkMethod 228 */ 229 public static inferSimpleTypeInMethod(arkMethod: ArkMethod): void { 230 const body = arkMethod.getBody(); 231 if (!body) { 232 logger.warn('empty body'); 233 return; 234 } 235 const cfg = body.getCfg(); 236 for (const block of cfg.getBlocks()) { 237 for (const stmt of block.getStmts()) { 238 TypeInference.inferSimpleTypeInStmt(stmt); 239 } 240 } 241 } 242 243 /** 244 * infer type for Exprs in stmt which invoke method. 245 * such as ArkInstanceInvokeExpr ArkStaticInvokeExpr ArkNewExpr 246 */ 247 private static resolveExprsInStmt(stmt: Stmt, arkMethod: ArkMethod): void { 248 for (const expr of stmt.getExprs()) { 249 const newExpr = expr.inferType(arkMethod); 250 if ( 251 stmt.containsInvokeExpr() && 252 ((expr instanceof ArkInstanceInvokeExpr && newExpr instanceof ArkStaticInvokeExpr) || newExpr instanceof ArkPtrInvokeExpr) 253 ) { 254 stmt.replaceUse(expr, newExpr); 255 } 256 } 257 if (stmt instanceof ArkAliasTypeDefineStmt && this.isUnclearType(stmt.getAliasType().getOriginalType())) { 258 stmt.getAliasType().setOriginalType(stmt.getAliasTypeExpr().getType()); 259 } 260 } 261 262 /** 263 * infer value type for TypeExprs in stmt which specify the type such as TypeQueryExpr 264 */ 265 private static resolveTypeExprsInStmt(stmt: Stmt, arkMethod: ArkMethod): void { 266 for (let typeExpr of stmt.getTypeExprs()) { 267 typeExpr.inferType(arkMethod); 268 } 269 } 270 271 /** 272 * infer type for fieldRefs in stmt. 273 */ 274 private static resolveFieldRefsInStmt(stmt: Stmt, arkMethod: ArkMethod): void { 275 for (const use of stmt.getUses()) { 276 if (use instanceof AbstractRef) { 277 this.processRef(use, stmt, arkMethod); 278 } 279 } 280 const stmtDef = stmt.getDef(); 281 if (stmtDef && stmtDef instanceof AbstractRef) { 282 if ( 283 arkMethod.getName() === INSTANCE_INIT_METHOD_NAME && 284 stmtDef instanceof ArkInstanceFieldRef && 285 stmtDef.getBase().getName() === THIS_NAME && 286 arkMethod.getDeclaringArkClass().isAnonymousClass() && 287 stmtDef.getFieldName().indexOf('.') === -1 288 ) { 289 return; 290 } 291 const fieldRef = stmtDef.inferType(arkMethod); 292 stmt.replaceDef(stmtDef, fieldRef); 293 } 294 } 295 296 private static processRef(use: AbstractRef | ArkInstanceFieldRef, stmt: Stmt, arkMethod: ArkMethod): void { 297 const fieldRef = use.inferType(arkMethod); 298 if (fieldRef instanceof ArkStaticFieldRef && stmt instanceof ArkAssignStmt) { 299 stmt.replaceUse(use, fieldRef); 300 } else if (use instanceof ArkInstanceFieldRef && fieldRef instanceof ArkArrayRef && stmt instanceof ArkAssignStmt) { 301 const index = fieldRef.getIndex(); 302 if (index instanceof Constant && index.getType() instanceof StringType) { 303 const local = arkMethod?.getBody()?.getLocals().get(index.getValue()); 304 if (local) { 305 fieldRef.setIndex(local); 306 } 307 } 308 stmt.replaceUse(use, fieldRef); 309 } 310 } 311 312 public static parseArkExport2Type(arkExport: ArkExport | undefined | null): Type | null { 313 if (!arkExport) { 314 return null; 315 } 316 if (arkExport instanceof ArkClass) { 317 return new ClassType(arkExport.getSignature(), arkExport.getGenericsTypes()); 318 } else if (arkExport instanceof ArkNamespace) { 319 return AnnotationNamespaceType.getInstance(arkExport.getSignature()); 320 } else if (arkExport instanceof ArkMethod) { 321 return new FunctionType(arkExport.getSignature()); 322 } else if (arkExport instanceof Local) { 323 if (arkExport.getType() instanceof UnknownType || arkExport.getType() instanceof UnclearReferenceType) { 324 return null; 325 } 326 return arkExport.getType(); 327 } else if (arkExport instanceof AliasType) { 328 return arkExport; 329 } else { 330 return null; 331 } 332 } 333 334 /** 335 * infer and pass type for ArkAssignStmt right and left 336 * @param stmt 337 * @param arkMethod 338 */ 339 public static resolveArkAssignStmt(stmt: Stmt, arkMethod: ArkMethod): void { 340 if (!(stmt instanceof ArkAssignStmt)) { 341 return; 342 } 343 const arkClass = arkMethod.getDeclaringArkClass(); 344 const rightOp = stmt.getRightOp(); 345 if (rightOp instanceof Local && rightOp.getType() instanceof UnknownType) { 346 IRInference.inferLocal(rightOp, arkMethod); 347 } 348 let rightType: Type | null | undefined = rightOp.getType(); 349 if (this.isUnclearType(rightType)) { 350 rightType = this.inferUnclearedType(rightType, arkClass); 351 if (rightType) { 352 this.setValueType(rightOp, rightType); 353 } 354 } 355 TypeInference.resolveLeftOp(stmt, arkClass, rightType, arkMethod); 356 } 357 358 private static resolveLeftOp(stmt: ArkAssignStmt, arkClass: ArkClass, rightType: Type | null | undefined, arkMethod: ArkMethod): void { 359 const leftOp = stmt.getLeftOp(); 360 let leftType: Type | null | undefined = leftOp.getType(); 361 if (this.isUnclearType(leftType)) { 362 const newLeftType = this.inferUnclearedType(leftType, arkClass); 363 if (!newLeftType && !this.isUnclearType(rightType)) { 364 leftType = rightType; 365 } else if (newLeftType) { 366 leftType = newLeftType; 367 } 368 } else if (leftOp instanceof Local && leftOp.getName() === THIS_NAME) { 369 const thisLocal = IRInference.inferThisLocal(arkMethod); 370 if (thisLocal) { 371 stmt.setLeftOp(thisLocal); 372 } else { 373 leftType = rightType; 374 } 375 } 376 if (leftType && !this.isUnclearType(leftType)) { 377 this.setValueType(leftOp, leftType); 378 if (leftOp instanceof Local && stmt.getOriginalText()?.startsWith(leftOp.getName())) { 379 let localDef = ModelUtils.findDeclaredLocal(leftOp, arkMethod); 380 if (localDef && this.isUnclearType(localDef.getType())) { 381 localDef.setType(leftType); 382 } 383 } 384 if (rightType) { 385 IRInference.inferRightWithSdkType(leftType, rightType, arkClass); 386 } 387 if (leftOp instanceof AbstractFieldRef) { 388 const declaringSignature = leftOp.getFieldSignature().getDeclaringSignature(); 389 if (declaringSignature instanceof NamespaceSignature && declaringSignature.getNamespaceName() === GLOBAL_THIS_NAME) { 390 SdkUtils.computeGlobalThis(leftOp, arkMethod); 391 } 392 } 393 } 394 } 395 396 private static setValueType(value: Value, type: Type): void { 397 if (value instanceof Local || value instanceof ArkParameterRef) { 398 value.setType(type); 399 } else if (value instanceof AbstractFieldRef) { 400 value.getFieldSignature().setType(type); 401 } 402 } 403 404 public static isUnclearType(type: Type | null | undefined): boolean { 405 // TODO: For UnionType, IntersectionType and TupleType, it should recurse check every item of them. 406 if (!type || type instanceof UnknownType || type instanceof UnclearReferenceType || type instanceof NullType || 407 type instanceof UndefinedType || type instanceof GenericType) { 408 return true; 409 } else if ( 410 type instanceof ClassType && 411 (type.getClassSignature().getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME || 412 (type.getClassSignature().getClassName() === PROMISE && !type.getRealGenericTypes()) || 413 (type.getClassSignature().getDeclaringFileSignature().getFileName() === Builtin.DUMMY_FILE_NAME && 414 type.getRealGenericTypes()?.find(t => t instanceof GenericType))) 415 ) { 416 return true; 417 } else if (type instanceof UnionType || type instanceof IntersectionType || type instanceof TupleType) { 418 return !!type.getTypes().find(t => this.checkType(t, e => e instanceof UnclearReferenceType)); 419 } else if (type instanceof ArrayType) { 420 const baseType = type.getBaseType(); 421 return this.checkType(baseType, t => t instanceof UnclearReferenceType) || baseType instanceof GenericType; 422 } else if (type instanceof AliasType) { 423 return this.isUnclearType(type.getOriginalType()); 424 } else if (type instanceof KeyofTypeExpr) { 425 return this.isUnclearType(type.getOpType()); 426 } else if (type instanceof TypeQueryExpr) { 427 return this.isUnclearType(type.getType()); 428 } 429 return false; 430 } 431 432 // This is the temporal function to check Type recursively and can be removed after typeInfer supports multiple candidate types. 433 public static checkType(type: Type, 434 check: (t: Type) => boolean, 435 visited: Set<Type> = new Set()): boolean { 436 if (visited.has(type)) { 437 return false; 438 } else { 439 visited.add(type); 440 } 441 if (check(type)) { 442 return true; 443 } else if (type instanceof ClassType) { 444 return !!type.getRealGenericTypes()?.find(t => this.checkType(t, check, visited)); 445 } else if (type instanceof UnionType || type instanceof IntersectionType || type instanceof TupleType) { 446 return !!type.getTypes().find(t => this.checkType(t, check, visited)); 447 } else if (type instanceof ArrayType) { 448 return this.checkType(type.getBaseType(), check, visited); 449 } else if (type instanceof AliasType) { 450 return this.checkType(type.getOriginalType(), check, visited); 451 } else if (type instanceof KeyofTypeExpr) { 452 return this.checkType(type.getOpType(), check, visited); 453 } else if (type instanceof TypeQueryExpr) { 454 return this.checkType(type.getType(), check, visited); 455 } 456 return false; 457 } 458 459 public static inferSimpleTypeInStmt(stmt: Stmt): void { 460 if (stmt instanceof ArkAssignStmt) { 461 const leftOp = stmt.getLeftOp(); 462 if (leftOp instanceof Local) { 463 const leftOpType = leftOp.getType(); 464 if (leftOpType instanceof UnknownType) { 465 const rightOp = stmt.getRightOp(); 466 leftOp.setType(rightOp.getType()); 467 } 468 } 469 } 470 } 471 472 // Deal only with simple situations 473 public static buildTypeFromStr(typeStr: string): Type { 474 switch (typeStr) { 475 case BOOLEAN_KEYWORD: 476 return BooleanType.getInstance(); 477 case NUMBER_KEYWORD: 478 return NumberType.getInstance(); 479 case STRING_KEYWORD: 480 return StringType.getInstance(); 481 case UNDEFINED_KEYWORD: 482 return UndefinedType.getInstance(); 483 case NULL_KEYWORD: 484 return NullType.getInstance(); 485 case ANY_KEYWORD: 486 return AnyType.getInstance(); 487 case VOID_KEYWORD: 488 return VoidType.getInstance(); 489 case NEVER_KEYWORD: 490 return NeverType.getInstance(); 491 case BIGINT_KEYWORD: 492 return BigIntType.getInstance(); 493 case 'RegularExpression': { 494 const classSignature = Builtin.REGEXP_CLASS_SIGNATURE; 495 return new ClassType(classSignature); 496 } 497 default: 498 return new UnclearReferenceType(typeStr); 499 } 500 } 501 502 public static inferValueType(value: Value, arkMethod: ArkMethod): Type | null { 503 if (value instanceof ArkInstanceFieldRef || value instanceof ArkInstanceInvokeExpr) { 504 this.inferValueType(value.getBase(), arkMethod); 505 } 506 if (value instanceof AbstractRef || value instanceof AbstractExpr || value instanceof Local) { 507 value.inferType(arkMethod); 508 } 509 return value.getType(); 510 } 511 512 public static inferParameterType(param: MethodParameter, arkMethod: ArkMethod): void { 513 let pType = param.getType(); 514 const arkClass = arkMethod.getDeclaringArkClass(); 515 let type; 516 if (pType instanceof AbstractTypeExpr) { 517 pType.inferType(arkMethod); 518 } else if (param.getName() === 'value' && arkClass.hasComponentDecorator() && arkMethod.getName() === CONSTRUCTOR_NAME) { 519 type = this.parseArkExport2Type(arkClass); 520 } else { 521 type = TypeInference.inferUnclearedType(pType, arkClass); 522 } 523 if (type) { 524 param.setType(type); 525 } 526 } 527 528 public static inferSignatureReturnType(oldSignature: MethodSignature, arkMethod: ArkMethod): void { 529 if (oldSignature.getMethodSubSignature().getMethodName() === CONSTRUCTOR_NAME) { 530 const newReturnType = new ClassType(oldSignature.getDeclaringClassSignature()); 531 oldSignature.getMethodSubSignature().setReturnType(newReturnType); 532 return; 533 } 534 const currReturnType = oldSignature.getType(); 535 if (!this.isUnclearType(currReturnType)) { 536 return; 537 } 538 539 if (currReturnType instanceof AbstractTypeExpr) { 540 currReturnType.inferType(arkMethod); 541 return; 542 } 543 544 if (currReturnType instanceof ArrayType && currReturnType.getBaseType() instanceof AbstractTypeExpr) { 545 (currReturnType.getBaseType() as AbstractTypeExpr).inferType(arkMethod); 546 return; 547 } 548 549 const newReturnType = this.inferUnclearedType(currReturnType, arkMethod.getDeclaringArkClass()); 550 if (newReturnType) { 551 oldSignature.getMethodSubSignature().setReturnType(newReturnType); 552 } else if (arkMethod.getBody()) { 553 const returnType = TypeInference.inferReturnType(arkMethod); 554 if (returnType) { 555 oldSignature.getMethodSubSignature().setReturnType(returnType); 556 } 557 } 558 } 559 560 private static inferReturnType(arkMethod: ArkMethod): Type | null { 561 const typeMap: Map<string, Type> = new Map(); 562 for (let returnValue of arkMethod.getReturnValues()) { 563 const type = returnValue.getType(); 564 if (type instanceof UnionType) { 565 type.flatType() 566 .filter(t => !TypeInference.isUnclearType(t)) 567 .forEach(t => typeMap.set(t.toString(), t)); 568 } else if (!TypeInference.isUnclearType(type)) { 569 typeMap.set(type.toString(), type); 570 } 571 } 572 if (typeMap.size > 0) { 573 const types: Type[] = Array.from(typeMap.values()); 574 let returnType = types.length === 1 ? types[0] : new UnionType(types); 575 if (arkMethod.containsModifier(ModifierType.ASYNC)) { 576 const promise = arkMethod.getDeclaringArkFile().getScene().getSdkGlobal(PROMISE); 577 if (promise instanceof ArkClass) { 578 returnType = new ClassType(promise.getSignature(), [returnType]); 579 } 580 } 581 return returnType; 582 } 583 return null; 584 } 585 586 public static inferGenericType(types: GenericType[] | undefined, arkClass: ArkClass): void { 587 types?.forEach(type => { 588 const defaultType = type.getDefaultType(); 589 if (defaultType instanceof UnclearReferenceType) { 590 const newDefaultType = TypeInference.inferUnclearRefName(defaultType.getName(), arkClass); 591 if (newDefaultType) { 592 type.setDefaultType(this.replaceTypeWithReal(newDefaultType)); 593 } 594 } 595 const constraint = type.getConstraint(); 596 if (constraint instanceof UnclearReferenceType) { 597 const newConstraint = TypeInference.inferUnclearRefName(constraint.getName(), arkClass); 598 if (newConstraint) { 599 type.setConstraint(this.replaceTypeWithReal(newConstraint)); 600 } 601 } 602 }); 603 } 604 605 /** 606 * Infer type for a given {@link UnclearReferenceType} type. 607 * It returns original type. 608 * The original type is null if it failed to infer the type. 609 * @param urType 610 * @param arkClass 611 * @returns 612 */ 613 public static inferUnclearRefType(urType: UnclearReferenceType, arkClass: ArkClass): Type | null { 614 const realTypes = urType.getGenericTypes(); 615 this.inferRealGenericTypes(realTypes, arkClass); 616 if (urType.getName() === Builtin.ARRAY) { 617 return new ArrayType(realTypes[0] ?? AnyType.getInstance(), 1); 618 } 619 const type = this.inferUnclearRefName(urType.getName(), arkClass); 620 return type ? this.replaceTypeWithReal(type, realTypes) : null; 621 } 622 623 /** 624 * Find out the original object and type for a given unclear reference type name. 625 * It returns original type. 626 * The original type is null if it failed to infer the type. 627 * @param refName 628 * @param arkClass 629 * @returns 630 */ 631 public static inferUnclearRefName(refName: string, arkClass: ArkClass): Type | null { 632 if (!refName) { 633 return null; 634 } 635 //split and iterate to infer each type 636 const singleNames = refName.split('.'); 637 let type = null; 638 for (let i = 0; i < singleNames.length; i++) { 639 let genericName: string = EMPTY_STRING; 640 const name = singleNames[i].replace(/<(\w+)>/, (match, group1) => { 641 genericName = group1; 642 return EMPTY_STRING; 643 }); 644 if (i === 0) { 645 type = singleNames.length > 1 ? this.inferBaseType(name, arkClass) : this.inferTypeByName(name, arkClass); 646 } else if (type) { 647 type = this.inferFieldType(type, name, arkClass)?.[1]; 648 } 649 if (!type) { 650 return null; 651 } 652 if (genericName) { 653 const realTypes = genericName.split(',').map(generic => { 654 const realType = this.inferUnclearRefName(generic, arkClass); 655 return realType ?? new UnclearReferenceType(generic); 656 }); 657 if (type instanceof ClassType) { 658 type = new ClassType(type.getClassSignature(), realTypes); 659 } else if (type instanceof FunctionType) { 660 type = new FunctionType(type.getMethodSignature(), realTypes); 661 } 662 } 663 } 664 return type; 665 } 666 667 /** 668 * Find out the original object and type for a given base type and the field name. 669 * It returns an array with 2 items, original object and original type. 670 * The original object is null if there is no object, or it failed to find the object. 671 * The original type is null if it failed to infer the type. 672 * @param baseType 673 * @param fieldName 674 * @param declareClass 675 * @returns 676 */ 677 public static inferFieldType(baseType: Type, fieldName: string, declareClass: ArkClass): [any, Type] | null { 678 if (baseType instanceof AliasType) { 679 baseType = baseType.getOriginalType(); 680 } else if (baseType instanceof UnionType && baseType.getCurrType()) { 681 baseType = baseType.getCurrType(); 682 } 683 let propertyAndType: [any, Type] | null = null; 684 if (baseType instanceof ClassType) { 685 if ( 686 fieldName === Builtin.ITERATOR_RESULT_VALUE && 687 baseType.getClassSignature().getDeclaringFileSignature().getProjectName() === Builtin.DUMMY_PROJECT_NAME 688 ) { 689 const types = baseType.getRealGenericTypes(); 690 if (types && types.length > 0) { 691 return [null, types[0]]; 692 } 693 return null; 694 } 695 propertyAndType = this.inferClassFieldType(declareClass, baseType, fieldName); 696 } else if (baseType instanceof AnnotationNamespaceType) { 697 const namespace = declareClass.getDeclaringArkFile().getScene().getNamespace(baseType.getNamespaceSignature()); 698 if (namespace) { 699 const property = ModelUtils.findPropertyInNamespace(fieldName, namespace); 700 const propertyType = this.parseArkExport2Type(property); 701 if (propertyType) { 702 propertyAndType = [property, propertyType]; 703 } 704 } 705 } else { 706 logger.warn('infer unclear reference type fail: ' + fieldName); 707 } 708 return propertyAndType; 709 } 710 711 private static inferClassFieldType(declareClass: ArkClass, baseType: ClassType, fieldName: string): [any, Type] | null { 712 const arkClass = declareClass.getDeclaringArkFile().getScene().getClass(baseType.getClassSignature()); 713 if (!arkClass) { 714 return null; 715 } 716 const property = ModelUtils.findPropertyInClass(fieldName, arkClass); 717 let propertyType: Type | null = null; 718 if (property instanceof ArkField) { 719 if (arkClass.getCategory() === ClassCategory.ENUM) { 720 let constant; 721 const propertyInitializer = property.getInitializer(); 722 const lastStmt = propertyInitializer[propertyInitializer.length - 1]; 723 if (lastStmt instanceof ArkAssignStmt && lastStmt.getRightOp() instanceof Constant) { 724 constant = lastStmt.getRightOp() as Constant; 725 } 726 propertyType = new EnumValueType(property.getSignature(), constant); 727 } else { 728 propertyType = this.replaceTypeWithReal(property.getType(), baseType.getRealGenericTypes()); 729 } 730 } else if (property) { 731 propertyType = this.parseArkExport2Type(property); 732 } 733 if (propertyType) { 734 return [property, propertyType]; 735 } else if (arkClass.isAnonymousClass()) { 736 const fieldType = this.inferUnclearRefName(fieldName, arkClass); 737 return fieldType ? [null, fieldType] : null; 738 } 739 return null; 740 } 741 742 /** 743 * Find out the original object and type for a given base name. 744 * It returns original type. 745 * The original type is null if failed to infer the type. 746 * @param baseName 747 * @param arkClass 748 * @returns 749 */ 750 public static inferBaseType(baseName: string, arkClass: ArkClass): Type | null { 751 if (SUPER_NAME === baseName) { 752 return this.parseArkExport2Type(arkClass.getSuperClass()); 753 } else if (DEFAULT === baseName) { 754 return this.parseArkExport2Type(arkClass.getDeclaringArkFile().getExportInfoBy(DEFAULT)?.getArkExport()); 755 } 756 const field = ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getLocals()?.get(baseName); 757 if (field && !this.isUnclearType(field.getType())) { 758 return field.getType(); 759 } 760 let arkExport: ArkExport | null = 761 ModelUtils.getClassWithName(baseName, arkClass) ?? 762 ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(baseName) ?? 763 ModelUtils.getNamespaceWithName(baseName, arkClass) ?? 764 ModelUtils.getDefaultClass(arkClass)?.getMethodWithName(baseName) ?? 765 ModelUtils.getArkExportInImportInfoWithName(baseName, arkClass.getDeclaringArkFile()); 766 if (!arkExport && !arkClass.getDeclaringArkFile().getImportInfoBy(baseName)) { 767 arkExport = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(baseName); 768 } 769 return this.parseArkExport2Type(arkExport); 770 } 771 772 public static inferTypeByName(typeName: string, arkClass: ArkClass): Type | null { 773 let arkExport: ArkExport | null = 774 ModelUtils.getClassWithName(typeName, arkClass) ?? 775 ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(typeName) ?? 776 ModelUtils.getArkExportInImportInfoWithName(typeName, arkClass.getDeclaringArkFile()); 777 if (!arkExport && !arkClass.getDeclaringArkFile().getImportInfoBy(typeName)) { 778 arkExport = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(typeName); 779 } 780 const type = this.parseArkExport2Type(arkExport); 781 if (type instanceof ClassType || type instanceof AliasType) { 782 return type; 783 } 784 return null; 785 } 786 787 public static getTypeByGlobalName(globalName: string, arkMethod: ArkMethod): Type | null { 788 const arkExport: ArkExport | null = arkMethod.getDeclaringArkFile().getScene().getSdkGlobal(globalName); 789 return this.parseArkExport2Type(arkExport); 790 } 791 792 public static inferRealGenericTypes(realTypes: Type[] | undefined, arkClass: ArkClass): void { 793 if (!realTypes) { 794 return; 795 } 796 for (let i = 0; i < realTypes.length; i++) { 797 const mayType = realTypes[i]; 798 if (this.isUnclearType(mayType)) { 799 const newType = this.inferUnclearedType(mayType, arkClass); 800 if (newType) { 801 realTypes[i] = newType; 802 } 803 } 804 } 805 } 806 807 public static inferDynamicImportType(from: string, arkClass: ArkClass): Type | null { 808 const importInfo = new ImportInfo(); 809 importInfo.setNameBeforeAs(ALL); 810 importInfo.setImportClauseName(ALL); 811 importInfo.setImportFrom(from); 812 importInfo.setDeclaringArkFile(arkClass.getDeclaringArkFile()); 813 return TypeInference.parseArkExport2Type(importInfo.getLazyExportInfo()?.getArkExport()); 814 } 815 816 public static replaceTypeWithReal(type: Type, realTypes?: Type[], visited: Set<Type> = new Set()): Type { 817 if (visited.has(type)) { 818 return type; 819 } else { 820 visited.add(type); 821 } 822 if (type instanceof GenericType) { 823 const realType = realTypes?.[type.getIndex()] ?? type.getDefaultType() ?? type.getConstraint(); 824 return realType ?? type; 825 } else if (type instanceof AnyType) { 826 const realType = realTypes?.[0]; 827 return realType ?? type; 828 } 829 return this.replaceRecursiveType(type, visited, realTypes); 830 } 831 832 public static replaceRecursiveType(type: Type, visited: Set<Type>, realTypes?: Type[]): Type { 833 if (type instanceof ClassType) { 834 const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes; 835 return replacedTypes && replacedTypes.length > 0 ? new ClassType(type.getClassSignature(), replacedTypes) : type; 836 } else if (type instanceof FunctionType) { 837 const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes; 838 return replacedTypes && replacedTypes.length > 0 ? new FunctionType(type.getMethodSignature(), replacedTypes) : type; 839 } else if (type instanceof AliasType && realTypes) { 840 const newObjectType = this.replaceTypeWithReal(type.getOriginalType(), realTypes, visited); 841 const replacedTypes = type.getRealGenericTypes()?.map(g => this.replaceTypeWithReal(g, realTypes, visited)) ?? realTypes; 842 if (replacedTypes.length > 0) { 843 const newAliasType = new AliasType(type.getName(), newObjectType, type.getSignature(), type.getGenericTypes()); 844 newAliasType.setRealGenericTypes(replacedTypes); 845 return newAliasType; 846 } 847 } else if (type instanceof UnionType && realTypes) { 848 const types: Type[] = []; 849 type.flatType().forEach(t => types.push(this.replaceTypeWithReal(t, realTypes, visited))); 850 return new UnionType(types, this.replaceTypeWithReal(type.getCurrType(), realTypes, visited)); 851 } else if (type instanceof IntersectionType && realTypes) { 852 const types: Type[] = []; 853 type.getTypes().forEach(t => types.push(this.replaceTypeWithReal(t, realTypes, visited))); 854 return new IntersectionType(types); 855 } else if (type instanceof ArrayType && realTypes) { 856 const replacedBaseType = this.replaceTypeWithReal(type.getBaseType(), realTypes, visited); 857 return new ArrayType(replacedBaseType, type.getDimension()); 858 } else if (type instanceof TupleType && realTypes) { 859 let replacedTypes: Type[] = []; 860 type.getTypes().forEach(t => replacedTypes.push(this.replaceTypeWithReal(t, realTypes, visited))); 861 return new TupleType(replacedTypes); 862 } 863 return type; 864 } 865 866 public static replaceAliasType(type: Type): Type { 867 let aliasType = type; 868 while (aliasType instanceof AliasType) { 869 aliasType = aliasType.getOriginalType(); 870 } 871 return aliasType; 872 } 873 874 public static inferFunctionType(argType: FunctionType, paramSubSignature: MethodSubSignature | undefined, realTypes: Type[] | undefined): void { 875 const returnType = argType.getMethodSignature().getMethodSubSignature().getReturnType(); 876 const declareType = paramSubSignature?.getReturnType(); 877 if (declareType instanceof GenericType && realTypes && !this.isUnclearType(returnType) && !(returnType instanceof VoidType)) { 878 realTypes[declareType.getIndex()] = returnType; 879 } 880 const params = paramSubSignature?.getParameters(); 881 if (!params) { 882 return; 883 } 884 argType 885 .getMethodSignature() 886 .getMethodSubSignature() 887 .getParameters() 888 .filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX)) 889 .forEach((p, i) => { 890 if (this.isUnclearType(p.getType())) { 891 let type = params?.[i]?.getType(); 892 if (type instanceof GenericType && realTypes) { 893 type = realTypes?.[type.getIndex()]; 894 } 895 if (type) { 896 p.setType(type); 897 } 898 } 899 }); 900 } 901 902 private static resolveArkReturnStmt(stmt: Stmt, arkMethod: ArkMethod): void { 903 if (!(stmt instanceof ArkReturnStmt)) { 904 return; 905 } 906 let returnType: Type | undefined = arkMethod.getSignature().getType(); 907 if (returnType instanceof ClassType && returnType.getClassSignature().getClassName() === PROMISE) { 908 returnType = returnType.getRealGenericTypes()?.[0]; 909 } 910 if (returnType) { 911 IRInference.inferRightWithSdkType(returnType, stmt.getOp().getType(), arkMethod.getDeclaringArkClass()); 912 } 913 } 914} 915