# HG changeset patch # User Andreas Woess # Date 1446037216 -3600 # Node ID 920c3ec54499868686083674de835629e755e5c7 # Parent 4db968103a7c4a8ce3020a2a3aa4ba7273e407a6 GraphPrintVisitor rewrite using javax.xml.stream diff -r 4db968103a7c -r 920c3ec54499 truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java Tue Oct 20 23:53:21 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java Wed Oct 28 14:00:16 2015 +0100 @@ -25,121 +25,168 @@ package com.oracle.truffle.api.nodes; import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; -import java.io.StringWriter; +import java.io.OutputStream; +import java.io.PrintStream; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import java.net.Socket; -import java.util.ArrayDeque; +import java.nio.charset.Charset; import java.util.ArrayList; -import java.util.Deque; import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.transform.OutputKeys; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerConfigurationException; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.stream.StreamResult; +import javax.xml.stream.FactoryConfigurationError; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; -import org.w3c.dom.Document; -import org.w3c.dom.Element; -import org.w3c.dom.NodeList; - +import com.oracle.truffle.api.TruffleOptions; import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind; /** * Utility class for creating output for the ideal graph visualizer. */ -public class GraphPrintVisitor { +public class GraphPrintVisitor implements Closeable { public static final String GraphVisualizerAddress = "127.0.0.1"; public static final int GraphVisualizerPort = 4444; + private static final XMLOutputFactory XML_OUTPUT_FACTORY = XMLOutputFactory.newInstance(); - private static DocumentBuilderFactory documentBuilderFactory; - private static TransformerFactory transformerFactory; + private Map nodeMap; + private List edgeList; + private Map prevNodeMap; + private int id; + private XMLStreamWriter xmlstream; + private OutputStream outputStream; + private int openGroupCount; + private int openGraphCount; + private String currentGraphName; + + private static class NodeElement { + private final int id; + private final Map properties; + + public NodeElement(int id) { + super(); + this.id = id; + this.properties = new LinkedHashMap<>(); + } + + public int getId() { + return id; + } - private Document dom; - private Map nodeMap; - private List edgeList; - private Map prevNodeMap; - private int id; - private Element graphDocument; - private Element graphElement; - private Element nodesElement; - private Element edgesElement; - private Deque groupElementStack; + public Map getProperties() { + return properties; + } + } + + private static class EdgeElement { + private final NodeElement from; + private final NodeElement to; + private final int index; + private final String label; + + public EdgeElement(NodeElement from, NodeElement to, int index, String label) { + this.from = from; + this.to = to; + this.index = index; + this.label = label; + } + + public NodeElement getFrom() { + return from; + } + + public NodeElement getTo() { + return to; + } + + public int getIndex() { + return index; + } + + public String getLabel() { + return label; + } + } public GraphPrintVisitor() { - DocumentBuilder db = newDocumentBuilder(); - dom = db.newDocument(); + this(new ByteArrayOutputStream()); + } - graphDocument = dom.createElement("graphDocument"); - dom.appendChild(graphDocument); - groupElementStack = new ArrayDeque<>(); + public GraphPrintVisitor(OutputStream outputStream) { + this.outputStream = outputStream; + try { + this.xmlstream = XML_OUTPUT_FACTORY.createXMLStreamWriter(outputStream); + this.xmlstream.writeStartDocument(); + this.xmlstream.writeStartElement("graphDocument"); + } catch (XMLStreamException | FactoryConfigurationError e) { + throw new RuntimeException(e); + } } public GraphPrintVisitor beginGroup(String groupName) { - Element parent = groupElement() != null ? groupElement() : graphDocument; + maybeEndGraph(); + openGroupCount++; + try { + xmlstream.writeStartElement("group"); + xmlstream.writeStartElement("properties"); - Element groupElement = dom.createElement("group"); - groupElementStack.push(groupElement); - parent.appendChild(groupElement); + if (!groupName.isEmpty()) { + // set group name + xmlstream.writeStartElement("p"); + xmlstream.writeAttribute("name", "name"); + xmlstream.writeCharacters(groupName); + xmlstream.writeEndElement(); + } - Element properties = dom.createElement("properties"); - groupElement.appendChild(properties); + xmlstream.writeEndElement(); // properties - if (!groupName.isEmpty()) { - // set group name - Element propName = dom.createElement("p"); - propName.setAttribute("name", "name"); - propName.setTextContent(groupName); - properties.appendChild(propName); + // forget old nodes + prevNodeMap = null; + nodeMap = new IdentityHashMap<>(); + edgeList = new ArrayList<>(); + + return this; + } catch (XMLStreamException e) { + throw new RuntimeException(e); } + } - // forget old nodes - nodeMap = prevNodeMap = null; - edgeList = null; + public GraphPrintVisitor endGroup() { + if (openGroupCount <= 0) { + throw new IllegalArgumentException("no open group"); + } + maybeEndGraph(); + openGroupCount--; + + try { + xmlstream.writeEndElement(); // group + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } return this; } - public GraphPrintVisitor endGroup() { - groupElementStack.pop(); - return this; - } - public GraphPrintVisitor beginGraph(String graphName) { - if (null == groupElement()) { + if (openGroupCount == 0) { beginGroup(graphName); } + maybeEndGraph(); + openGraphCount++; - graphElement = dom.createElement("graph"); - groupElement().appendChild(graphElement); - Element properties = dom.createElement("properties"); - graphElement.appendChild(properties); - nodesElement = dom.createElement("nodes"); - graphElement.appendChild(nodesElement); - edgesElement = dom.createElement("edges"); - graphElement.appendChild(edgesElement); - - // set graph name - Element propName = dom.createElement("p"); - propName.setAttribute("name", "name"); - propName.setTextContent(graphName); - properties.appendChild(propName); + this.currentGraphName = graphName; // save old nodes prevNodeMap = nodeMap; @@ -149,96 +196,161 @@ return this; } - private Element groupElement() { - return groupElementStack.peek(); + private void maybeEndGraph() { + if (openGraphCount > 0) { + endGraph(); + assert openGraphCount == 0; + } + } + + public GraphPrintVisitor endGraph() { + if (openGraphCount <= 0) { + throw new IllegalArgumentException("no open graph"); + } + openGraphCount--; + + try { + xmlstream.writeStartElement("graph"); + + xmlstream.writeStartElement("properties"); + + // set graph name + xmlstream.writeStartElement("p"); + xmlstream.writeAttribute("name", "name"); + xmlstream.writeCharacters(currentGraphName); + xmlstream.writeEndElement(); + + xmlstream.writeEndElement(); // properties + + xmlstream.writeStartElement("nodes"); + writeNodes(); + xmlstream.writeEndElement(); // nodes + + xmlstream.writeStartElement("edges"); + writeEdges(); + xmlstream.writeEndElement(); // edges + + xmlstream.writeEndElement(); // graph + + xmlstream.flush(); + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } + + return this; + } + + private void writeNodes() throws XMLStreamException { + for (NodeElement node : nodeMap.values()) { + xmlstream.writeStartElement("node"); + xmlstream.writeAttribute("id", String.valueOf(node.getId())); + + xmlstream.writeStartElement("properties"); + for (Map.Entry property : node.getProperties().entrySet()) { + xmlstream.writeStartElement("p"); + xmlstream.writeAttribute("name", property.getKey()); + xmlstream.writeCharacters(String.valueOf(property.getValue())); + xmlstream.writeEndElement(); // p + } + xmlstream.writeEndElement(); // properties + + xmlstream.writeEndElement(); // node + } + } + + private void writeEdges() throws XMLStreamException { + for (EdgeElement edge : edgeList) { + xmlstream.writeStartElement("edge"); + + xmlstream.writeAttribute("from", String.valueOf(edge.getFrom().getId())); + xmlstream.writeAttribute("to", String.valueOf(edge.getTo().getId())); + xmlstream.writeAttribute("index", String.valueOf(edge.getIndex())); + if (edge.getLabel() != null) { + xmlstream.writeAttribute("label", edge.getLabel()); + } + + xmlstream.writeEndElement(); // edge + } } @Override public String toString() { - if (null != dom) { - try { - Transformer tr = newTransformer(); - tr.setOutputProperty(OutputKeys.INDENT, "yes"); - tr.setOutputProperty(OutputKeys.METHOD, "xml"); - tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - - StringWriter strWriter = new StringWriter(); - tr.transform(new DOMSource(dom), new StreamResult(strWriter)); - return strWriter.toString(); - } catch (TransformerException e) { - e.printStackTrace(); - } + if (outputStream instanceof ByteArrayOutputStream) { + return new String(((ByteArrayOutputStream) outputStream).toByteArray(), Charset.forName("UTF-8")); } - return ""; + return super.toString(); } public void printToFile(File f) { - try { - Transformer tr = newTransformer(); - tr.setOutputProperty(OutputKeys.INDENT, "yes"); - tr.setOutputProperty(OutputKeys.METHOD, "xml"); - tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - - tr.transform(new DOMSource(dom), new StreamResult(new FileOutputStream(f))); - } catch (TransformerException | FileNotFoundException e) { - e.printStackTrace(); + close(); + if (outputStream instanceof ByteArrayOutputStream) { + try (OutputStream os = new FileOutputStream(f)) { + os.write(((ByteArrayOutputStream) outputStream).toByteArray()); + } catch (IOException e) { + e.printStackTrace(); + } } } public void printToSysout() { - try { - Transformer tr = newTransformer(); - tr.setOutputProperty(OutputKeys.INDENT, "yes"); - tr.setOutputProperty(OutputKeys.METHOD, "xml"); - tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); - - tr.transform(new DOMSource(dom), new StreamResult(System.out)); - } catch (TransformerException e) { - e.printStackTrace(); + close(); + if (outputStream instanceof ByteArrayOutputStream) { + PrintStream out = System.out; + out.println(toString()); } } public void printToNetwork(boolean ignoreErrors) { - try { - Transformer tr = newTransformer(); - tr.setOutputProperty(OutputKeys.METHOD, "xml"); - - Socket socket = new Socket(GraphVisualizerAddress, GraphVisualizerPort); - BufferedOutputStream stream = new BufferedOutputStream(socket.getOutputStream(), 0x4000); - tr.transform(new DOMSource(dom), new StreamResult(stream)); - } catch (TransformerException | IOException e) { + close(); + try (Socket socket = new Socket(GraphVisualizerAddress, GraphVisualizerPort); BufferedOutputStream os = new BufferedOutputStream(socket.getOutputStream(), 0x4000)) { + os.write(((ByteArrayOutputStream) outputStream).toByteArray()); + } catch (IOException e) { if (!ignoreErrors) { e.printStackTrace(); } } } - private String nextId() { - return String.valueOf(id++); + public void close() { + if (xmlstream == null) { + return; + } + try { + while (openGroupCount > 0) { + endGroup(); + } + + xmlstream.writeEndElement(); // graphDocument + xmlstream.writeEndDocument(); + xmlstream.flush(); + xmlstream = null; + } catch (XMLStreamException e) { + throw new RuntimeException(e); + } } - private String oldOrNextId(Object node) { + private int nextId() { + return id++; + } + + private int oldOrNextId(Object node) { if (null != prevNodeMap && prevNodeMap.containsKey(node)) { - Element nodeElem = prevNodeMap.get(node); - return nodeElem.getAttribute("id"); + NodeElement nodeElem = prevNodeMap.get(node); + return nodeElem.getId(); } else { return nextId(); } } - protected Element getElementByObject(Object op) { - return nodeMap.get(op); + protected NodeElement getElementByObject(Object obj) { + return nodeMap.get(obj); } protected void createElementForNode(Object node) { boolean exists = nodeMap.containsKey(node); if (!exists) { - Element nodeElem = dom.createElement("node"); - nodeElem.setAttribute("id", !exists ? oldOrNextId(node) : nextId()); - nodeMap.put(node, nodeElem); - Element properties = dom.createElement("properties"); - nodeElem.appendChild(properties); - nodesElement.appendChild(nodeElem); + int nodeId = !exists ? oldOrNextId(node) : nextId(); + nodeMap.put(node, new NodeElement(nodeId)); setNodeProperty(node, "name", node.getClass().getSimpleName().replaceFirst("Node$", "")); NodeInfo nodeInfo = node.getClass().getAnnotation(NodeInfo.class); @@ -256,33 +368,9 @@ } } - private Element getPropertyElement(Object node, String propertyName) { - Element nodeElem = getElementByObject(node); - Element propertiesElem = (Element) nodeElem.getElementsByTagName("properties").item(0); - if (propertiesElem == null) { - return null; - } - - NodeList propList = propertiesElem.getElementsByTagName("p"); - for (int i = 0; i < propList.getLength(); i++) { - Element p = (Element) propList.item(i); - if (propertyName.equals(p.getAttribute("name"))) { - return p; - } - } - return null; - } - protected void setNodeProperty(Object node, String propertyName, Object value) { - Element nodeElem = getElementByObject(node); - Element propElem = getPropertyElement(node, propertyName); - // if property exists, replace its value, otherwise create a new one - if (null == propElem) { - propElem = dom.createElement("p"); - propElem.setAttribute("name", propertyName); - nodeElem.getElementsByTagName("properties").item(0).appendChild(propElem); - } - propElem.setTextContent(String.valueOf(value)); + NodeElement nodeElem = getElementByObject(node); + nodeElem.getProperties().put(propertyName, value); } private void copyDebugProperties(Node node) { @@ -297,7 +385,7 @@ for (NodeFieldAccessor field : fields) { if (field.getKind() == NodeFieldKind.DATA) { String key = field.getName(); - if (getPropertyElement(node, key) == null) { + if (!getElementByObject(node).getProperties().containsKey(key)) { Object value = field.loadValue(node); setNodeProperty(node, key, value); } @@ -306,34 +394,25 @@ } protected void connectNodes(Object a, Object b, String label) { - if (getElementByObject(a) == null || getElementByObject(b) == null) { + NodeElement fromNode = getElementByObject(a); + NodeElement toNode = getElementByObject(b); + if (fromNode == null || toNode == null) { return; } - String fromId = getElementByObject(a).getAttribute("id"); - String toId = getElementByObject(b).getAttribute("id"); - // count existing to-edges int count = 0; - for (Element e : edgeList) { - if (e.getAttribute("to").equals(toId)) { + for (EdgeElement e : edgeList) { + if (e.getTo() == toNode) { ++count; } } - Element edgeElem = dom.createElement("edge"); - edgeElem.setAttribute("from", fromId); - edgeElem.setAttribute("to", toId); - edgeElem.setAttribute("index", String.valueOf(count)); - if (label != null) { - edgeElem.setAttribute("label", label); - } - edgesElement.appendChild(edgeElem); - edgeList.add(edgeElem); + edgeList.add(new EdgeElement(fromNode, toNode, count, label)); } public GraphPrintVisitor visit(Object node) { - if (null == graphElement) { + if (openGraphCount == 0) { beginGraph("truffle tree"); } @@ -343,7 +422,7 @@ } // respect node's custom handler - if (NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class) != null) { + if (!TruffleOptions.AOT && NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class) != null) { visit(node, createGraphPrintHandlerFromClass(NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class).handler())); } else if (NodeUtil.findAnnotation(node.getClass(), NullGraphPrintHandler.class) != null) { // ignore @@ -394,28 +473,6 @@ return nodes; } - private static DocumentBuilder newDocumentBuilder() { - try { - if (documentBuilderFactory == null) { - documentBuilderFactory = DocumentBuilderFactory.newInstance(); - } - return documentBuilderFactory.newDocumentBuilder(); - } catch (ParserConfigurationException ex) { - throw new RuntimeException(ex); - } - } - - private static Transformer newTransformer() { - try { - if (transformerFactory == null) { - transformerFactory = TransformerFactory.newInstance(); - } - return transformerFactory.newTransformer(); - } catch (TransformerConfigurationException ex) { - throw new RuntimeException(ex); - } - } - public class GraphPrintAdapter { public void createElementForNode(Object node) { @@ -449,17 +506,17 @@ public interface GraphPrintHandler { - void visit(Object node, GraphPrintAdapter gPrinter); + void visit(Object node, GraphPrintAdapter printer); } private static final class DefaultGraphPrintHandler implements GraphPrintHandler { - public void visit(Object node, GraphPrintAdapter gPrinter) { - gPrinter.createElementForNode(node); + public void visit(Object node, GraphPrintAdapter printer) { + printer.createElementForNode(node); if (node instanceof Node) { for (Map.Entry child : findNamedNodeChildren((Node) node).entrySet()) { - gPrinter.visit(child.getValue()); - gPrinter.connectNodes(node, child.getValue(), child.getKey()); + printer.visit(child.getValue()); + printer.connectNodes(node, child.getValue(), child.getKey()); } } } diff -r 4db968103a7c -r 920c3ec54499 truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java --- a/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Tue Oct 20 23:53:21 2015 +0200 +++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/NodeUtil.java Wed Oct 28 14:00:16 2015 +0100 @@ -421,9 +421,11 @@ if (clazz.getAnnotation(annotationClass) != null) { return clazz.getAnnotation(annotationClass); } else { - for (Class intf : clazz.getInterfaces()) { - if (intf.getAnnotation(annotationClass) != null) { - return intf.getAnnotation(annotationClass); + if (!TruffleOptions.AOT) { + for (Class intf : clazz.getInterfaces()) { + if (intf.getAnnotation(annotationClass) != null) { + return intf.getAnnotation(annotationClass); + } } } if (clazz.getSuperclass() != null) {