• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 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 {
17    ArkMethod,
18    ArkAssignStmt,
19    FieldSignature,
20    Stmt,
21    Scene,
22    Value,
23    DVFGBuilder,
24    CallGraph,
25    ArkParameterRef,
26    ArkInstanceFieldRef,
27    ArkNamespace,
28    Local,
29    ClassType,
30    ClassSignature,
31    Type,
32    BooleanType,
33    FunctionType,
34    AnyType,
35    MethodSignature,
36    UnknownType,
37    GenericType,
38    NumberType,
39    ArrayType,
40    MethodSubSignature,
41    ArkInvokeStmt,
42    AbstractInvokeExpr,
43    ArkInstanceInvokeExpr,
44    ArkPtrInvokeExpr,
45    ImportInfo,
46    UnionType,
47    FileSignature,
48    ArkStaticInvokeExpr,
49    UndefinedType,
50    VoidType,
51} from 'arkanalyzer/lib';
52import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
53import { BaseChecker, BaseMetaData } from '../BaseChecker';
54import { Rule, Defects, MatcherCallback } from '../../Index';
55import { IssueReport } from '../../model/Defects';
56import { DVFG, DVFGNode } from 'arkanalyzer/lib/VFG/DVFG';
57import { CALL_DEPTH_LIMIT, getGlobalsDefineInDefaultMethod, GlobalCallGraphHelper } from './Utils';
58import { WarnInfo } from '../../utils/common/Utils';
59import { ArkClass } from 'arkanalyzer/lib/core/model/ArkClass';
60import { Language } from 'arkanalyzer/lib/core/model/ArkFile';
61import { MethodParameter } from 'arkanalyzer/lib/core/model/builder/ArkMethodBuilder';
62import { AbstractFieldRef, ArkReturnStmt } from 'arkanalyzer';
63
64const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'DeprecatedBuiltInAPICheck');
65const gMetaData: BaseMetaData = {
66    severity: 1,
67    ruleDocPath: '',
68    description: '',
69};
70
71enum APIBaseCategory {
72    Array = 'Array',
73    Set = 'Set',
74    Map = 'Map',
75}
76
77class DeprecatedAPIInfo {
78    constructor(
79        public base: APIBaseCategory,
80        public name: string,
81        public isStatic: boolean,
82        public returnType?: Type, // return Type为undefined时表示不关心API的返回值
83        public params?: Type[], // return Type为undefined时表示不关心API的形参,应该是整个API都被废弃,而非某一重载形态
84        public targetParamIndex?: number // 若isStatic为tue,说明是静态接口,需要提供查找接口的第几个param是否来源arkts1.2,编号从0开始,不提供则查找base的来源
85    ) {}
86}
87
88class APIBasicType {
89    static readonly genericTypeT = new GenericType('T');
90    static readonly genericTypeU = new GenericType('U');
91    static readonly genericTypeV = new GenericType('V');
92    static readonly genericTypeK = new GenericType('K');
93    static readonly genericTypeS = new GenericType('S');
94    static readonly arrayT = new ArrayType(this.genericTypeT, 1);
95    static readonly arrayU = new ArrayType(this.genericTypeU, 1);
96    static readonly unknownType = UnknownType.getInstance();
97    static readonly numberType = NumberType.getInstance();
98    static readonly booleanType = BooleanType.getInstance();
99    static readonly anyType = AnyType.getInstance();
100    static readonly undefinedType = UndefinedType.getInstance();
101    static readonly voidType = VoidType.getInstance();
102}
103
104class APIComplicatedType {
105    static readonly anonyMethodName = 'anonymous';
106    static readonly predicateFunctionType = this.createPredicateFunctionType();
107    static readonly predicateFunction1Type = this.createPredicateFunction1Type();
108    static readonly concatItemType = this.createConcatItemType();
109    static readonly mapfnFunctionType = this.createMapfnFunctionType();
110    static readonly ArrayLikeType = new ClassType(new ClassSignature('ArrayLike', FileSignature.DEFAULT));
111    static readonly IterableType = new ClassType(new ClassSignature('Iterable', FileSignature.DEFAULT));
112    static readonly SetType = new ClassType(new ClassSignature('Set', FileSignature.DEFAULT));
113    static readonly MapType = new ClassType(new ClassSignature('Map', FileSignature.DEFAULT));
114    static readonly setCallbackFnFunctionType = this.createSetCallbackFnFunctionType();
115    static readonly mapCallbackFnFunctionType = this.createMapCallbackFnFunctionType();
116
117    // 对于参数类型一致,但是参数名称不同、返回值类型不同的FunctionType,视为同一个,不重新创建。因为FunctionType类型匹配的时候仅匹配参数类型
118    // 不同的API,仅形参为lambda函数且该lambda函数的返回值不同,例如unknown和value is S类型谓词形式,视为同一个API
119    private static createPredicateFunctionType(): FunctionType {
120        const predicateValueParam = new MethodParameter();
121        predicateValueParam.setName('value');
122        predicateValueParam.setType(APIBasicType.genericTypeT);
123        const predicateIndexParam = new MethodParameter();
124        predicateIndexParam.setName('index');
125        predicateIndexParam.setType(APIBasicType.numberType);
126        const predicateArrayParam = new MethodParameter();
127        predicateArrayParam.setName('array');
128        predicateArrayParam.setType(APIBasicType.arrayT);
129        const predicateMethodSubSignature = new MethodSubSignature(
130            this.anonyMethodName,
131            [predicateValueParam, predicateIndexParam, predicateArrayParam],
132            APIBasicType.unknownType
133        );
134        return new FunctionType(new MethodSignature(ClassSignature.DEFAULT, predicateMethodSubSignature));
135    }
136
137    private static createPredicateFunction1Type(): FunctionType {
138        const predicateThisParam = new MethodParameter();
139        predicateThisParam.setName('this');
140        predicateThisParam.setType(APIBasicType.voidType);
141        const predicateValueParam = new MethodParameter();
142        predicateValueParam.setName('value');
143        predicateValueParam.setType(APIBasicType.genericTypeT);
144        const predicateIndexParam = new MethodParameter();
145        predicateIndexParam.setName('index');
146        predicateIndexParam.setType(APIBasicType.numberType);
147        const predicateObjParam = new MethodParameter();
148        predicateObjParam.setName('obj');
149        predicateObjParam.setType(APIBasicType.arrayT);
150        const predicateMethodSubSignature = new MethodSubSignature(
151            this.anonyMethodName,
152            [predicateThisParam, predicateValueParam, predicateIndexParam, predicateObjParam],
153            APIBasicType.booleanType
154        );
155        return new FunctionType(new MethodSignature(ClassSignature.DEFAULT, predicateMethodSubSignature));
156    }
157
158    private static createConcatItemType(): UnionType {
159        return new UnionType([APIBasicType.genericTypeT, new ClassType(new ClassSignature('ConcatArray', FileSignature.DEFAULT))]);
160    }
161
162    private static createMapfnFunctionType(): FunctionType {
163        const mapfnParamV = new MethodParameter();
164        mapfnParamV.setName('v');
165        mapfnParamV.setType(APIBasicType.genericTypeT);
166        const mapfnParamK = new MethodParameter();
167        mapfnParamK.setName('k');
168        mapfnParamK.setType(APIBasicType.numberType);
169        const mapfnMethodSubSignature = new MethodSubSignature(this.anonyMethodName, [mapfnParamV, mapfnParamK], APIBasicType.genericTypeU);
170        return new FunctionType(new MethodSignature(ClassSignature.DEFAULT, mapfnMethodSubSignature));
171    }
172
173    private static createSetCallbackFnFunctionType(): FunctionType {
174        const callbackFnValueParam = new MethodParameter();
175        callbackFnValueParam.setName('value');
176        callbackFnValueParam.setType(APIBasicType.genericTypeT);
177        const callbackFnValue2Param = new MethodParameter();
178        callbackFnValue2Param.setName('value2');
179        callbackFnValue2Param.setType(APIBasicType.genericTypeT);
180        const callbackFnSetParam = new MethodParameter();
181        callbackFnSetParam.setName('set');
182        callbackFnSetParam.setType(this.SetType);
183        const predicateMethodSubSignature = new MethodSubSignature(
184            this.anonyMethodName,
185            [callbackFnValueParam, callbackFnValue2Param, callbackFnSetParam],
186            APIBasicType.voidType
187        );
188        return new FunctionType(new MethodSignature(ClassSignature.DEFAULT, predicateMethodSubSignature));
189    }
190
191    private static createMapCallbackFnFunctionType(): FunctionType {
192        const callbackFnValueParam = new MethodParameter();
193        callbackFnValueParam.setName('value');
194        callbackFnValueParam.setType(APIBasicType.genericTypeV);
195        const callbackFnKeyParam = new MethodParameter();
196        callbackFnKeyParam.setName('key');
197        callbackFnKeyParam.setType(APIBasicType.genericTypeK);
198        const callbackFnMapParam = new MethodParameter();
199        callbackFnMapParam.setName('map');
200        callbackFnMapParam.setType(this.MapType);
201        const predicateMethodSubSignature = new MethodSubSignature(
202            this.anonyMethodName,
203            [callbackFnValueParam, callbackFnKeyParam, callbackFnMapParam],
204            APIBasicType.voidType
205        );
206        return new FunctionType(new MethodSignature(ClassSignature.DEFAULT, predicateMethodSubSignature));
207    }
208}
209
210class DeprecatedAPIList {
211    static readonly DeprecatedAPIs: DeprecatedAPIInfo[] = [
212        this.createArrayEveryAPI1(),
213        this.createArrayFilterAPI1(),
214        this.createArrayFindAPI1(),
215        this.createArrayFindAPI2(),
216        this.createArrayFindIndexAPI(),
217        this.createArrayForEachAPI(),
218        this.createArrayMapAPI(),
219        this.createArraySomeAPI(),
220        this.createArrayConcatAPI(),
221        this.createArrayFlatAPI(),
222        this.createArrayFlatMapAPI(),
223        this.createArrayFromArrayLikeAPI(),
224        this.createArrayFromIterableAndArrayLikeAPI(),
225        this.createSetForEachAPI(),
226        this.createMapForEachAPI(),
227        this.createArraySymbolIteratorAPI(),
228        this.createSetSymbolIteratorAPI(),
229        this.createMapSymbolIteratorAPI(),
230    ];
231
232    private static createArrayEveryAPI1(): DeprecatedAPIInfo {
233        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'every', false, APIBasicType.booleanType, [
234            APIComplicatedType.predicateFunctionType,
235            APIBasicType.anyType,
236        ]);
237    }
238
239    private static createArrayFilterAPI1(): DeprecatedAPIInfo {
240        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'filter', false, APIBasicType.arrayT, [
241            APIComplicatedType.predicateFunctionType,
242            APIBasicType.anyType,
243        ]);
244    }
245
246    private static createArrayFindAPI1(): DeprecatedAPIInfo {
247        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'find', false, new UnionType([APIBasicType.genericTypeT, APIBasicType.undefinedType]), [
248            APIComplicatedType.predicateFunctionType,
249            APIBasicType.anyType,
250        ]);
251    }
252
253    private static createArrayFindAPI2(): DeprecatedAPIInfo {
254        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'find', false, new UnionType([APIBasicType.genericTypeS, APIBasicType.undefinedType]), [
255            APIComplicatedType.predicateFunction1Type,
256            APIBasicType.anyType,
257        ]);
258    }
259
260    private static createArrayFindIndexAPI(): DeprecatedAPIInfo {
261        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'findIndex', false, APIBasicType.numberType, [
262            APIComplicatedType.predicateFunctionType,
263            APIBasicType.anyType,
264        ]);
265    }
266
267    private static createArrayForEachAPI(): DeprecatedAPIInfo {
268        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'forEach', false, APIBasicType.voidType, [
269            APIComplicatedType.predicateFunctionType,
270            APIBasicType.anyType,
271        ]);
272    }
273
274    private static createArrayMapAPI(): DeprecatedAPIInfo {
275        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'map', false, APIBasicType.arrayU, [
276            APIComplicatedType.predicateFunctionType,
277            APIBasicType.anyType,
278        ]);
279    }
280
281    private static createArraySomeAPI(): DeprecatedAPIInfo {
282        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'some', false, APIBasicType.booleanType, [
283            APIComplicatedType.predicateFunctionType,
284            APIBasicType.anyType,
285        ]);
286    }
287
288    private static createArrayConcatAPI(): DeprecatedAPIInfo {
289        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'concat', false, APIBasicType.arrayT, [new ArrayType(APIComplicatedType.concatItemType, 1)]);
290    }
291
292    private static createArrayFlatAPI(): DeprecatedAPIInfo {
293        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'flat', false);
294    }
295
296    private static createArrayFlatMapAPI(): DeprecatedAPIInfo {
297        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'flatMap', false);
298    }
299
300    private static createArrayFromArrayLikeAPI(): DeprecatedAPIInfo {
301        const fromParams = [APIComplicatedType.ArrayLikeType, APIComplicatedType.mapfnFunctionType, APIBasicType.anyType];
302        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'from', true, APIBasicType.arrayU, fromParams, 0);
303    }
304
305    private static createArrayFromIterableAndArrayLikeAPI(): DeprecatedAPIInfo {
306        const fromParams = [
307            new UnionType([APIComplicatedType.ArrayLikeType, APIComplicatedType.IterableType]),
308            APIComplicatedType.mapfnFunctionType,
309            APIBasicType.anyType,
310        ];
311        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'from', true, APIBasicType.arrayU, fromParams, 0);
312    }
313
314    private static createSetForEachAPI(): DeprecatedAPIInfo {
315        return new DeprecatedAPIInfo(APIBaseCategory.Set, 'forEach', false, APIBasicType.voidType, [
316            APIComplicatedType.setCallbackFnFunctionType,
317            APIBasicType.anyType,
318        ]);
319    }
320
321    private static createMapForEachAPI(): DeprecatedAPIInfo {
322        return new DeprecatedAPIInfo(APIBaseCategory.Map, 'forEach', false, APIBasicType.voidType, [
323            APIComplicatedType.mapCallbackFnFunctionType,
324            APIBasicType.anyType,
325        ]);
326    }
327
328    private static createArraySymbolIteratorAPI(): DeprecatedAPIInfo {
329        return new DeprecatedAPIInfo(APIBaseCategory.Array, 'Symbol.iterator', false);
330    }
331
332    private static createSetSymbolIteratorAPI(): DeprecatedAPIInfo {
333        return new DeprecatedAPIInfo(APIBaseCategory.Set, 'Symbol.iterator', false);
334    }
335
336    private static createMapSymbolIteratorAPI(): DeprecatedAPIInfo {
337        return new DeprecatedAPIInfo(APIBaseCategory.Map, 'Symbol.iterator', false);
338    }
339}
340
341export class InteropDeprecatedBuiltInAPICheck implements BaseChecker {
342    readonly metaData: BaseMetaData = gMetaData;
343    public rule: Rule;
344    public defects: Defects[] = [];
345    public issues: IssueReport[] = [];
346    private cg: CallGraph;
347    private dvfg: DVFG;
348    private dvfgBuilder: DVFGBuilder;
349    private scene: Scene;
350    private visited: Set<ArkMethod> = new Set();
351
352    public registerMatchers(): MatcherCallback[] {
353        const matchBuildCb: MatcherCallback = {
354            matcher: undefined,
355            callback: this.check,
356        };
357        return [matchBuildCb];
358    }
359
360    public check = (scene: Scene): void => {
361        this.scene = scene;
362        this.cg = GlobalCallGraphHelper.getCGInstance(scene);
363
364        this.dvfg = new DVFG(this.cg);
365        this.dvfgBuilder = new DVFGBuilder(this.dvfg, scene);
366
367        for (let arkFile of scene.getFiles()) {
368            // 此规则仅对arkts1.1进行检查
369            if (!(arkFile.getLanguage() === Language.ARKTS1_1)) {
370                continue;
371            }
372            const defaultMethod = arkFile.getDefaultClass().getDefaultArkMethod();
373            let globalVarMap: Map<string, Stmt[]> = new Map();
374            if (defaultMethod) {
375                this.dvfgBuilder.buildForSingleMethod(defaultMethod);
376                globalVarMap = getGlobalsDefineInDefaultMethod(defaultMethod);
377            }
378            for (let arkClass of arkFile.getClasses()) {
379                this.processArkClass(arkClass, globalVarMap);
380            }
381            for (let namespace of arkFile.getAllNamespacesUnderThisFile()) {
382                this.processNameSpace(namespace, globalVarMap);
383            }
384        }
385    };
386
387    public processArkClass(arkClass: ArkClass, globalVarMap: Map<string, Stmt[]>): void {
388        this.processArkMethod(arkClass.getInstanceInitMethod(), globalVarMap);
389        this.processArkMethod(arkClass.getStaticInitMethod(), globalVarMap);
390        for (let mtd of arkClass.getMethods()) {
391            this.processArkMethod(mtd, globalVarMap);
392        }
393    }
394
395    public processNameSpace(namespace: ArkNamespace, globalVarMap: Map<string, Stmt[]>): void {
396        for (let ns of namespace.getNamespaces()) {
397            this.processNameSpace(ns, globalVarMap);
398        }
399        for (let arkClass of namespace.getClasses()) {
400            this.processArkClass(arkClass, globalVarMap);
401        }
402    }
403
404    public processArkMethod(target: ArkMethod, globalVarMap: Map<string, Stmt[]>): void {
405        const stmts = target.getBody()?.getCfg().getStmts() ?? [];
406        for (const stmt of stmts) {
407            // 查找到DeprecatedAPIs中的builtIn的API调用语句为sink点,从该点开始进行逆向数据流分析,分析base是否为arkts1.2中声明的对象实例或其引用
408            const targetLocal = this.getTargetLocalOfDeprecatedAPICall(stmt);
409            if (targetLocal === null) {
410                continue;
411            }
412
413            // 从base的最近一次赋值语句开始,使用逆向数据流进行查找
414            let checkStmt = this.getLastAssignStmt(targetLocal, stmt);
415            if (checkStmt === null) {
416                checkStmt = this.checkTargetLocalAsGlobal(target, stmt, targetLocal, globalVarMap);
417            }
418            if (checkStmt === null) {
419                continue;
420            }
421
422            if (!this.visited.has(target)) {
423                this.dvfgBuilder.buildForSingleMethod(target);
424                this.visited.add(target);
425            }
426
427            let checkAll = { value: true };
428            let visited: Set<Stmt> = new Set();
429            if (this.checkFromStmt(checkStmt, globalVarMap, checkAll, visited)) {
430                this.addIssueReport(stmt, targetLocal, checkAll.value);
431            }
432        }
433    }
434
435    private checkTargetLocalAsGlobal(targetMethod: ArkMethod, stmt: Stmt, targetLocal: Local, globalVarMap: Map<string, Stmt[]>): Stmt | null {
436        const globalDefs = globalVarMap.get(targetLocal.getName());
437        if (globalDefs === undefined) {
438            const importInfos = targetMethod.getDeclaringArkClass().getDeclaringArkFile().getImportInfos();
439            const importValue = this.isLocalFromImport(targetLocal, importInfos);
440            if (importValue && importValue.getDeclaringStmt() !== null) {
441                return importValue.getDeclaringStmt()!;
442            }
443            return null;
444        }
445        let lastLegalStmtLine = -1;
446        let lastIllegalStmtLine = -1;
447        for (const defStmt of globalDefs) {
448            if (!this.visited.has(targetMethod)) {
449                this.dvfgBuilder.buildForSingleMethod(targetMethod);
450                this.visited.add(targetMethod);
451            }
452
453            let checkAll = { value: true };
454            let visited: Set<Stmt> = new Set();
455
456            const currStmtLine = defStmt.getOriginPositionInfo().getLineNo();
457            if (this.checkFromStmt(defStmt, globalVarMap, checkAll, visited)) {
458                if (currStmtLine > lastIllegalStmtLine) {
459                    lastIllegalStmtLine = currStmtLine;
460                }
461            } else {
462                if (currStmtLine > lastLegalStmtLine) {
463                    lastLegalStmtLine = currStmtLine;
464                }
465            }
466        }
467        if (lastIllegalStmtLine > lastLegalStmtLine) {
468            this.addIssueReport(stmt, targetLocal);
469        }
470        return null;
471    }
472
473    private getLastAssignStmt(local: Local, currStmt: Stmt): Stmt | null {
474        // 获取local变量在currStmt使用语句之前的最近一次赋值/声明语句,未找到表示直接来自于全局变量或import信息,则返回null
475        const usedStmts = local.getUsedStmts();
476        let currLine = currStmt.getOriginPositionInfo().getLineNo();
477        let lastAssignStmt = local.getDeclaringStmt();
478        for (const stmt of usedStmts) {
479            if (stmt === currStmt || !(stmt instanceof ArkAssignStmt) || stmt.getLeftOp() !== local) {
480                continue;
481            }
482            const line = stmt.getOriginPositionInfo().getLineNo();
483            if (line < currLine) {
484                lastAssignStmt = stmt;
485                currLine = line;
486            }
487        }
488        return lastAssignStmt;
489    }
490
491    private isLocalDefinedInStaticArkTS(local: Local): boolean {
492        return local.getDeclaringStmt()?.getCfg().getDeclaringMethod().getLanguage() === Language.ARKTS1_2;
493    }
494
495    // 判断语句是否为废弃API接口的调用语句,若废弃接口仅为一系列重载中的某一种,需要判断这一具体重载形态,若是返回对应需要查找的Local对象,否则返回null
496    private getTargetLocalOfDeprecatedAPICall(stmt: Stmt): Local | null {
497        const invokeExpr = this.getInvokeExpr(stmt);
498        if (invokeExpr === null) {
499            return null;
500        }
501        if (invokeExpr instanceof ArkInstanceInvokeExpr) {
502            const base = invokeExpr.getBase();
503            if (this.isInstanceCallMethodInDeprecatedAPIs(base, stmt, invokeExpr.getMethodSignature(), invokeExpr.getArgs())) {
504                return base;
505            }
506            // Array.from的API调用ArkAnalyzer也表示为ArkInstanceInvokeExpr,因为API定义里没有明确的static标识
507            return this.getTargetLocalInArrayFrom(invokeExpr);
508        } else if (invokeExpr instanceof ArkPtrInvokeExpr) {
509            // TODO:可能存在ptr invoke的场景吗?
510            return null;
511        } else if (invokeExpr instanceof ArkStaticInvokeExpr) {
512            // Symbol.iterator API 检测的Reflect.get场景,是static invoke
513            return this.getTargetLocalInReflectGet(invokeExpr);
514        }
515        return null;
516    }
517
518    private getInvokeExpr(stmt: Stmt): AbstractInvokeExpr | null {
519        if (!(stmt instanceof ArkAssignStmt) && !(stmt instanceof ArkInvokeStmt)) {
520            return null;
521        }
522
523        if (stmt instanceof ArkInvokeStmt) {
524            return stmt.getInvokeExpr();
525        }
526
527        const rightOp = stmt.getRightOp();
528        if (rightOp instanceof AbstractInvokeExpr) {
529            return rightOp;
530        }
531        return null;
532    }
533
534    private compareParamTypes(apiParams: Type[], callApiParams: MethodParameter[]): boolean {
535        if (apiParams.length !== callApiParams.length) {
536            return false;
537        }
538        for (let i = 0; i < apiParams.length; i++) {
539            if (!this.isTypeMatch(apiParams[i], callApiParams[i].getType())) {
540                return false;
541            }
542        }
543        return true;
544    }
545
546    // 此函数仅用作Symbol.iterator API 检测的Reflect.get场景
547    private getTargetLocalInReflectGet(staticInvokeExpr: ArkStaticInvokeExpr): Local | null {
548        const method = staticInvokeExpr.getMethodSignature();
549        const methodName = method.getMethodSubSignature().getMethodName();
550        const namespaceName = method.getDeclaringClassSignature().getDeclaringNamespaceSignature()?.getNamespaceName();
551        if (namespaceName !== 'Reflect' || methodName !== 'get') {
552            return null;
553        }
554
555        const args = staticInvokeExpr.getArgs();
556        if (args.length < 2) {
557            return null;
558        }
559        const targetLocal = args[0];
560        const apiCall = args[1];
561        if (!(targetLocal instanceof Local) || !(apiCall instanceof Local)) {
562            return null;
563        }
564
565        const declaringStmt = apiCall.getDeclaringStmt();
566        if (declaringStmt === null || !(declaringStmt instanceof ArkAssignStmt)) {
567            return null;
568        }
569        const rightOp = declaringStmt.getRightOp();
570        if (!(rightOp instanceof ArkInstanceFieldRef)) {
571            return null;
572        }
573        const fieldBaseName = rightOp.getBase().getName();
574        const fieldName = rightOp.getFieldName();
575        if (fieldBaseName !== 'Symbol' || fieldName !== 'iterator') {
576            return null;
577        }
578
579        for (const api of DeprecatedAPIList.DeprecatedAPIs) {
580            if (api.name !== 'Symbol.iterator') {
581                continue;
582            }
583            if (api.base === APIBaseCategory.Array && targetLocal.getType() instanceof ArrayType) {
584                return targetLocal;
585            }
586            if (api.base === APIBaseCategory.Set) {
587                const localType = targetLocal.getType();
588                if (localType instanceof ClassType && localType.getClassSignature().getClassName() === 'Set') {
589                    return targetLocal;
590                }
591            }
592            if (api.base === APIBaseCategory.Map) {
593                const localType = targetLocal.getType();
594                if (localType instanceof ClassType && localType.getClassSignature().getClassName() === 'Map') {
595                    return targetLocal;
596                }
597            }
598        }
599        return null;
600    }
601
602    private getTargetLocalInArrayFrom(invokeExpr: ArkInstanceInvokeExpr): Local | null {
603        const callApiMethod = invokeExpr.getMethodSignature();
604        const callApiClass = callApiMethod.getDeclaringClassSignature();
605        for (const api of DeprecatedAPIList.DeprecatedAPIs) {
606            if (!api.isStatic) {
607                continue;
608            }
609            if (api.name !== callApiMethod.getMethodSubSignature().getMethodName()) {
610                continue;
611            }
612            // Array.from 形式的调用,from方法实际的class为ArrayConstructor
613            if (api.base !== callApiClass.getClassName() && `${api.base}Constructor` !== callApiClass.getClassName()) {
614                continue;
615            }
616            // 在本条规则检查范围内的static API的调用一定是带参数的,并且其中某个参数即为需要进行进一步查找的value
617            if (api.params === undefined) {
618                continue;
619            }
620            if (this.compareParamTypes(api.params, callApiMethod.getMethodSubSignature().getParameters())) {
621                const args = invokeExpr.getArgs();
622                // 形参匹配的情况下,进一步比较传入的实参,因为当前废弃接口大多数为去掉any类型的第二个可选参数
623                // TODO:这里需要考虑如何做的更通用
624                if (args.length !== api.params.length) {
625                    continue;
626                }
627                const index = api.targetParamIndex;
628                // 成功匹配到指定的API后,如果未提供下一步需要查找的目标param的index,则返回null。理论上不应该走到这个分支。
629                if (index === undefined) {
630                    logger.error(`Missing targetParamIndex, api: ${api.name}, category ${api.base}`);
631                    return null;
632                }
633
634                if (args.length <= index) {
635                    logger.error(`Invalid targetParamIndex ${index}, totally invoke args size ${args.length}, api: ${api.name}, category ${api.base}`);
636                    return null;
637                }
638                const target = args[index];
639                if (target instanceof Local) {
640                    return target;
641                }
642                logger.error(`Need to handle non-local target ${target.getType().getTypeString()}`);
643                return null;
644            }
645        }
646        return null;
647    }
648
649    private isInstanceCallMethodInDeprecatedAPIs(callBase: Local, stmt: Stmt, callMethod: MethodSignature, args: Value[]): boolean {
650        const callApiName = callMethod.getMethodSubSignature().getMethodName();
651        const callApiParams = callMethod.getMethodSubSignature().getParameters();
652        for (const api of DeprecatedAPIList.DeprecatedAPIs) {
653            // 对于map[Symbol.iterator]这样的API调用,callApiName是临时变量,需要进一步匹配
654            if (api.name !== callApiName) {
655                continue;
656            }
657            // 对于for...of的语句,ArkAnalyzer会为其生成Symbol.iterator的调用语句,此处从源码中查找关键字以区分是源码中有还是自动生成的
658            if (api.name === 'Symbol.iterator' && !stmt.getOriginalText()?.includes('Symbol.iterator')) {
659                continue;
660            }
661            if (api.isStatic) {
662                continue;
663            }
664            if (!this.isBaseTypeMatchAPIBase(api.base, callBase)) {
665                continue;
666            }
667
668            // Array concat API ArkAnalyzer当前无法很好处理...items形式的入参,此处作为特例处理
669            if (api.name === 'concat' && api.base === APIBaseCategory.Array) {
670                return this.isMatchArrayConcatAPI(args);
671            }
672
673            const apiParams = api.params;
674            if (apiParams === undefined) {
675                return true;
676            }
677            let allParamTypeMatch = this.compareParamTypes(apiParams, callApiParams);
678            if (allParamTypeMatch) {
679                // 形参匹配的情况下,进一步比较传入的实参,因为当前废弃接口大多数为去掉any类型的第二个可选参数
680                // TODO:这里需要考虑如何做的更通用
681                if (args.length !== apiParams.length) {
682                    continue;
683                }
684                return true;
685            }
686            // 形参类型不匹配的情形,可能是由于ArkAnalyzer的类型推导未能找到正确的API,需要根据实参类型进行二次匹配
687            if (apiParams.length !== args.length) {
688                continue;
689            }
690            allParamTypeMatch = true;
691            for (let i = 0; i < apiParams.length; i++) {
692                // 对于lambda函数作为参数类型,此处不严格校验lambda的参数类型,仅判断是否为FunctionType
693                if (apiParams[i] instanceof FunctionType && args[i].getType() instanceof FunctionType) {
694                    continue;
695                }
696                if (!this.isTypeMatch(apiParams[i], args[i].getType())) {
697                    allParamTypeMatch = false;
698                    break;
699                }
700            }
701            if (allParamTypeMatch) {
702                return true;
703            }
704        }
705        return false;
706    }
707
708    // 判断入参是否都为数组,不允许有单个元素
709    private isMatchArrayConcatAPI(args: Value[]): boolean {
710        for (const arg of args) {
711            if (!(arg.getType() instanceof ArrayType)) {
712                return true;
713            }
714        }
715        return false;
716    }
717
718    private isTypeMatch(apiType: Type, callApiType: Type): boolean {
719        const apiTypeStr = apiType.getTypeString();
720        const callApiTypeStr = callApiType.getTypeString();
721        if (callApiType instanceof FunctionType && apiType instanceof FunctionType) {
722            // 若类型为FunctionType,仅需匹配string中的形参部分
723            const regex = /\(([^()]*)\)/;
724            const apiMatch = apiTypeStr.match(regex);
725            const callApiMatch = callApiTypeStr.match(regex);
726            if (apiMatch === null || callApiMatch === null) {
727                return false;
728            }
729            // 移除字符串中的类型的文件签名、类签名、泛型等信息后进行比较
730            let apiParamsStr = apiMatch[0].replace(/@[^:]+:/, '').replace(/<[^>]+>/, '');
731            let callApiParamsStr = callApiMatch[0].replace(/@[^:]+:/, '').replace(/<[^>]+>/, '');
732            return apiParamsStr === callApiParamsStr;
733        } else if (callApiType instanceof ClassType && apiType instanceof ClassType) {
734            // 若类型为FunctionType,仅需匹配class name,因为apiTypeStr类型推导后有可能为@%unk/%unk: ArrayLike,而callApiTypeStr有明确的declaring file
735            return callApiType.getClassSignature().getClassName() === apiType.getClassSignature().getClassName();
736        } else if (apiType instanceof AnyType) {
737            return true;
738        } else {
739            // 其他场景需严格判断字符串相等
740            return apiTypeStr === callApiTypeStr;
741        }
742    }
743
744    private isBaseTypeMatchAPIBase(apiBase: APIBaseCategory, callBase: Local): boolean {
745        if (apiBase === APIBaseCategory.Array && callBase.getType() instanceof ArrayType) {
746            return true;
747        }
748        if (apiBase === APIBaseCategory.Map) {
749            const callBaseType = callBase.getType();
750            return callBaseType instanceof ClassType && callBaseType.getClassSignature().getClassName() === 'Map';
751        }
752        if (apiBase === APIBaseCategory.Set) {
753            const callBaseType = callBase.getType();
754            return callBaseType instanceof ClassType && callBaseType.getClassSignature().getClassName() === 'Set';
755        }
756        return false;
757    }
758
759    private checkFromStmt(stmt: Stmt, globalVarMap: Map<string, Stmt[]>, checkAll: { value: boolean }, visited: Set<Stmt>, depth: number = 0): boolean {
760        if (depth > CALL_DEPTH_LIMIT) {
761            checkAll.value = false;
762            return true;
763        }
764        const node = this.dvfg.getOrNewDVFGNode(stmt);
765        let worklist: DVFGNode[] = [node];
766        while (worklist.length > 0) {
767            const current = worklist.shift()!;
768            const currentStmt = current.getStmt();
769            if (visited.has(currentStmt)) {
770                continue;
771            }
772            visited.add(currentStmt);
773
774            if (this.isLeftOpOrReturnOpDefinedInStaticArkTS(currentStmt)) {
775                return true;
776            }
777
778            // 当前语句的右值是全局变量,查找全局变量的定义语句
779            const gv = this.isRightOpGlobalVar(currentStmt);
780            if (gv) {
781                const globalDefs = globalVarMap.get(gv.getName());
782                if (globalDefs === undefined) {
783                    const importInfos = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile().getImportInfos();
784                    const importValue = this.isLocalFromImport(gv, importInfos);
785                    if (importValue && importValue.getDeclaringStmt() !== null) {
786                        worklist.push(this.dvfg.getOrNewDVFGNode(importValue.getDeclaringStmt()!));
787                    }
788                } else {
789                    globalDefs.forEach(d => worklist.push(this.dvfg.getOrNewDVFGNode(d)));
790                }
791                continue;
792            }
793
794            // 当前语句的右值是函数返回值,查找调用函数的所有return语句
795            const callsite = this.cg.getCallSiteByStmt(currentStmt);
796            for (const cs of callsite) {
797                const declaringMtd = this.cg.getArkMethodByFuncID(cs.calleeFuncID);
798                if (!declaringMtd || !declaringMtd.getCfg()) {
799                    continue;
800                }
801                if (!this.visited.has(declaringMtd)) {
802                    this.dvfgBuilder.buildForSingleMethod(declaringMtd);
803                    this.visited.add(declaringMtd);
804                }
805                const returnStmts = declaringMtd.getReturnStmt();
806                for (const stmt of returnStmts) {
807                    const res = this.checkFromStmt(stmt, globalVarMap, checkAll, visited, depth + 1);
808                    if (res) {
809                        return true;
810                    }
811                }
812            }
813
814            // 当前语句的右值是函数参数赋值语句,查找所有调用处的入参情况
815            const paramRef = this.isFromParameter(currentStmt);
816            if (paramRef) {
817                const paramIdx = paramRef.getIndex();
818                const callsites = this.cg.getInvokeStmtByMethod(currentStmt.getCfg().getDeclaringMethod().getSignature());
819                this.processCallsites(callsites);
820                const argDefs = this.collectArgDefs(paramIdx, callsites);
821                for (const stmt of argDefs) {
822                    const res = this.checkFromStmt(stmt, globalVarMap, checkAll, visited, depth + 1);
823                    if (res) {
824                        return true;
825                    }
826                }
827            }
828
829            // 当前语句的右值是属性赋值语句,查找该属性的初始化语句
830            if (currentStmt instanceof ArkAssignStmt && currentStmt.getRightOp() instanceof AbstractFieldRef) {
831                const fieldSignature = (currentStmt.getRightOp() as AbstractFieldRef).getFieldSignature();
832                const classSignature = fieldSignature.getDeclaringSignature();
833                if (classSignature instanceof ClassSignature) {
834                    const field = this.scene.getClass(classSignature)?.getField(fieldSignature);
835                    if (field) {
836                        field.getInitializer().forEach(s => worklist.push(this.dvfg.getOrNewDVFGNode(s)));
837                    }
838                }
839            }
840
841            // 当前语句是return语句,查找return操作数的相关语句
842            if (currentStmt instanceof ArkReturnStmt) {
843                const returnOp = currentStmt.getOp();
844                if (returnOp instanceof Local) {
845                    let checkStmt =
846                        this.getLastAssignStmt(returnOp, stmt) ??
847                        this.checkTargetLocalAsGlobal(currentStmt.getCfg().getDeclaringMethod(), stmt, returnOp, globalVarMap);
848                    if (checkStmt !== null) {
849                        worklist.push(this.dvfg.getOrNewDVFGNode(checkStmt));
850                    }
851                }
852            }
853            current.getIncomingEdge().forEach(e => worklist.push(e.getSrcNode() as DVFGNode));
854        }
855        return false;
856    }
857
858    private isRightOpGlobalVar(stmt: Stmt): Local | undefined {
859        if (stmt instanceof ArkAssignStmt) {
860            const rightOp = stmt.getRightOp();
861            if (rightOp instanceof Local && !rightOp.getDeclaringStmt()) {
862                return rightOp;
863            }
864        }
865        return undefined;
866    }
867
868    private isLocalFromImport(local: Local, importInfos: ImportInfo[]): Local | undefined {
869        for (const importInfo of importInfos) {
870            if (importInfo.getImportClauseName() === local.getName()) {
871                const exportInfo = importInfo.getLazyExportInfo();
872                if (exportInfo === null) {
873                    return undefined;
874                }
875                const arkExport = exportInfo.getArkExport();
876                if (arkExport === null || arkExport === undefined) {
877                    return undefined;
878                }
879                if (!(arkExport instanceof Local)) {
880                    return undefined;
881                }
882                return arkExport;
883            }
884        }
885        return undefined;
886    }
887
888    private processCallsites(callsites: Stmt[]): void {
889        callsites.forEach(cs => {
890            const declaringMtd = cs.getCfg().getDeclaringMethod();
891            if (!this.visited.has(declaringMtd)) {
892                this.dvfgBuilder.buildForSingleMethod(declaringMtd);
893                this.visited.add(declaringMtd);
894            }
895        });
896    }
897
898    // 判断语句是否为赋值语句,且左值的定义来自于ArkTS1.2
899    private isLeftOpOrReturnOpDefinedInStaticArkTS(stmt: Stmt): boolean {
900        if (!(stmt instanceof ArkAssignStmt) && !(stmt instanceof ArkReturnStmt)) {
901            return false;
902        }
903        let operand: Value;
904        if (stmt instanceof ArkAssignStmt) {
905            operand = stmt.getLeftOp();
906        } else {
907            operand = stmt.getOp();
908        }
909        if (!(operand instanceof Local)) {
910            return false;
911        }
912        return this.isLocalDefinedInStaticArkTS(operand);
913    }
914
915    private isFromParameter(stmt: Stmt): ArkParameterRef | undefined {
916        if (!(stmt instanceof ArkAssignStmt)) {
917            return undefined;
918        }
919        const rightOp = stmt.getRightOp();
920        if (rightOp instanceof ArkParameterRef) {
921            return rightOp;
922        }
923        return undefined;
924    }
925
926    private collectArgDefs(argIdx: number, callsites: Stmt[]): Stmt[] {
927        const getKey = (v: Value): Value | FieldSignature => {
928            return v instanceof ArkInstanceFieldRef ? v.getFieldSignature() : v;
929        };
930        return callsites.flatMap(callsite => {
931            const target: Value | FieldSignature = getKey(callsite.getInvokeExpr()!.getArg(argIdx));
932            let refs = Array.from(this.dvfg.getOrNewDVFGNode(callsite).getIncomingEdge())
933                .map(e => (e.getSrcNode() as DVFGNode).getStmt())
934                .filter(s => {
935                    return s instanceof ArkAssignStmt && target === getKey(s.getLeftOp());
936                });
937            // 以上步骤未找到defs语句,说明入参变量来源自import信息
938            if (refs.length === 0 && target instanceof Local) {
939                const importInfos = callsite.getCfg().getDeclaringMethod().getDeclaringArkFile().getImportInfos();
940                const importValue = this.isLocalFromImport(target, importInfos);
941                if (importValue && importValue.getDeclaringStmt() !== null) {
942                    return importValue.getDeclaringStmt()!;
943                }
944            }
945            return refs;
946        });
947    }
948
949    private addIssueReport(stmt: Stmt, operand: Value, checkAll: boolean = true): void {
950        const severity = this.rule.alert ?? this.metaData.severity;
951        const warnInfo = this.getLineAndColumn(stmt, operand);
952        const problem = 'builtin-api';
953        let desc = `Builtin API is not support in ArkTS1.2 (${this.rule.ruleId.replace('@migration/', '')})`;
954        if (!checkAll) {
955            desc = `Can not check when function call chain depth exceeds ${CALL_DEPTH_LIMIT}, please check it manually (${this.rule.ruleId.replace('@migration/', '')})`;
956        }
957
958        let defects = new Defects(
959            warnInfo.line,
960            warnInfo.startCol,
961            warnInfo.endCol,
962            problem,
963            desc,
964            severity,
965            this.rule.ruleId,
966            warnInfo.filePath,
967            this.metaData.ruleDocPath,
968            true,
969            false,
970            false
971        );
972        this.issues.push(new IssueReport(defects, undefined));
973    }
974
975    private getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
976        const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
977        const originPosition = stmt.getOperandOriginalPosition(operand);
978        if (arkFile && originPosition) {
979            const originPath = arkFile.getFilePath();
980            const line = originPosition.getFirstLine();
981            const startCol = originPosition.getFirstCol();
982            const endCol = startCol;
983            return { line, startCol, endCol, filePath: originPath };
984        } else {
985            logger.debug('ArkFile is null.');
986        }
987        return { line: -1, startCol: -1, endCol: -1, filePath: '' };
988    }
989}
990