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.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 1.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 
GraphOutput(GraphProtocol<G, ?, ?, ?, ?, M, ?, ?, ?, ?> p)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      */
newBuilder(GraphStructure<G, N, C, P> structure)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      */
beginGroup(G forGraph, String name, String shortName, M method, int bci, Map<? extends Object, ? extends Object> properties)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      */
print(G graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args)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      */
endGroup()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
close()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 1.0
116      */
117     @Override
isOpen()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 1.0
129      */
130     @Override
write(ByteBuffer src)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 
Builder(GraphStructure<G, N, ?, ?> structure)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          */
protocolVersion(int majorVersion, int minorVersion)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 1.0
182          */
embedded(boolean embedded)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          */
types(GraphTypes graphTypes)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          */
blocks(GraphBlocks<G, ?, N> graphBlocks)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          */
elements(GraphElements<E, ?, ?, P> graphElements)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"})
elementsAndLocations(GraphElements<E, ?, ?, P> graphElements, GraphLocations<E, P, ?> graphLocations)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          */
build(WritableByteChannel channel)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          */
build(GraphOutput<?, ?> parent)265         public GraphOutput<G, M> build(GraphOutput<?, ?> parent) {
266             return buildImpl(elementsAndLocations, parent);
267         }
268 
buildImpl(ElementsAndLocations<M, L, P> e, WritableByteChannel channel)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 
buildImpl(ElementsAndLocations<M, L, P> e, GraphOutput<?, ?> parent)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 
ElementsAndLocations(GraphElements<M, ?, ?, P> elements, GraphLocations<M, P, L> locations)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 
StackLocations(GraphElements<M, ?, ?, P> graphElements)307         StackLocations(GraphElements<M, ?, ?, P> graphElements) {
308             this.graphElements = graphElements;
309         }
310 
311         @Override
methodLocation(M method, int bci, P pos)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
locationURI(StackTraceElement location)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
locationLineNumber(StackTraceElement location)328         public int locationLineNumber(StackTraceElement location) {
329             return location.getLineNumber();
330         }
331 
332         @Override
locationLanguage(StackTraceElement location)333         public String locationLanguage(StackTraceElement location) {
334             return "Java";
335         }
336 
337         @Override
locationOffsetStart(StackTraceElement location)338         public int locationOffsetStart(StackTraceElement location) {
339             return -1;
340         }
341 
342         @Override
locationOffsetEnd(StackTraceElement location)343         public int locationOffsetEnd(StackTraceElement location) {
344             return -1;
345         }
346     }
347 }
348