• 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 { FuncID } from '../model/CallGraph';
17
18export type ContextID = number;
19export const DUMMY_CID = 0;
20
21class Context {
22    private contextElems: number[];
23    static sEmptyCtx: Context = new Context([]);
24
25    constructor(contextElems: number[] = []) {
26        this.contextElems = contextElems;
27    }
28
29    static newEmpty(): Context {
30        return new Context();
31    }
32
33    static new(contextElems: number[]): Context {
34        return new Context(contextElems);
35    }
36
37    // use old context and a new element to create a new k-limited Context
38    static newKLimitedContext(oldCtx: Context, elem: number, k: number): Context {
39        let elems: number[] = [];
40        if (k > 0) {
41            elems.push(elem);
42            if (oldCtx.contextElems.length < k) {
43                elems = elems.concat(oldCtx.contextElems);
44            } else {
45                elems = elems.concat(oldCtx.contextElems.slice(0, k - 1));
46            }
47        }
48        return new Context(elems);
49    }
50
51    static kLimitedContext(ctx: Context, k: number): Context {
52        if (ctx.length() <= k) {
53            return new Context(ctx.contextElems);
54        } else {
55            const elems = ctx.contextElems.slice(0, k);
56            return new Context(elems);
57        }
58    }
59
60    public length(): number {
61        return this.contextElems.length;
62    }
63
64    public get(index: number): number {
65        if (index < 0 || index >= this.contextElems.length) {
66            throw new Error('Index out of bounds');
67        }
68        return this.contextElems[index];
69    }
70
71    public toString(): String {
72        return this.contextElems.join('-');
73    }
74}
75
76class ContextCache {
77    private contextList: Context[] = [];
78    private contextToIDMap: Map<String, number> = new Map();
79
80    constructor() {
81        this.contextList = [];
82        this.contextToIDMap = new Map();
83    }
84
85    public getOrNewContextID(context: Context): ContextID {
86        let cStr = context.toString();
87        if (this.contextToIDMap.has(cStr)) {
88            return this.contextToIDMap.get(cStr) as ContextID;
89        } else {
90            // real cid start from 1
91            const id = this.contextList.length;
92            this.contextList.push(context);
93            this.contextToIDMap.set(cStr, id);
94            return id;
95        }
96    }
97
98    public updateContext(id: ContextID, newContext: Context, oldContext: Context): boolean {
99        if (this.contextList.length < id) {
100            return false;
101        }
102        this.contextList[id] = newContext;
103        let oldCStr = oldContext.toString();
104        let newCStr = newContext.toString();
105        this.contextToIDMap.delete(oldCStr);
106        this.contextToIDMap.set(newCStr, id);
107        return true;
108    }
109
110    public getContextID(context: Context): ContextID | undefined {
111        let cStr = context.toString();
112        if (this.contextToIDMap.has(cStr)) {
113            return this.contextToIDMap.get(cStr) as ContextID;
114        }
115
116        return undefined;
117    }
118
119    public getContext(id: number): Context | undefined {
120        if (id > this.contextList.length) {
121            return undefined;
122        }
123        return this.contextList[id];
124    }
125
126    public getContextList(): Context[] {
127        return this.contextList;
128    }
129}
130
131export class KLimitedContextSensitive {
132    k: number;
133    ctxCache: ContextCache;
134
135    constructor(k: number) {
136        this.k = k;
137        this.ctxCache = new ContextCache();
138        // put dummy cid
139        this.getEmptyContextID();
140    }
141
142    public emptyContext(): Context {
143        return new Context([]);
144    }
145
146    public getEmptyContextID(): ContextID {
147        return this.getContextID(Context.newEmpty());
148    }
149
150    public getContextID(context: Context): ContextID {
151        return this.ctxCache.getOrNewContextID(context);
152    }
153
154    public getContextByID(context_id: number): Context | undefined {
155        return this.ctxCache.getContext(context_id);
156    }
157
158    public getNewContextID(callerFuncId: FuncID): ContextID {
159        return this.ctxCache.getOrNewContextID(Context.new([callerFuncId]));
160    }
161
162    public getOrNewContext(callerCid: ContextID, calleeFuncId: FuncID, findCalleeAsTop: boolean = false): ContextID {
163        const callerCtx = this.ctxCache.getContext(callerCid);
164        if (!callerCtx) {
165            throw new Error(`Context with id ${callerCid} not found.`);
166        }
167
168        const calleeNewCtx = Context.newKLimitedContext(callerCtx, calleeFuncId, this.k);
169        if (findCalleeAsTop) {
170            const calleeAsTopCtx = Context.newKLimitedContext(Context.sEmptyCtx, calleeFuncId, this.k);
171            let topID = this.ctxCache.getContextID(calleeAsTopCtx);
172            if (topID) {
173                this.ctxCache.updateContext(topID, calleeNewCtx, calleeAsTopCtx);
174                return topID;
175            }
176        }
177
178        const calleeCid = this.ctxCache.getOrNewContextID(calleeNewCtx);
179        return calleeCid;
180    }
181}
182