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