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