diff truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java @ 21951:9c8c0937da41

Moving all sources into truffle subdirectory
author Jaroslav Tulach <jaroslav.tulach@oracle.com>
date Wed, 17 Jun 2015 10:58:08 +0200
parents graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java@8dc73c226c63
children 5bc7f7b867ab
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/truffle/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java	Wed Jun 17 10:58:08 2015 +0200
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2012, 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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 com.oracle.truffle.api.nodes;
+
+import java.io.*;
+import java.lang.annotation.*;
+import java.net.*;
+import java.util.*;
+
+import javax.xml.parsers.*;
+import javax.xml.transform.*;
+import javax.xml.transform.dom.*;
+import javax.xml.transform.stream.*;
+
+import org.w3c.dom.*;
+
+import com.oracle.truffle.api.nodes.NodeFieldAccessor.NodeFieldKind;
+
+/**
+ * Utility class for creating output for the ideal graph visualizer.
+ */
+public class GraphPrintVisitor {
+
+    public static final String GraphVisualizerAddress = "127.0.0.1";
+    public static final int GraphVisualizerPort = 4444;
+
+    private Document dom;
+    private Map<Object, Element> nodeMap;
+    private List<Element> edgeList;
+    private Map<Object, Element> prevNodeMap;
+    private int id;
+    private Element graphDocument;
+    private Element groupElement;
+    private Element graphElement;
+    private Element nodesElement;
+    private Element edgesElement;
+
+    public GraphPrintVisitor() {
+        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
+        try {
+            DocumentBuilder db = dbf.newDocumentBuilder();
+
+            dom = db.newDocument();
+        } catch (ParserConfigurationException ex) {
+            throw new RuntimeException(ex);
+        }
+
+        graphDocument = dom.createElement("graphDocument");
+        dom.appendChild(graphDocument);
+    }
+
+    public GraphPrintVisitor beginGroup(String groupName) {
+        groupElement = dom.createElement("group");
+        graphDocument.appendChild(groupElement);
+        Element properties = dom.createElement("properties");
+        groupElement.appendChild(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
+        nodeMap = prevNodeMap = null;
+        edgeList = null;
+
+        return this;
+    }
+
+    public GraphPrintVisitor beginGraph(String graphName) {
+        if (null == groupElement) {
+            beginGroup("");
+        } else if (null != prevNodeMap) {
+            // TODO: difference (create removeNode,removeEdge elements)
+        }
+
+        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);
+
+        // save old nodes
+        prevNodeMap = nodeMap;
+        nodeMap = new IdentityHashMap<>();
+        edgeList = new ArrayList<>();
+
+        return this;
+    }
+
+    @Override
+    public String toString() {
+        if (null != dom) {
+            try {
+                Transformer tr = TransformerFactory.newInstance().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();
+            }
+        }
+        return "";
+    }
+
+    public void printToFile(File f) {
+        try {
+            Transformer tr = TransformerFactory.newInstance().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();
+        }
+    }
+
+    public void printToSysout() {
+        try {
+            Transformer tr = TransformerFactory.newInstance().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();
+        }
+    }
+
+    public void printToNetwork(boolean ignoreErrors) {
+        try {
+            Transformer tr = TransformerFactory.newInstance().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) {
+            if (!ignoreErrors) {
+                e.printStackTrace();
+            }
+        }
+    }
+
+    private String nextId() {
+        return String.valueOf(id++);
+    }
+
+    private String oldOrNextId(Object node) {
+        if (null != prevNodeMap && prevNodeMap.containsKey(node)) {
+            Element nodeElem = prevNodeMap.get(node);
+            return nodeElem.getAttribute("id");
+        } else {
+            return nextId();
+        }
+    }
+
+    protected Element getElementByObject(Object op) {
+        return nodeMap.get(op);
+    }
+
+    protected void createElementForNode(Object node) {
+        boolean exists = nodeMap.containsKey(node);
+        if (!exists || NodeUtil.findAnnotation(node.getClass(), GraphDuplicate.class) != null) {
+            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);
+
+            setNodeProperty(node, "name", node.getClass().getSimpleName().replaceFirst("Node$", ""));
+            NodeInfo nodeInfo = node.getClass().getAnnotation(NodeInfo.class);
+            if (nodeInfo != null) {
+                setNodeProperty(node, "cost", nodeInfo.cost());
+                if (!nodeInfo.shortName().isEmpty()) {
+                    setNodeProperty(node, "shortName", nodeInfo.shortName());
+                }
+            }
+            setNodeProperty(node, "class", node.getClass().getSimpleName());
+            if (node instanceof Node) {
+                readNodeProperties((Node) node);
+                copyDebugProperties((Node) node);
+            }
+        }
+    }
+
+    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
+        if (null == propElem) { // if property doesn't exist, create one
+            propElem = dom.createElement("p");
+            propElem.setAttribute("name", propertyName);
+            nodeElem.getElementsByTagName("properties").item(0).appendChild(propElem);
+        }
+        propElem.setTextContent(String.valueOf(value));
+    }
+
+    private void copyDebugProperties(Node node) {
+        Map<String, Object> debugProperties = node.getDebugProperties();
+        for (Map.Entry<String, Object> property : debugProperties.entrySet()) {
+            setNodeProperty(node, property.getKey(), property.getValue());
+        }
+    }
+
+    private void readNodeProperties(Node node) {
+        NodeFieldAccessor[] fields = node.getNodeClass().getFields();
+        for (NodeFieldAccessor field : fields) {
+            if (field.getKind() == NodeFieldKind.DATA) {
+                String key = field.getName();
+                if (getPropertyElement(node, key) == null) {
+                    Object value = field.loadValue(node);
+                    setNodeProperty(node, key, value);
+                }
+            }
+        }
+    }
+
+    protected void connectNodes(Object a, Object b, String label) {
+        if (nodeMap.get(a) == null || nodeMap.get(b) == null) {
+            return;
+        }
+
+        String fromId = nodeMap.get(a).getAttribute("id");
+        String toId = nodeMap.get(b).getAttribute("id");
+
+        // count existing to-edges
+        int count = 0;
+        for (Element e : edgeList) {
+            if (e.getAttribute("to").equals(toId)) {
+                ++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);
+    }
+
+    public GraphPrintVisitor visit(Object node) {
+        if (null == graphElement) {
+            beginGraph("truffle tree");
+        }
+
+        // if node is visited once again, skip
+        if (getElementByObject(node) != null && NodeUtil.findAnnotation(node.getClass(), GraphDuplicate.class) == null) {
+            return this;
+        }
+
+        // respect node's custom handler
+        if (NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class) != null) {
+            Class<? extends GraphPrintHandler> customHandlerClass = NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class).handler();
+            try {
+                GraphPrintHandler customHandler = customHandlerClass.newInstance();
+                customHandler.visit(node, new GraphPrintAdapter());
+            } catch (InstantiationException | IllegalAccessException e) {
+                assert false : e;
+            }
+        } else if (NodeUtil.findAnnotation(node.getClass(), NullGraphPrintHandler.class) != null) {
+            // ignore
+        } else {
+            // default handler
+            createElementForNode(node);
+
+            if (node instanceof Node) {
+                for (Map.Entry<String, Node> child : findNamedNodeChildren((Node) node).entrySet()) {
+                    visit(child.getValue());
+                    connectNodes(node, child.getValue(), child.getKey());
+                }
+            }
+        }
+
+        return this;
+    }
+
+    private static LinkedHashMap<String, Node> findNamedNodeChildren(Node node) {
+        LinkedHashMap<String, Node> nodes = new LinkedHashMap<>();
+        NodeClass nodeClass = node.getNodeClass();
+
+        for (NodeFieldAccessor field : nodeClass.getFields()) {
+            NodeFieldKind kind = field.getKind();
+            if (kind == NodeFieldKind.CHILD || kind == NodeFieldKind.CHILDREN) {
+                Object value = field.loadValue(node);
+                if (value != null) {
+                    if (kind == NodeFieldKind.CHILD) {
+                        nodes.put(field.getName(), (Node) value);
+                    } else if (kind == NodeFieldKind.CHILDREN) {
+                        Object[] children = (Object[]) value;
+                        for (int i = 0; i < children.length; i++) {
+                            if (children[i] != null) {
+                                nodes.put(field.getName() + "[" + i + "]", (Node) children[i]);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        return nodes;
+    }
+
+    public class GraphPrintAdapter {
+
+        public void createElementForNode(Object node) {
+            GraphPrintVisitor.this.createElementForNode(node);
+        }
+
+        public void visit(Object node) {
+            GraphPrintVisitor.this.visit(node);
+        }
+
+        public void connectNodes(Object node, Object child) {
+            GraphPrintVisitor.this.connectNodes(node, child, null);
+        }
+
+        public void setNodeProperty(Object node, String propertyName, Object value) {
+            GraphPrintVisitor.this.setNodeProperty(node, propertyName, value);
+        }
+    }
+
+    public interface GraphPrintHandler {
+
+        void visit(Object node, GraphPrintAdapter gPrinter);
+    }
+
+    public interface ChildSupplier {
+
+        /** Supplies an additional child if available. */
+        Object startNode(Object callNode);
+
+        void endNode(Object callNode);
+
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    public @interface CustomGraphPrintHandler {
+
+        Class<? extends GraphPrintHandler> handler();
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    public @interface NullGraphPrintHandler {
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.TYPE)
+    public @interface GraphDuplicate {
+    }
+
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.FIELD)
+    public @interface HiddenField {
+    }
+}