• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2024-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { Scene } from '../../Scene';
17import { AbstractInvokeExpr } from '../base/Expr';
18import { ArkInvokeStmt, ArkReturnStmt, ArkReturnVoidStmt, Stmt } from '../base/Stmt';
19import { ArkMethod } from '../model/ArkMethod';
20import { DataflowProblem, FlowFunction } from './DataflowProblem';
21import { PathEdge, PathEdgePoint } from './Edge';
22import { BasicBlock } from '../graph/BasicBlock';
23import { CallGraph } from '../../callgraph/model/CallGraph';
24import { ClassHierarchyAnalysis } from '../../callgraph/algorithm/ClassHierarchyAnalysis';
25import { addCfg2Stmt } from '../../utils/entryMethodUtils';
26import { getRecallMethodInParam } from './Util';
27import { CallGraphBuilder } from '../../callgraph/model/builder/CallGraphBuilder';
28
29/*
30this program is roughly an implementation of the paper: Practical Extensions to the IFDS Algorithm.
31compare to the original ifds paper : Precise Interprocedural Dataflow Analysis via Graph Reachability,
32it have several improvments:
331. construct supergraph on demand(implement in this program);
342. use endSummary and incoming tables to speed up the program(implement in this program)
353. handle ssa form(not implement)
364. handle data facts which subsume another(not implement)
37*/
38type CallToReturnCacheEdge<D> = PathEdge<D>;
39
40export abstract class DataflowSolver<D> {
41    protected problem: DataflowProblem<D>;
42    protected workList: Array<PathEdge<D>>;
43    protected pathEdgeSet: Set<PathEdge<D>>;
44    protected zeroFact: D;
45    protected inComing: Map<PathEdgePoint<D>, Set<PathEdgePoint<D>>>;
46    protected endSummary: Map<PathEdgePoint<D>, Set<PathEdgePoint<D>>>;
47    protected summaryEdge: Set<CallToReturnCacheEdge<D>>; // summaryEdge不是加速一个函数内多次调用同一个函数,而是加速多次调用同一个函数f时,f内的函数调用
48    protected scene: Scene;
49    protected CHA!: ClassHierarchyAnalysis;
50    protected stmtNexts: Map<Stmt, Set<Stmt>>;
51    protected laterEdges: Set<PathEdge<D>> = new Set();
52
53    constructor(problem: DataflowProblem<D>, scene: Scene) {
54        this.problem = problem;
55        this.scene = scene;
56        scene.inferTypes();
57        this.zeroFact = problem.createZeroValue();
58        this.workList = new Array<PathEdge<D>>();
59        this.pathEdgeSet = new Set<PathEdge<D>>();
60        this.inComing = new Map<PathEdgePoint<D>, Set<PathEdgePoint<D>>>();
61        this.endSummary = new Map<PathEdgePoint<D>, Set<PathEdgePoint<D>>>();
62        this.summaryEdge = new Set<CallToReturnCacheEdge<D>>();
63        this.stmtNexts = new Map();
64    }
65
66    public solve(): void {
67        this.init();
68        this.doSolve();
69    }
70
71    protected computeResult(stmt: Stmt, d: D): boolean {
72        for (let pathEdge of this.pathEdgeSet) {
73            if (pathEdge.edgeEnd.node === stmt && pathEdge.edgeEnd.fact === d) {
74                return true;
75            }
76        }
77        return false;
78    }
79
80    protected getChildren(stmt: Stmt): Stmt[] {
81        return Array.from(this.stmtNexts.get(stmt) || []);
82    }
83
84    protected init(): void {
85        let edgePoint: PathEdgePoint<D> = new PathEdgePoint<D>(this.problem.getEntryPoint(), this.zeroFact);
86        let edge: PathEdge<D> = new PathEdge<D>(edgePoint, edgePoint);
87        this.workList.push(edge);
88        this.pathEdgeSet.add(edge);
89
90        // build CHA
91        let cg = new CallGraph(this.scene);
92        this.CHA = new ClassHierarchyAnalysis(this.scene, cg, new CallGraphBuilder(cg, this.scene));
93        this.buildStmtMapInClass();
94        this.setCfg4AllStmt();
95        return;
96    }
97
98    protected buildStmtMapInClass(): void {
99        const methods = this.scene.getMethods();
100        methods.push(this.problem.getEntryMethod());
101        for (const method of methods) {
102            const cfg = method.getCfg();
103            const blocks: BasicBlock[] = [];
104            if (cfg) {
105                blocks.push(...cfg.getBlocks());
106            }
107            for (const block of blocks) {
108                this.buildStmtMapInBlock(block);
109            }
110        }
111    }
112
113    protected buildStmtMapInBlock(block: BasicBlock): void {
114        const stmts = block.getStmts();
115        for (let stmtIndex = 0; stmtIndex < stmts.length; stmtIndex++) {
116            const stmt = stmts[stmtIndex];
117            if (stmtIndex !== stmts.length - 1) {
118                this.stmtNexts.set(stmt, new Set([stmts[stmtIndex + 1]]));
119            } else {
120                const set: Set<Stmt> = new Set();
121                for (const successor of block.getSuccessors()) {
122                    set.add(successor.getStmts()[0]);
123                }
124                this.stmtNexts.set(stmt, set);
125            }
126        }
127    }
128
129    protected setCfg4AllStmt(): void {
130        for (const cls of this.scene.getClasses()) {
131            for (const mtd of cls.getMethods(true)) {
132                addCfg2Stmt(mtd);
133            }
134        }
135    }
136
137    protected getAllCalleeMethods(callNode: ArkInvokeStmt): Set<ArkMethod> {
138        const callSites = this.CHA.resolveCall(
139            this.CHA.getCallGraph().getCallGraphNodeByMethod(this.problem.getEntryMethod().getSignature()).getID(),
140            callNode
141        );
142        const methods: Set<ArkMethod> = new Set();
143        for (const callSite of callSites) {
144            const method = this.scene.getMethod(this.CHA.getCallGraph().getMethodByFuncID(callSite.calleeFuncID)!);
145            if (method) {
146                methods.add(method);
147            }
148        }
149        return methods;
150    }
151
152    protected getReturnSiteOfCall(call: Stmt): Stmt {
153        return [...this.stmtNexts.get(call)!][0];
154    }
155
156    protected getStartOfCallerMethod(call: Stmt): Stmt {
157        const cfg = call.getCfg()!;
158        const paraNum = cfg.getDeclaringMethod().getParameters().length;
159        return [...cfg.getBlocks()][0].getStmts()[paraNum];
160    }
161
162    protected pathEdgeSetHasEdge(edge: PathEdge<D>): boolean {
163        for (const path of this.pathEdgeSet) {
164            this.problem.factEqual(path.edgeEnd.fact, edge.edgeEnd.fact);
165            if (
166                path.edgeEnd.node === edge.edgeEnd.node &&
167                this.problem.factEqual(path.edgeEnd.fact, edge.edgeEnd.fact) &&
168                path.edgeStart.node === edge.edgeStart.node &&
169                this.problem.factEqual(path.edgeStart.fact, edge.edgeStart.fact)
170            ) {
171                return true;
172            }
173        }
174        return false;
175    }
176
177    protected propagate(edge: PathEdge<D>): void {
178        if (!this.pathEdgeSetHasEdge(edge)) {
179            let index = this.workList.length;
180            for (let i = 0; i < this.workList.length; i++) {
181                if (this.laterEdges.has(this.workList[i])) {
182                    index = i;
183                    break;
184                }
185            }
186            this.workList.splice(index, 0, edge);
187            this.pathEdgeSet.add(edge);
188        }
189    }
190
191    protected processExitNode(edge: PathEdge<D>): void {
192        let startEdgePoint: PathEdgePoint<D> = edge.edgeStart;
193        let exitEdgePoint: PathEdgePoint<D> = edge.edgeEnd;
194        const summary = this.endSummary.get(startEdgePoint);
195        if (summary === undefined) {
196            this.endSummary.set(startEdgePoint, new Set([exitEdgePoint]));
197        } else {
198            summary.add(exitEdgePoint);
199        }
200        const callEdgePoints = this.inComing.get(startEdgePoint);
201        if (callEdgePoints === undefined) {
202            if (startEdgePoint.node.getCfg()!.getDeclaringMethod() === this.problem.getEntryMethod()) {
203                return;
204            }
205            throw new Error('incoming does not have ' + startEdgePoint.node.getCfg()?.getDeclaringMethod().toString());
206        }
207        for (let callEdgePoint of callEdgePoints) {
208            let returnSite: Stmt = this.getReturnSiteOfCall(callEdgePoint.node);
209            let returnFlowFunc: FlowFunction<D> = this.problem.getExitToReturnFlowFunction(exitEdgePoint.node, returnSite, callEdgePoint.node);
210            this.handleFacts(returnFlowFunc, returnSite, exitEdgePoint, callEdgePoint);
211        }
212    }
213
214    private handleFacts(returnFlowFunc: FlowFunction<D>, returnSite: Stmt, exitEdgePoint: PathEdgePoint<D>, callEdgePoint: PathEdgePoint<D>): void {
215        for (let fact of returnFlowFunc.getDataFacts(exitEdgePoint.fact)) {
216            let returnSitePoint: PathEdgePoint<D> = new PathEdgePoint<D>(returnSite, fact);
217            let cacheEdge: CallToReturnCacheEdge<D> = new PathEdge<D>(callEdgePoint, returnSitePoint);
218            let summaryEdgeHasCacheEdge = false;
219            for (const sEdge of this.summaryEdge) {
220                if (sEdge.edgeStart === callEdgePoint && sEdge.edgeEnd.node === returnSite && sEdge.edgeEnd.fact === fact) {
221                    summaryEdgeHasCacheEdge = true;
222                    break;
223                }
224            }
225            if (summaryEdgeHasCacheEdge) {
226                continue;
227            }
228            this.summaryEdge.add(cacheEdge);
229            let startOfCaller: Stmt = this.getStartOfCallerMethod(callEdgePoint.node);
230            for (let pathEdge of this.pathEdgeSet) {
231                if (pathEdge.edgeStart.node === startOfCaller && pathEdge.edgeEnd === callEdgePoint) {
232                    this.propagate(new PathEdge<D>(pathEdge.edgeStart, returnSitePoint));
233                }
234            }
235        }
236    }
237
238    protected processNormalNode(edge: PathEdge<D>): void {
239        let start: PathEdgePoint<D> = edge.edgeStart;
240        let end: PathEdgePoint<D> = edge.edgeEnd;
241        let stmts: Stmt[] = [...this.getChildren(end.node)].reverse();
242        for (let stmt of stmts) {
243            let flowFunction: FlowFunction<D> = this.problem.getNormalFlowFunction(end.node, stmt);
244            let set: Set<D> = flowFunction.getDataFacts(end.fact);
245            for (let fact of set) {
246                let edgePoint: PathEdgePoint<D> = new PathEdgePoint<D>(stmt, fact);
247                const edge = new PathEdge<D>(start, edgePoint);
248                this.propagate(edge);
249                this.laterEdges.add(edge);
250            }
251        }
252    }
253
254    protected processCallNode(edge: PathEdge<D>): void {
255        let start: PathEdgePoint<D> = edge.edgeStart;
256        let callEdgePoint: PathEdgePoint<D> = edge.edgeEnd;
257        const invokeStmt = callEdgePoint.node as ArkInvokeStmt;
258        let callees: Set<ArkMethod>;
259        if (this.scene.getFile(invokeStmt.getInvokeExpr().getMethodSignature().getDeclaringClassSignature().getDeclaringFileSignature())) {
260            callees = this.getAllCalleeMethods(callEdgePoint.node as ArkInvokeStmt);
261        } else {
262            callees = new Set([getRecallMethodInParam(invokeStmt)!]);
263        }
264        let returnSite: Stmt = this.getReturnSiteOfCall(callEdgePoint.node);
265        for (let callee of callees) {
266            let callFlowFunc: FlowFunction<D> = this.problem.getCallFlowFunction(invokeStmt, callee);
267            if (!callee.getCfg()) {
268                continue;
269            }
270            let firstStmt: Stmt = [...callee.getCfg()!.getBlocks()][0].getStmts()[callee.getParameters().length];
271            let facts: Set<D> = callFlowFunc.getDataFacts(callEdgePoint.fact);
272            for (let fact of facts) {
273                this.callNodeFactPropagate(edge, firstStmt, fact, returnSite);
274            }
275        }
276        let callToReturnflowFunc: FlowFunction<D> = this.problem.getCallToReturnFlowFunction(edge.edgeEnd.node, returnSite);
277        let set: Set<D> = callToReturnflowFunc.getDataFacts(callEdgePoint.fact);
278        for (let fact of set) {
279            this.propagate(new PathEdge<D>(start, new PathEdgePoint<D>(returnSite, fact)));
280        }
281        for (let cacheEdge of this.summaryEdge) {
282            if (cacheEdge.edgeStart === edge.edgeEnd && cacheEdge.edgeEnd.node === returnSite) {
283                this.propagate(new PathEdge<D>(start, cacheEdge.edgeEnd));
284            }
285        }
286    }
287
288    protected callNodeFactPropagate(edge: PathEdge<D>, firstStmt: Stmt, fact: D, returnSite: Stmt): void {
289        let callEdgePoint: PathEdgePoint<D> = edge.edgeEnd;
290        // method start loop path edge
291        let startEdgePoint: PathEdgePoint<D> = new PathEdgePoint(firstStmt, fact);
292        this.propagate(new PathEdge<D>(startEdgePoint, startEdgePoint));
293        //add callEdgePoint in inComing.get(startEdgePoint)
294        let coming: Set<PathEdgePoint<D>> | undefined;
295        for (const incoming of this.inComing.keys()) {
296            if (incoming.fact === startEdgePoint.fact && incoming.node === startEdgePoint.node) {
297                coming = this.inComing.get(incoming);
298                break;
299            }
300        }
301        if (coming === undefined) {
302            this.inComing.set(startEdgePoint, new Set([callEdgePoint]));
303        } else {
304            coming.add(callEdgePoint);
305        }
306        let exitEdgePoints: Set<PathEdgePoint<D>> = new Set();
307        for (const end of Array.from(this.endSummary.keys())) {
308            if (end.fact === fact && end.node === firstStmt) {
309                exitEdgePoints = this.endSummary.get(end)!;
310            }
311        }
312        for (let exitEdgePoint of exitEdgePoints) {
313            let returnFlowFunc = this.problem.getExitToReturnFlowFunction(exitEdgePoint.node, returnSite, callEdgePoint.node);
314            for (let returnFact of returnFlowFunc.getDataFacts(exitEdgePoint.fact)) {
315                this.summaryEdge.add(new PathEdge<D>(edge.edgeEnd, new PathEdgePoint<D>(returnSite, returnFact)));
316            }
317        }
318    }
319
320    protected doSolve(): void {
321        while (this.workList.length !== 0) {
322            let pathEdge: PathEdge<D> = this.workList.shift()!;
323            if (this.laterEdges.has(pathEdge)) {
324                this.laterEdges.delete(pathEdge);
325            }
326            let targetStmt: Stmt = pathEdge.edgeEnd.node;
327            if (this.isCallStatement(targetStmt)) {
328                this.processCallNode(pathEdge);
329            } else if (this.isExitStatement(targetStmt)) {
330                this.processExitNode(pathEdge);
331            } else {
332                this.processNormalNode(pathEdge);
333            }
334        }
335    }
336
337    protected isCallStatement(stmt: Stmt): boolean {
338        for (const expr of stmt.getExprs()) {
339            if (expr instanceof AbstractInvokeExpr) {
340                if (this.scene.getFile(expr.getMethodSignature().getDeclaringClassSignature().getDeclaringFileSignature())) {
341                    return true;
342                }
343                if (stmt instanceof ArkInvokeStmt && getRecallMethodInParam(stmt)) {
344                    return true;
345                }
346            }
347        }
348        return false;
349    }
350
351    protected isExitStatement(stmt: Stmt): boolean {
352        return stmt instanceof ArkReturnStmt || stmt instanceof ArkReturnVoidStmt;
353    }
354
355    public getPathEdgeSet(): Set<PathEdge<D>> {
356        return this.pathEdgeSet;
357    }
358}
359