1 /* 2 * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 package org.graalvm.compiler.printer; 24 25 import java.io.BufferedOutputStream; 26 import java.io.OutputStream; 27 import java.io.PrintStream; 28 import java.io.UnsupportedEncodingException; 29 import java.nio.charset.Charset; 30 import java.util.Map; 31 import java.util.Map.Entry; 32 33 /** 34 * Elementary, generic generator of Ideal Graph Visualizer input for use in printers for specific 35 * data structures. 36 */ 37 class BasicIdealGraphPrinter { 38 39 /** 40 * Edge between two nodes. 41 */ 42 protected static class Edge { 43 44 final String from; 45 final int fromIndex; 46 final String to; 47 final int toIndex; 48 final String label; 49 50 public Edge(String from, int fromIndex, String to, int toIndex, String label) { 51 assert (from != null && to != null); 52 this.from = from; 53 this.fromIndex = fromIndex; 54 this.to = to; 55 this.toIndex = toIndex; 56 this.label = label; 57 } 58 59 @Override 60 public int hashCode() { 61 int h = from.hashCode() ^ to.hashCode(); 62 h = 3 * h + fromIndex; 63 h = 5 * h + toIndex; 64 if (label != null) { 65 h ^= label.hashCode(); 66 } 67 return h; 68 } 69 70 @Override 71 public boolean equals(Object obj) { 72 if (obj == this) { 73 return true; 74 } 75 if (obj instanceof Edge) { 76 Edge other = (Edge) obj; 77 return from.equals(other.from) && fromIndex == other.fromIndex && to.equals(other.to) && toIndex == other.toIndex && 78 (label == other.label || (label != null && label.equals(other.label))); 79 } 80 return false; 81 } 82 } 83 84 private final PrintStream stream; 85 86 /** 87 * Creates a new {@link IdealGraphPrinter} that writes to the specified output stream. 88 */ 89 protected BasicIdealGraphPrinter(OutputStream stream) { 90 try { 91 OutputStream buffered; 92 if (stream instanceof BufferedOutputStream) { 93 buffered = stream; 94 } else { 95 buffered = new BufferedOutputStream(stream, 256 * 1024); 96 } 97 this.stream = new PrintStream(buffered, false, Charset.defaultCharset().name()); 98 } catch (UnsupportedEncodingException e) { 99 throw new RuntimeException(e); 100 } 101 } 102 103 /** 104 * Flushes any buffered output. 105 */ 106 protected void flush() { 107 stream.flush(); 108 } 109 110 /** 111 * Starts a new graph document. 112 */ 113 protected void begin() { 114 stream.println("<graphDocument>"); 115 } 116 117 protected void beginGroup() { 118 stream.println("<group>"); 119 } 120 121 protected void beginMethod(String name, String shortName, int bci) { 122 stream.printf(" <method name='%s' shortName='%s' bci='%d'>%n", escape(name), escape(shortName), bci); 123 } 124 125 protected void beginBytecodes() { 126 stream.println(" <bytecodes>\n<![CDATA["); 127 } 128 129 protected void printBytecode(int bci, String mnemonic, int[] extra) { 130 stream.print(bci); 131 stream.print(' '); 132 stream.print(mnemonic); 133 if (extra != null) { 134 for (int b : extra) { 135 stream.print(' '); 136 stream.print(b); 137 } 138 } 139 stream.println(); 140 } 141 142 protected void endBytecodes() { 143 stream.println(" ]]></bytecodes>"); 144 } 145 146 protected void printBytecodes(String disassembly) { 147 beginBytecodes(); 148 stream.println(disassembly); 149 endBytecodes(); 150 } 151 152 protected void endMethod() { 153 stream.println(" </method>"); 154 } 155 156 protected void beginGraph(String title) { 157 stream.printf(" <graph name='%s'>%n", escape(title)); 158 } 159 160 protected void beginProperties() { 161 stream.print("<properties>"); 162 } 163 164 protected void printProperty(String name, String value) { 165 stream.printf("<p name='%s'>%s</p>", escape(name), escape(value)); 166 } 167 168 protected void endProperties() { 169 stream.print("</properties>"); 170 } 171 172 protected void printProperties(Map<String, String> properties) { 173 beginProperties(); 174 for (Entry<String, String> entry : properties.entrySet()) { 175 printProperty(entry.getKey(), entry.getValue()); 176 } 177 endProperties(); 178 } 179 180 protected void beginNodes() { 181 stream.println(" <nodes>"); 182 } 183 184 protected void beginNode(String id) { 185 stream.printf(" <node id='%s'>", escape(id)); 186 } 187 188 protected void endNode() { 189 stream.println(" </node>"); 190 } 191 192 protected void printNode(String id, Map<String, String> properties) { 193 beginNode(id); 194 if (properties != null) { 195 printProperties(properties); 196 } 197 endNode(); 198 } 199 200 protected void endNodes() { 201 stream.println(" </nodes>"); 202 } 203 204 protected void beginEdges() { 205 stream.println(" <edges>"); 206 } 207 208 protected void printEdge(Edge edge) { 209 stream.printf(" <edge from='%s' fromIndex='%d' to='%s' toIndex='%d' label='%s' />%n", escape(edge.from), edge.fromIndex, escape(edge.to), edge.toIndex, escape(edge.label)); 210 } 211 212 protected void endEdges() { 213 stream.println(" </edges>"); 214 } 215 216 protected void beginControlFlow() { 217 stream.println(" <controlFlow>"); 218 } 219 220 protected void beginBlock(String name) { 221 stream.printf(" <block name='%s'>%n", escape(name)); 222 } 223 224 protected void beginSuccessors() { 225 stream.println(" <successors>"); 226 } 227 228 protected void printSuccessor(String name) { 229 stream.printf(" <successor name='%s'/>%n", escape(name)); 230 } 231 232 protected void endSuccessors() { 233 stream.println(" </successors>"); 234 } 235 236 protected void beginBlockNodes() { 237 stream.println(" <nodes>"); 238 } 239 240 protected void printBlockNode(String nodeId) { 241 stream.printf(" <node id='%s'/>%n", escape(nodeId)); 242 } 243 244 protected void endBlockNodes() { 245 stream.println(" </nodes>"); 246 } 247 248 protected void endBlock() { 249 stream.println(" </block>"); 250 } 251 252 protected void endControlFlow() { 253 stream.println(" </controlFlow>"); 254 } 255 256 protected void endGraph() { 257 stream.println(" </graph>"); 258 } 259 260 /** 261 * Ends the current group. 262 */ 263 public void endGroup() { 264 stream.println("</group>"); 265 } 266 267 /** 268 * Finishes the graph document and flushes the output stream. 269 */ 270 protected void end() { 271 stream.println("</graphDocument>"); 272 flush(); 273 } 274 275 public void close() { 276 end(); 277 stream.close(); 278 } 279 280 public boolean isValid() { 281 return !stream.checkError(); 282 } 283 284 private static String escape(String s) { 285 StringBuilder str = null; 286 for (int i = 0; i < s.length(); i++) { 287 char c = s.charAt(i); 288 switch (c) { 289 case '&': 290 case '<': 291 case '>': 292 case '"': 293 case '\'': 294 if (str == null) { 295 str = new StringBuilder(); 296 str.append(s, 0, i); 297 } 298 switch (c) { 299 case '&': 300 str.append("&"); 301 break; 302 case '<': 303 str.append("<"); 304 break; 305 case '>': 306 str.append(">"); 307 break; 308 case '"': 309 str.append("""); 310 break; 311 case '\'': 312 str.append("'"); 313 break; 314 default: 315 assert false; 316 } 317 break; 318 case '\u0000': 319 case '\u0001': 320 case '\u0002': 321 case '\u0003': 322 case '\u0004': 323 case '\u0005': 324 case '\u0006': 325 case '\u0007': 326 case '\u0008': 327 case '\u000b': 328 case '\u000c': 329 case '\u000e': 330 case '\u000f': 331 case '\u0010': 332 case '\u0011': 333 case '\u0012': 334 case '\u0013': 335 case '\u0014': 336 case '\u0015': 337 case '\u0016': 338 case '\u0017': 339 case '\u0018': 340 case '\u0019': 341 case '\u001a': 342 case '\u001b': 343 case '\u001c': 344 case '\u001d': 345 case '\u001e': 346 case '\u001f': 347 if (str == null) { 348 str = new StringBuilder(); 349 str.append(s, 0, i); 350 } 351 str.append("'0x").append(Integer.toHexString(c)); 352 break; 353 default: 354 if (str != null) { 355 str.append(c); 356 } 357 break; 358 } 359 } 360 if (str == null) { 361 return s; 362 } else { 363 return str.toString(); 364 } 365 } 366 }