1 /*
2  * Copyright (c) 2011, 2017, 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 
GraphOutput(GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?, ?> p)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      */
newBuilder(GraphStructure<G, N, C, P> structure)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      */
beginGroup(G forGraph, String name, String shortName, M method, int bci, Map<? extends Object, ? extends Object> properties)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      */
print(G graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args)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      */
endGroup()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
close()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 
Builder(GraphStructure<G, N, ?, ?> structure)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          */
protocolVersion(int majorVersion, int minorVersion)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          */
types(GraphTypes graphTypes)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          */
blocks(GraphBlocks<G, ?, N> graphBlocks)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          */
elements(GraphElements<E, ?, ?, P> graphElements)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"})
elementsAndLocations(GraphElements<E, ?, ?, P> graphElements, GraphLocations<E, P, ?> graphLocations)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          */
build(WritableByteChannel channel)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 #beginGroup(java.lang.Object, java.lang.String, java.lang.String, java.lang.Object, int, java.util.Map)
215          * begin}, {@link #endGroup() end} of group or
216          * {@link #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          */
build(GraphOutput<?, ?> parent)222         public GraphOutput<G, M> build(GraphOutput<?, ?> parent) {
223             return buildImpl(elementsAndLocations, parent);
224         }
225 
buildImpl(ElementsAndLocations<M, L, P> e, WritableByteChannel channel)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 
buildImpl(ElementsAndLocations<M, L, P> e, GraphOutput<?, ?> parent)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 
ElementsAndLocations(GraphElements<M, ?, ?, P> elements, GraphLocations<M, P, L> locations)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 
StackLocations(GraphElements<M, ?, ?, P> graphElements)264         StackLocations(GraphElements<M, ?, ?, P> graphElements) {
265             this.graphElements = graphElements;
266         }
267 
268         @Override
methodLocation(M method, int bci, P pos)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
locationURI(StackTraceElement location)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
locationLineNumber(StackTraceElement location)285         public int locationLineNumber(StackTraceElement location) {
286             return location.getLineNumber();
287         }
288 
289         @Override
locationLanguage(StackTraceElement location)290         public String locationLanguage(StackTraceElement location) {
291             return "Java";
292         }
293 
294         @Override
locationOffsetStart(StackTraceElement location)295         public int locationOffsetStart(StackTraceElement location) {
296             return -1;
297         }
298 
299         @Override
locationOffsetEnd(StackTraceElement location)300         public int locationOffsetEnd(StackTraceElement location) {
301             return -1;
302         }
303     }
304 }
305