comparison graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/GraphPrintVisitor.java @ 7267:a4b84ba6dc2e

Introduction of the Truffle API for efficient implementation of dynamic languages on top of the Graal VM. New projects com.oracle.truffle.api for the API definition and com.oracle.truffle.api.test for API tests and documentation.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Tue, 18 Dec 2012 15:33:55 +0100
parents
children 5e3d1a68664e
comparison
equal deleted inserted replaced
7259:494d99e07614 7267:a4b84ba6dc2e
1 /*
2 * Copyright (c) 2012, 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 package com.oracle.truffle.api.nodes;
24
25 import java.io.*;
26 import java.lang.annotation.*;
27 import java.lang.reflect.*;
28 import java.net.*;
29 import java.util.*;
30
31 import javax.xml.parsers.*;
32 import javax.xml.transform.*;
33 import javax.xml.transform.dom.*;
34 import javax.xml.transform.stream.*;
35
36 import org.w3c.dom.*;
37
38 /**
39 * Utility class for creating output for the ideal graph visualizer.
40 */
41 public class GraphPrintVisitor {
42
43 public static final String GraphVisualizerAddress = "127.0.0.1";
44 public static final int GraphVisualizerPort = 4444;
45
46 private Document dom;
47 private Map<Object, Element> nodeMap;
48 private List<Element> edgeList;
49 private Map<Object, Element> prevNodeMap;
50 private int id;
51 private Element graphDocument;
52 private Element groupElement;
53 private Element graphElement;
54 private Element nodesElement;
55 private Element edgesElement;
56
57 public GraphPrintVisitor() {
58 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
59 try {
60 DocumentBuilder db = dbf.newDocumentBuilder();
61
62 dom = db.newDocument();
63 } catch (ParserConfigurationException ex) {
64 System.out.println("Error while trying to instantiate DocumentBuilder " + ex);
65 }
66
67 graphDocument = dom.createElement("graphDocument");
68 dom.appendChild(graphDocument);
69 }
70
71 public GraphPrintVisitor beginGroup(String groupName) {
72 groupElement = dom.createElement("group");
73 graphDocument.appendChild(groupElement);
74 Element properties = dom.createElement("properties");
75 groupElement.appendChild(properties);
76
77 if (!groupName.isEmpty()) {
78 // set group name
79 Element propName = dom.createElement("p");
80 propName.setAttribute("name", "name");
81 propName.setTextContent(groupName);
82 properties.appendChild(propName);
83 }
84
85 // forget old nodes
86 nodeMap = prevNodeMap = null;
87 edgeList = null;
88
89 return this;
90 }
91
92 public GraphPrintVisitor beginGraph(String graphName) {
93 if (null == groupElement) {
94 beginGroup("");
95 } else if (null != prevNodeMap) {
96 // TODO: difference (create removeNode,removeEdge elements)
97 }
98
99 graphElement = dom.createElement("graph");
100 groupElement.appendChild(graphElement);
101 Element properties = dom.createElement("properties");
102 graphElement.appendChild(properties);
103 nodesElement = dom.createElement("nodes");
104 graphElement.appendChild(nodesElement);
105 edgesElement = dom.createElement("edges");
106 graphElement.appendChild(edgesElement);
107
108 // set graph name
109 Element propName = dom.createElement("p");
110 propName.setAttribute("name", "name");
111 propName.setTextContent(graphName);
112 properties.appendChild(propName);
113
114 // save old nodes
115 prevNodeMap = nodeMap;
116 nodeMap = new HashMap<>();
117 edgeList = new ArrayList<>();
118
119 return this;
120 }
121
122 @Override
123 public String toString() {
124 if (null != dom) {
125 try {
126 Transformer tr = TransformerFactory.newInstance().newTransformer();
127 tr.setOutputProperty(OutputKeys.INDENT, "yes");
128 tr.setOutputProperty(OutputKeys.METHOD, "xml");
129 tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
130
131 StringWriter strWriter = new StringWriter();
132 tr.transform(new DOMSource(dom), new StreamResult(strWriter));
133 return strWriter.toString();
134 } catch (TransformerException e) {
135 e.printStackTrace();
136 }
137 }
138 return "";
139 }
140
141 public void printToFile(File f) {
142 try {
143 Transformer tr = TransformerFactory.newInstance().newTransformer();
144 tr.setOutputProperty(OutputKeys.INDENT, "yes");
145 tr.setOutputProperty(OutputKeys.METHOD, "xml");
146 tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
147
148 tr.transform(new DOMSource(dom), new StreamResult(new FileOutputStream(f)));
149 } catch (TransformerException | FileNotFoundException e) {
150 e.printStackTrace();
151 }
152 }
153
154 public void printToSysout() {
155 try {
156 Transformer tr = TransformerFactory.newInstance().newTransformer();
157 tr.setOutputProperty(OutputKeys.INDENT, "yes");
158 tr.setOutputProperty(OutputKeys.METHOD, "xml");
159 tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
160
161 tr.transform(new DOMSource(dom), new StreamResult(System.out));
162 } catch (TransformerException e) {
163 e.printStackTrace();
164 }
165 }
166
167 public void printToNetwork() {
168 try {
169 Transformer tr = TransformerFactory.newInstance().newTransformer();
170 tr.setOutputProperty(OutputKeys.METHOD, "xml");
171
172 Socket socket = new Socket(GraphVisualizerAddress, GraphVisualizerPort);
173 BufferedOutputStream stream = new BufferedOutputStream(socket.getOutputStream(), 0x4000);
174 tr.transform(new DOMSource(dom), new StreamResult(stream));
175 } catch (TransformerException | IOException e) {
176 e.printStackTrace();
177 }
178 }
179
180 private String nextId() {
181 return String.valueOf(id++);
182 }
183
184 private String oldOrNextId(Object node) {
185 if (null != prevNodeMap && prevNodeMap.containsKey(node)) {
186 Element nodeElem = prevNodeMap.get(node);
187 return nodeElem.getAttribute("id");
188 } else {
189 return nextId();
190 }
191 }
192
193 protected Element getElementByObject(Object op) {
194 return nodeMap.get(op);
195 }
196
197 protected void createElementForNode(Object node) {
198 boolean exists = nodeMap.containsKey(node);
199 if (!exists || NodeUtil.findAnnotation(node.getClass(), GraphDuplicate.class) != null) {
200 Element nodeElem = dom.createElement("node");
201 nodeElem.setAttribute("id", !exists ? oldOrNextId(node) : nextId());
202 nodeMap.put(node, nodeElem);
203 Element properties = dom.createElement("properties");
204 nodeElem.appendChild(properties);
205 nodesElement.appendChild(nodeElem);
206
207 setNodeProperty(node, "name", node.getClass().getSimpleName().replaceFirst("Node$", ""));
208 NodeInfo nodeInfo = node.getClass().getAnnotation(NodeInfo.class);
209 if (nodeInfo != null && !nodeInfo.shortName().isEmpty()) {
210 setNodeProperty(node, "shortName", nodeInfo.shortName());
211 }
212 setNodeProperty(node, "nodeType", (Node.class.isAssignableFrom(node.getClass()) ? Node.class.getSimpleName() : "other"));
213 setNodeProperty(node, "nodeClass", node.getClass().getSimpleName());
214 copyDebugProperties(node); // TODO: may overwrite property "name"? (currently allowed)
215 readNodeProperties(node);
216 }
217 }
218
219 private Element getPropertyElement(Object node, String propertyName) {
220 Element nodeElem = getElementByObject(node);
221 Element propertiesElem = (Element) nodeElem.getElementsByTagName("properties").item(0);
222 if (propertiesElem == null) {
223 return null;
224 }
225
226 NodeList propList = propertiesElem.getElementsByTagName("p");
227 for (int i = 0; i < propList.getLength(); i++) {
228 Element p = (Element) propList.item(i);
229 if (propertyName.equals(p.getAttribute("name"))) {
230 return p;
231 }
232 }
233 return null;
234 }
235
236 protected void setNodeProperty(Object node, String propertyName, Object value) {
237 Element nodeElem = getElementByObject(node);
238 Element propElem = getPropertyElement(node, propertyName); // if property exists, replace its value
239 if (null == propElem) { // if property doesn't exist, create one
240 propElem = dom.createElement("p");
241 propElem.setAttribute("name", propertyName);
242 nodeElem.getElementsByTagName("properties").item(0).appendChild(propElem);
243 }
244 propElem.setTextContent(String.valueOf(value));
245 }
246
247 private void copyDebugProperties(Object node) {
248 if (node instanceof Node) {
249 Map<String, Object> debugProperties = ((Node) node).getDebugProperties();
250 for (Map.Entry<String, Object> property : debugProperties.entrySet()) {
251 setNodeProperty(node, property.getKey(), property.getValue());
252 }
253 }
254 }
255
256 private void readNodeProperties(Object node) {
257 Field[] fields = NodeUtil.getAllFields(node.getClass());
258 for (Field field : fields) {
259 if (Modifier.isStatic(field.getModifiers())) {
260 continue;
261 }
262 if (Node.class.isAssignableFrom(field.getType()) || (field.getType().getComponentType() != null && Node.class.isAssignableFrom(field.getType()))) {
263 continue;
264 }
265 String key = field.getName();
266 if (field.getAnnotation(HiddenField.class) == null && getPropertyElement(node, key) == null) {
267 try {
268 field.setAccessible(true);
269 Object value = field.get(node);
270 setNodeProperty(node, key, value);
271 } catch (IllegalArgumentException | IllegalAccessException e) {
272 assert false : e;
273 }
274 }
275 }
276 }
277
278 protected void connectNodes(Object a, Object b) {
279 if (nodeMap.get(a) == null || nodeMap.get(b) == null) {
280 return;
281 }
282
283 String fromId = nodeMap.get(a).getAttribute("id");
284 String toId = nodeMap.get(b).getAttribute("id");
285
286 // count existing to-edges
287 int count = 0;
288 for (Element e : edgeList) {
289 if (e.getAttribute("to").equals(toId)) {
290 ++count;
291 }
292 }
293
294 Element edgeElem = dom.createElement("edge");
295 edgeElem.setAttribute("from", fromId);
296 edgeElem.setAttribute("to", toId);
297 edgeElem.setAttribute("index", String.valueOf(count));
298 edgesElement.appendChild(edgeElem);
299 edgeList.add(edgeElem);
300 }
301
302 public GraphPrintVisitor visit(Object node) {
303 if (null == graphElement) {
304 beginGraph("truffle tree");
305 }
306
307 // if node is visited once again, skip
308 if (getElementByObject(node) == null || NodeUtil.findAnnotation(node.getClass(), GraphDuplicate.class) != null) {
309 visitAny(node);
310 }
311
312 return this;
313 }
314
315 private void visitAny(Object node) {
316 // respect node's custom handler
317 if (NodeUtil.findAnnotation(node.getClass(), NullGraphPrintHandler.class) != null) {
318 return;
319 }
320 if (NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class) != null) {
321 Class<? extends GraphPrintHandler> gpHandlerClass = NodeUtil.findAnnotation(node.getClass(), CustomGraphPrintHandler.class).handler();
322 try {
323 GraphPrintHandler gpHandler = gpHandlerClass.newInstance();
324 gpHandler.visit(node, new GraphPrintAdapter());
325 } catch (InstantiationException e) {
326 assert false;
327 } catch (IllegalAccessException e) {
328 assert false;
329 }
330 return;
331 }
332
333 // default handler
334 createElementForNode(node);
335
336 List<Object> children = NodeUtil.findNodeChildren(node);
337 for (Object child : children) {
338 if (child == null) {
339 continue;
340 } else if (child instanceof Node) {
341 visit(child);
342 } else {
343 continue;
344 }
345 connectNodes(node, child);
346 }
347 }
348
349 public class GraphPrintAdapter {
350 public void createElementForNode(Object node) {
351 GraphPrintVisitor.this.createElementForNode(node);
352 }
353 public void visit(Object node) {
354 GraphPrintVisitor.this.visit(node);
355 }
356 public void connectNodes(Object node, Object child) {
357 GraphPrintVisitor.this.connectNodes(node, child);
358 }
359 public void setNodeProperty(Object node, String propertyName, Object value) {
360 GraphPrintVisitor.this.setNodeProperty(node, propertyName, value);
361 }
362 }
363
364 public interface GraphPrintHandler {
365 void visit(Object node, GraphPrintAdapter gPrinter);
366 }
367
368 @Retention(RetentionPolicy.RUNTIME)
369 @Target(ElementType.TYPE)
370 public @interface CustomGraphPrintHandler {
371 Class<? extends GraphPrintHandler> handler();
372 }
373 @Retention(RetentionPolicy.RUNTIME)
374 @Target(ElementType.TYPE)
375 public @interface NullGraphPrintHandler {
376 }
377 @Retention(RetentionPolicy.RUNTIME)
378 @Target(ElementType.TYPE)
379 public @interface GraphDuplicate {
380 }
381 @Retention(RetentionPolicy.RUNTIME)
382 @Target(ElementType.FIELD)
383 public @interface HiddenField {
384 }
385 }