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 { BasicBlock } from '../core/graph/BasicBlock'; 17import { ArkClass } from '../core/model/ArkClass'; 18import { ArkFile } from '../core/model/ArkFile'; 19import { ArkMethod } from '../core/model/ArkMethod'; 20import { ArkNamespace } from '../core/model/ArkNamespace'; 21import { Printer } from './Printer'; 22import { Cfg } from '../core/graph/Cfg'; 23 24/** 25 * @category save 26 */ 27export class DotMethodPrinter extends Printer { 28 method: ArkMethod; 29 nesting: boolean; 30 31 constructor(method: ArkMethod, nesting: boolean = false) { 32 super(); 33 this.method = method; 34 this.nesting = nesting; 35 } 36 37 public dump(): string { 38 this.printer.clear(); 39 if (this.nesting) { 40 this.printer.writeIndent().writeLine(`subgraph "cluster_${this.method.getSignature()}" {`); 41 } else { 42 this.printer.writeIndent().writeLine(`digraph "${this.method.getSignature()}" {`); 43 } 44 this.printer.incIndent(); 45 this.printer.writeIndent().writeLine(`label="${this.method.getSignature()}";`); 46 47 let blocks = (this.method.getCfg() as Cfg)?.getBlocks(); 48 let prefix = `Node${this.stringHashCode(this.method.getSignature().toString())}`; 49 this.printBlocks(blocks, prefix); 50 51 this.printer.decIndent(); 52 this.printer.writeIndent().writeLine('}'); 53 54 return this.printer.toString(); 55 } 56 57 protected stringHashCode(name: string): number { 58 let hashCode = 0; 59 for (let i = 0; i < name.length; i++) { 60 hashCode += name.charCodeAt(i); 61 } 62 return Math.abs(hashCode); 63 } 64 65 private printBlocks(blocks: Set<BasicBlock>, prefix: string): void { 66 if (!blocks) { 67 return; 68 } 69 let blockToNode: Map<BasicBlock, string> = new Map<BasicBlock, string>(); 70 let index = 0; 71 for (let block of blocks) { 72 let name = prefix + index++; 73 blockToNode.set(block, name); 74 /** Node0 [label="entry"]; */ 75 this.printer.writeIndent().writeLine(`${name} [label="${this.getBlockContent(block, this.printer.getIndent())}"];`); 76 } 77 78 for (let block of blocks) { 79 for (let nextBlock of block.getSuccessors()) { 80 // Node0 -> Node1; 81 this.printer.writeIndent().writeLine(`${blockToNode.get(block)} -> ${blockToNode.get(nextBlock)};`); 82 } 83 84 let exceptionalNextBlock = block.getExceptionalSuccessorBlocks(); 85 if (!exceptionalNextBlock) { 86 continue; 87 } 88 for (const nextBlock of exceptionalNextBlock) { 89 this.printer.writeIndent().writeLine(`${blockToNode.get(block)} -> ${blockToNode.get(nextBlock)}[style="dotted"];`); 90 } 91 } 92 } 93 94 private getBlockContent(block: BasicBlock, indent: string): string { 95 let content: string[] = [`id:${block.getId()}`]; 96 for (let stmt of block.getStmts()) { 97 content.push(stmt.toString().replace(/"/g, '\\"')); 98 } 99 return content.join('\n ' + indent); 100 } 101} 102 103/** 104 * @category save 105 */ 106export class DotClassPrinter extends Printer { 107 cls: ArkClass; 108 nesting: boolean; 109 110 constructor(cls: ArkClass, nesting: boolean = false) { 111 super(); 112 this.cls = cls; 113 this.nesting = nesting; 114 } 115 116 public dump(): string { 117 this.printer.clear(); 118 if (!this.nesting) { 119 this.printer.writeLine(`digraph "${this.cls.getName()}" {`); 120 this.printer.incIndent(); 121 } 122 123 for (let method of this.cls.getMethods()) { 124 let mtd = new DotMethodPrinter(method, true); 125 this.printer.write(mtd.dump()); 126 } 127 128 if (!this.nesting) { 129 this.printer.decIndent(); 130 this.printer.writeLine(`}`); 131 } 132 133 return this.printer.toString(); 134 } 135} 136 137/** 138 * @category save 139 */ 140export class DotNamespacePrinter extends Printer { 141 ns: ArkNamespace; 142 nesting: boolean; 143 144 constructor(ns: ArkNamespace, nesting: boolean = false) { 145 super(); 146 this.ns = ns; 147 this.nesting = nesting; 148 } 149 150 public dump(): string { 151 this.printer.clear(); 152 if (!this.nesting) { 153 this.printer.writeLine(`digraph "${this.ns.getName()}" {`); 154 this.printer.incIndent(); 155 } 156 157 for (let method of this.ns.getAllMethodsUnderThisNamespace()) { 158 let mtd = new DotMethodPrinter(method, true); 159 this.printer.write(mtd.dump()); 160 } 161 162 if (!this.nesting) { 163 this.printer.decIndent(); 164 this.printer.writeLine(`}`); 165 } 166 167 return this.printer.toString(); 168 } 169} 170 171/** 172 * @category save 173 */ 174export class DotFilePrinter extends Printer { 175 arkFile: ArkFile; 176 177 constructor(arkFile: ArkFile) { 178 super(); 179 this.arkFile = arkFile; 180 } 181 182 public dump(): string { 183 this.printer.clear(); 184 this.printer.writeLine(`digraph "${this.arkFile.getName()}" {`); 185 this.printer.incIndent(); 186 187 for (let ns of this.arkFile.getNamespaces()) { 188 let nsPrinter = new DotNamespacePrinter(ns, true); 189 this.printer.write(nsPrinter.dump()); 190 } 191 192 // print class 193 for (let cls of this.arkFile.getClasses()) { 194 let clsPrinter = new DotClassPrinter(cls, true); 195 this.printer.write(clsPrinter.dump()); 196 } 197 198 this.printer.decIndent(); 199 this.printer.writeLine('}'); 200 201 return this.printer.toString(); 202 } 203} 204