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 }