src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java
changeset 47216 71c04702a3d5
parent 46762 f7defa99f173
child 47667 390896759aa2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,678 @@
+/*
+ * Copyright (c) 2011, 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.graphio;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.WritableByteChannel;
+import java.nio.charset.Charset;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+
+abstract class GraphProtocol<Graph, Node, NodeClass, Edges, Block, ResolvedJavaMethod, ResolvedJavaField, Signature, NodeSourcePosition> implements Closeable {
+    private static final Charset UTF8 = Charset.forName("UTF-8");
+
+    private static final int CONSTANT_POOL_MAX_SIZE = 8000;
+
+    private static final int BEGIN_GROUP = 0x00;
+    private static final int BEGIN_GRAPH = 0x01;
+    private static final int CLOSE_GROUP = 0x02;
+
+    private static final int POOL_NEW = 0x00;
+    private static final int POOL_STRING = 0x01;
+    private static final int POOL_ENUM = 0x02;
+    private static final int POOL_CLASS = 0x03;
+    private static final int POOL_METHOD = 0x04;
+    private static final int POOL_NULL = 0x05;
+    private static final int POOL_NODE_CLASS = 0x06;
+    private static final int POOL_FIELD = 0x07;
+    private static final int POOL_SIGNATURE = 0x08;
+    private static final int POOL_NODE_SOURCE_POSITION = 0x09;
+
+    private static final int PROPERTY_POOL = 0x00;
+    private static final int PROPERTY_INT = 0x01;
+    private static final int PROPERTY_LONG = 0x02;
+    private static final int PROPERTY_DOUBLE = 0x03;
+    private static final int PROPERTY_FLOAT = 0x04;
+    private static final int PROPERTY_TRUE = 0x05;
+    private static final int PROPERTY_FALSE = 0x06;
+    private static final int PROPERTY_ARRAY = 0x07;
+    private static final int PROPERTY_SUBGRAPH = 0x08;
+
+    private static final int KLASS = 0x00;
+    private static final int ENUM_KLASS = 0x01;
+
+    private static final byte[] MAGIC_BYTES = {'B', 'I', 'G', 'V'};
+
+    private final ConstantPool constantPool;
+    private final ByteBuffer buffer;
+    private final WritableByteChannel channel;
+    private final int versionMajor;
+    private final int versionMinor;
+
+    protected GraphProtocol(WritableByteChannel channel) throws IOException {
+        this(channel, 4, 0);
+    }
+
+    private GraphProtocol(WritableByteChannel channel, int major, int minor) throws IOException {
+        if (major > 4) {
+            throw new IllegalArgumentException();
+        }
+        if (major == 4 && minor > 0) {
+            throw new IllegalArgumentException();
+        }
+        this.versionMajor = major;
+        this.versionMinor = minor;
+        this.constantPool = new ConstantPool();
+        this.buffer = ByteBuffer.allocateDirect(256 * 1024);
+        this.channel = channel;
+        writeVersion();
+    }
+
+    @SuppressWarnings("all")
+    public final void print(Graph graph, Map<? extends Object, ? extends Object> properties, int id, String format, Object... args) throws IOException {
+        writeByte(BEGIN_GRAPH);
+        if (versionMajor >= 3) {
+            writeInt(id);
+            writeString(format);
+            writeInt(args.length);
+            for (Object a : args) {
+                writePropertyObject(graph, a);
+            }
+        } else {
+            writePoolObject(formatTitle(graph, id, format, args));
+        }
+        writeGraph(graph, properties);
+        flush();
+    }
+
+    public final void beginGroup(Graph noGraph, String name, String shortName, ResolvedJavaMethod method, int bci, Map<? extends Object, ? extends Object> properties) throws IOException {
+        writeByte(BEGIN_GROUP);
+        writePoolObject(name);
+        writePoolObject(shortName);
+        writePoolObject(method);
+        writeInt(bci);
+        writeProperties(noGraph, properties);
+    }
+
+    public final void endGroup() throws IOException {
+        writeByte(CLOSE_GROUP);
+    }
+
+    @Override
+    public final void close() {
+        try {
+            flush();
+            channel.close();
+        } catch (IOException ex) {
+            throw new Error(ex);
+        }
+    }
+
+    protected abstract Graph findGraph(Graph current, Object obj);
+
+    protected abstract ResolvedJavaMethod findMethod(Object obj);
+
+    protected abstract NodeClass findNodeClass(Object obj);
+
+    /**
+     * Find a Java class. The returned object must be acceptable by
+     * {@link #findJavaTypeName(java.lang.Object)} and return valid name for the class.
+     *
+     * @param clazz node class object
+     * @return object representing the class, for example {@link Class}
+     */
+    protected abstract Object findJavaClass(NodeClass clazz);
+
+    protected abstract Object findEnumClass(Object enumValue);
+
+    protected abstract String findNameTemplate(NodeClass clazz);
+
+    protected abstract Edges findClassEdges(NodeClass nodeClass, boolean dumpInputs);
+
+    protected abstract int findNodeId(Node n);
+
+    protected abstract void findExtraNodes(Node node, Collection<? super Node> extraNodes);
+
+    protected abstract boolean hasPredecessor(Node node);
+
+    protected abstract int findNodesCount(Graph info);
+
+    protected abstract Iterable<? extends Node> findNodes(Graph info);
+
+    protected abstract void findNodeProperties(Node node, Map<String, Object> props, Graph info);
+
+    protected abstract Collection<? extends Node> findBlockNodes(Graph info, Block block);
+
+    protected abstract int findBlockId(Block sux);
+
+    protected abstract Collection<? extends Block> findBlocks(Graph graph);
+
+    protected abstract Collection<? extends Block> findBlockSuccessors(Block block);
+
+    protected abstract String formatTitle(Graph graph, int id, String format, Object... args);
+
+    protected abstract int findSize(Edges edges);
+
+    protected abstract boolean isDirect(Edges edges, int i);
+
+    protected abstract String findName(Edges edges, int i);
+
+    protected abstract Object findType(Edges edges, int i);
+
+    protected abstract Collection<? extends Node> findNodes(Graph graph, Node node, Edges edges, int i);
+
+    protected abstract int findEnumOrdinal(Object obj);
+
+    protected abstract String[] findEnumTypeValues(Object clazz);
+
+    protected abstract String findJavaTypeName(Object obj);
+
+    protected abstract byte[] findMethodCode(ResolvedJavaMethod method);
+
+    protected abstract int findMethodModifiers(ResolvedJavaMethod method);
+
+    protected abstract Signature findMethodSignature(ResolvedJavaMethod method);
+
+    protected abstract String findMethodName(ResolvedJavaMethod method);
+
+    protected abstract Object findMethodDeclaringClass(ResolvedJavaMethod method);
+
+    protected abstract int findFieldModifiers(ResolvedJavaField field);
+
+    protected abstract String findFieldTypeName(ResolvedJavaField field);
+
+    protected abstract String findFieldName(ResolvedJavaField field);
+
+    protected abstract Object findFieldDeclaringClass(ResolvedJavaField field);
+
+    protected abstract ResolvedJavaField findJavaField(Object object);
+
+    protected abstract Signature findSignature(Object object);
+
+    protected abstract int findSignatureParameterCount(Signature signature);
+
+    protected abstract String findSignatureParameterTypeName(Signature signature, int index);
+
+    protected abstract String findSignatureReturnTypeName(Signature signature);
+
+    protected abstract NodeSourcePosition findNodeSourcePosition(Object object);
+
+    protected abstract ResolvedJavaMethod findNodeSourcePositionMethod(NodeSourcePosition pos);
+
+    protected abstract NodeSourcePosition findNodeSourcePositionCaller(NodeSourcePosition pos);
+
+    protected abstract int findNodeSourcePositionBCI(NodeSourcePosition pos);
+
+    protected abstract StackTraceElement findMethodStackTraceElement(ResolvedJavaMethod method, int bci, NodeSourcePosition pos);
+
+    private void writeVersion() throws IOException {
+        writeBytesRaw(MAGIC_BYTES);
+        writeByte(versionMajor);
+        writeByte(versionMinor);
+    }
+
+    private void flush() throws IOException {
+        buffer.flip();
+        /*
+         * Try not to let interrupted threads aborting the write. There's still a race here but an
+         * interrupt that's been pending for a long time shouldn't stop this writing.
+         */
+        boolean interrupted = Thread.interrupted();
+        try {
+            channel.write(buffer);
+        } finally {
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        buffer.compact();
+    }
+
+    private void ensureAvailable(int i) throws IOException {
+        assert buffer.capacity() >= i : "Can not make " + i + " bytes available, buffer is too small";
+        while (buffer.remaining() < i) {
+            flush();
+        }
+    }
+
+    private void writeByte(int b) throws IOException {
+        ensureAvailable(1);
+        buffer.put((byte) b);
+    }
+
+    private void writeInt(int b) throws IOException {
+        ensureAvailable(4);
+        buffer.putInt(b);
+    }
+
+    private void writeLong(long b) throws IOException {
+        ensureAvailable(8);
+        buffer.putLong(b);
+    }
+
+    private void writeDouble(double b) throws IOException {
+        ensureAvailable(8);
+        buffer.putDouble(b);
+    }
+
+    private void writeFloat(float b) throws IOException {
+        ensureAvailable(4);
+        buffer.putFloat(b);
+    }
+
+    private void writeShort(char b) throws IOException {
+        ensureAvailable(2);
+        buffer.putChar(b);
+    }
+
+    private void writeString(String str) throws IOException {
+        byte[] bytes = str.getBytes(UTF8);
+        writeBytes(bytes);
+    }
+
+    private void writeBytes(byte[] b) throws IOException {
+        if (b == null) {
+            writeInt(-1);
+        } else {
+            writeInt(b.length);
+            writeBytesRaw(b);
+        }
+    }
+
+    private void writeBytesRaw(byte[] b) throws IOException {
+        int bytesWritten = 0;
+        while (bytesWritten < b.length) {
+            int toWrite = Math.min(b.length - bytesWritten, buffer.capacity());
+            ensureAvailable(toWrite);
+            buffer.put(b, bytesWritten, toWrite);
+            bytesWritten += toWrite;
+        }
+    }
+
+    private void writeInts(int[] b) throws IOException {
+        if (b == null) {
+            writeInt(-1);
+        } else {
+            writeInt(b.length);
+            int sizeInBytes = b.length * 4;
+            ensureAvailable(sizeInBytes);
+            buffer.asIntBuffer().put(b);
+            buffer.position(buffer.position() + sizeInBytes);
+        }
+    }
+
+    private void writeDoubles(double[] b) throws IOException {
+        if (b == null) {
+            writeInt(-1);
+        } else {
+            writeInt(b.length);
+            int sizeInBytes = b.length * 8;
+            ensureAvailable(sizeInBytes);
+            buffer.asDoubleBuffer().put(b);
+            buffer.position(buffer.position() + sizeInBytes);
+        }
+    }
+
+    private void writePoolObject(Object object) throws IOException {
+        if (object == null) {
+            writeByte(POOL_NULL);
+            return;
+        }
+        Character id = constantPool.get(object);
+        if (id == null) {
+            addPoolEntry(object);
+        } else {
+            if (object instanceof Enum<?> || findEnumOrdinal(object) >= 0) {
+                writeByte(POOL_ENUM);
+            } else if (object instanceof Class<?> || findJavaTypeName(object) != null) {
+                writeByte(POOL_CLASS);
+            } else if (findJavaField(object) != null) {
+                writeByte(POOL_FIELD);
+            } else if (findSignature(object) != null) {
+                writeByte(POOL_SIGNATURE);
+            } else if (versionMajor >= 4 && findNodeSourcePosition(object) != null) {
+                writeByte(POOL_NODE_SOURCE_POSITION);
+            } else {
+                if (findNodeClass(object) != null) {
+                    writeByte(POOL_NODE_CLASS);
+                } else if (findMethod(object) != null) {
+                    writeByte(POOL_METHOD);
+                } else {
+                    writeByte(POOL_STRING);
+                }
+            }
+            writeShort(id.charValue());
+        }
+    }
+
+    private void writeGraph(Graph graph, Map<? extends Object, ? extends Object> properties) throws IOException {
+        writeProperties(graph, properties);
+        writeNodes(graph);
+        writeBlocks(findBlocks(graph), graph);
+    }
+
+    private void writeNodes(Graph info) throws IOException {
+        Map<String, Object> props = new HashMap<>();
+
+        final int size = findNodesCount(info);
+        writeInt(size);
+        int cnt = 0;
+        for (Node node : findNodes(info)) {
+            NodeClass nodeClass = findNodeClass(node);
+            if (nodeClass == null) {
+                throw new IOException("No class for " + node);
+            }
+            findNodeProperties(node, props, info);
+
+            writeInt(findNodeId(node));
+            writePoolObject(nodeClass);
+            writeByte(hasPredecessor(node) ? 1 : 0);
+            writeProperties(info, props);
+            writeEdges(info, node, true);
+            writeEdges(info, node, false);
+
+            props.clear();
+            cnt++;
+        }
+        if (size != cnt) {
+            throw new IOException("Expecting " + size + " nodes, but found " + cnt);
+        }
+    }
+
+    private void writeEdges(Graph graph, Node node, boolean dumpInputs) throws IOException {
+        NodeClass clazz = findNodeClass(node);
+        Edges edges = findClassEdges(clazz, dumpInputs);
+        int size = findSize(edges);
+        for (int i = 0; i < size; i++) {
+            Collection<? extends Node> list = findNodes(graph, node, edges, i);
+            if (isDirect(edges, i)) {
+                if (list != null && list.size() != 1) {
+                    throw new IOException("Edge " + i + " in " + edges + " is direct, but list isn't singleton: " + list);
+                }
+                Node n = null;
+                if (list != null && !list.isEmpty()) {
+                    n = list.iterator().next();
+                }
+                writeNodeRef(n);
+            } else {
+                if (list == null) {
+                    writeShort((char) 0);
+                } else {
+                    int listSize = list.size();
+                    assert listSize == ((char) listSize);
+                    writeShort((char) listSize);
+                    for (Node edge : list) {
+                        writeNodeRef(edge);
+                    }
+                }
+            }
+        }
+    }
+
+    private void writeNodeRef(Node node) throws IOException {
+        writeInt(findNodeId(node));
+    }
+
+    private void writeBlocks(Collection<? extends Block> blocks, Graph info) throws IOException {
+        if (blocks != null) {
+            for (Block block : blocks) {
+                Collection<? extends Node> nodes = findBlockNodes(info, block);
+                if (nodes == null) {
+                    writeInt(0);
+                    return;
+                }
+            }
+            writeInt(blocks.size());
+            for (Block block : blocks) {
+                Collection<? extends Node> nodes = findBlockNodes(info, block);
+                writeInt(findBlockId(block));
+                writeInt(nodes.size());
+                for (Node node : nodes) {
+                    writeInt(findNodeId(node));
+                }
+                final Collection<? extends Block> successors = findBlockSuccessors(block);
+                writeInt(successors.size());
+                for (Block sux : successors) {
+                    writeInt(findBlockId(sux));
+                }
+            }
+        } else {
+            writeInt(0);
+        }
+    }
+
+    private void writeEdgesInfo(NodeClass nodeClass, boolean dumpInputs) throws IOException {
+        Edges edges = findClassEdges(nodeClass, dumpInputs);
+        int size = findSize(edges);
+        writeShort((char) size);
+        for (int i = 0; i < size; i++) {
+            writeByte(isDirect(edges, i) ? 0 : 1);
+            writePoolObject(findName(edges, i));
+            if (dumpInputs) {
+                writePoolObject(findType(edges, i));
+            }
+        }
+    }
+
+    @SuppressWarnings("all")
+    private void addPoolEntry(Object object) throws IOException {
+        ResolvedJavaField field;
+        String typeName;
+        Signature signature;
+        NodeSourcePosition pos;
+        int enumOrdinal;
+        char index = constantPool.add(object);
+        writeByte(POOL_NEW);
+        writeShort(index);
+        if ((typeName = findJavaTypeName(object)) != null) {
+            writeByte(POOL_CLASS);
+            writeString(typeName);
+            String[] enumValueNames = findEnumTypeValues(object);
+            if (enumValueNames != null) {
+                writeByte(ENUM_KLASS);
+                writeInt(enumValueNames.length);
+                for (String o : enumValueNames) {
+                    writePoolObject(o);
+                }
+            } else {
+                writeByte(KLASS);
+            }
+        } else if ((enumOrdinal = findEnumOrdinal(object)) >= 0) {
+            writeByte(POOL_ENUM);
+            writePoolObject(findEnumClass(object));
+            writeInt(enumOrdinal);
+        } else if ((field = findJavaField(object)) != null) {
+            writeByte(POOL_FIELD);
+            writePoolObject(findFieldDeclaringClass(field));
+            writePoolObject(findFieldName(field));
+            writePoolObject(findFieldTypeName(field));
+            writeInt(findFieldModifiers(field));
+        } else if ((signature = findSignature(object)) != null) {
+            writeByte(POOL_SIGNATURE);
+            int args = findSignatureParameterCount(signature);
+            writeShort((char) args);
+            for (int i = 0; i < args; i++) {
+                writePoolObject(findSignatureParameterTypeName(signature, i));
+            }
+            writePoolObject(findSignatureReturnTypeName(signature));
+        } else if (versionMajor >= 4 && (pos = findNodeSourcePosition(object)) != null) {
+            writeByte(POOL_NODE_SOURCE_POSITION);
+            ResolvedJavaMethod method = findNodeSourcePositionMethod(pos);
+            writePoolObject(method);
+            final int bci = findNodeSourcePositionBCI(pos);
+            writeInt(bci);
+            StackTraceElement ste = findMethodStackTraceElement(method, bci, pos);
+            if (ste != null) {
+                writePoolObject(ste.getFileName());
+                writeInt(ste.getLineNumber());
+            } else {
+                writePoolObject(null);
+            }
+            writePoolObject(findNodeSourcePositionCaller(pos));
+        } else {
+            NodeClass nodeClass = findNodeClass(object);
+            if (nodeClass != null) {
+                writeByte(POOL_NODE_CLASS);
+                final Object clazz = findJavaClass(nodeClass);
+                if (versionMajor >= 3) {
+                    writePoolObject(clazz);
+                    writeString(findNameTemplate(nodeClass));
+                } else {
+                    writeString(((Class<?>) clazz).getSimpleName());
+                    String nameTemplate = findNameTemplate(nodeClass);
+                    writeString(nameTemplate);
+                }
+                writeEdgesInfo(nodeClass, true);
+                writeEdgesInfo(nodeClass, false);
+                return;
+            }
+            ResolvedJavaMethod method = findMethod(object);
+            if (method == null) {
+                writeByte(POOL_STRING);
+                writeString(object.toString());
+                return;
+            }
+            writeByte(POOL_METHOD);
+            writePoolObject(findMethodDeclaringClass(method));
+            writePoolObject(findMethodName(method));
+            writePoolObject(findMethodSignature(method));
+            writeInt(findMethodModifiers(method));
+            writeBytes(findMethodCode(method));
+        }
+    }
+
+    private void writePropertyObject(Graph graph, Object obj) throws IOException {
+        if (obj instanceof Integer) {
+            writeByte(PROPERTY_INT);
+            writeInt(((Integer) obj).intValue());
+        } else if (obj instanceof Long) {
+            writeByte(PROPERTY_LONG);
+            writeLong(((Long) obj).longValue());
+        } else if (obj instanceof Double) {
+            writeByte(PROPERTY_DOUBLE);
+            writeDouble(((Double) obj).doubleValue());
+        } else if (obj instanceof Float) {
+            writeByte(PROPERTY_FLOAT);
+            writeFloat(((Float) obj).floatValue());
+        } else if (obj instanceof Boolean) {
+            if (((Boolean) obj).booleanValue()) {
+                writeByte(PROPERTY_TRUE);
+            } else {
+                writeByte(PROPERTY_FALSE);
+            }
+        } else if (obj != null && obj.getClass().isArray()) {
+            Class<?> componentType = obj.getClass().getComponentType();
+            if (componentType.isPrimitive()) {
+                if (componentType == Double.TYPE) {
+                    writeByte(PROPERTY_ARRAY);
+                    writeByte(PROPERTY_DOUBLE);
+                    writeDoubles((double[]) obj);
+                } else if (componentType == Integer.TYPE) {
+                    writeByte(PROPERTY_ARRAY);
+                    writeByte(PROPERTY_INT);
+                    writeInts((int[]) obj);
+                } else {
+                    writeByte(PROPERTY_POOL);
+                    writePoolObject(obj);
+                }
+            } else {
+                writeByte(PROPERTY_ARRAY);
+                writeByte(PROPERTY_POOL);
+                Object[] array = (Object[]) obj;
+                writeInt(array.length);
+                for (Object o : array) {
+                    writePoolObject(o);
+                }
+            }
+        } else {
+            Graph g = findGraph(graph, obj);
+            if (g == null) {
+                writeByte(PROPERTY_POOL);
+                writePoolObject(obj);
+            } else {
+                writeByte(PROPERTY_SUBGRAPH);
+                writeGraph(g, null);
+            }
+        }
+    }
+
+    private void writeProperties(Graph graph, Map<? extends Object, ? extends Object> props) throws IOException {
+        if (props == null) {
+            writeShort((char) 0);
+            return;
+        }
+        final int size = props.size();
+        // properties
+        writeShort((char) size);
+        int cnt = 0;
+        for (Map.Entry<? extends Object, ? extends Object> entry : props.entrySet()) {
+            String key = entry.getKey().toString();
+            writePoolObject(key);
+            writePropertyObject(graph, entry.getValue());
+            cnt++;
+        }
+        if (size != cnt) {
+            throw new IOException("Expecting " + size + " properties, but found only " + cnt);
+        }
+    }
+
+    private static final class ConstantPool extends LinkedHashMap<Object, Character> {
+
+        private final LinkedList<Character> availableIds;
+        private char nextId;
+        private static final long serialVersionUID = -2676889957907285681L;
+
+        ConstantPool() {
+            super(50, 0.65f);
+            availableIds = new LinkedList<>();
+        }
+
+        @Override
+        protected boolean removeEldestEntry(java.util.Map.Entry<Object, Character> eldest) {
+            if (size() > CONSTANT_POOL_MAX_SIZE) {
+                availableIds.addFirst(eldest.getValue());
+                return true;
+            }
+            return false;
+        }
+
+        private Character nextAvailableId() {
+            if (!availableIds.isEmpty()) {
+                return availableIds.removeFirst();
+            }
+            return nextId++;
+        }
+
+        public char add(Object obj) {
+            Character id = nextAvailableId();
+            put(obj, id);
+            return id;
+        }
+    }
+
+}