1 /* 2 * Copyright (c) 2011, 2019, 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 24 25 package org.graalvm.graphio; 26 27 import java.io.Closeable; 28 import java.io.IOException; 29 import java.net.URI; 30 import java.net.URISyntaxException; 31 import java.nio.ByteBuffer; 32 import java.nio.channels.WritableByteChannel; 33 import java.util.Collections; 34 import java.util.Map; 35 36 /** 37 * Instance of output to dump informations about a compiler compilations. 38 * 39 * @param <G> the type of graph this instance handles 40 * @param <M> the type of methods this instance handles 41 * @since 19.0 a {@link WritableByteChannel} is implemented 42 */ 43 public final class GraphOutput<G, M> implements Closeable, WritableByteChannel { 44 private final GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?, ?> printer; 45 46 private GraphOutput(GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?, ?> p) { 47 this.printer = p; 48 } 49 50 /** 51 * Creates new builder to configure a future instance of {@link GraphOutput}. 52 * 53 * @param <G> the type of the graph 54 * @param <N> the type of the nodes 55 * @param <C> the type of the node classes 56 * @param <P> the type of the ports 57 * 58 * @param structure description of the structure of the graph 59 * @return the builder to configure 60 */ 61 public static <G, N, C, P> Builder<G, N, ?> newBuilder(GraphStructure<G, N, C, P> structure) { 62 return new Builder<>(structure); 63 } 64 65 /** 66 * Begins a compilation group. 67 * 68 * @param forGraph 69 * @param name 70 * @param shortName 71 * @param method 72 * @param bci 73 * @param properties 74 * @throws IOException 75 */ 76 public void beginGroup(G forGraph, String name, String shortName, M method, int bci, Map<? extends Object, ? extends Object> properties) throws IOException { 77 printer.beginGroup(forGraph, name, shortName, method, bci, properties); 78 } 79 80 /** 81 * Prints a single graph. 82 * 83 * @param graph 84 * @param properties 85 * @param id 86 * @param format 87 * @param args 88 * @throws IOException 89 */ 90 public void print(G graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args) throws IOException { 91 printer.print(graph, properties, id, format, args); 92 } 93 94 /** 95 * Ends compilation group. 96 * 97 * @throws IOException 98 */ 99 public void endGroup() throws IOException { 100 printer.endGroup(); 101 } 102 103 /** 104 * Closes the output. Closes allocated resources and associated output channel. 105 */ 106 @Override 107 public void close() { 108 printer.close(); 109 } 110 111 /** 112 * Checks if the {@link GraphOutput} is open. 113 * 114 * @return true if the {@link GraphOutput} is open. 115 * @since 19.0 116 */ 117 @Override 118 public boolean isOpen() { 119 return printer.isOpen(); 120 } 121 122 /** 123 * Writes raw bytes into {@link GraphOutput}. 124 * 125 * @param src the bytes to write 126 * @return the number of bytes written, possibly zero 127 * @throws IOException in case of IO error 128 * @since 19.0 129 */ 130 @Override 131 public int write(ByteBuffer src) throws IOException { 132 return printer.write(src); 133 } 134 135 /** 136 * Builder to configure and create an instance of {@link GraphOutput}. 137 * 138 * @param <G> the type of the (root element of) graph 139 * @param <N> the type of the nodes 140 * @param <M> the type of the methods 141 */ 142 public static final class Builder<G, N, M> { 143 private final GraphStructure<G, N, ?, ?> structure; 144 private ElementsAndLocations<M, ?, ?> elementsAndLocations; 145 146 private GraphTypes types = DefaultGraphTypes.DEFAULT; 147 private GraphBlocks<G, ?, N> blocks = DefaultGraphBlocks.empty(); 148 private int major = 4; 149 private int minor = 0; 150 private boolean embeddedGraphOutput; 151 152 Builder(GraphStructure<G, N, ?, ?> structure) { 153 this.structure = structure; 154 } 155 156 /** 157 * Chooses which version of the protocol to use. The default version is <code>4.0</code> 158 * (when the {@link GraphOutput} & co. classes were introduced). The default can be changed 159 * to other known versions manually by calling this method. 160 * 161 * @param majorVersion by default 4, newer version may be known 162 * @param minorVersion usually 0 163 * @return this builder 164 * @since 0.28 165 */ 166 public Builder<G, N, M> protocolVersion(int majorVersion, int minorVersion) { 167 this.major = majorVersion; 168 this.minor = minorVersion; 169 return this; 170 } 171 172 /** 173 * Sets {@link GraphOutput} as embedded. The embedded {@link GraphOutput} shares 174 * {@link WritableByteChannel channel} with another already open non parent 175 * {@link GraphOutput}. The embedded {@link GraphOutput} flushes data after each 176 * {@link GraphOutput#print print}, {@link GraphOutput#beginGroup beginGroup} and 177 * {@link GraphOutput#endGroup endGroup} call. 178 * 179 * @param embedded if {@code true} the builder creates an embedded {@link GraphOutput} 180 * @return this builder 181 * @since 19.0 182 */ 183 public Builder<G, N, M> embedded(boolean embedded) { 184 this.embeddedGraphOutput = embedded; 185 return this; 186 } 187 188 /** 189 * Associates different implementation of types. 190 * 191 * @param graphTypes implementation of types and enum recognition 192 * @return this builder 193 */ 194 public Builder<G, N, M> types(GraphTypes graphTypes) { 195 this.types = graphTypes; 196 return this; 197 } 198 199 /** 200 * Associates implementation of blocks. 201 * 202 * @param graphBlocks the blocks implementation 203 * @return this builder 204 */ 205 public Builder<G, N, M> blocks(GraphBlocks<G, ?, N> graphBlocks) { 206 this.blocks = graphBlocks; 207 return this; 208 } 209 210 /** 211 * Associates implementation of graph elements. 212 * 213 * @param graphElements the elements implementation 214 * @return this builder 215 */ 216 public <E, P> Builder<G, N, E> elements(GraphElements<E, ?, ?, P> graphElements) { 217 StackLocations<E, P> loc = new StackLocations<>(graphElements); 218 return elementsAndLocations(graphElements, loc); 219 } 220 221 /** 222 * Associates implementation of graph elements and an advanced way to interpret their 223 * locations. 224 * 225 * @param graphElements the elements implementation 226 * @param graphLocations the locations for the elements 227 * @return this builder 228 * @since 0.33 GraalVM 0.33 229 */ 230 @SuppressWarnings({"unchecked", "rawtypes"}) 231 public <E, P> Builder<G, N, E> elementsAndLocations(GraphElements<E, ?, ?, P> graphElements, GraphLocations<E, P, ?> graphLocations) { 232 ElementsAndLocations both = new ElementsAndLocations<>(graphElements, graphLocations); 233 this.elementsAndLocations = both; 234 return (Builder<G, N, E>) this; 235 } 236 237 /** 238 * Creates new {@link GraphOutput} to output to provided channel. The output will use 239 * interfaces currently associated with this builder. 240 * 241 * @param channel the channel to output to 242 * @return new graph output 243 * @throws IOException if something goes wrong when writing to the channel 244 */ 245 public GraphOutput<G, M> build(WritableByteChannel channel) throws IOException { 246 return buildImpl(elementsAndLocations, channel); 247 } 248 249 /** 250 * Support for nesting heterogenous graphs. The newly created output uses all the interfaces 251 * currently associated with this builder, but shares with {@code parent} the output 252 * {@code channel}, internal constant pool and {@link #protocolVersion(int, int) protocol 253 * version}. 254 * <p> 255 * Both GraphOutput (the {@code parent} and the returned one) has to be used in 256 * synchronization - e.g. only one 257 * {@link GraphOutput#beginGroup(java.lang.Object, java.lang.String, java.lang.String, java.lang.Object, int, java.util.Map) 258 * begin}, {@link GraphOutput#endGroup() end} of group or 259 * {@link GraphOutput#print(java.lang.Object, java.util.Map, int, java.lang.String, java.lang.Object...) 260 * printing} can be on at a given moment. 261 * 262 * @param parent the output to inherit {@code channel} and protocol version from 263 * @return new output sharing {@code channel} and other internals with {@code parent} 264 */ 265 public GraphOutput<G, M> build(GraphOutput<?, ?> parent) { 266 return buildImpl(elementsAndLocations, parent); 267 } 268 269 private <L, P> GraphOutput<G, M> buildImpl(ElementsAndLocations<M, L, P> e, WritableByteChannel channel) throws IOException { 270 // @formatter:off 271 ProtocolImpl<G, N, ?, ?, ?, M, ?, ?, ?, ?> p = new ProtocolImpl<>( 272 major, minor, embeddedGraphOutput, structure, types, blocks, 273 e == null ? null : e.elements, 274 e == null ? null : e.locations, channel 275 ); 276 // @formatter:on 277 return new GraphOutput<>(p); 278 } 279 280 private <L, P> GraphOutput<G, M> buildImpl(ElementsAndLocations<M, L, P> e, GraphOutput<?, ?> parent) { 281 // @formatter:off 282 ProtocolImpl<G, N, ?, ?, ?, M, ?, ?, ?, ?> p = new ProtocolImpl<>( 283 parent.printer, structure, types, blocks, 284 e == null ? null : e.elements, 285 e == null ? null : e.locations 286 ); 287 // @formatter:on 288 return new GraphOutput<>(p); 289 } 290 } 291 292 private static final class ElementsAndLocations<M, P, L> { 293 final GraphElements<M, ?, ?, P> elements; 294 final GraphLocations<M, P, L> locations; 295 296 ElementsAndLocations(GraphElements<M, ?, ?, P> elements, GraphLocations<M, P, L> locations) { 297 elements.getClass(); 298 locations.getClass(); 299 this.elements = elements; 300 this.locations = locations; 301 } 302 } 303 304 private static final class StackLocations<M, P> implements GraphLocations<M, P, StackTraceElement> { 305 private final GraphElements<M, ?, ?, P> graphElements; 306 307 StackLocations(GraphElements<M, ?, ?, P> graphElements) { 308 this.graphElements = graphElements; 309 } 310 311 @Override 312 public Iterable<StackTraceElement> methodLocation(M method, int bci, P pos) { 313 StackTraceElement ste = this.graphElements.methodStackTraceElement(method, bci, pos); 314 return Collections.singleton(ste); 315 } 316 317 @Override 318 public URI locationURI(StackTraceElement location) { 319 String path = location.getFileName(); 320 try { 321 return path == null ? null : new URI(null, null, path, null); 322 } catch (URISyntaxException ex) { 323 throw new IllegalArgumentException(ex); 324 } 325 } 326 327 @Override 328 public int locationLineNumber(StackTraceElement location) { 329 return location.getLineNumber(); 330 } 331 332 @Override 333 public String locationLanguage(StackTraceElement location) { 334 return "Java"; 335 } 336 337 @Override 338 public int locationOffsetStart(StackTraceElement location) { 339 return -1; 340 } 341 342 @Override 343 public int locationOffsetEnd(StackTraceElement location) { 344 return -1; 345 } 346 } 347 }