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 { NodeID, BaseEdge, BaseExplicitGraph, BaseNode, Kind } from '../../core/graph/BaseExplicitGraph'; 17import { CallGraph, CallSite, DynCallSite } from '../model/CallGraph'; 18import { Value } from '../../core/base/Value'; 19import { ArkAssignStmt, ArkReturnStmt, Stmt } from '../../core/base/Stmt'; 20import { AbstractExpr, ArkNewArrayExpr, ArkNewExpr } from '../../core/base/Expr'; 21import { AbstractFieldRef, ArkArrayRef, ArkInstanceFieldRef, ArkParameterRef, ArkStaticFieldRef, ArkThisRef } from '../../core/base/Ref'; 22import { Local } from '../../core/base/Local'; 23import { GraphPrinter } from '../../save/GraphPrinter'; 24import { PrinterBuilder } from '../../save/PrinterBuilder'; 25import { Constant } from '../../core/base/Constant'; 26import { FunctionType, UnclearReferenceType } from '../../core/base/Type'; 27import { ClassSignature, FieldSignature, FileSignature, MethodSignature } from '../../core/model/ArkSignature'; 28import { ContextID } from './Context'; 29import Logger, { LOG_MODULE_TYPE } from '../../utils/logger'; 30import { GLOBAL_THIS_NAME } from '../../core/common/TSConst'; 31import { ExportInfo } from '../../core/model/ArkExport'; 32import { BuiltApiType, getBuiltInApiType, IsCollectionClass } from './PTAUtils'; 33import { IPtsCollection } from './PtsDS'; 34import { PointerAnalysisConfig } from './PointerAnalysisConfig'; 35 36const logger = Logger.getLogger(LOG_MODULE_TYPE.ARKANALYZER, 'PTA'); 37export type PagNodeType = Value; 38 39/* 40 * Implementation of pointer-to assignment graph for pointer analysis 41 */ 42 43const DUMMY_PAG_NODE_ID = -1; 44 45export enum PagEdgeKind { 46 Address, 47 Copy, 48 Load, 49 Write, 50 This, 51 Unknown, 52 InterProceduralCopy, 53} 54 55export enum StorageType { 56 APP_STORAGE, 57 LOCAL_STORAGE, 58 Undefined, 59} 60 61export enum StorageLinkEdgeType { 62 Property2Local, 63 Local2Property, 64 TwoWay, 65} 66 67export class PagEdge extends BaseEdge { 68 private stmt: Stmt | undefined; 69 70 constructor(n: PagNode, d: PagNode, k: PagEdgeKind, s?: Stmt) { 71 super(n, d, k); 72 this.stmt = s; 73 } 74 75 public getDotAttr(): string { 76 switch (this.getKind()) { 77 case PagEdgeKind.Address: 78 return 'color=green'; 79 case PagEdgeKind.Copy: 80 if (this.stmt?.getInvokeExpr() !== undefined || this.stmt instanceof ArkReturnStmt) { 81 return 'color=black,style=dotted'; 82 } 83 return 'color=black'; 84 case PagEdgeKind.Load: 85 return 'color=red'; 86 case PagEdgeKind.Write: 87 return 'color=blue'; 88 case PagEdgeKind.This: 89 return 'color=orange'; 90 case PagEdgeKind.InterProceduralCopy: 91 return 'color=purple,style=dashed'; 92 default: 93 return 'color=black'; 94 } 95 } 96} 97 98export class AddrPagEdge extends PagEdge { 99 constructor(n: PagNode, d: PagNode, s: Stmt) { 100 super(n, d, PagEdgeKind.Address, s); 101 } 102} 103 104export class CopyPagEdge extends PagEdge { 105 constructor(n: PagNode, d: PagNode, s: Stmt) { 106 super(n, d, PagEdgeKind.Copy, s); 107 } 108} 109 110export class LoadPagEdge extends PagEdge { 111 constructor(n: PagNode, d: PagNode, s: Stmt) { 112 super(n, d, PagEdgeKind.Copy, s); 113 } 114} 115 116export class WritePagEdge extends PagEdge { 117 constructor(n: PagNode, d: PagNode, s: Stmt) { 118 super(n, d, PagEdgeKind.Write, s); 119 } 120} 121 122export class ThisPagEdge extends PagEdge { 123 constructor(n: PagNode, d: PagNode, s: Stmt) { 124 super(n, d, PagEdgeKind.This, s); 125 } 126} 127 128type PagEdgeSet = Set<PagEdge>; 129 130export enum PagNodeKind { 131 HeapObj, 132 LocalVar, 133 RefVar, 134 Param, 135 ThisRef, 136 Function, 137 GlobalThis, 138 ExportInfo, 139} 140 141export class PagNode extends BaseNode { 142 private cid: ContextID | undefined; 143 private value: Value; 144 private stmt: Stmt | undefined; // stmt is just used for graph print 145 private pointTo: IPtsCollection<NodeID>; 146 147 private addressInEdges!: PagEdgeSet; 148 private addressOutEdges!: PagEdgeSet; 149 private copyInEdges!: PagEdgeSet; 150 private copyOutEdges!: PagEdgeSet; 151 private loadInEdges!: PagEdgeSet; 152 private loadOutEdges!: PagEdgeSet; 153 private writeInEdges!: PagEdgeSet; 154 private writeOutEdges!: PagEdgeSet; 155 private thisInEdges!: PagEdgeSet; 156 private thisOutEdges!: PagEdgeSet; 157 158 // Point-to node of base class 159 // Only PagInstanceRefNode has this field 160 // Define in base class is for dot print 161 protected basePt!: NodeID; 162 protected clonedFrom!: NodeID; 163 164 constructor(id: NodeID, cid: ContextID | undefined = undefined, value: Value, k: Kind, s?: Stmt) { 165 super(id, k); 166 this.cid = cid; 167 this.value = value; 168 this.stmt = s; 169 let ptaConfig = PointerAnalysisConfig.getInstance(); 170 this.pointTo = new ptaConfig.ptsCollectionCtor(); 171 } 172 173 public getBasePt(): NodeID { 174 return this.basePt; 175 } 176 177 public setBasePt(pt: NodeID): void { 178 this.basePt = pt; 179 } 180 181 public getCid(): ContextID { 182 if (this.cid === undefined) { 183 throw new Error('cid is undefine'); 184 } 185 return this.cid; 186 } 187 188 public setCid(cid: ContextID): void { 189 this.cid = cid; 190 } 191 192 public setStmt(s: Stmt): void { 193 this.stmt = s; 194 } 195 196 public getStmt(): Stmt | undefined { 197 return this.stmt; 198 } 199 200 public hasOutgoingCopyEdge(): boolean { 201 return this.copyOutEdges.size !== 0; 202 } 203 204 public getOutgoingCopyEdges(): PagEdgeSet { 205 return this.copyOutEdges; 206 } 207 208 public getIncomingCopyEdges(): PagEdgeSet { 209 return this.copyInEdges; 210 } 211 212 public getOutgoingLoadEdges(): PagEdgeSet { 213 return this.loadOutEdges; 214 } 215 216 public getOutgoingWriteEdges(): PagEdgeSet { 217 return this.writeOutEdges; 218 } 219 220 public getIncomingWriteEdges(): PagEdgeSet { 221 return this.writeInEdges; 222 } 223 224 public getOutgoingThisEdges(): PagEdgeSet { 225 return this.thisOutEdges; 226 } 227 228 public getIncomingThisEdges(): PagEdgeSet { 229 return this.thisInEdges; 230 } 231 232 public addAddressInEdge(e: AddrPagEdge): void { 233 this.addressInEdges === undefined ? (this.addressInEdges = new Set()) : undefined; 234 this.addressInEdges.add(e); 235 this.addIncomingEdge(e); 236 } 237 238 public addAddressOutEdge(e: AddrPagEdge): void { 239 this.addressOutEdges === undefined ? (this.addressOutEdges = new Set()) : undefined; 240 this.addressOutEdges.add(e); 241 this.addOutgoingEdge(e); 242 } 243 244 public addCopyInEdge(e: CopyPagEdge): void { 245 this.copyInEdges === undefined ? (this.copyInEdges = new Set()) : undefined; 246 this.copyInEdges.add(e); 247 this.addIncomingEdge(e); 248 } 249 250 public addCopyOutEdge(e: CopyPagEdge): void { 251 this.copyOutEdges === undefined ? (this.copyOutEdges = new Set()) : undefined; 252 253 this.copyOutEdges.add(e); 254 this.addOutgoingEdge(e); 255 } 256 257 public addLoadInEdge(e: LoadPagEdge): void { 258 this.loadInEdges === undefined ? (this.loadInEdges = new Set()) : undefined; 259 this.loadInEdges.add(e); 260 this.addIncomingEdge(e); 261 } 262 263 public addLoadOutEdge(e: LoadPagEdge): void { 264 this.loadOutEdges === undefined ? (this.loadOutEdges = new Set()) : undefined; 265 this.loadOutEdges.add(e); 266 this.addOutgoingEdge(e); 267 } 268 269 public addWriteInEdge(e: WritePagEdge): void { 270 this.writeInEdges = this.writeInEdges ?? new Set(); 271 this.writeInEdges.add(e); 272 this.addIncomingEdge(e); 273 } 274 275 public addWriteOutEdge(e: LoadPagEdge): void { 276 this.writeOutEdges = this.writeOutEdges ?? new Set(); 277 this.writeOutEdges.add(e); 278 this.addOutgoingEdge(e); 279 } 280 281 public addThisInEdge(e: ThisPagEdge): void { 282 this.thisInEdges = this.thisInEdges ?? new Set(); 283 this.thisInEdges.add(e); 284 this.addIncomingEdge(e); 285 } 286 287 public addThisOutEdge(e: ThisPagEdge): void { 288 this.thisOutEdges = this.thisOutEdges ?? new Set(); 289 this.thisOutEdges.add(e); 290 this.addOutgoingEdge(e); 291 } 292 293 public getValue(): Value { 294 return this.value; 295 } 296 297 public getPointTo(): IPtsCollection<NodeID> { 298 return this.pointTo; 299 } 300 301 public addPointToElement(node: NodeID): void { 302 this.pointTo.insert(node); 303 } 304 305 public setPointTo(pts: IPtsCollection<NodeID>): void { 306 this.pointTo = pts; 307 } 308 309 public getOutEdges(): { 310 AddressEdge: PagEdgeSet; 311 CopyEdge: PagEdgeSet; 312 LoadEdge: PagEdgeSet; 313 WriteEdge: PagEdgeSet; 314 } { 315 return { 316 AddressEdge: this.addressOutEdges, 317 CopyEdge: this.copyOutEdges, 318 LoadEdge: this.loadOutEdges, 319 WriteEdge: this.writeOutEdges, 320 }; 321 } 322 323 public getClonedFrom(): NodeID { 324 return this.clonedFrom; 325 } 326 327 public setClonedFrom(id: NodeID): void { 328 this.clonedFrom = id; 329 } 330 331 public getDotAttr(): string { 332 switch (this.getKind()) { 333 case PagNodeKind.HeapObj: 334 case PagNodeKind.Function: 335 case PagNodeKind.GlobalThis: 336 return 'shape=box3d'; 337 case PagNodeKind.LocalVar: 338 return 'shape=box'; 339 case PagNodeKind.RefVar: 340 return 'shape=component'; 341 case PagNodeKind.Param: 342 return 'shape=box'; 343 case PagNodeKind.ExportInfo: 344 return 'shape=tab,color=purple'; 345 case PagNodeKind.ThisRef: 346 return 'shape=box,color=orange'; 347 default: 348 return 'shape=box'; 349 } 350 } 351 352 public getDotLabel(): string { 353 let label: string; 354 let param: ArkParameterRef; 355 356 label = PagNodeKind[this.getKind()]; 357 label = label + ` ID: ${this.getID()} Ctx: ${this.cid}`; 358 if (this.basePt) { 359 label = label + ` base:{${this.basePt}}`; 360 } 361 label = label + ` pts:{${Array.from(this.pointTo).join(',')}}`; 362 363 if (this.getKind() === PagNodeKind.Param) { 364 param = this.value as ArkParameterRef; 365 label = label + `\nParam#${param.getIndex()} ${param.toString()}`; 366 } 367 368 if (this.getKind() === PagNodeKind.ThisRef) { 369 label = label + `\n${(this.value as ArkThisRef).toString()}`; 370 } 371 372 if (this.getKind() === PagNodeKind.Function) { 373 label = label + ` thisPt:{${(this as unknown as PagFuncNode).getThisPt()}}`; 374 } 375 376 if (this.stmt) { 377 label = label + `\n${this.stmt.toString()}`; 378 let method = this.stmt.getCfg()?.getDeclaringMethod().getSubSignature().toString(); 379 if (method) { 380 label = label + '\n' + method; 381 } 382 label = label + ' ln: ' + this.stmt.getOriginPositionInfo().getLineNo(); 383 } else if (this.value) { 384 label += `\n${this.value.toString()}`; 385 } 386 387 return label; 388 } 389} 390 391export class PagLocalNode extends PagNode { 392 private relatedDynamicCallSite?: Set<DynCallSite>; 393 private relatedUnknownCallSite?: Set<CallSite>; 394 private storageLinked: boolean = false; 395 private storageType?: StorageType; 396 private propertyName?: string; 397 398 private sdkParam: boolean = false; 399 400 constructor(id: NodeID, cid: ContextID | undefined = undefined, value: Local, stmt?: Stmt) { 401 super(id, cid, value, PagNodeKind.LocalVar, stmt); 402 } 403 404 public addRelatedDynCallSite(cs: DynCallSite): void { 405 this.relatedDynamicCallSite = this.relatedDynamicCallSite ?? new Set(); 406 407 this.relatedDynamicCallSite.add(cs); 408 } 409 410 public getRelatedDynCallSites(): Set<DynCallSite> { 411 return this.relatedDynamicCallSite ?? new Set(); 412 } 413 414 public addRelatedUnknownCallSite(cs: CallSite): void { 415 this.relatedUnknownCallSite = this.relatedUnknownCallSite ?? new Set(); 416 417 this.relatedUnknownCallSite.add(cs); 418 } 419 420 public getRelatedUnknownCallSites(): Set<CallSite> { 421 return this.relatedUnknownCallSite ?? new Set(); 422 } 423 424 public setStorageLink(storageType: StorageType, propertyName: string): void { 425 this.storageLinked = true; 426 this.storageType = storageType; 427 this.propertyName = propertyName; 428 } 429 430 public getStorage(): { StorageType: StorageType; PropertyName: string } { 431 return { 432 StorageType: this.storageType!, 433 PropertyName: this.propertyName!, 434 }; 435 } 436 437 public isStorageLinked(): boolean { 438 return this.storageLinked; 439 } 440 441 public setSdkParam(): void { 442 this.sdkParam = true; 443 } 444 445 public isSdkParam(): boolean { 446 return this.sdkParam; 447 } 448} 449 450export class PagInstanceFieldNode extends PagNode { 451 constructor(id: NodeID, cid: ContextID | undefined = undefined, instanceFieldRef: ArkInstanceFieldRef, stmt?: Stmt) { 452 super(id, cid, instanceFieldRef, PagNodeKind.RefVar, stmt); 453 } 454} 455 456export class PagStaticFieldNode extends PagNode { 457 constructor(id: NodeID, cid: ContextID | undefined = undefined, staticFieldRef: ArkStaticFieldRef, stmt?: Stmt) { 458 super(id, cid, staticFieldRef, PagNodeKind.RefVar, stmt); 459 } 460} 461 462export class PagThisRefNode extends PagNode { 463 pointToNode: NodeID[]; 464 constructor(id: NodeID, thisRef: ArkThisRef) { 465 super(id, DUMMY_PAG_NODE_ID, thisRef, PagNodeKind.ThisRef); 466 this.pointToNode = []; 467 } 468 469 public getThisPTNode(): NodeID[] { 470 return this.pointToNode; 471 } 472 473 public addPTNode(ptNode: NodeID): void { 474 this.pointToNode.push(ptNode); 475 } 476} 477 478export class PagArrayNode extends PagNode { 479 base: Value; 480 481 constructor(id: NodeID, cid: ContextID | undefined = undefined, expr: ArkArrayRef, stmt?: Stmt) { 482 super(id, cid, expr, PagNodeKind.LocalVar, stmt); 483 this.base = expr.getBase(); 484 } 485} 486 487/** 488 * below is heapObj like Node 489 */ 490export class PagNewExprNode extends PagNode { 491 // store the cloned field node 492 fieldNodes!: Map<string, NodeID>; 493 494 constructor(id: NodeID, cid: ContextID | undefined = undefined, expr: AbstractExpr, stmt?: Stmt) { 495 super(id, cid, expr, PagNodeKind.HeapObj, stmt); 496 } 497 498 public addFieldNode(fieldSignature: AbstractFieldRef, nodeID: NodeID): boolean { 499 if (!this.fieldNodes) { 500 this.fieldNodes = new Map(); 501 } 502 if (this.fieldNodes.has(fieldSignature.getFieldSignature().toString())) { 503 return false; 504 } 505 this.fieldNodes.set(fieldSignature.getFieldSignature().toString(), nodeID); 506 return true; 507 } 508 509 public getFieldNode(fieldSignature: AbstractFieldRef): NodeID | undefined { 510 if (!this.fieldNodes) { 511 return undefined; 512 } 513 return this.fieldNodes.get(fieldSignature.getFieldSignature().toString()); 514 } 515 516 public getFieldNodes(): Map<string, NodeID> | undefined { 517 if (!this.fieldNodes) { 518 return undefined; 519 } 520 return this.fieldNodes; 521 } 522} 523 524export class PagNewContainerExprNode extends PagNode { 525 // store the cloned array ref node 526 elementNode: NodeID | undefined; 527 528 constructor(id: NodeID, cid: ContextID | undefined = undefined, expr: Value, stmt?: Stmt) { 529 super(id, cid, expr, PagNodeKind.HeapObj, stmt); 530 } 531 532 public addElementNode(nodeID: NodeID): boolean { 533 if (!this.elementNode) { 534 this.elementNode = nodeID; 535 } 536 537 return true; 538 } 539 540 public getElementNode(): NodeID | undefined { 541 if (this.elementNode) { 542 return this.elementNode; 543 } 544 return undefined; 545 } 546} 547 548export class PagParamNode extends PagNode { 549 constructor(id: NodeID, cid: ContextID | undefined = undefined, r: ArkParameterRef, stmt?: Stmt) { 550 super(id, cid, r, PagNodeKind.Param, stmt); 551 } 552} 553 554export class PagFuncNode extends PagNode { 555 private methodSignature!: MethodSignature; 556 private thisPt!: NodeID; 557 private methodType!: BuiltApiType; 558 // for Function.bind, store the original call message and caller cid 559 private originCallSite!: CallSite; 560 private argsOffset: number = 0; 561 private originCid!: ContextID; 562 // TODO: may add obj interface 563 564 constructor(id: NodeID, cid: ContextID | undefined = undefined, r: Value, stmt?: Stmt, method?: MethodSignature, thisInstanceID?: NodeID) { 565 super(id, cid, r, PagNodeKind.Function, stmt); 566 if (method) { 567 this.methodSignature = method; 568 this.methodType = getBuiltInApiType(method); 569 } 570 571 if (thisInstanceID) { 572 this.thisPt = thisInstanceID; 573 } 574 } 575 576 public setMethod(method: MethodSignature): void { 577 this.methodSignature = method; 578 this.methodType = getBuiltInApiType(method); 579 } 580 581 public getMethod(): MethodSignature { 582 return this.methodSignature; 583 } 584 585 public setThisPt(thisPt: NodeID): void { 586 this.thisPt = thisPt; 587 } 588 589 public getThisPt(): NodeID { 590 return this.thisPt; 591 } 592 593 public setCS(callsite: CallSite): void { 594 this.originCallSite = callsite; 595 } 596 597 public getCS(): CallSite { 598 return this.originCallSite; 599 } 600 601 public setArgsOffset(offset: number): void { 602 this.argsOffset = offset; 603 } 604 605 public getArgsOffset(): number { 606 return this.argsOffset; 607 } 608 609 public getMethodType(): BuiltApiType { 610 return this.methodType; 611 } 612 613 public setOriginCid(cid: ContextID): void { 614 this.originCid = cid; 615 } 616 617 public getOriginCid(): ContextID { 618 return this.originCid; 619 } 620} 621 622/** 623 * almost same as PagNewExprNode, used only for globalThis and its field reference 624 */ 625export class PagGlobalThisNode extends PagNode { 626 fieldNodes: Map<string, NodeID>; 627 628 constructor(id: NodeID, cid: ContextID | undefined = undefined, r: Value, stmt?: Stmt) { 629 super(id, cid, r, PagNodeKind.GlobalThis, stmt); 630 this.fieldNodes = new Map(); 631 } 632 633 public addFieldNode(fieldSignature: AbstractFieldRef, nodeID: NodeID): boolean { 634 if (this.fieldNodes.has(fieldSignature.getFieldSignature().toString())) { 635 return false; 636 } 637 this.fieldNodes.set(fieldSignature.getFieldSignature().toString(), nodeID); 638 return true; 639 } 640 641 public getFieldNode(fieldSignature: AbstractFieldRef): NodeID | undefined { 642 return this.fieldNodes.get(fieldSignature.getFieldSignature().toString()); 643 } 644 645 public getFieldNodes(): Map<string, NodeID> | undefined { 646 return this.fieldNodes; 647 } 648} 649 650export class Pag extends BaseExplicitGraph { 651 private cg!: CallGraph; 652 private contextValueToIdMap: Map<Value, Map<ContextID, NodeID>> = new Map(); 653 private ExportInfoToIdMap?: Map<ExportInfo, NodeID>; 654 // contextBaseToIdMap will only be used in instance field 655 // Value: instance field base value, NodeID: abstract nodes 656 private contextBaseToIdMap: Map<Value, Map<ContextID, NodeID[]>> = new Map(); 657 // for reanalyze, will return new addr edges 658 private stashAddrEdge: PagEdgeSet = new Set(); 659 private addrEdge: PagEdgeSet = new Set(); 660 private clonedNodeMap: Map<NodeID, Map<NodeID, NodeID>> = new Map(); 661 662 public getCG(): CallGraph { 663 return this.cg; 664 } 665 666 /* 667 * Clone a PagNode with same cid/value/stmt, 668 * but different Node ID 669 */ 670 public getOrClonePagNode(src: PagNode, basePt: NodeID): PagNode { 671 if (src.getBasePt() !== undefined) { 672 throw new Error('This is a cloned ref node, can not be cloned again'); 673 } 674 675 let cloneSet = this.clonedNodeMap.get(src.getID()); 676 if (!cloneSet) { 677 cloneSet = new Map<NodeID, NodeID>(); 678 this.clonedNodeMap.set(src.getID(), cloneSet); 679 } else { 680 let nodeID = cloneSet.get(basePt); 681 if (nodeID) { 682 return this.getNode(nodeID) as PagNode; 683 } 684 } 685 686 // Not found 687 let cloneNode = this.addPagNode(src.getCid(), src.getValue(), src.getStmt(), false); 688 cloneNode.setClonedFrom(src.getID()); 689 cloneSet.set(basePt, cloneNode.getID()); 690 return cloneNode; 691 } 692 693 public getOrClonePagFieldNode(src: PagInstanceFieldNode, basePt: NodeID): PagInstanceFieldNode | undefined { 694 let baseNode = this.getNode(basePt); 695 if (baseNode instanceof PagNewExprNode || baseNode instanceof PagGlobalThisNode) { 696 // check if real field node has been created with basePT, using FieldSignature as key 697 let existedNode = baseNode.getFieldNode(src.getValue() as ArkInstanceFieldRef); 698 if (existedNode) { 699 return this.getNode(existedNode) as PagInstanceFieldNode; 700 } 701 702 let fieldNode = this.getOrClonePagNode(src, basePt); 703 baseNode.addFieldNode(src.getValue() as ArkInstanceFieldRef, fieldNode.getID()); 704 fieldNode.setBasePt(basePt); 705 return fieldNode; 706 } else { 707 logger.error(`Error clone field node ${src.getValue()}`); 708 return undefined; 709 } 710 } 711 712 public getOrClonePagContainerFieldNode(basePt: NodeID, src?: PagArrayNode, base?: Local): PagInstanceFieldNode | undefined { 713 let baseNode = this.getNode(basePt) as PagNode; 714 if (baseNode instanceof PagNewContainerExprNode) { 715 // check if Array Ref real node has been created or not, if not: create a real Array Ref node 716 let existedNode = baseNode.getElementNode(); 717 let fieldNode!: PagNode; 718 if (existedNode) { 719 return this.getNode(existedNode) as PagInstanceFieldNode; 720 } 721 722 if (src) { 723 fieldNode = this.getOrClonePagNode(src, basePt); 724 } else if (base) { 725 const containerFieldSignature = new FieldSignature( 726 'field', 727 new ClassSignature('container', new FileSignature('container', 'lib.es2015.collection.d.ts')), 728 new UnclearReferenceType('') 729 ); 730 fieldNode = this.getOrClonePagNode( 731 // TODO: cid check 732 this.addPagNode(0, new ArkInstanceFieldRef(base, containerFieldSignature)), 733 basePt 734 ); 735 } 736 737 baseNode.addElementNode(fieldNode.getID()); 738 fieldNode.setBasePt(basePt); 739 return fieldNode; 740 } else if (baseNode instanceof PagNewExprNode) { 741 // In some cases, the value of a variable of array type may not be an explicit array object. 742 // For example, it could be a return value of a function (assuming that the call depth has 743 // exceeded the k-limit). 744 // In such situation, the `baseNode` will be a PagNewExprNode instead of a PagNewContainerExprNode, 745 // and a warning will be raised. 746 logger.warn(`[PTA]: Trying to clone an array from a PagNewExprNode instead of a PagNewContainerExprNode`); 747 } else { 748 throw new Error(`Error clone array field node ${baseNode.getValue()}`); 749 } 750 return undefined; 751 } 752 753 public getOrClonePagFuncNode(basePt: NodeID): PagFuncNode | undefined { 754 let baseNode = this.getNode(basePt) as PagNode; 755 if (baseNode instanceof PagFuncNode) { 756 let clonedFuncNode = this.getOrClonePagNode(baseNode, basePt) as PagFuncNode; 757 return clonedFuncNode; 758 } else { 759 logger.error(`Error clone func node ${baseNode.getValue()}`); 760 return undefined; 761 } 762 } 763 764 public addPagNode(cid: ContextID, value: PagNodeType, stmt?: Stmt, refresh: boolean = true): PagNode { 765 let id: NodeID = this.nodeNum + 1; 766 let pagNode: PagNode; 767 768 if (value instanceof Local) { 769 pagNode = this.handleLocalNode(id, cid, value, stmt); 770 } else if (value instanceof ArkInstanceFieldRef) { 771 pagNode = this.handleInstanceFieldNode(id, cid, value, stmt); 772 } else if (value instanceof ArkStaticFieldRef) { 773 pagNode = this.handleStaticFieldNode(id, cid, value, stmt); 774 } else if (value instanceof ArkArrayRef) { 775 pagNode = new PagArrayNode(id, cid, value, stmt); 776 } else if (value instanceof ArkNewExpr) { 777 pagNode = this.handleNewExprNode(id, cid, value, stmt); 778 } else if (value instanceof ArkNewArrayExpr) { 779 pagNode = new PagNewContainerExprNode(id, cid, value, stmt); 780 } else if (value instanceof ArkParameterRef) { 781 pagNode = new PagParamNode(id, cid, value, stmt); 782 } else if (value instanceof ArkThisRef) { 783 throw new Error('This Node needs to use addThisNode method'); 784 } else { 785 throw new Error('unsupported Value type ' + value.getType().toString()); 786 } 787 788 this.addNode(pagNode!); 789 this.addContextOrExportInfoMap(refresh, cid, id, value, pagNode, stmt); 790 791 return pagNode!; 792 } 793 794 private handleLocalNode(id: NodeID, cid: ContextID, value: Local, stmt?: Stmt): PagNode { 795 const valueType = value.getType(); 796 if (valueType instanceof FunctionType && value.getDeclaringStmt() === null) { 797 return new PagFuncNode(id, cid, value, stmt, valueType.getMethodSignature()); 798 } else if (value.getName() === GLOBAL_THIS_NAME && value.getDeclaringStmt() == null) { 799 return new PagGlobalThisNode(id, -1, value); 800 } else { 801 return new PagLocalNode(id, cid, value, stmt); 802 } 803 } 804 805 private handleInstanceFieldNode(id: NodeID, cid: ContextID, value: ArkInstanceFieldRef, stmt?: Stmt): PagNode { 806 return this.createFieldNode(id, cid, value, stmt); 807 } 808 809 private handleStaticFieldNode(id: NodeID, cid: ContextID, value: ArkStaticFieldRef, stmt?: Stmt): PagNode { 810 return this.createFieldNode(id, cid, value, stmt); 811 } 812 813 private createFieldNode(id: NodeID, cid: ContextID, value: any, stmt?: Stmt): PagNode { 814 if (value.getType() instanceof FunctionType) { 815 return new PagFuncNode(id, cid, value, stmt, (value.getType() as FunctionType).getMethodSignature()); 816 } else { 817 return value instanceof ArkStaticFieldRef ? new PagStaticFieldNode(id, cid, value, stmt) : new PagInstanceFieldNode(id, cid, value, stmt); 818 } 819 } 820 821 private handleNewExprNode(id: NodeID, cid: ContextID, value: ArkNewExpr, stmt?: Stmt): PagNode { 822 const classSignature = value.getClassType().getClassSignature(); 823 if (IsCollectionClass(classSignature)) { 824 return new PagNewContainerExprNode(id, cid, value, stmt); 825 } else { 826 return new PagNewExprNode(id, cid, value, stmt); 827 } 828 } 829 830 private addContextOrExportInfoMap(refresh: boolean, cid: ContextID, id: NodeID, value: PagNodeType, pagNode: PagNode, stmt?: Stmt): void { 831 if (!(value instanceof ExportInfo)) { 832 this.addContextMap(refresh, cid, id, value, stmt!, pagNode!); 833 } else { 834 this.addExportInfoMap(id, value); 835 } 836 } 837 838 private addExportInfoMap(id: NodeID, v: ExportInfo): void { 839 this.ExportInfoToIdMap = this.ExportInfoToIdMap ?? new Map(); 840 this.ExportInfoToIdMap.set(v, id); 841 } 842 843 private addContextMap(refresh: boolean, cid: ContextID, id: NodeID, value: Value, stmt: Stmt, pagNode: PagNode): void { 844 if (!refresh) { 845 return; 846 } 847 848 let ctx2NdMap = this.contextValueToIdMap.get(value); 849 if (!ctx2NdMap) { 850 ctx2NdMap = new Map(); 851 this.contextValueToIdMap.set(value, ctx2NdMap); 852 } 853 ctx2NdMap.set(cid, id); 854 855 if (!(value instanceof ArkInstanceFieldRef || value instanceof ArkArrayRef)) { 856 return; 857 } 858 859 let base = value.getBase(); 860 //TODO: remove below once this Local is not uniq in %instInit is fix 861 if (base instanceof Local && base.getName() === 'this') { 862 stmt 863 ?.getCfg() 864 ?.getStmts() 865 .forEach(s => { 866 if (s instanceof ArkAssignStmt && s.getLeftOp() instanceof Local && (s.getLeftOp() as Local).getName() === 'this') { 867 base = s.getLeftOp() as Local; 868 return; 869 } 870 }); 871 } 872 let ctxMap = this.contextBaseToIdMap.get(base); 873 if (ctxMap === undefined) { 874 ctxMap = new Map(); 875 ctxMap.set(cid, [pagNode.getID()]); 876 } else { 877 let nodes = ctxMap.get(cid); 878 if (nodes === undefined) { 879 nodes = [pagNode.getID()]; 880 } else { 881 nodes.push(pagNode.getID()); 882 } 883 ctxMap.set(cid, nodes); 884 } 885 this.contextBaseToIdMap.set(base, ctxMap); 886 } 887 888 /* 889 * This node has no context info 890 * but point to node info 891 */ 892 public addPagThisRefNode(value: ArkThisRef): PagNode { 893 let id: NodeID = this.nodeNum + 1; 894 let pagNode = new PagThisRefNode(id, value); 895 this.addNode(pagNode); 896 897 return pagNode; 898 } 899 900 public addPagThisLocalNode(ptNode: NodeID, value: Local): PagNode { 901 let id: NodeID = this.nodeNum + 1; 902 let pagNode = new PagLocalNode(id, ptNode, value); 903 this.addNode(pagNode); 904 905 return pagNode; 906 } 907 908 public getOrNewThisRefNode(thisRefNodeID: NodeID, value: ArkThisRef): PagNode { 909 if (thisRefNodeID !== -1) { 910 return this.getNode(thisRefNodeID) as PagNode; 911 } 912 913 let thisRefNode = this.addPagThisRefNode(value); 914 return thisRefNode; 915 } 916 917 public getOrNewThisLocalNode(cid: ContextID, ptNode: NodeID, value: Local, s?: Stmt): PagNode { 918 if (ptNode !== -1) { 919 return this.getNode(ptNode) as PagNode; 920 } else { 921 return this.getOrNewNode(cid, value, s); 922 } 923 } 924 925 public hasExportNode(v: ExportInfo): NodeID | undefined { 926 this.ExportInfoToIdMap = this.ExportInfoToIdMap ?? new Map(); 927 return this.ExportInfoToIdMap.get(v); 928 } 929 930 public hasCtxNode(cid: ContextID, v: Value): NodeID | undefined { 931 let ctx2nd = this.contextValueToIdMap.get(v); 932 if (!ctx2nd) { 933 return undefined; 934 } 935 936 let ndId = ctx2nd.get(cid); 937 if (!ndId) { 938 return undefined; 939 } 940 941 return ndId; 942 } 943 944 public hasCtxRetNode(cid: ContextID, v: Value): NodeID | undefined { 945 let ctx2nd = this.contextValueToIdMap.get(v); 946 if (!ctx2nd) { 947 return undefined; 948 } 949 950 let ndId = ctx2nd.get(cid); 951 if (!ndId) { 952 return undefined; 953 } 954 955 return ndId; 956 } 957 public getOrNewNode(cid: ContextID, v: PagNodeType, s?: Stmt): PagNode { 958 let nodeId; 959 // Value 960 if (!(v instanceof ExportInfo)) { 961 nodeId = this.hasCtxNode(cid, v); 962 } else { 963 // ExportInfo 964 nodeId = this.hasExportNode(v); 965 } 966 967 if (nodeId !== undefined) { 968 return this.getNode(nodeId) as PagNode; 969 } 970 971 return this.addPagNode(cid, v, s); 972 } 973 974 public getNodesByValue(v: Value): Map<ContextID, NodeID> | undefined { 975 return this.contextValueToIdMap.get(v); 976 } 977 978 public getNodesByBaseValue(v: Value): Map<ContextID, NodeID[]> | undefined { 979 return this.contextBaseToIdMap.get(v); 980 } 981 982 public addPagEdge(src: PagNode, dst: PagNode, kind: PagEdgeKind, stmt?: Stmt): boolean { 983 // TODO: check if the edge already existing 984 let edge = new PagEdge(src, dst, kind, stmt); 985 if (this.ifEdgeExisting(edge)) { 986 return false; 987 } 988 989 switch (kind) { 990 case PagEdgeKind.Copy: 991 case PagEdgeKind.InterProceduralCopy: 992 src.addCopyOutEdge(edge); 993 dst.addCopyInEdge(edge); 994 if (src instanceof PagFuncNode || src instanceof PagGlobalThisNode || src instanceof PagNewExprNode || src instanceof PagNewContainerExprNode) { 995 this.addrEdge.add(edge); 996 this.stashAddrEdge.add(edge); 997 } 998 break; 999 case PagEdgeKind.Address: 1000 src.addAddressOutEdge(edge); 1001 dst.addAddressInEdge(edge); 1002 this.addrEdge.add(edge); 1003 this.stashAddrEdge.add(edge); 1004 break; 1005 case PagEdgeKind.Write: 1006 src.addWriteOutEdge(edge); 1007 dst.addWriteInEdge(edge); 1008 break; 1009 case PagEdgeKind.Load: 1010 src.addLoadOutEdge(edge); 1011 dst.addLoadInEdge(edge); 1012 break; 1013 case PagEdgeKind.This: 1014 src.addThisOutEdge(edge); 1015 dst.addThisInEdge(edge); 1016 break; 1017 default: 1018 } 1019 return true; 1020 } 1021 1022 public getAddrEdges(): PagEdgeSet { 1023 return this.stashAddrEdge; 1024 } 1025 1026 public resetAddrEdges(): void { 1027 this.stashAddrEdge.clear(); 1028 } 1029 1030 public getGraphName(): string { 1031 return 'PAG'; 1032 } 1033 1034 public dump(name: string): void { 1035 let printer = new GraphPrinter<this>(this); 1036 PrinterBuilder.dump(printer, name); 1037 } 1038} 1039 1040export type InterProceduralSrcType = Local; 1041export type IntraProceduralEdge = { 1042 src: Value; 1043 dst: Value; 1044 kind: PagEdgeKind; 1045 stmt: Stmt; 1046}; 1047export type InterProceduralEdge = { 1048 src: InterProceduralSrcType; 1049 dst: Value; 1050 kind: PagEdgeKind; 1051}; 1052 1053export class FuncPag { 1054 private internalEdges!: Set<IntraProceduralEdge>; 1055 private normalCallSites!: Set<CallSite>; 1056 private dynamicCallSites!: Set<DynCallSite>; 1057 private unknownCallSites!: Set<CallSite>; 1058 1059 public getInternalEdges(): Set<IntraProceduralEdge> | undefined { 1060 return this.internalEdges; 1061 } 1062 1063 public addNormalCallSite(cs: CallSite): void { 1064 this.normalCallSites = this.normalCallSites ?? new Set(); 1065 this.normalCallSites.add(cs); 1066 } 1067 1068 public getNormalCallSites(): Set<CallSite> { 1069 this.normalCallSites = this.normalCallSites ?? new Set(); 1070 return this.normalCallSites; 1071 } 1072 1073 public addDynamicCallSite(cs: DynCallSite): void { 1074 this.dynamicCallSites = this.dynamicCallSites ?? new Set(); 1075 this.dynamicCallSites.add(cs); 1076 } 1077 1078 public getDynamicCallSites(): Set<DynCallSite> { 1079 this.dynamicCallSites = this.dynamicCallSites ?? new Set(); 1080 return this.dynamicCallSites; 1081 } 1082 1083 public addUnknownCallSite(cs: CallSite): void { 1084 this.unknownCallSites = this.unknownCallSites ?? new Set(); 1085 this.unknownCallSites.add(cs); 1086 } 1087 1088 public getUnknownCallSites(): Set<CallSite> { 1089 this.unknownCallSites = this.unknownCallSites ?? new Set(); 1090 return this.unknownCallSites; 1091 } 1092 1093 public addInternalEdge(stmt: ArkAssignStmt, k: PagEdgeKind): boolean { 1094 this.internalEdges === undefined ? (this.internalEdges = new Set()) : undefined; 1095 let lhOp = stmt.getLeftOp(); 1096 let rhOp = stmt.getRightOp(); 1097 1098 if (rhOp instanceof Constant) { 1099 return false; 1100 } 1101 1102 let iEdge: IntraProceduralEdge = { 1103 src: rhOp, 1104 dst: lhOp, 1105 kind: k, 1106 stmt: stmt, 1107 }; 1108 this.internalEdges.add(iEdge); 1109 1110 return true; 1111 } 1112} 1113 1114export class InterFuncPag { 1115 private interFuncEdges: Set<InterProceduralEdge>; 1116 1117 constructor() { 1118 this.interFuncEdges = new Set(); 1119 } 1120 1121 public getInterProceduralEdges(): Set<InterProceduralEdge> { 1122 return this.interFuncEdges; 1123 } 1124 1125 public addToInterProceduralEdgeSet(e: InterProceduralEdge): void { 1126 this.interFuncEdges.add(e); 1127 } 1128} 1129