--- /dev/null 2017-03-16 11:03:49.895616831 -0700 +++ new/src/jdk.internal.vm.compiler/share/classes/org.graalvm.graphio/src/org/graalvm/graphio/GraphProtocol.java 2017-08-03 22:43:01.829925554 -0700 @@ -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 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 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 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 extraNodes); + + protected abstract boolean hasPredecessor(Node node); + + protected abstract int findNodesCount(Graph info); + + protected abstract Iterable findNodes(Graph info); + + protected abstract void findNodeProperties(Node node, Map props, Graph info); + + protected abstract Collection findBlockNodes(Graph info, Block block); + + protected abstract int findBlockId(Block sux); + + protected abstract Collection findBlocks(Graph graph); + + protected abstract Collection 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 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 properties) throws IOException { + writeProperties(graph, properties); + writeNodes(graph); + writeBlocks(findBlocks(graph), graph); + } + + private void writeNodes(Graph info) throws IOException { + Map 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 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 blocks, Graph info) throws IOException { + if (blocks != null) { + for (Block block : blocks) { + Collection nodes = findBlockNodes(info, block); + if (nodes == null) { + writeInt(0); + return; + } + } + writeInt(blocks.size()); + for (Block block : blocks) { + Collection nodes = findBlockNodes(info, block); + writeInt(findBlockId(block)); + writeInt(nodes.size()); + for (Node node : nodes) { + writeInt(findNodeId(node)); + } + final Collection 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 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 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 { + + private final LinkedList 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 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; + } + } + +}