diff --git a/ets2panda/linter/arkanalyzer/OAT.xml b/ets2panda/linter/arkanalyzer/OAT.xml
index 642093936053babe5d8da7bdfa8add284ca5bb60..a33eba98c976d5cdaec8e23aa741d143defe9b80 100644
--- a/ets2panda/linter/arkanalyzer/OAT.xml
+++ b/ets2panda/linter/arkanalyzer/OAT.xml
@@ -19,6 +19,7 @@
+
diff --git a/ets2panda/linter/arkanalyzer/src/Config.ts b/ets2panda/linter/arkanalyzer/src/Config.ts
index bff8ddf474ca034ddd0a8478c982dbdff0b2e6a5..1a222ff6658b3be89fc2c6a8ee5eb31af6961ad7 100644
--- a/ets2panda/linter/arkanalyzer/src/Config.ts
+++ b/ets2panda/linter/arkanalyzer/src/Config.ts
@@ -86,6 +86,7 @@ export class SceneConfig {
public buildConfig(targetProjectName: string, targetProjectDirectory: string, sdks: Sdk[], fullFilePath?: string[]): void {
this.targetProjectName = targetProjectName;
this.targetProjectDirectory = targetProjectDirectory;
+ this.projectFiles = getAllFiles(targetProjectDirectory, this.options.supportFileExts!, this.options.ignoreFileNames);
this.sdksObj = sdks;
if (fullFilePath) {
this.projectFiles.push(...fullFilePath);
diff --git a/ets2panda/linter/arkanalyzer/src/Scene.ts b/ets2panda/linter/arkanalyzer/src/Scene.ts
index 72c782822638b95a35109c472eeaa37aaaf1e76f..84ef44b99aede1220d3ac8275bb9297a17107fef 100644
--- a/ets2panda/linter/arkanalyzer/src/Scene.ts
+++ b/ets2panda/linter/arkanalyzer/src/Scene.ts
@@ -41,6 +41,8 @@ import { ImportInfo } from './core/model/ArkImport';
import { ALL, CONSTRUCTOR_NAME, TSCONFIG_JSON } from './core/common/TSConst';
import { BUILD_PROFILE_JSON5, OH_PACKAGE_JSON5 } from './core/common/EtsConst';
import { SdkUtils } from './core/common/SdkUtils';
+import { PointerAnalysisConfig } from './callgraph/pointerAnalysis/PointerAnalysisConfig';
+import { ValueUtil } from './core/common/ValueUtil';
const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'Scene');
@@ -99,6 +101,17 @@ export class Scene {
constructor() {}
+ /*
+ * Set all static field to be null, then all related objects could be freed by GC.
+ * This method could be called before drop Scene.
+ */
+ public dispose(): void {
+ PointerAnalysisConfig.dispose();
+ SdkUtils.dispose();
+ ValueUtil.dispose();
+ ModelUtils.dispose();
+ }
+
public getOptions(): SceneOptions {
return this.options;
}
@@ -322,12 +335,13 @@ export class Scene {
}
}
+ ModelUtils.dispose();
this.buildStage = SceneBuildStage.METHOD_DONE;
}
private genArkFiles(): void {
this.projectFiles.forEach(file => {
- logger.info('=== parse file:', file);
+ logger.trace('=== parse file:', file);
try {
const arkFile: ArkFile = new ArkFile(FileUtils.getFileLanguage(file, this.fileLanguages));
arkFile.setScene(this);
@@ -469,9 +483,11 @@ export class Scene {
}
private findDependenciesByRule(originPath: string, arkFile: ArkFile): void {
- const extNameArray = ['.ets', '.ts', '.d.ets', '.d.ts', '.js'];
- if (!this.findFilesByPathArray(originPath, this.indexPathArray, arkFile) && !this.findFilesByExtNameArray(originPath, extNameArray, arkFile)) {
- logger.info(originPath + 'module mapperInfo is not found!');
+ if (
+ !this.findFilesByPathArray(originPath, this.indexPathArray, arkFile) &&
+ !this.findFilesByExtNameArray(originPath, this.options.supportFileExts!, arkFile)
+ ) {
+ logger.trace(originPath + 'module mapperInfo is not found!');
}
}
@@ -542,7 +558,7 @@ export class Scene {
this.addFileNode2DependencyGrap(originPath, arkFile);
}
if (!this.findFilesByPathArray(originPath, this.indexPathArray, arkFile)) {
- logger.info(originPath + 'module mapperInfo is not found!');
+ logger.trace(originPath + 'module mapperInfo is not found!');
}
}
}
@@ -574,7 +590,7 @@ export class Scene {
private buildSdk(sdkName: string, sdkPath: string): void {
const allFiles = getAllFiles(sdkPath, this.options.supportFileExts!, this.options.ignoreFileNames);
allFiles.forEach(file => {
- logger.info('=== parse sdk file:', file);
+ logger.trace('=== parse sdk file:', file);
try {
const arkFile: ArkFile = new ArkFile(FileUtils.getFileLanguage(file, this.fileLanguages));
arkFile.setScene(this);
@@ -1044,6 +1060,7 @@ export class Scene {
this.getMethodsMap(true);
this.buildStage = SceneBuildStage.TYPE_INFERRED;
}
+ SdkUtils.dispose();
}
/**
@@ -1458,7 +1475,7 @@ export class ModuleScene {
private genArkFiles(supportFileExts: string[]): void {
getAllFiles(this.modulePath, supportFileExts, this.projectScene.getOptions().ignoreFileNames).forEach(file => {
- logger.info('=== parse file:', file);
+ logger.trace('=== parse file:', file);
try {
const arkFile: ArkFile = new ArkFile(FileUtils.getFileLanguage(file, this.projectScene.getFileLanguages()));
arkFile.setScene(this.projectScene);
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/AbstractAnalysis.ts b/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/AbstractAnalysis.ts
index 4fb8a6f7b7185608987af0b265e98a098ee24047..9a3e7fa8e184f04adbc9c186b35f3e11e13fd64e 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/AbstractAnalysis.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/AbstractAnalysis.ts
@@ -30,13 +30,14 @@ const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'CG');
export abstract class AbstractAnalysis {
protected scene: Scene;
- protected cg!: CallGraph;
+ protected cg: CallGraph;
protected cgBuilder!: CallGraphBuilder;
protected workList: FuncID[] = [];
protected processedMethod!: IPtsCollection;
- constructor(s: Scene) {
+ constructor(s: Scene, cg: CallGraph) {
this.scene = s;
+ this.cg = cg;
}
public getScene(): Scene {
@@ -96,11 +97,13 @@ export abstract class AbstractAnalysis {
}
public projectStart(displayGeneratedMethod: boolean): void {
- this.scene.getMethods().forEach((method) => {
- let cgNode = this.cg.getCallGraphNodeByMethod(method.getSignature()) as CallGraphNode;
+ this.cgBuilder.buildCGNodes(this.scene.getMethods());
+
+ for (let n of this.cg.getNodesIter()) {
+ let cgNode = n as CallGraphNode;
if (cgNode.isSdkMethod()) {
- return;
+ continue;
}
this.preProcessMethod(cgNode.getID());
@@ -108,7 +111,9 @@ export abstract class AbstractAnalysis {
this.processMethod(cgNode.getID()).forEach((cs: CallSite) => {
this.processCallSite(cgNode.getID(), cs, displayGeneratedMethod, true);
});
- });
+ }
+
+ this.cgBuilder.setEntries();
}
private processCallSite(method: FuncID, cs: CallSite, displayGeneratedMethod: boolean, isProject: boolean = false): void {
@@ -128,7 +133,7 @@ export abstract class AbstractAnalysis {
if (displayGeneratedMethod || !me?.isGenerated()) {
this.workList.push(cs.calleeFuncID);
- logger.info(`New workList item ${cs.calleeFuncID}: ${this.cg.getArkMethodByFuncID(cs.calleeFuncID)?.getSignature().toString()}`);
+ logger.trace(`New workList item ${cs.calleeFuncID}: ${this.cg.getArkMethodByFuncID(cs.calleeFuncID)?.getSignature().toString()}`);
}
}
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/ClassHierarchyAnalysis.ts b/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/ClassHierarchyAnalysis.ts
index 029d85541ea8172eeeaf5b3bb1cd21b0aa67df9e..df58f9e5712d064aedbfc6b7a9fda02f9676861d 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/ClassHierarchyAnalysis.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/ClassHierarchyAnalysis.ts
@@ -20,11 +20,12 @@ import { ArkClass } from '../../core/model/ArkClass';
import { NodeID } from '../../core/graph/BaseExplicitGraph';
import { CallGraph, CallSite } from '../model/CallGraph';
import { AbstractAnalysis } from './AbstractAnalysis';
+import { CallGraphBuilder } from '../model/builder/CallGraphBuilder';
export class ClassHierarchyAnalysis extends AbstractAnalysis {
- constructor(scene: Scene, cg: CallGraph) {
- super(scene);
- this.cg = cg;
+ constructor(scene: Scene, cg: CallGraph, cb: CallGraphBuilder) {
+ super(scene, cg);
+ this.cgBuilder = cb;
}
public resolveCall(callerMethod: NodeID, invokeStmt: Stmt): CallSite[] {
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/RapidTypeAnalysis.ts b/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/RapidTypeAnalysis.ts
index b4ecc267ac48cc1ed19e38c685663ef8195fa732..b14816d26bbac4e37aeb443a553c5875447fc052 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/RapidTypeAnalysis.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/algorithm/RapidTypeAnalysis.ts
@@ -33,8 +33,7 @@ export class RapidTypeAnalysis extends AbstractAnalysis {
private ignoredCalls: Map> = new Map();
constructor(scene: Scene, cg: CallGraph) {
- super(scene);
- this.cg = cg;
+ super(scene, cg);
}
public resolveCall(callerMethod: NodeID, invokeStmt: Stmt): CallSite[] {
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/common/Statistics.ts b/ets2panda/linter/arkanalyzer/src/callgraph/common/Statistics.ts
index 61246908adb1ecf0ba1446fa8a77d7860d0fb458..112539b16867550716b2818687ef2af0acf279d2 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/common/Statistics.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/common/Statistics.ts
@@ -196,6 +196,7 @@ export class CGStat extends StatTraits {
numVirtual: number = 0;
numIntrinsic: number = 0;
numConstructor: number = 0;
+ numBlank: number = 0;
public startStat(): void {
this.startTime = new Date().getTime();
@@ -221,6 +222,7 @@ export class CGStat extends StatTraits {
this.numIntrinsic++;
break;
default:
+ this.numBlank++;
}
this.numTotalNode++;
}
@@ -232,7 +234,8 @@ export class CGStat extends StatTraits {
output = output + `Real function\t\t${this.numReal}\n`;
output = output + `Intrinsic function\t${this.numIntrinsic}\n`;
output = output + `Constructor function\t${this.numConstructor}\n`;
- output = output + `Blank function\t\t${this.numVirtual}\n`;
+ output = output + `Virtual function\t\t${this.numVirtual}\n`;
+ output = output + `Blank function\t\t${this.numBlank}\n`;
output = output + `Total\t\t\t${this.numTotalNode}\n`;
return output;
}
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/model/CallGraph.ts b/ets2panda/linter/arkanalyzer/src/callgraph/model/CallGraph.ts
index 954f47804a2413dbdba1747ea31f02b19edc1cd6..0ddeb630076cbd606e014cdcaccc2bb5e60f7685 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/model/CallGraph.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/model/CallGraph.ts
@@ -15,63 +15,27 @@
import { MethodSignature } from '../../core/model/ArkSignature';
import { Stmt } from '../../core/base/Stmt';
-import { Value } from '../../core/base/Value';
import { Scene } from '../../Scene';
import { ArkMethod } from '../../core/model/ArkMethod';
import { GraphPrinter } from '../../save/GraphPrinter';
import { PrinterBuilder } from '../../save/PrinterBuilder';
import { BaseEdge, BaseNode, BaseExplicitGraph, NodeID } from '../../core/graph/BaseExplicitGraph';
import { CGStat } from '../common/Statistics';
-import { ContextID } from '../pointerAnalysis/Context';
import { UNKNOWN_FILE_NAME } from '../../core/common/Const';
+import { CallSite, CallSiteID, DynCallSite, ICallSite } from './CallSite';
export type Method = MethodSignature;
-export type CallSiteID = number;
export type FuncID = number;
type StmtSet = Set;
+export { CallSite, DynCallSite, ICallSite };
+
export enum CallGraphNodeKind {
- real,
+ real, // method from project and has body
vitual,
- intrinsic,
- constructor,
-}
-
-export class CallSite {
- public callStmt: Stmt;
- public args: Value[] | undefined;
- public calleeFuncID: FuncID;
- public callerFuncID: FuncID;
-
- constructor(s: Stmt, a: Value[] | undefined, ce: FuncID, cr: FuncID) {
- this.callStmt = s;
- this.args = a;
- this.calleeFuncID = ce;
- this.callerFuncID = cr;
- }
-}
-
-export class DynCallSite {
- public callerFuncID: FuncID;
- public callStmt: Stmt;
- public args: Value[] | undefined;
- public protentialCalleeFuncID: FuncID | undefined;
-
- constructor(caller: FuncID, s: Stmt, a: Value[] | undefined, ptcCallee: FuncID | undefined) {
- this.callerFuncID = caller;
- this.callStmt = s;
- this.args = a;
- this.protentialCalleeFuncID = ptcCallee;
- }
-}
-
-export class CSCallSite extends CallSite {
- public cid: ContextID;
-
- constructor(id: ContextID, cs: CallSite) {
- super(cs.callStmt, cs.args, cs.calleeFuncID, cs.callerFuncID);
- this.cid = id;
- }
+ intrinsic, // method created by AA, which arkMethod.isGenrated is true
+ constructor, // constructor
+ blank, // method without body
}
export class CallGraphEdge extends BaseEdge {
@@ -119,7 +83,6 @@ export class CallGraphEdge extends BaseEdge {
export class CallGraphNode extends BaseNode {
private method: Method;
private ifSdkMethod: boolean = false;
- private isBlank: boolean = false;
constructor(id: number, m: Method, k: CallGraphNodeKind = CallGraphNodeKind.real) {
super(id, k);
@@ -139,11 +102,7 @@ export class CallGraphNode extends BaseNode {
}
public get isBlankMethod(): boolean {
- return this.isBlank;
- }
-
- public set isBlankMethod(is: boolean) {
- this.isBlank = is;
+ return this.kind === CallGraphNodeKind.blank;
}
public getDotAttr(): string {
@@ -195,11 +154,6 @@ export class CallGraph extends BaseExplicitGraph {
// check if sdk method
cgNode.setSdkMethod(this.scene.hasSdkFile(method.getDeclaringClassSignature().getDeclaringFileSignature()));
- let arkMethod = this.scene.getMethod(method);
- if (!arkMethod || !arkMethod.getCfg()) {
- cgNode.isBlankMethod = true;
- }
-
this.addNode(cgNode);
this.methodToCGNodeMap.set(method.toString(), cgNode.getID());
this.cgStat.addNodeStat(kind);
@@ -285,7 +239,7 @@ export class CallGraph extends BaseExplicitGraph {
}
let args = callStmt.getInvokeExpr()?.getArgs();
- let cs = new DynCallSite(callerNode.getID(), callStmt, args, calleeNode?.getID());
+ let cs = new DynCallSite(callStmt, args, calleeNode?.getID(), callerNode.getID());
this.stmtToDynCallSitemap.set(callStmt, cs);
}
@@ -467,4 +421,4 @@ export class CallGraph extends BaseExplicitGraph {
public getGraphName(): string {
return 'CG';
}
-}
+}
\ No newline at end of file
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/model/CallSite.ts b/ets2panda/linter/arkanalyzer/src/callgraph/model/CallSite.ts
new file mode 100644
index 0000000000000000000000000000000000000000..40dbb3b492233cdcd519b3ac0ad6dc5b90a5fee4
--- /dev/null
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/model/CallSite.ts
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2025 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Stmt } from '../../core/base/Stmt';
+import { Value } from '../../core/base/Value';
+import { ContextID } from '../pointerAnalysis/Context';
+import { FuncID } from './CallGraph';
+
+export type CallSiteID = number;
+
+export interface ICallSite {
+ callStmt: Stmt;
+ args: Value[] | undefined;
+ callerFuncID: FuncID;
+}
+
+export class CallSite implements ICallSite {
+ public callStmt: Stmt;
+ public args: Value[] | undefined;
+ public calleeFuncID: FuncID;
+ public callerFuncID: FuncID;
+
+ constructor(s: Stmt, a: Value[] | undefined, ce: FuncID, cr: FuncID) {
+ this.callStmt = s;
+ this.args = a;
+ this.calleeFuncID = ce;
+ this.callerFuncID = cr;
+ }
+}
+
+export class DynCallSite implements ICallSite {
+ public callStmt: Stmt;
+ public args: Value[] | undefined;
+ public protentialCalleeFuncID: FuncID | undefined;
+ public callerFuncID: FuncID;
+
+ constructor(s: Stmt, a: Value[] | undefined, ptcCallee: FuncID | undefined, caller: FuncID) {
+ this.callerFuncID = caller;
+ this.callStmt = s;
+ this.args = a;
+ this.protentialCalleeFuncID = ptcCallee;
+ }
+}
+
+export class CSCallSite extends CallSite {
+ public cid: ContextID;
+
+ constructor(id: ContextID, cs: CallSite) {
+ super(cs.callStmt, cs.args, cs.calleeFuncID, cs.callerFuncID);
+ this.cid = id;
+ }
+}
\ No newline at end of file
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/model/builder/CallGraphBuilder.ts b/ets2panda/linter/arkanalyzer/src/callgraph/model/builder/CallGraphBuilder.ts
index c631c4333fc41b233ee3fba8988fb5f0ebb530b3..5947b9c66e050cdca5474e002320b789939ebed0 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/model/builder/CallGraphBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/model/builder/CallGraphBuilder.ts
@@ -38,19 +38,27 @@ export class CallGraphBuilder {
this.setEntries();
}
- public buildDirectCallGraph(methods: ArkMethod[]): void {
+ /*
+ * Create CG Node for ArkMethods
+ */
+ public buildCGNodes(methods: ArkMethod[]): void {
for (const method of methods) {
let m = method.getSignature();
let kind = CallGraphNodeKind.real;
if (method.isGenerated()) {
kind = CallGraphNodeKind.intrinsic;
- }
- if (method.getName() === 'constructor') {
+ } else if (method.getBody() === undefined || method.getCfg() === undefined) {
+ kind = CallGraphNodeKind.blank;
+ } else if (method.getName() === 'constructor') {
kind = CallGraphNodeKind.constructor;
}
this.cg.addCallGraphNode(m, kind);
}
+ }
+
+ public buildDirectCallGraph(methods: ArkMethod[]): void {
+ this.buildCGNodes(methods);
for (const method of methods) {
let cfg = method.getCfg();
@@ -70,8 +78,7 @@ export class CallGraphBuilder {
if (callee && invokeExpr instanceof ArkStaticInvokeExpr) {
this.cg.addDirectOrSpecialCallEdge(method.getSignature(), callee, stmt);
} else if (
- callee &&
- invokeExpr instanceof ArkInstanceInvokeExpr &&
+ callee && invokeExpr instanceof ArkInstanceInvokeExpr &&
(this.isConstructor(callee) || this.scene.getMethod(callee)?.isGenerated())
) {
this.cg.addDirectOrSpecialCallEdge(method.getSignature(), callee, stmt, false);
@@ -89,12 +96,12 @@ export class CallGraphBuilder {
});
this.cg.setEntries(cgEntries);
- let classHierarchyAnalysis: ClassHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg);
+ let classHierarchyAnalysis: ClassHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg, this);
classHierarchyAnalysis.start(displayGeneratedMethod);
}
public buildCHA4WholeProject(displayGeneratedMethod: boolean = false): void {
- let classHierarchyAnalysis: ClassHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg);
+ let classHierarchyAnalysis: ClassHierarchyAnalysis = new ClassHierarchyAnalysis(this.scene, this.cg, this);
classHierarchyAnalysis.projectStart(displayGeneratedMethod);
}
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PTAUtils.ts b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PTAUtils.ts
index a8940d5d3c21b561450e4ac051f3684fa728fb7e..0c120d6000b1192188cf68ebf52401f5d68aab3e 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PTAUtils.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PTAUtils.ts
@@ -51,6 +51,7 @@ export function getBuiltInApiType(method: MethodSignature): BuiltApiType {
return BuiltApiType.FunctionApply;
case 'bind':
return BuiltApiType.FunctionBind;
+ default:
}
}
}
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PagBuilder.ts b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PagBuilder.ts
index bd3917b09083d00bd5fdf67ae0129a69b8630daa..5249cd8d2ce2cbf5ce0f7e3426533e353564899c 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PagBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PagBuilder.ts
@@ -13,7 +13,7 @@
* limitations under the License.
*/
-import { CallGraph, CallGraphNode, CallGraphNodeKind, CallSite, DynCallSite, FuncID } from '../model/CallGraph';
+import { CallGraph, CallGraphNode, CallGraphNodeKind, CallSite, DynCallSite, FuncID, ICallSite } from '../model/CallGraph';
import { Scene } from '../../Scene';
import { ArkAssignStmt, ArkInvokeStmt, ArkReturnStmt, Stmt } from '../../core/base/Stmt';
import {
@@ -527,7 +527,7 @@ export class PagBuilder {
});
}
- public addDynamicCallEdge(cs: DynCallSite | CallSite, baseClassPTNode: NodeID, cid: ContextID): NodeID[] {
+ public addDynamicCallEdge(cs: ICallSite, baseClassPTNode: NodeID, cid: ContextID): NodeID[] {
let srcNodes: NodeID[] = [];
let ivkExpr = cs.callStmt.getInvokeExpr();
@@ -577,7 +577,7 @@ export class PagBuilder {
* all possible callee methods of a dynamic call site
* handle both PtrInvokeExpr and InstanceInvokeExpr
*/
- private getDynamicCallee(ptNode: PagNode, value: Value, ivkExpr: AbstractInvokeExpr, cs: DynCallSite | CallSite): ArkMethod[] {
+ private getDynamicCallee(ptNode: PagNode, value: Value, ivkExpr: AbstractInvokeExpr, cs: ICallSite): ArkMethod[] {
let callee: ArkMethod[] = [];
if (ptNode instanceof PagFuncNode) {
@@ -646,7 +646,10 @@ export class PagBuilder {
// Pass base's pts to callee's this pointer
if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkInstanceInvokeExpr) {
let srcBaseNode = this.addThisRefCallEdge(baseClassPTNode, cid, ivkExpr.getBase(), callee!, calleeCid, staticCS.callerFuncID);
- srcNodes.push(srcBaseNode);
+
+ if (srcBaseNode !== -1) {
+ srcNodes.push(srcBaseNode);
+ }
} else if (!dstCGNode.isSdkMethod() && ivkExpr instanceof ArkPtrInvokeExpr) {
let originCS = (ptNode as PagFuncNode).getCS();
if (!originCS) {
@@ -712,6 +715,7 @@ export class PagBuilder {
*/
this.handleFunctionBind(staticCS, cid, baseClassPTNode, srcNodes);
break;
+ default:
}
return srcNodes;
@@ -799,7 +803,11 @@ export class PagBuilder {
private addThisEdge(staticCS: CallSite, cid: ContextID, realCallee: ArkMethod, srcNodes: NodeID[], baseClassPTNode: NodeID, calleeCid: ContextID): void {
if (!(staticCS.args![0] instanceof NullConstant) && !realCallee.isStatic()) {
- srcNodes.push(this.addThisRefCallEdge(baseClassPTNode, cid, staticCS.args![0] as Local, realCallee, calleeCid, staticCS.callerFuncID));
+ let srcNodeID = this.addThisRefCallEdge(baseClassPTNode, cid, staticCS.args![0] as Local, realCallee, calleeCid, staticCS.callerFuncID);
+
+ if (srcNodeID !== -1) {
+ srcNodes.push(srcNodeID);
+ }
}
}
@@ -915,6 +923,10 @@ export class PagBuilder {
callerFunID: FuncID
): NodeID {
let thisRefNodeID = this.recordThisRefNode(baseClassPTNode, callee, calleeCid);
+ if (thisRefNodeID === -1) {
+ return -1;
+ }
+
let thisRefNode = this.pag.getNode(thisRefNodeID) as PagThisRefNode;
let srcBaseLocal = baseLocal;
srcBaseLocal = this.getRealThisLocal(srcBaseLocal, callerFunID);
@@ -1233,7 +1245,7 @@ export class PagBuilder {
let sdkParamInvokeStmt = new ArkInvokeStmt(new ArkPtrInvokeExpr((arg.getType() as FunctionType).getMethodSignature(), paramValue as Local, []));
// create new DynCallSite
- let sdkParamCallSite = new DynCallSite(funcID, sdkParamInvokeStmt, undefined, undefined);
+ let sdkParamCallSite = new DynCallSite(sdkParamInvokeStmt, undefined, undefined, funcID);
dstPagNode.addRelatedDynCallSite(sdkParamCallSite);
}
@@ -1542,7 +1554,7 @@ export class PagBuilder {
* process Storage API
* @returns boolean: check if the cs represent a Storage API, no matter the API will success or fail
*/
- private processStorage(cs: CallSite | DynCallSite, calleeCGNode: CallGraphNode, cid: ContextID): boolean {
+ private processStorage(cs: ICallSite, calleeCGNode: CallGraphNode, cid: ContextID): boolean {
let storageName = calleeCGNode.getMethod().getDeclaringClassSignature().getClassName();
let storageType: StorageType = this.getStorageType(storageName, cs, cid);
@@ -1570,7 +1582,7 @@ export class PagBuilder {
return false;
}
- private processStorageSetOrCreate(cs: CallSite | DynCallSite, cid: ContextID): void {
+ private processStorageSetOrCreate(cs: ICallSite, cid: ContextID): void {
let propertyStr = this.getPropertyName(cs.args![0]);
if (!propertyStr) {
return;
@@ -1583,7 +1595,7 @@ export class PagBuilder {
this.addPropertyLinkEdge(propertyNode, storageObj, cid, cs.callStmt, StorageLinkEdgeType.Local2Property);
}
- private processStorageLink(cs: CallSite | DynCallSite, cid: ContextID): void {
+ private processStorageLink(cs: ICallSite, cid: ContextID): void {
let propertyStr = this.getPropertyName(cs.args![0]);
if (!propertyStr) {
return;
@@ -1601,7 +1613,7 @@ export class PagBuilder {
this.pag.addPagEdge(linkedOpNode, propertyNode, PagEdgeKind.Copy);
}
- private processStorageProp(cs: CallSite | DynCallSite, cid: ContextID): void {
+ private processStorageProp(cs: ICallSite, cid: ContextID): void {
let propertyStr = this.getPropertyName(cs.args![0]);
if (!propertyStr) {
return;
@@ -1618,7 +1630,7 @@ export class PagBuilder {
this.pag.addPagEdge(propertyNode, linkedOpNode, PagEdgeKind.Copy);
}
- private processStorageSet(cs: CallSite | DynCallSite, cid: ContextID): void {
+ private processStorageSet(cs: ICallSite, cid: ContextID): void {
let ivkExpr: AbstractInvokeExpr = cs.callStmt.getInvokeExpr()!;
if (ivkExpr instanceof ArkInstanceInvokeExpr) {
let base = ivkExpr.getBase();
@@ -1634,7 +1646,7 @@ export class PagBuilder {
}
}
- private processStorageGet(cs: CallSite | DynCallSite, cid: ContextID): void {
+ private processStorageGet(cs: ICallSite, cid: ContextID): void {
if (!(cs.callStmt instanceof ArkAssignStmt)) {
return;
}
@@ -1686,7 +1698,7 @@ export class PagBuilder {
* @param cid: for search PAG node in SubscribedAbstractProperty
* @returns StorageType enum
*/
- private getStorageType(storageName: string, cs: CallSite | DynCallSite, cid: ContextID): StorageType {
+ private getStorageType(storageName: string, cs: ICallSite, cid: ContextID): StorageType {
switch (storageName) {
case 'AppStorage':
return StorageType.APP_STORAGE;
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysis.ts b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysis.ts
index 902a05b902faa419cf511850bd2ccc1871a915fc..4fc4a16471955a8a6b426e88b5cab661b91adf19 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysis.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysis.ts
@@ -47,9 +47,8 @@ export class PointerAnalysis extends AbstractAnalysis {
private config: PointerAnalysisConfig;
constructor(p: Pag, cg: CallGraph, s: Scene, config: PointerAnalysisConfig) {
- super(s);
+ super(s, cg);
this.pag = p;
- this.cg = cg;
this.ptd = new DiffPTData>(config.ptsCollectionCtor);
this.pagBuilder = new PagBuilder(this.pag, this.cg, s, config.kLimit, config.analysisScale);
this.cgBuilder = new CallGraphBuilder(this.cg, s);
diff --git a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysisConfig.ts b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysisConfig.ts
index 1e68c981db8221514da887c76692e267a90cbe2f..0dad1053cff49245da9e67e7127cc72d046e4c88 100644
--- a/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysisConfig.ts
+++ b/ets2panda/linter/arkanalyzer/src/callgraph/pointerAnalysis/PointerAnalysisConfig.ts
@@ -64,6 +64,15 @@ export class PointerAnalysisConfig {
}
}
+ /*
+ * Set static field to be null, then all related objects could be freed by GC.
+ * Class PointerAnalysisConfig has been exported by ArkAnalyzer, the dispose method should be called by users themselves before free this class.
+ */
+ public static dispose(): void {
+ // @ts-ignore
+ this.instance = null;
+ }
+
/*
* Create Singleton instance
* The instance can be created multi-times and be overwrited
diff --git a/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts b/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts
index dcf4695331ac437403e3d04b0ce248b533806264..310e8228635e2fc143b248e87409f2e631f0ec16 100644
--- a/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/base/Expr.ts
@@ -40,8 +40,9 @@ import { ArkMethod } from '../model/ArkMethod';
import { UNKNOWN_FILE_NAME } from '../common/Const';
import { IRInference } from '../common/IRInference';
import { ImportInfo } from '../model/ArkImport';
-import { ArkClass } from '../model/ArkClass';
+import { ArkClass, ClassCategory } from '../model/ArkClass';
import { ArkField } from '../model/ArkField';
+import { ModelUtils } from '../common/ModelUtils';
/**
* @category core/base/expr
@@ -342,14 +343,30 @@ export class ArkNewExpr extends AbstractExpr {
const classSignature = this.classType.getClassSignature();
if (classSignature.getDeclaringFileSignature().getFileName() === UNKNOWN_FILE_NAME) {
const className = classSignature.getClassName();
- let type = TypeInference.inferUnclearRefName(className, arkMethod.getDeclaringArkClass());
+ let type: Type | null | undefined = ModelUtils.findDeclaredLocal(new Local(className), arkMethod, 1)?.getType();
+ if (TypeInference.isUnclearType(type)) {
+ type = TypeInference.inferUnclearRefName(className, arkMethod.getDeclaringArkClass());
+ }
if (type && type instanceof ClassType) {
- let realGenericTypes = this.classType.getRealGenericTypes();
- this.classType = realGenericTypes ? new ClassType(type.getClassSignature(), realGenericTypes) : type;
+ const instanceType = this.constructorSignature(type, arkMethod) ?? type;
+ this.classType.setClassSignature(instanceType.getClassSignature());
+ TypeInference.inferRealGenericTypes(this.classType.getRealGenericTypes(), arkMethod.getDeclaringArkClass());
}
}
return this;
}
+
+ private constructorSignature(type: ClassType, arkMethod: ArkMethod): ClassType | undefined {
+ const classConstructor = arkMethod.getDeclaringArkFile().getScene().getClass(type.getClassSignature());
+ if (classConstructor?.getCategory() === ClassCategory.INTERFACE) {
+ const type = classConstructor.getMethodWithName('construct-signature')?.getReturnType();
+ if (type) {
+ const returnType = TypeInference.replaceAliasType(type);
+ return returnType instanceof ClassType ? returnType : undefined;
+ }
+ }
+ return undefined;
+ }
}
export class ArkNewArrayExpr extends AbstractExpr {
@@ -695,9 +712,13 @@ export abstract class AbstractBinopExpr extends AbstractExpr {
case '^':
case '<<':
case '>>':
+ if (op1Type === NumberType.getInstance() && op2Type === NumberType.getInstance()) {
+ type = NumberType.getInstance();
+ }
if (op1Type === BigIntType.getInstance() && op2Type === BigIntType.getInstance()) {
type = BigIntType.getInstance();
}
+ break;
case '>>>':
if (op1Type === NumberType.getInstance() && op2Type === NumberType.getInstance()) {
type = NumberType.getInstance();
@@ -1042,10 +1063,14 @@ export class AliasTypeExpr extends AbstractExpr {
public getType(): Type {
function getTypeOfImportInfo(importInfo: ImportInfo): Type {
const arkExport = importInfo.getLazyExportInfo()?.getArkExport();
- if (arkExport) {
- return TypeInference.parseArkExport2Type(arkExport) ?? UnknownType.getInstance();
+ const importClauseName = importInfo.getImportClauseName();
+ let type;
+ if (importClauseName.includes('.') && arkExport instanceof ArkClass) {
+ type = TypeInference.inferUnclearRefName(importClauseName, arkExport);
+ } else if (arkExport) {
+ type = TypeInference.parseArkExport2Type(arkExport);
}
- return UnknownType.getInstance();
+ return type ?? UnknownType.getInstance();
}
const operator = this.getOriginalObject();
diff --git a/ets2panda/linter/arkanalyzer/src/core/base/Local.ts b/ets2panda/linter/arkanalyzer/src/core/base/Local.ts
index aa4fbebdf0597b1972cea69e7fcdb70bba21b64b..10bb1896276ea7b362a4edb63303c7e2c130498d 100644
--- a/ets2panda/linter/arkanalyzer/src/core/base/Local.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/base/Local.ts
@@ -14,7 +14,7 @@
*/
import { Stmt } from './Stmt';
-import { ClassType, Type, UnknownType } from './Type';
+import { ClassType, FunctionType, Type, UnknownType } from './Type';
import { Value } from './Value';
import { TypeInference } from '../common/TypeInference';
import { ArkExport, ExportType } from '../model/ArkExport';
@@ -54,11 +54,17 @@ export class Local implements Value, ArkExport {
const declaringArkClass = arkMethod.getDeclaringArkClass();
this.type = new ClassType(declaringArkClass.getSignature(), declaringArkClass.getRealTypes());
} else if (TypeInference.isUnclearType(this.type)) {
- const type = TypeInference.inferBaseType(this.name, arkMethod.getDeclaringArkClass()) ?? ModelUtils.findDeclaredLocal(this, arkMethod)?.getType();
+ const type = TypeInference.inferBaseType(this.name, arkMethod.getDeclaringArkClass()) ??
+ ModelUtils.findDeclaredLocal(this, arkMethod)?.getType();
if (type) {
this.type = type;
}
}
+ if (this.type instanceof FunctionType) {
+ this.type.getMethodSignature().getMethodSubSignature().getParameters()
+ .forEach(p => TypeInference.inferParameterType(p, arkMethod));
+ TypeInference.inferSignatureReturnType(this.type.getMethodSignature(), arkMethod);
+ }
return this;
}
diff --git a/ets2panda/linter/arkanalyzer/src/core/common/ArkIRTransformer.ts b/ets2panda/linter/arkanalyzer/src/core/common/ArkIRTransformer.ts
index 31f9ecf635744368612a3a08b551ecca62c1b4a5..c0a61f1f5da2c7354f361842436f88f568acee4b 100644
--- a/ets2panda/linter/arkanalyzer/src/core/common/ArkIRTransformer.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/common/ArkIRTransformer.ts
@@ -46,7 +46,6 @@ import { DEFAULT, PROMISE } from './TSConst';
import { buildGenericType, buildModifiers, buildTypeParameters } from '../model/builder/builderUtils';
import { ArkValueTransformer } from './ArkValueTransformer';
import { ImportInfo } from '../model/ArkImport';
-import { TypeInference } from './TypeInference';
import { AbstractTypeExpr } from '../base/TypeExpr';
import { buildNormalArkClassFromArkMethod } from '../model/builder/ArkClassBuilder';
import { ArkClass } from '../model/ArkClass';
@@ -339,12 +338,6 @@ export class ArkIRTransformer {
let expr: AliasTypeExpr;
if (ts.isImportTypeNode(rightOp)) {
expr = this.resolveImportTypeNode(rightOp);
- const typeObject = expr.getOriginalObject();
- if (typeObject instanceof ImportInfo && typeObject.getLazyExportInfo() !== null) {
- const arkExport = typeObject.getLazyExportInfo()!.getArkExport();
- rightType = TypeInference.parseArkExport2Type(arkExport) ?? UnknownType.getInstance();
- aliasType.setOriginalType(rightType);
- }
} else if (ts.isTypeQueryNode(rightOp)) {
const localName = rightOp.exprName.getText(this.sourceFile);
const originalLocal = Array.from(this.arkValueTransformer.getLocals()).find(local => local.getName() === localName);
@@ -402,8 +395,6 @@ export class ArkIRTransformer {
importInfo.build(importClauseName, importType, importFrom, LineColPosition.buildFromNode(importTypeNode, this.sourceFile), 0);
importInfo.setDeclaringArkFile(this.declaringMethod.getDeclaringArkFile());
- // Function getLazyExportInfo will automatically try to infer the export info if it's undefined at the beginning.
- importInfo.getLazyExportInfo();
return new AliasTypeExpr(importInfo, importTypeNode.isTypeOf);
}
diff --git a/ets2panda/linter/arkanalyzer/src/core/common/IRInference.ts b/ets2panda/linter/arkanalyzer/src/core/common/IRInference.ts
index 08083474900627f2a07599c37994fc757e87c692..a9642db052c75198302db6fe4e987d34739d69e5 100644
--- a/ets2panda/linter/arkanalyzer/src/core/common/IRInference.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/common/IRInference.ts
@@ -63,8 +63,7 @@ import {
ArkArrayRef,
ArkInstanceFieldRef,
ArkParameterRef,
- ArkStaticFieldRef,
- GlobalRef
+ ArkStaticFieldRef
} from '../base/Ref';
import { Value } from '../base/Value';
import { Constant } from '../base/Constant';
@@ -130,6 +129,7 @@ export class IRInference {
}
return 0;
});
+ arkClass.getAllHeritageClasses();
methods.forEach(arkMethod => TypeInference.inferTypeInMethod(arkMethod));
});
this.inferExportInfos(file);
@@ -283,6 +283,11 @@ export class IRInference {
private static inferBase(instance: ArkInstanceFieldRef | ArkInstanceInvokeExpr, arkMethod: ArkMethod): void {
const base = instance.getBase();
if (base.getName() === THIS_NAME) {
+ const name = instance instanceof ArkInstanceFieldRef ? instance.getFieldName() :
+ instance.getMethodSignature().getMethodSubSignature().getMethodName();
+ if (name.includes('.')) {
+ return;
+ }
const declaringArkClass = arkMethod.getDeclaringArkClass();
if (declaringArkClass.isAnonymousClass()) {
let newBase = this.inferThisLocal(arkMethod);
@@ -293,14 +298,6 @@ export class IRInference {
base.setType(new ClassType(declaringArkClass.getSignature(), declaringArkClass.getRealTypes()));
}
} else {
- const value = arkMethod.getBody()?.getUsedGlobals()?.get(base.getName());
- if (value instanceof GlobalRef && !value.getRef()) {
- const arkExport = ModelUtils.findGlobalRef(base.getName(), arkMethod);
- if (arkExport instanceof Local) {
- arkExport.getUsedStmts().push(...base.getUsedStmts());
- value.setRef(arkExport);
- }
- }
this.inferLocal(instance.getBase(), arkMethod);
}
}
@@ -686,7 +683,7 @@ export class IRInference {
private static assignAnonMethod(anonMethod: ArkMethod | null, declaredMethod: ArkMethod | null): void {
if (declaredMethod && anonMethod) {
- anonMethod.setImplementationSignature(declaredMethod.matchMethodSignature(anonMethod.getSubSignature().getParameters()));
+ anonMethod.setDeclareSignatures(declaredMethod.matchMethodSignature(anonMethod.getSubSignature().getParameters()));
}
}
@@ -815,7 +812,8 @@ export class IRInference {
public static inferParameterRef(ref: ArkParameterRef, arkMethod: ArkMethod): AbstractRef {
const paramType = ref.getType();
if (paramType instanceof UnknownType || paramType instanceof UnclearReferenceType) {
- const type1 = arkMethod.getSignature().getMethodSubSignature().getParameters()[ref.getIndex()]?.getType();
+ const signature = arkMethod.getDeclareSignatures()?.at(0) ?? arkMethod.getSignature();
+ const type1 = signature.getMethodSubSignature().getParameters()[ref.getIndex()]?.getType();
if (!TypeInference.isUnclearType(type1)) {
ref.setType(type1);
return ref;
diff --git a/ets2panda/linter/arkanalyzer/src/core/common/ModelUtils.ts b/ets2panda/linter/arkanalyzer/src/core/common/ModelUtils.ts
index 7b90129608f4f99a927f46f22ad6f4491e4f6572..41eb1a39f4839b85b5a89b2e9e1e823648450f8f 100644
--- a/ets2panda/linter/arkanalyzer/src/core/common/ModelUtils.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/common/ModelUtils.ts
@@ -37,14 +37,7 @@ import path from 'path';
import { Sdk } from '../../Config';
import { ALL, DEFAULT, THIS_NAME } from './TSConst';
import { buildDefaultExportInfo } from '../model/builder/ArkExportBuilder';
-import {
- AnnotationNamespaceType,
- ClassType,
- FunctionType,
- Type,
- UnclearReferenceType,
- UnknownType
-} from '../base/Type';
+import { AnnotationNamespaceType, ClassType, FunctionType, Type, UnclearReferenceType, UnknownType } from '../base/Type';
import { Scene } from '../../Scene';
import { DEFAULT_ARK_CLASS_NAME, DEFAULT_ARK_METHOD_NAME, NAME_DELIMITER, TEMP_LOCAL_PREFIX } from './Const';
import { EMPTY_STRING } from './ValueUtil';
@@ -56,6 +49,14 @@ import { SdkUtils } from './SdkUtils';
export class ModelUtils {
public static implicitArkUIBuilderMethods: Set = new Set();
+ /*
+ * Set static field to be null, then all related objects could be freed by GC.
+ * Static field implicitArkUIBuilderMethods is only internally used by ArkAnalyzer build method body, the dispose method should be called after build all body.
+ */
+ public static dispose(): void {
+ this.implicitArkUIBuilderMethods.clear();
+ }
+
public static getMethodSignatureFromArkClass(arkClass: ArkClass, methodName: string): MethodSignature | null {
for (const arkMethod of arkClass.getMethods()) {
if (arkMethod.getName() === methodName) {
@@ -570,8 +571,7 @@ export function getArkFile(im: FromInfo): ArkFile | null | undefined {
export function findExportInfo(fromInfo: FromInfo): ExportInfo | null {
let file = getArkFile(fromInfo);
if (!file) {
- logger.warn(`${fromInfo.getOriginName()} ${fromInfo.getFrom()} file not found:
- ${fromInfo.getDeclaringArkFile()?.getFileSignature()?.toString()}`);
+ logger.warn(`${fromInfo.getOriginName()} ${fromInfo.getFrom()} file not found: ${fromInfo.getDeclaringArkFile()?.getFileSignature()?.toString()}`);
return null;
}
if (fileSignatureCompare(file.getFileSignature(), fromInfo.getDeclaringArkFile().getFileSignature())) {
@@ -623,8 +623,8 @@ export function findArkExport(exportInfo: ExportInfo | undefined): ArkExport | n
if (arkExport) {
exportInfo.setArkExport(arkExport);
} else {
- logger.warn(`${exportInfo.getExportClauseName()} get arkExport fail from ${exportInfo.getFrom()} at
- ${exportInfo.getDeclaringArkFile().getFileSignature().toString()}`);
+ const file = exportInfo.getDeclaringArkFile().getFileSignature().toString();
+ logger.warn(`${exportInfo.getExportClauseName()} get arkExport fail from ${exportInfo.getFrom()} at ${file}`);
}
return arkExport || null;
}
diff --git a/ets2panda/linter/arkanalyzer/src/core/common/SdkUtils.ts b/ets2panda/linter/arkanalyzer/src/core/common/SdkUtils.ts
index 84f185987813e6866c2bf4fc398e9e2c9a0d20d1..644983c5442741a03c86cf3449298bb418530510 100644
--- a/ets2panda/linter/arkanalyzer/src/core/common/SdkUtils.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/common/SdkUtils.ts
@@ -28,11 +28,22 @@ import { ClassType } from '../base/Type';
import { AbstractFieldRef } from '../base/Ref';
import { ArkNamespace } from '../model/ArkNamespace';
import { TypeInference } from './TypeInference';
+import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
+
+const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'SdkUtils');
export class SdkUtils {
private static sdkImportMap: Map = new Map();
+ /*
+ * Set static field to be null, then all related objects could be freed by GC.
+ * Class SdkUtils is only internally used by ArkAnalyzer type inference, the dispose method should be called at the end of type inference.
+ */
+ public static dispose(): void {
+ this.sdkImportMap.clear();
+ }
+
public static buildSdkImportMap(file: ArkFile): void {
const fileName = path.basename(file.getName());
if (fileName.startsWith('@')) {
@@ -84,15 +95,18 @@ export class SdkUtils {
private static loadClass(globalMap: Map, cls: ArkClass): void {
const old = globalMap.get(cls.getName());
- if (old instanceof ArkClass) {
+ if (old instanceof ArkClass && old.getDeclaringArkFile().getProjectName() === cls.getDeclaringArkFile().getProjectName()) {
if (old.getCategory() === ClassCategory.CLASS) {
- this.copyMethod(cls, old);
+ this.copyMembers(cls, old);
} else {
- this.copyMethod(old, cls);
+ this.copyMembers(old, cls);
globalMap.delete(cls.getName());
globalMap.set(cls.getName(), cls);
}
- } else if (!old) {
+ } else {
+ if (old) {
+ logger.error(`${old.getSignature()} is override`);
+ }
globalMap.set(cls.getName(), cls);
}
}
@@ -105,10 +119,7 @@ export class SdkUtils {
const instance = globalMap.get(name + 'Interface');
const attr = globalMap.get(name + COMPONENT_ATTRIBUTE);
if (attr instanceof ArkClass && instance instanceof ArkClass) {
- instance
- .getMethods()
- .filter(m => !attr.getMethodWithName(m.getName()))
- .forEach(m => attr.addMethod(m));
+ this.copyMembers(instance, attr);
globalMap.set(name, attr);
return;
}
@@ -119,15 +130,12 @@ export class SdkUtils {
} else if (old instanceof ArkClass && local.getType() instanceof ClassType) {
const localConstructor = scene.getClass((local.getType() as ClassType).getClassSignature());
if (localConstructor) {
- localConstructor
- .getMethods()
- .filter(m => !old.getMethodWithName(m.getName()))
- .forEach(m => old.addMethod(m));
+ this.copyMembers(localConstructor, old);
}
}
}
- private static copyMethod(from: ArkClass, to: ArkClass): void {
+ private static copyMembers(from: ArkClass, to: ArkClass): void {
from.getMethods().forEach(method => {
const dist = method.isStatic() ? to.getStaticMethodWithName(method.getName()) : to.getMethodWithName(method.getName());
const distSignatures = dist?.getDeclareSignatures();
@@ -137,6 +145,12 @@ export class SdkUtils {
to.addMethod(method);
}
});
+ from.getFields().forEach(field => {
+ const dist = field.isStatic() ? to.getStaticFieldWithName(field.getName()) : to.getFieldWithName(field.getName());
+ if (!dist) {
+ to.addField(field);
+ }
+ });
}
public static computeGlobalThis(leftOp: AbstractFieldRef, arkMethod: ArkMethod): void {
diff --git a/ets2panda/linter/arkanalyzer/src/core/common/TypeInference.ts b/ets2panda/linter/arkanalyzer/src/core/common/TypeInference.ts
index e754272cbfd07f2cd1290ef386b91ac55e12fa2a..07fc7faf50cf34ae8ea0fff25018347710541aa4 100644
--- a/ets2panda/linter/arkanalyzer/src/core/common/TypeInference.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/common/TypeInference.ts
@@ -16,7 +16,14 @@
import Logger, { LOG_MODULE_TYPE } from '../../utils/logger';
import { AbstractExpr, ArkInstanceInvokeExpr, ArkPtrInvokeExpr, ArkStaticInvokeExpr } from '../base/Expr';
import { Local } from '../base/Local';
-import { AbstractFieldRef, AbstractRef, ArkArrayRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef } from '../base/Ref';
+import {
+ AbstractFieldRef,
+ AbstractRef,
+ ArkArrayRef,
+ ArkInstanceFieldRef,
+ ArkParameterRef,
+ ArkStaticFieldRef, GlobalRef
+} from '../base/Ref';
import { ArkAliasTypeDefineStmt, ArkAssignStmt, ArkReturnStmt, Stmt } from '../base/Stmt';
import {
AliasType,
@@ -54,7 +61,7 @@ import {
ANY_KEYWORD,
BIGINT_KEYWORD,
BOOLEAN_KEYWORD,
- CONSTRUCTOR_NAME,
+ CONSTRUCTOR_NAME, DEFAULT,
GLOBAL_THIS_NAME,
NEVER_KEYWORD,
NULL_KEYWORD,
@@ -157,7 +164,7 @@ export class TypeInference {
type = leftOpType;
}
} else if (leftOpType instanceof AnnotationNamespaceType) {
- type = this.inferUnclearRefName(leftOpType.getOriginType(), declaringArkClass);
+ type = this.inferBaseType(leftOpType.getOriginType(), declaringArkClass);
} else if (leftOpType instanceof UnclearReferenceType) {
type = this.inferUnclearRefType(leftOpType, declaringArkClass);
}
@@ -185,6 +192,15 @@ export class TypeInference {
signatures.forEach(s => this.inferSignatureReturnType(s, arkMethod));
return;
}
+ body.getUsedGlobals()?.forEach((value, key) => {
+ if (value instanceof GlobalRef && !value.getRef()) {
+ const arkExport = ModelUtils.findGlobalRef(key, arkMethod);
+ if (arkExport instanceof Local) {
+ arkExport.getUsedStmts().push(...value.getUsedStmts());
+ value.setRef(arkExport);
+ }
+ }
+ });
const cfg = body.getCfg();
for (const block of cfg.getBlocks()) {
for (const stmt of block.getStmts()) {
@@ -387,7 +403,8 @@ export class TypeInference {
public static isUnclearType(type: Type | null | undefined): boolean {
// TODO: For UnionType, IntersectionType and TupleType, it should recurse check every item of them.
- if (!type || type instanceof UnknownType || type instanceof UnclearReferenceType || type instanceof NullType || type instanceof UndefinedType) {
+ if (!type || type instanceof UnknownType || type instanceof UnclearReferenceType || type instanceof NullType ||
+ type instanceof UndefinedType || type instanceof GenericType) {
return true;
} else if (
type instanceof ClassType &&
@@ -488,7 +505,7 @@ export class TypeInference {
return value.getType();
}
- private static inferParameterType(param: MethodParameter, arkMethod: ArkMethod): void {
+ public static inferParameterType(param: MethodParameter, arkMethod: ArkMethod): void {
let pType = param.getType();
const arkClass = arkMethod.getDeclaringArkClass();
let type;
@@ -703,7 +720,7 @@ export class TypeInference {
}
propertyType = new EnumValueType(property.getSignature(), constant);
} else {
- propertyType = property.getType();
+ propertyType = this.replaceTypeWithReal(property.getType(), baseType.getRealGenericTypes());
}
} else if (property) {
propertyType = this.parseArkExport2Type(property);
@@ -728,6 +745,8 @@ export class TypeInference {
public static inferBaseType(baseName: string, arkClass: ArkClass): Type | null {
if (SUPER_NAME === baseName) {
return this.parseArkExport2Type(arkClass.getSuperClass());
+ } else if (DEFAULT === baseName) {
+ return this.parseArkExport2Type(arkClass.getDeclaringArkFile().getExportInfoBy(DEFAULT)?.getArkExport());
}
const field = ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getLocals()?.get(baseName);
if (field && !this.isUnclearType(field.getType())) {
@@ -750,14 +769,12 @@ export class TypeInference {
ModelUtils.getClassWithName(typeName, arkClass) ??
ModelUtils.getDefaultClass(arkClass)?.getDefaultArkMethod()?.getBody()?.getAliasTypeByName(typeName) ??
ModelUtils.getArkExportInImportInfoWithName(typeName, arkClass.getDeclaringArkFile());
- if (arkExport instanceof ArkClass || arkExport instanceof AliasType) {
- return this.parseArkExport2Type(arkExport);
- }
- if (!arkClass.getDeclaringArkFile().getImportInfoBy(typeName)) {
+ if (!arkExport && !arkClass.getDeclaringArkFile().getImportInfoBy(typeName)) {
arkExport = arkClass.getDeclaringArkFile().getScene().getSdkGlobal(typeName);
}
- if (arkExport instanceof ArkClass || arkExport instanceof AliasType) {
- return this.parseArkExport2Type(arkExport);
+ const type = this.parseArkExport2Type(arkExport);
+ if (type instanceof ClassType || type instanceof AliasType) {
+ return type;
}
return null;
}
@@ -860,12 +877,14 @@ export class TypeInference {
.getParameters()
.filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX))
.forEach((p, i) => {
- let type = params?.[i]?.getType();
- if (type instanceof GenericType && realTypes) {
- type = realTypes?.[type.getIndex()];
- }
- if (type) {
- p.setType(type);
+ if (this.isUnclearType(p.getType())) {
+ let type = params?.[i]?.getType();
+ if (type instanceof GenericType && realTypes) {
+ type = realTypes?.[type.getIndex()];
+ }
+ if (type) {
+ p.setType(type);
+ }
}
});
}
diff --git a/ets2panda/linter/arkanalyzer/src/core/common/ValueUtil.ts b/ets2panda/linter/arkanalyzer/src/core/common/ValueUtil.ts
index 1731b701b04d71d60c4e12b93931b2c9ec0547aa..8a29e565b84e8db412f6ef951fca7c0046516794 100644
--- a/ets2panda/linter/arkanalyzer/src/core/common/ValueUtil.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/common/ValueUtil.ts
@@ -21,6 +21,14 @@ export class ValueUtil {
private static readonly NumberConstantCache: Map = new Map();
public static readonly EMPTY_STRING_CONSTANT = new StringConstant(EMPTY_STRING);
+ /*
+ * Set static field to be null, then all related objects could be freed by GC.
+ * Class SdkUtils is only internally used by ArkAnalyzer, the dispose method should be called by users themselves before drop Scene.
+ */
+ public static dispose(): void {
+ this.NumberConstantCache.clear();
+ }
+
public static getOrCreateNumberConst(n: number): Constant {
let constant = this.NumberConstantCache.get(n);
if (constant === undefined) {
diff --git a/ets2panda/linter/arkanalyzer/src/core/dataflow/DataflowSolver.ts b/ets2panda/linter/arkanalyzer/src/core/dataflow/DataflowSolver.ts
index 523dd9a41b17104bc1b850c060e8a50fd796ba03..325fa23634c05a967cc4ce7192c623e4351f0926 100644
--- a/ets2panda/linter/arkanalyzer/src/core/dataflow/DataflowSolver.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/dataflow/DataflowSolver.ts
@@ -24,6 +24,7 @@ import { CallGraph } from '../../callgraph/model/CallGraph';
import { ClassHierarchyAnalysis } from '../../callgraph/algorithm/ClassHierarchyAnalysis';
import { addCfg2Stmt } from '../../utils/entryMethodUtils';
import { getRecallMethodInParam } from './Util';
+import { CallGraphBuilder } from '../../callgraph/model/builder/CallGraphBuilder';
/*
this program is roughly an implementation of the paper: Practical Extensions to the IFDS Algorithm.
@@ -88,7 +89,7 @@ export abstract class DataflowSolver {
// build CHA
let cg = new CallGraph(this.scene);
- this.CHA = new ClassHierarchyAnalysis(this.scene, cg);
+ this.CHA = new ClassHierarchyAnalysis(this.scene, cg, new CallGraphBuilder(cg, this.scene));
this.buildStmtMapInClass();
this.setCfg4AllStmt();
return;
diff --git a/ets2panda/linter/arkanalyzer/src/core/graph/builder/CfgBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/graph/builder/CfgBuilder.ts
index 5cd44402ec52488fb5bef3750f8b7af54b0c2f3e..13ff5584cc34d710121af364d4475715059cf1e9 100644
--- a/ets2panda/linter/arkanalyzer/src/core/graph/builder/CfgBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/graph/builder/CfgBuilder.ts
@@ -677,6 +677,9 @@ export class CfgBuilder {
for (let i = stmt.nexts.length - 1; i >= 0; i--) {
stmtQueue.push(stmt.nexts[i]);
}
+ if (stmt.afterSwitch && stmt.afterSwitch.lasts.size == 0) {
+ stmtQueue.push(stmt.afterSwitch);
+ }
} else if (stmt instanceof TryStatementBuilder) {
if (stmt.finallyStatement) {
stmtQueue.push(stmt.finallyStatement);
diff --git a/ets2panda/linter/arkanalyzer/src/core/graph/builder/TrapBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/graph/builder/TrapBuilder.ts
index 160df9fdc0420bd843f79edbbe7f43552c9e3699..9094994dc5d4099e379f3cd032f84eb9ad448c5a 100644
--- a/ets2panda/linter/arkanalyzer/src/core/graph/builder/TrapBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/graph/builder/TrapBuilder.ts
@@ -19,7 +19,15 @@ import { Trap } from '../../base/Trap';
import { ArkCaughtExceptionRef } from '../../base/Ref';
import { UnknownType } from '../../base/Type';
import { FullPosition } from '../../base/Position';
-import { ArkAssignStmt, ArkIfStmt, ArkInvokeStmt, ArkReturnStmt, ArkReturnVoidStmt, ArkThrowStmt, Stmt } from '../../base/Stmt';
+import {
+ ArkAssignStmt,
+ ArkIfStmt,
+ ArkInvokeStmt,
+ ArkReturnStmt,
+ ArkReturnVoidStmt,
+ ArkThrowStmt,
+ Stmt,
+} from '../../base/Stmt';
import { BlockBuilder, TryStatementBuilder } from './CfgBuilder';
import Logger, { LOG_MODULE_TYPE } from '../../../utils/logger';
@@ -217,16 +225,17 @@ export class TrapBuilder {
bfsBlocks.push(currBlock);
const childList = currBlockBuilder.nexts;
- if (childList.length === 0 || (childList.length !== 0 && childList[0] === endBlockBuilder)) {
- if (childList[0] === endBlockBuilder) {
- tailBlocks.push(currBlock);
- continue;
- }
- }
if (childList.length !== 0) {
for (const child of childList) {
- queue.push(child);
+ // A tail block's successor may be within the traversal range
+ if (child === endBlockBuilder) {
+ tailBlocks.push(currBlock);
+ } else {
+ queue.push(child);
+ }
}
+ } else {
+ tailBlocks.push(currBlock);
}
}
return { bfsBlocks, tailBlocks };
@@ -247,19 +256,16 @@ export class TrapBuilder {
copyFinallyBfsBlocks[0].getPredecessors().splice(0, finallyPredecessorsCnt);
const throwStmt = new ArkThrowStmt(exceptionValue);
let copyFinallyTailBlocks = copyFinallyBfsBlocks.splice(copyFinallyBfsBlocks.length - finallyTailBlocks.length, finallyTailBlocks.length);
- copyFinallyTailBlocks.forEach((copyFinallyTailBlock: BasicBlock) => {
- const successorsCnt = copyFinallyTailBlock.getSuccessors().length;
- copyFinallyTailBlock.getSuccessors().splice(0, successorsCnt);
- });
if (copyFinallyTailBlocks.length > 1) {
const newCopyFinallyTailBlock = new BasicBlock();
copyFinallyTailBlocks.forEach((copyFinallyTailBlock: BasicBlock) => {
copyFinallyTailBlock.addSuccessorBlock(newCopyFinallyTailBlock);
newCopyFinallyTailBlock.addPredecessorBlock(copyFinallyTailBlock);
});
+ copyFinallyBfsBlocks.push(...copyFinallyTailBlocks);
copyFinallyTailBlocks = [newCopyFinallyTailBlock];
}
- copyFinallyTailBlocks[0]?.addStmt(throwStmt);
+ copyFinallyTailBlocks[0].addStmt(throwStmt);
copyFinallyBfsBlocks.push(...copyFinallyTailBlocks);
copyFinallyBfsBlocks.forEach((copyFinallyBfsBlock: BasicBlock) => {
basicBlockSet.add(copyFinallyBfsBlock);
@@ -281,12 +287,19 @@ export class TrapBuilder {
for (const sourceBlock of sourceBlocks) {
const targetBlock = sourceToTarget.get(sourceBlock)!;
for (const predecessor of sourceBlock.getPredecessors()) {
- const targetPredecessor = sourceToTarget.get(predecessor)!;
- targetBlock.addPredecessorBlock(targetPredecessor);
+ const targetPredecessor = sourceToTarget.get(predecessor);
+ // Only include blocks within the copy range, so that predecessor and successor relationships to
+ // external blocks can be trimmed
+ if (targetPredecessor) {
+ targetBlock.addPredecessorBlock(targetPredecessor);
+ }
+
}
for (const successor of sourceBlock.getSuccessors()) {
- const targetSuccessor = sourceToTarget.get(successor)!;
- targetBlock.addSuccessorBlock(targetSuccessor);
+ const targetSuccessor = sourceToTarget.get(successor);
+ if (targetSuccessor) {
+ targetBlock.addSuccessorBlock(targetSuccessor);
+ }
}
}
return targetBlocks;
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/ArkImport.ts b/ets2panda/linter/arkanalyzer/src/core/model/ArkImport.ts
index 8ff3ff50acd2f3c10cdee22d87c4f6994626bac8..05c5c24a805125d6cfd6cdb947b74961b9cd75b3 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/ArkImport.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/ArkImport.ts
@@ -72,7 +72,7 @@ export class ImportInfo extends ArkBaseModel implements FromInfo {
* @returns The export information. If there is no export information, the return will be a **null**.
*/
public getLazyExportInfo(): ExportInfo | null {
- if (this.lazyExportInfo === undefined) {
+ if (this.lazyExportInfo === undefined && this.declaringArkFile.getScene().getStage() >= 2) {
this.lazyExportInfo = findExportInfo(this);
}
return this.lazyExportInfo || null;
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/ArkMethod.ts b/ets2panda/linter/arkanalyzer/src/core/model/ArkMethod.ts
index f3476d3567b9c43686118ab0ebe8d4f1d266d61e..12682223ba15473c312103cceaeb21f822318aa4 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/ArkMethod.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/ArkMethod.ts
@@ -15,7 +15,16 @@
import { ArkParameterRef, ArkThisRef } from '../base/Ref';
import { ArkAssignStmt, ArkReturnStmt, Stmt } from '../base/Stmt';
-import { ClassType, EnumValueType, FunctionType, GenericType, LiteralType, Type, UnionType } from '../base/Type';
+import {
+ AliasType,
+ ClassType,
+ EnumValueType,
+ FunctionType,
+ GenericType,
+ LiteralType,
+ Type,
+ UnionType
+} from '../base/Type';
import { Value } from '../base/Value';
import { Cfg } from '../graph/Cfg';
import { ViewTree } from '../graph/ViewTree';
@@ -24,17 +33,17 @@ import { ArkClass, ClassCategory } from './ArkClass';
import { MethodSignature, MethodSubSignature } from './ArkSignature';
import { BodyBuilder } from './builder/BodyBuilder';
import { ArkExport, ExportType } from './ArkExport';
-import { ANONYMOUS_METHOD_PREFIX, DEFAULT_ARK_METHOD_NAME } from '../common/Const';
+import { ANONYMOUS_METHOD_PREFIX, DEFAULT_ARK_METHOD_NAME, LEXICAL_ENV_NAME_PREFIX } from '../common/Const';
import { getColNo, getLineNo, LineCol, setCol, setLine } from '../base/Position';
import { ArkBaseModel, ModifierType } from './ArkBaseModel';
import { ArkError, ArkErrorCode } from '../common/ArkError';
import { CALL_BACK } from '../common/EtsConst';
-import { Scene } from '../../Scene';
import { Constant } from '../base/Constant';
import { Local } from '../base/Local';
import { ArkFile, Language } from './ArkFile';
import { CONSTRUCTOR_NAME } from '../common/TSConst';
import { MethodParameter } from './builder/ArkMethodBuilder';
+import { TypeInference } from '../common/TypeInference';
export const arkMethodNodeKind = [
'MethodDeclaration',
@@ -623,45 +632,41 @@ export class ArkMethod extends ArkBaseModel implements ArkExport {
}
return args.length >= min && args.length <= max;
});
- const scene = this.getDeclaringArkFile().getScene();
return (
- signatures?.find(p => {
- const parameters = p.getMethodSubSignature().getParameters();
- for (let i = 0; i < parameters.length; i++) {
- if (!args[i]) {
- return parameters[i].isOptional();
- }
- const isMatched = this.matchParam(parameters[i].getType(), args[i], scene);
- if (!isMatched) {
- return false;
- }
- }
- return true;
- }) ??
+ signatures?.find(p => this.isMatched(p.getMethodSubSignature().getParameters(), args)) ??
signatures?.[0] ??
this.getSignature()
);
}
- private matchParam(paramType: Type, arg: Value, scene: Scene): boolean {
- const argType = arg.getType();
- if (arg instanceof Local) {
- const stmt = arg.getDeclaringStmt();
- if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof Constant) {
- arg = stmt.getRightOp();
+ private isMatched(parameters: MethodParameter[], args: Value[], isArrowFunc: boolean = false): boolean {
+ for (let i = 0; i < parameters.length; i++) {
+ if (!args[i]) {
+ return isArrowFunc ? true : parameters[i].isOptional();
}
+ const isMatched = this.matchParam(parameters[i].getType(), args[i]);
+ if (!isMatched) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private matchParam(paramType: Type, arg: Value): boolean {
+ arg = ArkMethod.parseArg(arg);
+ const argType = arg.getType();
+ if (paramType instanceof AliasType && !(argType instanceof AliasType)) {
+ paramType = TypeInference.replaceAliasType(paramType);
}
if (paramType instanceof UnionType) {
- let matched = false;
- for (const e of paramType.getTypes()) {
- if (argType.constructor === e.constructor) {
- matched = true;
- break;
- }
- }
- return matched;
+ return !!paramType.getTypes().find(p => this.matchParam(p, arg));
} else if (argType instanceof FunctionType && paramType instanceof FunctionType) {
- return argType.getMethodSignature().getParamLength() === paramType.getMethodSignature().getParamLength();
+ if (argType.getMethodSignature().getParamLength() > paramType.getMethodSignature().getParamLength()) {
+ return false;
+ }
+ const parameters = paramType.getMethodSignature().getMethodSubSignature().getParameters();
+ const args = argType.getMethodSignature().getMethodSubSignature().getParameters().filter(p => !p.getName().startsWith(LEXICAL_ENV_NAME_PREFIX));
+ return this.isMatched(parameters, args, true);
} else if (paramType instanceof ClassType && paramType.getClassSignature().getClassName().includes(CALL_BACK)) {
return argType instanceof FunctionType;
} else if (paramType instanceof LiteralType && arg instanceof Constant) {
@@ -684,6 +689,19 @@ export class ArkMethod extends ArkBaseModel implements ArkExport {
return argType.constructor === paramType.constructor;
}
+ private static parseArg(arg: Value): Value {
+ if (arg instanceof Local) {
+ const stmt = arg.getDeclaringStmt();
+ const argType = arg.getType();
+ if (argType instanceof EnumValueType && argType.getConstant()) {
+ arg = argType.getConstant()!;
+ } else if (stmt instanceof ArkAssignStmt && stmt.getRightOp() instanceof Constant) {
+ arg = stmt.getRightOp();
+ }
+ }
+ return arg;
+ }
+
public getOuterMethod(): ArkMethod | undefined {
return this.outerMethod;
}
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkClassBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkClassBuilder.ts
index 7c3f16938470b32b10ed1636e6a0354baa1baa73..48a7217d1a027f434603bbefcdc34abf7bdcc5f6 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkClassBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkClassBuilder.ts
@@ -370,7 +370,17 @@ function buildArkClassMembers(clsNode: ClassLikeNode, cls: ArkClass, sourceFile:
const instanceInitStmts: Stmt[] = [];
let staticBlockId = 0;
clsNode.members.forEach(member => {
- if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) {
+ if (
+ ts.isMethodDeclaration(member) ||
+ ts.isConstructorDeclaration(member) ||
+ ts.isMethodSignature(member) ||
+ ts.isConstructSignatureDeclaration(member) ||
+ ts.isAccessor(member) ||
+ ts.isCallSignatureDeclaration(member)
+ ) {
+ // these node types have been handled at the beginning of this function by calling buildMethodsForClass
+ return;
+ } else if (ts.isPropertyDeclaration(member) || ts.isPropertySignature(member)) {
const arkField = buildProperty2ArkField(member, sourceFile, cls);
if (ts.isClassDeclaration(clsNode) || ts.isClassExpression(clsNode) || ts.isStructDeclaration(clsNode)) {
if (arkField.isStatic()) {
@@ -395,9 +405,9 @@ function buildArkClassMembers(clsNode: ClassLikeNode, cls: ArkClass, sourceFile:
const staticBlockInvokeExpr = new ArkStaticInvokeExpr(currStaticBlockMethodSig, []);
staticInitStmts.push(new ArkInvokeStmt(staticBlockInvokeExpr));
} else if (ts.isSemicolonClassElement(member)) {
- logger.debug('Skip these members.');
+ logger.trace('Skip these members.');
} else {
- logger.warn('Please contact developers to support new member type!');
+ logger.warn(`Please contact developers to support new member in class: ${cls.getSignature().toString()}, member: ${member.getText()}!`);
}
});
if (ts.isClassDeclaration(clsNode) || ts.isClassExpression(clsNode) || ts.isStructDeclaration(clsNode)) {
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFieldBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFieldBuilder.ts
index ede55e7c9373fe86d87b73f0907a24c8e4cc9157..b41b7dd419ebec64520d466cb3be1f954a739f72 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFieldBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFieldBuilder.ts
@@ -47,7 +47,7 @@ export function buildProperty2ArkField(
} else if (ts.isPropertyAccessExpression(member.name.expression)) {
fieldName = handlePropertyAccessExpression(member.name.expression);
} else {
- logger.warn('Other property expression type found!');
+ logger.warn(`Other property expression type found: ${member.name.expression.getText()}!`);
}
} else if (member.name && (ts.isIdentifier(member.name) || ts.isLiteralExpression(member.name))) {
fieldName = member.name.text;
@@ -56,7 +56,7 @@ export function buildProperty2ArkField(
fieldName = propertyName.substring(1);
field.addModifier(ModifierType.PRIVATE);
} else {
- logger.warn('Other type of property name found!');
+ logger.warn(`Other type of property name found: ${member.getText()}!`);
}
let fieldType: Type = UnknownType.getInstance();
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFileBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFileBuilder.ts
index 1e201453b195b0a2bc6bc66d515215471dc277dc..386cbca0855a900802890868b8bf10e6dddd5152 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFileBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkFileBuilder.ts
@@ -110,7 +110,7 @@ function buildArkFile(arkFile: ArkFile, astRoot: ts.SourceFile): void {
}
// TODO: Check
else if (ts.isMethodDeclaration(child)) {
- logger.warn('This is a MethodDeclaration in ArkFile.');
+ logger.trace('This is a MethodDeclaration in ArkFile.');
let mthd: ArkMethod = new ArkMethod();
buildArkMethodFromArkClass(child, arkFile.getDefaultClass(), mthd, astRoot);
@@ -143,7 +143,7 @@ function buildArkFile(arkFile: ArkFile, astRoot: ts.SourceFile): void {
} else if (ts.isExpressionStatement(child) && ts.isStringLiteral(child.expression)) {
child.expression.text.trim() === ARKTS_STATIC_MARK && arkFile.setLanguage(Language.ARKTS1_2);
} else {
- logger.info('Child joined default method of arkFile: ', ts.SyntaxKind[child.kind]);
+ logger.trace('Child joined default method of arkFile: ', ts.SyntaxKind[child.kind]);
}
});
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkMethodBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkMethodBuilder.ts
index 29221c377c4737302b92774d61b2e79941b2246d..cb00c3dff71d660b405974c57774b3914e7cb2ba 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkMethodBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkMethodBuilder.ts
@@ -113,14 +113,13 @@ export function buildArkMethodFromArkClass(
mtd.setImplementationSignature(methodSignature);
mtd.setLine(line + 1);
mtd.setColumn(character + 1);
+ let bodyBuilder = new BodyBuilder(mtd.getSignature(), methodNode, mtd, sourceFile);
+ mtd.setBodyBuilder(bodyBuilder);
} else {
mtd.setDeclareSignatures(methodSignature);
mtd.setDeclareLinesAndCols([line + 1], [character + 1]);
}
- let bodyBuilder = new BodyBuilder(mtd.getSignature(), methodNode, mtd, sourceFile);
- mtd.setBodyBuilder(bodyBuilder);
-
if (mtd.hasBuilderDecorator()) {
mtd.setViewTree(buildViewTree(mtd));
} else if (declaringClass.hasComponentDecorator() && mtd.getSubSignature().toString() === 'build()' && !mtd.isStatic()) {
@@ -430,10 +429,11 @@ export function addInitInConstructor(constructor: ArkMethod): void {
if (!thisLocal) {
return;
}
- const blocks = constructor.getCfg()?.getBlocks();
- if (!blocks) {
+ const cfg = constructor.getCfg();
+ if (cfg === undefined) {
return;
}
+ const blocks = cfg.getBlocks();
const firstBlockStmts = [...blocks][0].getStmts();
let index = 0;
for (let i = 0; i < firstBlockStmts.length; i++) {
@@ -454,6 +454,7 @@ export function addInitInConstructor(constructor: ArkMethod): void {
const initInvokeStmt = new ArkInvokeStmt(
new ArkInstanceInvokeExpr(thisLocal, constructor.getDeclaringArkClass().getInstanceInitMethod().getSignature(), [])
);
+ initInvokeStmt.setCfg(cfg);
firstBlockStmts.splice(index, 0, initInvokeStmt);
}
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkNamespaceBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkNamespaceBuilder.ts
index 930a0bba21cd77dab5d45b67ebc9b3a897842fef..b336fe921b54235da74958625d7e5083b3e70a35 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkNamespaceBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/builder/ArkNamespaceBuilder.ts
@@ -69,12 +69,12 @@ export function buildArkNamespace(node: ts.ModuleDeclaration, declaringInstance:
// NamespaceDeclaration extends ModuleDeclaration
//TODO: Check
else if (ts.isModuleDeclaration(node.body)) {
- logger.warn('This ModuleBody is an NamespaceDeclaration.');
+ logger.trace('This ModuleBody is an NamespaceDeclaration.');
let childNs: ArkNamespace = new ArkNamespace();
buildArkNamespace(node.body, ns, childNs, sourceFile);
ns.addNamespace(childNs);
} else if (ts.isIdentifier(node.body)) {
- logger.warn('ModuleBody is Identifier.');
+ logger.warn('ModuleBody is Identifier');
} else {
logger.warn('JSDocNamespaceDeclaration found.');
}
@@ -108,7 +108,7 @@ function buildNamespaceMembers(node: ts.ModuleBlock, namespace: ArkNamespace, so
}
// TODO: Check
else if (ts.isMethodDeclaration(child)) {
- logger.warn('This is a MethodDeclaration in ArkNamespace.');
+ logger.trace('This is a MethodDeclaration in ArkNamespace.');
let mthd: ArkMethod = new ArkMethod();
buildArkMethodFromArkClass(child, namespace.getDefaultClass(), mthd, sourceFile);
@@ -131,7 +131,7 @@ function buildNamespaceMembers(node: ts.ModuleBlock, namespace: ArkNamespace, so
} else if (ts.isVariableStatement(child) && isExported(child.modifiers)) {
buildExportVariableStatement(child, sourceFile, namespace.getDeclaringArkFile(), namespace).forEach(item => namespace.addExportInfo(item));
} else {
- logger.info('Child joined default method of arkFile: ', ts.SyntaxKind[child.kind]);
+ logger.trace('Child joined default method of arkFile: ', ts.SyntaxKind[child.kind]);
// join default method
}
});
diff --git a/ets2panda/linter/arkanalyzer/src/core/model/builder/BodyBuilder.ts b/ets2panda/linter/arkanalyzer/src/core/model/builder/BodyBuilder.ts
index 77b28a411fc80b6884620cdf50e2d5e564b93a81..8a6286bd30e384e17be1bae4bfbfd520df6bcbd1 100644
--- a/ets2panda/linter/arkanalyzer/src/core/model/builder/BodyBuilder.ts
+++ b/ets2panda/linter/arkanalyzer/src/core/model/builder/BodyBuilder.ts
@@ -571,6 +571,7 @@ export class BodyBuilder {
const closuresLocal = new Local(closuresParam.getName(), lexicalEnv);
body.addLocal(closuresLocal.getName(), closuresLocal);
let assignStmt = new ArkAssignStmt(closuresLocal, parameterRef);
+ assignStmt.setCfg(body.getCfg());
stmts.splice(index, 0, assignStmt);
closuresLocal.setDeclaringStmt(assignStmt);
@@ -590,6 +591,7 @@ export class BodyBuilder {
index++;
const closureFieldRef = new ClosureFieldRef(closuresParam, closure.getName(), closure.getType());
let assignStmt = new ArkAssignStmt(local, closureFieldRef);
+ assignStmt.setCfg(body.getCfg());
stmts.splice(index, 0, assignStmt);
local.setDeclaringStmt(assignStmt);
closuresLocal.addUsedStmt(assignStmt);
diff --git a/ets2panda/linter/arkanalyzer/src/save/JsonPrinter.ts b/ets2panda/linter/arkanalyzer/src/save/JsonPrinter.ts
index 283de26011aae158e95d678fccdac4a6ceed70b4..e9c2696bf8a6b6df1c57670ffd481b09075eb1e1 100644
--- a/ets2panda/linter/arkanalyzer/src/save/JsonPrinter.ts
+++ b/ets2panda/linter/arkanalyzer/src/save/JsonPrinter.ts
@@ -22,20 +22,20 @@ import { ArkField } from '../core/model/ArkField';
import {
AliasType,
AnnotationNamespaceType,
- AnnotationType,
AnnotationTypeQueryType,
AnyType,
ArrayType,
BigIntType,
BooleanType,
ClassType,
+ EnumValueType,
FunctionType,
GenericType,
+ IntersectionType,
LiteralType,
NeverType,
NullType,
NumberType,
- PrimitiveType,
StringType,
TupleType,
Type,
@@ -46,11 +46,16 @@ import {
VoidType,
} from '../core/base/Type';
import { Value } from '../core/base/Value';
-import { ArkAssignStmt, ArkIfStmt, ArkInvokeStmt, ArkReturnStmt, ArkReturnVoidStmt, ArkThrowStmt, Stmt } from '../core/base/Stmt';
import {
- AbstractBinopExpr,
- AbstractExpr,
- AbstractInvokeExpr,
+ ArkAssignStmt,
+ ArkIfStmt,
+ ArkInvokeStmt,
+ ArkReturnStmt,
+ ArkReturnVoidStmt,
+ ArkThrowStmt,
+ Stmt,
+} from '../core/base/Stmt';
+import {
ArkAwaitExpr,
ArkCastExpr,
ArkConditionExpr,
@@ -73,13 +78,12 @@ import { ImportInfo } from '../core/model/ArkImport';
import { ExportInfo } from '../core/model/ArkExport';
import { AliasTypeSignature, ClassSignature, FieldSignature, FileSignature, MethodSignature, NamespaceSignature } from '../core/model/ArkSignature';
import { LineColPosition } from '../core/base/Position';
-import { AbstractFieldRef, AbstractRef, ArkArrayRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef, ArkThisRef } from '../core/base/Ref';
+import { ArkArrayRef, ArkCaughtExceptionRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef, ArkThisRef, ClosureFieldRef, GlobalRef } from '../core/base/Ref';
import { Local } from '../core/base/Local';
import { Cfg } from '../core/graph/Cfg';
import { BasicBlock } from '../core/graph/BasicBlock';
import { ArkBody } from '../core/model/ArkBody';
import { Decorator } from '../core/base/Decorator';
-import util from 'util';
export class JsonPrinter extends Printer {
constructor(private arkFile: ArkFile) {
@@ -113,8 +117,9 @@ export class JsonPrinter extends Printer {
return {
signature: this.serializeClassSignature(clazz.getSignature()),
modifiers: clazz.getModifiers(),
- decorators: clazz.getDecorators().map(decorator => this.serializeDecorator(decorator)),
- typeParameters: clazz.getGenericsTypes()?.map(type => this.serializeType(type)),
+ decorators: clazz.getDecorators().map((decorator) => this.serializeDecorator(decorator)),
+ typeParameters: clazz.getGenericsTypes()?.map((type) => this.serializeType(type)),
+ category: clazz.getCategory(),
superClassName: clazz.getSuperClassName(),
implementedInterfaceNames: clazz.getImplementedInterfaceNames(),
fields: clazz.getFields().map(field => this.serializeField(field)),
@@ -155,6 +160,7 @@ export class JsonPrinter extends Printer {
name: parameter.getName(),
type: this.serializeType(parameter.getType()),
isOptional: parameter.isOptional(),
+ isRest: parameter.hasDotDotDotToken(),
};
}
@@ -220,6 +226,11 @@ export class JsonPrinter extends Printer {
_: 'UnionType',
types: type.getTypes().map(type => this.serializeType(type)),
};
+ } else if (type instanceof IntersectionType) {
+ return {
+ _: 'IntersectionType',
+ types: type.getTypes().map((type) => this.serializeType(type)),
+ };
} else if (type instanceof TupleType) {
return {
_: 'TupleType',
@@ -254,8 +265,6 @@ export class JsonPrinter extends Printer {
_: 'LiteralType',
literal: type.getLiteralName(),
};
- } else if (type instanceof PrimitiveType) {
- throw new Error('Unhandled PrimitiveType: ' + util.inspect(type, { showHidden: true, depth: null }));
} else if (type instanceof ClassType) {
return {
_: 'ClassType',
@@ -281,13 +290,13 @@ export class JsonPrinter extends Printer {
typeParameters: type.getGenericTypes().map(type => this.serializeType(type)),
};
} else if (type instanceof GenericType) {
- let defaultType = type.getDefaultType();
let constraint = type.getConstraint();
+ let defaultType = type.getDefaultType();
return {
_: 'GenericType',
name: type.getName(),
- defaultType: defaultType && this.serializeType(defaultType),
constraint: constraint && this.serializeType(constraint),
+ defaultType: defaultType && this.serializeType(defaultType),
};
} else if (type instanceof AliasType) {
return {
@@ -307,10 +316,19 @@ export class JsonPrinter extends Printer {
_: 'AnnotationTypeQueryType',
originType: type.getOriginType(),
};
- } else if (type instanceof AnnotationType) {
- throw new Error('Unhandled AnnotationType: ' + util.inspect(type, { showHidden: true, depth: null }));
+ } else if (type instanceof EnumValueType) {
+ const c = type.getConstant();
+ return {
+ _: 'EnumValueType',
+ signature: this.serializeFieldSignature(type.getFieldSignature()),
+ constant: c && this.serializeValue(c),
+ };
} else {
- throw new Error('Unhandled Type: ' + util.inspect(type, { showHidden: true, depth: null }));
+ console.warn(`Unhandled Type: ${type.constructor.name} (${type.toString()})`);
+ return {
+ _: type.constructor.name,
+ text: type.toString(),
+ };
}
}
@@ -415,6 +433,13 @@ export class JsonPrinter extends Printer {
};
}
+ private serializeConstant(constant: Constant): object {
+ return {
+ value: constant.getValue(),
+ type: this.serializeType(constant.getType()),
+ };
+ }
+
private serializeValue(value: Value): object {
if (value === undefined) {
throw new Error('Value is undefined');
@@ -428,8 +453,7 @@ export class JsonPrinter extends Printer {
} else if (value instanceof Constant) {
return {
_: 'Constant',
- value: value.getValue(),
- type: this.serializeType(value.getType()),
+ ...this.serializeConstant(value),
};
} else if (value instanceof ArkNewExpr) {
return {
@@ -498,8 +522,6 @@ export class JsonPrinter extends Printer {
left: this.serializeValue(value.getOp1()),
right: this.serializeValue(value.getOp2()),
};
- } else if (value instanceof AbstractBinopExpr) {
- return new Error('Unhandled BinopExpr: ' + util.inspect(value, { showHidden: true, depth: null }));
} else if (value instanceof ArkUnopExpr) {
return {
_: 'UnopExpr',
@@ -526,8 +548,6 @@ export class JsonPrinter extends Printer {
method: this.serializeMethodSignature(value.getMethodSignature()),
args: value.getArgs().map(arg => this.serializeValue(arg)),
};
- } else if (value instanceof AbstractInvokeExpr) {
- throw new Error('Unhandled CallExpr: ' + util.inspect(value, { showHidden: true, depth: null }));
} else if (value instanceof ArkThisRef) {
return {
_: 'ThisRef',
@@ -546,6 +566,25 @@ export class JsonPrinter extends Printer {
index: this.serializeValue(value.getIndex()),
type: this.serializeType(value.getType()),
};
+ } else if (value instanceof ArkCaughtExceptionRef) {
+ return {
+ _: 'CaughtExceptionRef',
+ type: this.serializeType(value.getType()),
+ };
+ } else if (value instanceof GlobalRef) {
+ let ref = value.getRef();
+ return {
+ _: 'GlobalRef',
+ name: value.getName(),
+ ref: ref ? this.serializeValue(ref) : null,
+ };
+ } else if (value instanceof ClosureFieldRef) {
+ return {
+ _: 'ClosureFieldRef',
+ base: this.serializeLocal(value.getBase()),
+ fieldName: value.getFieldName(),
+ type: this.serializeType(value.getType()),
+ };
} else if (value instanceof ArkInstanceFieldRef) {
return {
_: 'InstanceFieldRef',
@@ -557,14 +596,13 @@ export class JsonPrinter extends Printer {
_: 'StaticFieldRef',
field: this.serializeFieldSignature(value.getFieldSignature()),
};
- } else if (value instanceof AbstractFieldRef) {
- throw new Error('Unhandled FieldRef: ' + util.inspect(value, { showHidden: true, depth: null }));
- } else if (value instanceof AbstractRef) {
- throw new Error('Unhandled Ref: ' + util.inspect(value, { showHidden: true, depth: null }));
- } else if (value instanceof AbstractExpr) {
- throw new Error('Unhandled Expr: ' + util.inspect(value, { showHidden: true, depth: null }));
} else {
- throw new Error('Unhandled Value: ' + util.inspect(value, { showHidden: true, depth: null }));
+ console.warn(`Unhandled Value: ${value.constructor.name} (${value.toString()})`);
+ return {
+ _: value.constructor.name,
+ text: value.toString(),
+ type: this.serializeType(value.getType()),
+ };
}
}
@@ -600,7 +638,11 @@ export class JsonPrinter extends Printer {
arg: this.serializeValue(stmt.getOp()),
};
} else {
- throw new Error('Unhandled Stmt: ' + util.inspect(stmt, { showHidden: true, depth: null }));
+ console.warn(`Unhandled Stmt: ${stmt.constructor.name} (${stmt.toString()})`);
+ return {
+ _: stmt.constructor.name,
+ text: stmt.toString(),
+ };
}
}
}
diff --git a/ets2panda/linter/arkanalyzer/src/utils/SparseBitVector.ts b/ets2panda/linter/arkanalyzer/src/utils/SparseBitVector.ts
index fcb5c816ac5f5b5ef8e9b4f3ace185db43c041c4..313fb88f359040faa8c3ffbe7c681a5120e1a9ae 100644
--- a/ets2panda/linter/arkanalyzer/src/utils/SparseBitVector.ts
+++ b/ets2panda/linter/arkanalyzer/src/utils/SparseBitVector.ts
@@ -538,7 +538,6 @@ export class SparseBitVector {
return changed;
}
- // Dump as string
toString(): string {
let ar = [...this];
return ar.toString();
diff --git a/ets2panda/linter/arkanalyzer/typedoc.json b/ets2panda/linter/arkanalyzer/typedoc.json
index 1b08d68d6925c2b388da76585a545eef75630cf4..e582cf129b3186e882113f1961d5b90c729e0745 100644
--- a/ets2panda/linter/arkanalyzer/typedoc.json
+++ b/ets2panda/linter/arkanalyzer/typedoc.json
@@ -16,6 +16,7 @@
],
"excludeInternal": true,
"useTsLinkResolution": true,
+ "plugin": "typedoc-plugin-markdown",
"out": "docs/api_docs",
"readme": "./README.en.md"
}
\ No newline at end of file
diff --git a/ets2panda/linter/homecheck/src/Index.ts b/ets2panda/linter/homecheck/src/Index.ts
index 6ad78b6bfe25006f6ab5f366effe38a2e7136803..b3228e9bd598c32c541a12874d949bdf8559b560 100644
--- a/ets2panda/linter/homecheck/src/Index.ts
+++ b/ets2panda/linter/homecheck/src/Index.ts
@@ -42,4 +42,4 @@ export { Utils } from './utils/common/Utils';
// tools
export { runTool, Tools } from './tools/toolEntry';
-export { MigrationTool } from './tools/migrationTool/MigrationTool';
+export { MigrationTool } from './tools/migrationTool/MigrationTool';
\ No newline at end of file
diff --git a/ets2panda/linter/homecheck/src/checker/migration/AppStorageGetCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/AppStorageGetCheck.ts
index 693f8914126a9a7d9a5da0d9e697963b77e5e460..8e1ef29f16e35b25605f8c61be37708dc72dd945 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/AppStorageGetCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/AppStorageGetCheck.ts
@@ -139,7 +139,7 @@ export class AppStorageGetCheck implements BaseChecker {
}
private getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const originPosition = stmt.getOperandOriginalPosition(operand);
if (arkFile && originPosition) {
const originPath = arkFile.getFilePath();
diff --git a/ets2panda/linter/homecheck/src/checker/migration/CustomBuilderCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/CustomBuilderCheck.ts
index b8dcb06db7d2c47226110a65dfc9298937fdc42c..ac46794b550b697820f4dc515f0318c443545155 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/CustomBuilderCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/CustomBuilderCheck.ts
@@ -208,7 +208,7 @@ export class CustomBuilderCheck implements BaseChecker {
}
private getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const originPosition = stmt.getOperandOriginalPosition(operand);
if (arkFile && originPosition) {
const originPath = arkFile.getFilePath();
@@ -229,7 +229,7 @@ export class CustomBuilderCheck implements BaseChecker {
fixPosition.endLine = endPosition.line;
fixPosition.endCol = endPosition.col;
}
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const sourceFile = AstTreeUtils.getASTNode(arkFile.getName(), arkFile.getCode());
const range = FixUtils.getRangeWithAst(sourceFile, fixPosition);
ruleFix.range = range;
diff --git a/ets2panda/linter/homecheck/src/checker/migration/InteropAssignCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/InteropAssignCheck.ts
index d956fe529c7181a55527a35c870cabde40d9c4d8..a17b9064bad53d862df042578e47360a71a8513b 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/InteropAssignCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/InteropAssignCheck.ts
@@ -98,7 +98,7 @@ export class InteropAssignCheck implements BaseChecker {
callsites.forEach(cs => {
let hasTargetArg = false;
const invoke = cs.getInvokeExpr()!;
- const csMethod = cs.getCfg()?.getDeclaringMethod();
+ const csMethod = cs.getCfg().getDeclaringMethod();
invoke.getArgs().forEach(arg => {
const argTy = arg.getType();
if (argTy instanceof PrimitiveType || this.isBoxedType(argTy)) {
@@ -149,7 +149,7 @@ export class InteropAssignCheck implements BaseChecker {
const desc = `${this.metaData.description} (${RULE_ID})`;
const severity = this.metaData.severity;
const ruleId = this.rule.ruleId;
- const filePath = assign.getCfg()?.getDeclaringMethod().getDeclaringArkFile()?.getFilePath() ?? '';
+ const filePath = assign.getCfg().getDeclaringMethod().getDeclaringArkFile()?.getFilePath() ?? '';
const defeats = new Defects(line, column, column, problem, desc, severity, ruleId, filePath, '', true, false, false);
this.issues.push(new IssueReport(defeats, undefined));
});
@@ -265,8 +265,6 @@ export class InteropAssignCheck implements BaseChecker {
}
if (file) {
return file.getLanguage();
- } else {
- logger.error(`fail to identify which file the type definition ${type.toString()} is in.`);
}
return undefined;
}
diff --git a/ets2panda/linter/homecheck/src/checker/migration/InteropBackwardDFACheck.ts b/ets2panda/linter/homecheck/src/checker/migration/InteropBackwardDFACheck.ts
index 9651894a32e058ab673a856c3b29f5581600baa6..85a045b7f0334b703db5fd7fc2017f73ad7ca002 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/InteropBackwardDFACheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/InteropBackwardDFACheck.ts
@@ -214,7 +214,7 @@ export class InteropBackwardDFACheck implements BaseChecker {
private reportIssue(objDefInfo: ObjDefInfo, apiLang: Language, isReflect: boolean) {
const problemStmt = objDefInfo.problemStmt;
- const problemStmtMtd = problemStmt.getCfg()?.getDeclaringMethod();
+ const problemStmtMtd = problemStmt.getCfg().getDeclaringMethod();
const problemStmtLang = problemStmtMtd?.getLanguage();
const objLanguage = objDefInfo.objLanguage;
if (objLanguage === Language.UNKNOWN || problemStmtLang === Language.UNKNOWN) {
diff --git a/ets2panda/linter/homecheck/src/checker/migration/InteropBoxedTypeCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/InteropBoxedTypeCheck.ts
index 42b4dc0973a789fe6d96f7a86e20bb8323dbbdf0..d0a1c9b10fa1064e34fd46de5cbbdbb79c1e2859 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/InteropBoxedTypeCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/InteropBoxedTypeCheck.ts
@@ -40,7 +40,7 @@ import { WarnInfo } from '../../utils/common/Utils';
import { Language } from 'arkanalyzer/lib/core/model/ArkFile';
import { getLanguageStr } from './Utils';
-const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'ObservedDecoratorCheck');
+const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'InteropBoxedTypeCheck');
const gMetaData: BaseMetaData = {
severity: 1,
ruleDocPath: '',
diff --git a/ets2panda/linter/homecheck/src/checker/migration/InteropJSModifyPropertyCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/InteropJSModifyPropertyCheck.ts
index 3b912361f2bb7896b526e8a434388ddb7de83703..16ea7584bcb4d8889287c3486c4da61c259c7eca 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/InteropJSModifyPropertyCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/InteropJSModifyPropertyCheck.ts
@@ -276,10 +276,8 @@ export class InteropJSModifyPropertyCheck implements BaseChecker {
}
if (file) {
return file.getLanguage();
- } else {
- logger.error(`fail to identify which file the type definition ${type.toString()} is in.`);
- return Language.UNKNOWN;
}
+ return Language.UNKNOWN;
}
private reportIssue(problemStmt: Stmt) {
@@ -289,7 +287,7 @@ export class InteropJSModifyPropertyCheck implements BaseChecker {
const desc = `${this.metaData.description} (${RULE_ID})`;
const severity = this.metaData.severity;
const ruleId = this.rule.ruleId;
- const filePath = problemStmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile()?.getFilePath() ?? '';
+ const filePath = problemStmt.getCfg().getDeclaringMethod().getDeclaringArkFile()?.getFilePath() ?? '';
const defeats = new Defects(
line,
column,
diff --git a/ets2panda/linter/homecheck/src/checker/migration/ModifyStateVarCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/ModifyStateVarCheck.ts
index c25cc59611ec6e7b8182054beace616da724ba7a..dfabd40fe05228e763bbd99a440a70c3072378db 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/ModifyStateVarCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/ModifyStateVarCheck.ts
@@ -171,7 +171,7 @@ export class ModifyStateVarCheck implements BaseChecker {
}
private getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const originPosition = stmt.getOperandOriginalPosition(operand);
if (arkFile && originPosition) {
const originPath = arkFile.getFilePath();
diff --git a/ets2panda/linter/homecheck/src/checker/migration/NoTSLikeAsCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/NoTSLikeAsCheck.ts
index f45c63723cfb7b492c0972d4079cd9d6ef5d6740..3e6f594aa1aff68dc4c3412b80abd932b6702e5f 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/NoTSLikeAsCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/NoTSLikeAsCheck.ts
@@ -37,6 +37,13 @@ import {
BasicBlock,
ArkIfStmt,
ArkUnopExpr,
+ RelationalBinaryOperator,
+ LineColPosition,
+ UnaryOperator,
+ ArkNormalBinopExpr,
+ NormalBinaryOperator,
+ AbstractFieldRef,
+ ClassSignature,
} from 'arkanalyzer/lib';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { BaseChecker, BaseMetaData } from '../BaseChecker';
@@ -47,6 +54,7 @@ import { CALL_DEPTH_LIMIT, getGlobalsDefineInDefaultMethod, GlobalCallGraphHelpe
import { WarnInfo } from '../../utils/common/Utils';
import { ClassCategory } from 'arkanalyzer/lib/core/model/ArkClass';
import { Language } from 'arkanalyzer/lib/core/model/ArkFile';
+import { BooleanConstant } from 'arkanalyzer/lib/core/base/Constant';
const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'NoTSLikeAsCheck');
const gMetaData: BaseMetaData = {
@@ -55,6 +63,12 @@ const gMetaData: BaseMetaData = {
description: '',
};
+enum TypeAssuranceCondition {
+ Positive,
+ Negative,
+ NotExist,
+}
+
export class NoTSLikeAsCheck implements BaseChecker {
readonly metaData: BaseMetaData = gMetaData;
public rule: Rule;
@@ -181,21 +195,19 @@ export class NoTSLikeAsCheck implements BaseChecker {
return false;
}
for (const block of cfg.getBlocks()) {
- // 这里仅判断了cast op是否进行了instanceof判断,如果op是由op1赋值,op1进行了instanceof判断,此处不认为是做了有效检查
- // TODO: 还需进行复杂条件中包含类型守卫判断的情况,涉及&&,||等的复合
- const positiveCheck = this.isCastExprWithTypeAssurancePositive(block, castExpr);
- const negativeCheck = this.isCastExprWithTypeAssuranceNegative(block, castExpr);
- if (!(positiveCheck || negativeCheck)) {
+ // 这里仅判断了cast op是否进行了instanceof判断,如果op是由op1赋值,op1进行了instanceof判断,此处不认为是做了有效检查,因为此赋值链可能很长且中途发生类型变化,极易判断错误
+ const checkRes = this.checkTypeAssuranceInBasicBlock(block, castExpr);
+ if (checkRes === TypeAssuranceCondition.NotExist) {
continue;
}
let checkedBB: Set = new Set();
let needCheckBB: number[] = [];
checkedBB.add(block.getId());
const allSuccessors = block.getSuccessors();
- if (allSuccessors.length > 0 && positiveCheck) {
+ if (allSuccessors.length > 0 && checkRes === TypeAssuranceCondition.Positive) {
needCheckBB.push(allSuccessors[0].getId());
}
- if (allSuccessors.length > 1 && negativeCheck) {
+ if (allSuccessors.length > 1 && checkRes === TypeAssuranceCondition.Negative) {
needCheckBB.push(allSuccessors[1].getId());
}
while (needCheckBB.length > 0) {
@@ -221,26 +233,7 @@ export class NoTSLikeAsCheck implements BaseChecker {
return false;
}
- private isStmtInBlock(stmt: Stmt, block: BasicBlock): boolean {
- for (const s of block.getStmts()) {
- if (s === stmt) {
- return true;
- }
- }
- return false;
- }
-
- private getBlockWithId(id: number, cfg: Cfg): BasicBlock | null {
- const blocks = cfg.getBlocks();
- for (const bb of blocks) {
- if (bb.getId() === id) {
- return bb;
- }
- }
- return null;
- }
-
- private isCastExprWithTypeAssurancePositive(bb: BasicBlock, castExpr: ArkCastExpr): boolean {
+ private checkTypeAssuranceInBasicBlock(bb: BasicBlock, castExpr: ArkCastExpr): TypeAssuranceCondition {
for (const stmt of bb.getStmts()) {
if (!(stmt instanceof ArkIfStmt)) {
continue;
@@ -248,98 +241,109 @@ export class NoTSLikeAsCheck implements BaseChecker {
const conditionExpr = stmt.getConditionExpr();
const op1 = conditionExpr.getOp1();
const op2 = conditionExpr.getOp2();
- if (op1 instanceof Local) {
- const declareStmt = op1.getDeclaringStmt();
- if (declareStmt !== null && this.isStmtWithTypeAssurancePositive(declareStmt, castExpr)) {
- return true;
- }
- }
- if (op2 instanceof Local) {
- const declareStmt = op2.getDeclaringStmt();
- if (declareStmt !== null && this.isStmtWithTypeAssurancePositive(declareStmt, castExpr)) {
- return true;
- }
+ const operator = conditionExpr.getOperator();
+ // 对于if (i instanceof A)这种条件语句,op1总是临时变量,op2总是false,操作符总是!=
+ if (!(op1 instanceof Local && op2 instanceof BooleanConstant && op2.getValue() === 'false' && operator === RelationalBinaryOperator.InEquality)) {
+ break;
}
+ return this.checkTypeAssuranceWithLocal(op1, castExpr, stmt.getOriginPositionInfo(), true);
}
- return false;
+ return TypeAssuranceCondition.NotExist;
}
- private isCastExprWithTypeAssuranceNegative(bb: BasicBlock, castExpr: ArkCastExpr): boolean {
- for (const stmt of bb.getStmts()) {
- if (!(stmt instanceof ArkIfStmt)) {
- continue;
+ private checkTypeAssuranceWithLocal(operand: Local, castExpr: ArkCastExpr, ifStmtPos: LineColPosition, shouldBe: boolean): TypeAssuranceCondition {
+ const declaringStmt = operand.getDeclaringStmt();
+ if (declaringStmt === null) {
+ return TypeAssuranceCondition.NotExist;
+ }
+ // if语句中的所有条件遵从三地址码原则拆分成多个语句时,所有语句的位置信息是一致的,不一致时表示是条件语句之前的赋值或声明情况,不在本判断范围内
+ const stmtPos = declaringStmt.getOriginPositionInfo();
+ if (stmtPos.getLineNo() !== ifStmtPos.getLineNo() || stmtPos.getColNo() !== ifStmtPos.getColNo()) {
+ return TypeAssuranceCondition.NotExist;
+ }
+ if (!(declaringStmt instanceof ArkAssignStmt)) {
+ return TypeAssuranceCondition.NotExist;
+ }
+ const rightOp = declaringStmt.getRightOp();
+ if (rightOp instanceof ArkInstanceOfExpr) {
+ if (this.isTypeAssuranceMatchCast(rightOp, castExpr)) {
+ if (shouldBe) {
+ return TypeAssuranceCondition.Positive;
+ } else {
+ return TypeAssuranceCondition.Negative;
+ }
}
- const conditionExpr = stmt.getConditionExpr();
- const op1 = conditionExpr.getOp1();
- const op2 = conditionExpr.getOp2();
- if (op1 instanceof Local) {
- const declareStmt = op1.getDeclaringStmt();
- if (declareStmt !== null && this.isStmtWithTypeAssuranceNegative(declareStmt, castExpr)) {
- return true;
+ return TypeAssuranceCondition.NotExist;
+ }
+ if (rightOp instanceof ArkUnopExpr && rightOp.getOperator() === UnaryOperator.LogicalNot) {
+ const unaryOp = rightOp.getOp();
+ if (unaryOp instanceof Local) {
+ return this.checkTypeAssuranceWithLocal(unaryOp, castExpr, ifStmtPos, !shouldBe);
+ }
+ return TypeAssuranceCondition.NotExist;
+ }
+ if (rightOp instanceof ArkNormalBinopExpr) {
+ const op1 = rightOp.getOp1();
+ const op2 = rightOp.getOp2();
+ const operator = rightOp.getOperator();
+ // 这里仅判断&&和||两种逻辑运算符的场景,其他场景在包含类型守卫判断的条件语句中不常见,暂不考虑
+ let res: TypeAssuranceCondition;
+ if (operator === NormalBinaryOperator.LogicalAnd) {
+ if (op1 instanceof Local) {
+ res = this.checkTypeAssuranceWithLocal(op1, castExpr, ifStmtPos, shouldBe);
+ if (res !== TypeAssuranceCondition.NotExist) {
+ return res;
+ }
+ }
+ if (op2 instanceof Local) {
+ res = this.checkTypeAssuranceWithLocal(op2, castExpr, ifStmtPos, shouldBe);
+ if (res !== TypeAssuranceCondition.NotExist) {
+ return res;
+ }
}
+ return TypeAssuranceCondition.NotExist;
}
- if (op2 instanceof Local) {
- const declareStmt = op2.getDeclaringStmt();
- if (declareStmt !== null && this.isStmtWithTypeAssuranceNegative(declareStmt, castExpr)) {
- return true;
+ if (operator === NormalBinaryOperator.LogicalOr) {
+ // a or b,不论a或b里是类型守卫判断,均无法保证分支中的类型明确
+ if (shouldBe) {
+ return TypeAssuranceCondition.NotExist;
}
}
}
- return false;
+ return TypeAssuranceCondition.NotExist;
}
- private isStmtWithTypeAssurancePositive(declareStmt: Stmt, castExpr: ArkCastExpr): boolean {
- if (!(declareStmt instanceof ArkAssignStmt)) {
- return false;
- }
- const rightOp = declareStmt.getRightOp();
- if (!(rightOp instanceof ArkInstanceOfExpr)) {
- return false;
- }
+ private isTypeAssuranceMatchCast(instanceOfExpr: ArkInstanceOfExpr, castExpr: ArkCastExpr): boolean {
const castOp = castExpr.getOp();
const castType = castExpr.getType();
- const instanceofType = rightOp.getCheckType();
+ const instanceofType = instanceOfExpr.getCheckType();
if (castType.getTypeString() !== instanceofType.getTypeString()) {
return false;
}
- const instanceofOp = rightOp.getOp();
+ const instanceofOp = instanceOfExpr.getOp();
if (!(castOp instanceof Local && instanceofOp instanceof Local)) {
return false;
}
return castOp.getName() === instanceofOp.getName();
}
- private isStmtWithTypeAssuranceNegative(declareStmt: Stmt, castExpr: ArkCastExpr): boolean {
- if (!(declareStmt instanceof ArkAssignStmt)) {
- return false;
- }
- const rightOp = declareStmt.getRightOp();
- if (!(rightOp instanceof ArkUnopExpr && rightOp.getOperator() === '!')) {
- return false;
- }
- const unaryOp = rightOp.getOp();
- if (!(unaryOp instanceof Local)) {
- return false;
- }
- const unaryOpDeclareStmt = unaryOp.getDeclaringStmt();
- if (unaryOpDeclareStmt === null || !(unaryOpDeclareStmt instanceof ArkAssignStmt)) {
- return false;
- }
- const unaryOpRightOp = unaryOpDeclareStmt.getRightOp();
- if (!(unaryOpRightOp instanceof ArkInstanceOfExpr)) {
- return false;
- }
- const castOp = castExpr.getOp();
- const castType = castExpr.getType();
- const instanceofType = unaryOpRightOp.getCheckType();
- if (castType.getTypeString() !== instanceofType.getTypeString()) {
- return false;
+ private isStmtInBlock(stmt: Stmt, block: BasicBlock): boolean {
+ for (const s of block.getStmts()) {
+ if (s === stmt) {
+ return true;
+ }
}
- const instanceofOp = unaryOpRightOp.getOp();
- if (!(castOp instanceof Local && instanceofOp instanceof Local)) {
- return false;
+ return false;
+ }
+
+ private getBlockWithId(id: number, cfg: Cfg): BasicBlock | null {
+ const blocks = cfg.getBlocks();
+ for (const bb of blocks) {
+ if (bb.getId() === id) {
+ return bb;
+ }
}
- return castOp.getName() === instanceofOp.getName();
+ return null;
}
private checkFromStmt(
@@ -366,6 +370,11 @@ export class NoTSLikeAsCheck implements BaseChecker {
if (this.isWithInterfaceAnnotation(currentStmt, scene)) {
return currentStmt;
}
+
+ const fieldDeclareStmt = this.isCastOpFieldWithInterfaceType(currentStmt, scene);
+ if (fieldDeclareStmt) {
+ return fieldDeclareStmt;
+ }
const gv = this.checkIfCastOpIsGlobalVar(currentStmt);
if (gv) {
const globalDefs = globalVarMap.get(gv.getName());
@@ -401,9 +410,7 @@ export class NoTSLikeAsCheck implements BaseChecker {
const paramRef = this.isFromParameter(currentStmt);
if (paramRef) {
const paramIdx = paramRef.getIndex();
- const callsites = this.cg.getInvokeStmtByMethod(
- currentStmt.getCfg().getDeclaringMethod().getSignature()
- );
+ const callsites = this.cg.getInvokeStmtByMethod(currentStmt.getCfg().getDeclaringMethod().getSignature());
this.processCallsites(callsites);
const argDefs = this.collectArgDefs(paramIdx, callsites);
for (const stmt of argDefs) {
@@ -418,6 +425,34 @@ export class NoTSLikeAsCheck implements BaseChecker {
return null;
}
+ private isCastOpFieldWithInterfaceType(stmt: Stmt, scene: Scene): Stmt | undefined {
+ const obj = this.getCastOp(stmt);
+ if (obj === null || !(obj instanceof Local)) {
+ return undefined;
+ }
+ const declaringStmt = obj.getDeclaringStmt();
+ if (declaringStmt === null || !(declaringStmt instanceof ArkAssignStmt)) {
+ return undefined;
+ }
+ const rightOp = declaringStmt.getRightOp();
+ if (!(rightOp instanceof AbstractFieldRef)) {
+ return undefined;
+ }
+ const fieldDeclaring = rightOp.getFieldSignature().getDeclaringSignature();
+ if (fieldDeclaring instanceof ClassSignature) {
+ const field = scene.getClass(fieldDeclaring)?.getField(rightOp.getFieldSignature());
+ if (!field) {
+ return undefined;
+ }
+ const fieldInitializer = field.getInitializer();
+ const lastStmt = fieldInitializer[fieldInitializer.length - 1];
+ if (this.isWithInterfaceAnnotation(lastStmt, scene)) {
+ return lastStmt;
+ }
+ }
+ return undefined;
+ }
+
private checkIfCastOpIsGlobalVar(stmt: Stmt): Local | undefined {
const obj = this.getCastOp(stmt);
if (obj instanceof Local && !obj.getDeclaringStmt()) {
@@ -568,7 +603,7 @@ export class NoTSLikeAsCheck implements BaseChecker {
}
private getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const originPosition = stmt.getOperandOriginalPosition(operand);
if (arkFile && originPosition) {
const originPath = arkFile.getFilePath();
diff --git a/ets2panda/linter/homecheck/src/checker/migration/ObjectLiteralCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/ObjectLiteralCheck.ts
index 66a5c66380b92443cde465bcf577ad4e05791028..b582c90aaf7befd5ec9c52e4dd94af836da2f2dc 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/ObjectLiteralCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/ObjectLiteralCheck.ts
@@ -68,7 +68,7 @@ export class ObjectLiteralCheck implements BaseChecker {
for (let arkFile of scene.getFiles()) {
const topLevelVarMap: Map = new Map();
- this.collectImportedVar(topLevelVarMap, arkFile);
+ this.collectImportedVar(topLevelVarMap, arkFile, scene);
this.collectTopLevelVar(topLevelVarMap, arkFile, scene);
const handleClass = (cls: ArkClass): void => {
@@ -106,7 +106,7 @@ export class ObjectLiteralCheck implements BaseChecker {
}
}
- private collectImportedVar(importVarMap: Map, file: ArkFile) {
+ private collectImportedVar(importVarMap: Map, file: ArkFile, scene: Scene) {
file.getImportInfos().forEach(importInfo => {
const exportInfo = importInfo.getLazyExportInfo();
if (exportInfo === null) {
@@ -120,6 +120,7 @@ export class ObjectLiteralCheck implements BaseChecker {
if (!declaringStmt) {
return;
}
+ DVFGHelper.buildSingleDVFG(declaringStmt.getCfg().getDeclaringMethod(), scene);
importVarMap.set(arkExport.getName(), [declaringStmt]);
});
}
@@ -382,7 +383,7 @@ export class ObjectLiteralCheck implements BaseChecker {
}
private getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const originPosition = stmt.getOperandOriginalPosition(operand);
if (arkFile && originPosition) {
const originPath = arkFile.getFilePath();
diff --git a/ets2panda/linter/homecheck/src/checker/migration/ObservedDecoratorCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/ObservedDecoratorCheck.ts
index a6a56e9b4d2ef3d9d95783a0f6d5e03f4fd5004e..8f9c5cdb03a5d1dce03fee6273281375b72ca439 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/ObservedDecoratorCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/ObservedDecoratorCheck.ts
@@ -266,7 +266,7 @@ export class ObservedDecoratorCheck implements BaseChecker {
canFindAllTargets: boolean = true
): string {
if (issueClass === null || !canFindAllTargets) {
- return `can not find all classes, please check this field manually`;
+ return `can not find all classes, please check this field manually (arkui-data-observation)`;
}
const fieldLine = field.getOriginPosition().getLineNo();
const fieldColumn = field.getOriginPosition().getColNo();
@@ -275,10 +275,10 @@ export class ObservedDecoratorCheck implements BaseChecker {
const issueClassSig = issueClass.getDeclaringArkFile().getFileSignature();
let res = `but it's not be annotated by @Observed (arkui-data-observation)`;
if (fileSignatureCompare(fieldFileSig, issueClassSig)) {
- res = `The class is used by state property in [${fieldLine}, ${fieldColumn}], ` + res;
+ res = `Class ${issueClass.getName()} is used by state property in [${fieldLine}, ${fieldColumn}], ` + res;
} else {
const filePath = path.normalize(fieldFileSig.getFileName());
- res = `The class is used by state property in file ${filePath} [${fieldLine}, ${fieldColumn}], ` + res;
+ res = `Class ${issueClass.getName()} is used by state property in file ${filePath} [${fieldLine}, ${fieldColumn}], ` + res;
}
return res;
}
diff --git a/ets2panda/linter/homecheck/src/checker/migration/ThisBindCheck.ts b/ets2panda/linter/homecheck/src/checker/migration/ThisBindCheck.ts
index af8657687c1de1e597a6b4a0da9e3d2730f0479e..b290beab8f562da19286921e56995edf7723aca0 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/ThisBindCheck.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/ThisBindCheck.ts
@@ -348,7 +348,7 @@ export class ThisBindCheck implements BaseChecker {
}
private getLineAndColumn(stmt: ArkAssignStmt, operand: Value): WarnInfo {
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const originPosition = stmt.getOperandOriginalPosition(operand);
if (arkFile && originPosition) {
const originPath = arkFile.getFilePath();
diff --git a/ets2panda/linter/homecheck/src/checker/migration/Utils.ts b/ets2panda/linter/homecheck/src/checker/migration/Utils.ts
index 0992bcbe88d2748a8d25a3b7b12b9c8b6492578c..02f114bd0f9d453a33f779834c19a9159a462b57 100644
--- a/ets2panda/linter/homecheck/src/checker/migration/Utils.ts
+++ b/ets2panda/linter/homecheck/src/checker/migration/Utils.ts
@@ -13,7 +13,18 @@
* limitations under the License.
*/
-import { ArkAssignStmt, ArkMethod, CallGraph, CallGraphBuilder, Local, LOG_MODULE_TYPE, Logger, Scene, Stmt, Value } from 'arkanalyzer/lib';
+import {
+ ArkAssignStmt,
+ ArkMethod,
+ CallGraph,
+ CallGraphBuilder,
+ Local,
+ LOG_MODULE_TYPE,
+ Logger,
+ Scene,
+ Stmt,
+ Value,
+} from 'arkanalyzer/lib';
import { WarnInfo } from '../../utils/common/Utils';
import { Language } from 'arkanalyzer/lib/core/model/ArkFile';
import { DVFG, DVFGNode } from 'arkanalyzer/lib/VFG/DVFG';
@@ -25,6 +36,10 @@ export const CALL_DEPTH_LIMIT = 2;
export class CallGraphHelper {
private static cgInstance: CallGraph | null = null;
+ public static dispose(): void {
+ this.cgInstance = null;
+ }
+
public static getCGInstance(scene: Scene): CallGraph {
if (!this.cgInstance) {
this.cgInstance = new CallGraph(scene);
@@ -36,6 +51,10 @@ export class CallGraphHelper {
export class GlobalCallGraphHelper {
private static cgInstance: CallGraph | null = null;
+ public static dispose(): void {
+ this.cgInstance = null;
+ }
+
public static getCGInstance(scene: Scene): CallGraph {
if (!this.cgInstance) {
this.cgInstance = new CallGraph(scene);
@@ -51,6 +70,14 @@ export class DVFGHelper {
private static dvfgBuilder: DVFGBuilder;
private static built: Set = new Set();
+ public static dispose(): void {
+ // @ts-ignore
+ this.dvfgInstance = null;
+ // @ts-ignore
+ this.dvfgBuilder = null;
+ this.built.clear();
+ }
+
private static createDVFGInstance(scene: Scene): void {
if (!this.dvfgInstance) {
this.dvfgInstance = new DVFG(GlobalCallGraphHelper.getCGInstance(scene));
@@ -117,7 +144,7 @@ export function getLanguageStr(language: Language): string {
}
export function getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
- const arkFile = stmt.getCfg()?.getDeclaringMethod().getDeclaringArkFile();
+ const arkFile = stmt.getCfg().getDeclaringMethod().getDeclaringArkFile();
const originPosition = stmt.getOperandOriginalPosition(operand);
if (arkFile && originPosition) {
const originPath = arkFile.getFilePath();
@@ -126,7 +153,7 @@ export function getLineAndColumn(stmt: Stmt, operand: Value): WarnInfo {
const endCol = startCol;
return { line, startCol, endCol, filePath: originPath };
} else {
- logger.debug('ArkFile is null.');
+ logger.debug('ArkFile or operand position is null.');
}
return { line: -1, startCol: -1, endCol: -1, filePath: '' };
}
diff --git a/ets2panda/linter/homecheck/src/tools/migrationTool/MigrationTool.ts b/ets2panda/linter/homecheck/src/tools/migrationTool/MigrationTool.ts
index 5c3f6478eda979502d63bbb9e1cd817ead03cd0e..7be52a159ab6906519aff74c6c3634d503716c0e 100644
--- a/ets2panda/linter/homecheck/src/tools/migrationTool/MigrationTool.ts
+++ b/ets2panda/linter/homecheck/src/tools/migrationTool/MigrationTool.ts
@@ -21,7 +21,8 @@ import { CheckerStorage } from '../../utils/common/CheckerStorage';
import Logger, { LOG_MODULE_TYPE } from 'arkanalyzer/lib/utils/logger';
import { FileUtils } from '../../utils/common/FileUtils';
import { DefaultMessage } from '../../model/Message';
-import { FileIssues } from "../../model/Defects";
+import { FileIssues } from '../../model/Defects';
+import { CallGraphHelper, DVFGHelper, GlobalCallGraphHelper } from '../../checker/migration/Utils';
const logger = Logger.getLogger(LOG_MODULE_TYPE.HOMECHECK, 'MigrationTool');
@@ -68,7 +69,16 @@ export class MigrationTool {
await this.checkEntry.runAll();
let result = this.checkEntry.sortIssues();
+ this.dispose();
logger.info(`MigrationTool run end`);
return result;
}
+
+ private dispose(): void {
+ CallGraphHelper.dispose();
+ GlobalCallGraphHelper.dispose();
+ DVFGHelper.dispose();
+ CheckerStorage.dispose();
+ this.checkEntry.scene.dispose();
+ }
}
\ No newline at end of file
diff --git a/ets2panda/linter/homecheck/src/utils/common/CheckerStorage.ts b/ets2panda/linter/homecheck/src/utils/common/CheckerStorage.ts
index f3bedd5017d59910d6579916b5fb00d2a2f1a9c3..922b716e95b63524327ca53c62cd89ff72d5b855 100644
--- a/ets2panda/linter/homecheck/src/utils/common/CheckerStorage.ts
+++ b/ets2panda/linter/homecheck/src/utils/common/CheckerStorage.ts
@@ -21,6 +21,11 @@ export class CheckerStorage {
private apiVersion: number = 16;
private product: string = '';
+ public static dispose(): void {
+ // @ts-ignore
+ this.instance = null;
+ }
+
/**
* 获取 CheckerStorage 的单例实例
* @returns {CheckerStorage} CheckerStorage 的单例实例
diff --git a/ets2panda/linter/homecheck/src/utils/common/FileUtils.ts b/ets2panda/linter/homecheck/src/utils/common/FileUtils.ts
index 48b06a414dc94e9e5474890f8a89dd32be5116fd..f17425f284605ab9f4cd9738a2ec9fc9c72121ba 100644
--- a/ets2panda/linter/homecheck/src/utils/common/FileUtils.ts
+++ b/ets2panda/linter/homecheck/src/utils/common/FileUtils.ts
@@ -232,7 +232,7 @@ export class FileUtils {
}
private static shouldSkipFile(fileName: string): boolean {
- return ['oh_modules', 'node_modules', 'hvigorfile.ts', 'ohosTest'].includes(fileName);
+ return ['oh_modules', 'node_modules', 'hvigorfile.ts', 'hvigorfile.js', 'hvigor-wrapper.js', 'ohosTest'].includes(fileName);
}
private static shouldAddFile(filePath: string, exts: string[]): boolean {
@@ -310,4 +310,4 @@ export class FileUtils {
export enum WriteFileMode {
OVERWRITE,
APPEND
-}
\ No newline at end of file
+}
diff --git a/ets2panda/linter/src/cli/LinterCLI.ts b/ets2panda/linter/src/cli/LinterCLI.ts
index 45bd9fddcd788362a353d59c33e646bac8156c4e..b7c4dc169cd5149870f90708f7f354e3c3947e0f 100644
--- a/ets2panda/linter/src/cli/LinterCLI.ts
+++ b/ets2panda/linter/src/cli/LinterCLI.ts
@@ -58,9 +58,10 @@ async function runIdeInteractiveMode(cmdOptions: CommandLineOptions): Promise