javaastviewer.js revision 802:235d22ccfd24
1169689Skan#// Usage: jjs -fx javaastviewer.js -- <.java files> 2169689Skan 3169689Skan/* 4169689Skan * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 5169689Skan * 6169689Skan * Redistribution and use in source and binary forms, with or without 7169689Skan * modification, are permitted provided that the following conditions 8169689Skan * are met: 9169689Skan * 10169689Skan * - Redistributions of source code must retain the above copyright 11169689Skan * notice, this list of conditions and the following disclaimer. 12169689Skan * 13169689Skan * - Redistributions in binary form must reproduce the above copyright 14169689Skan * notice, this list of conditions and the following disclaimer in the 15169689Skan * documentation and/or other materials provided with the distribution. 16169689Skan * 17169689Skan * - Neither the name of Oracle nor the names of its 18169689Skan * contributors may be used to endorse or promote products derived 19169689Skan * from this software without specific prior written permission. 20169689Skan * 21169689Skan * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 22169689Skan * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 23169689Skan * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 24169689Skan * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 25169689Skan * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26169689Skan * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27169689Skan * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 28169689Skan * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 29169689Skan * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 30169689Skan * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 31169689Skan * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32169689Skan */ 33169689Skan 34169689Skan// This example demonstrates Java subclassing by Java.extend 35169689Skan// and javac Compiler and Tree API. This example also uses 36169689Skan// -fx and javafx TreeView to visualize Java AST as TreeView 37169689Skan 38169689Skanif (!$OPTIONS._fx || arguments.length == 0) { 39169689Skan print("Usage: jjs -fx javaastviewer.js -- <.java files>"); 40169689Skan exit(1); 41169689Skan} 42169689Skan 43169689Skan// Java types used 44169689Skanvar Enum = Java.type("java.lang.Enum"); 45169689Skanvar HashSet = Java.type("java.util.HashSet"); 46169689Skanvar Name = Java.type("javax.lang.model.element.Name"); 47169689Skanvar List = Java.type("java.util.List"); 48169689Skanvar Set = Java.type("java.util.Set"); 49169689Skanvar SimpleTreeVisitor = Java.type("com.sun.source.util.SimpleTreeVisitor"); 50169689Skanvar StringArray = Java.type("java.lang.String[]"); 51169689Skanvar ToolProvider = Java.type("javax.tools.ToolProvider"); 52169689Skanvar Tree = Java.type("com.sun.source.tree.Tree"); 53169689Skan 54169689Skanfunction javaASTToScriptObject(args) { 55169689Skan // properties ignored (javac implementation class properties) in AST view. 56169689Skan // may not be exhaustive - any getAbc would become "abc" property or 57169689Skan // public field becomes a property of same name. 58169689Skan var ignoredProps = new HashSet(); 59169689Skan for each (var word in 60169689Skan ['extending', 'implementing', 'init', 'mods', 'clazz', 'defs', 61169689Skan 'expr', 'tag', 'preferredPosition', 'qualid', 'recvparam', 62169689Skan 'restype', 'params', 'startPosition', 'thrown', 63169689Skan 'tree', 'typarams', 'typetag', 'vartype']) { 64169689Skan ignoredProps.add(word); 65169689Skan } 66169689Skan 67169689Skan // get the system compiler tool 68169689Skan var compiler = ToolProvider.systemJavaCompiler; 69169689Skan 70169689Skan // get standard file manager 71169689Skan var fileMgr = compiler.getStandardFileManager(null, null, null); 72169689Skan 73169689Skan // make a list of compilation unit from command line argument file names 74169689Skan // Using Java.to convert script array (arguments) to a Java String[] 75169689Skan var compUnits = fileMgr.getJavaFileObjects(Java.to(args, StringArray)); 76169689Skan 77169689Skan // create a new compilation task 78169689Skan var task = compiler.getTask(null, fileMgr, null, null, null, compUnits); 79169689Skan 80169689Skan // subclass SimpleTreeVisitor - converts Java AST node to 81169689Skan // a simple script object by walking through it 82169689Skan var ConverterVisitor = Java.extend(SimpleTreeVisitor); 83169689Skan 84169689Skan var visitor = new ConverterVisitor() { 85169689Skan // convert java AST node to a friendly script object 86169689Skan // which can be viewed. Every node ends up in defaultAction 87169689Skan // method of SimpleTreeVisitor method. 88169689Skan 89169689Skan defaultAction: function (node, p) { 90169689Skan var resultObj = {}; 91169689Skan // Nashorn does not iterate properties and methods of Java objects 92169689Skan // But, we can bind properties of any object (including java objects) 93169689Skan // to a script object and iterate it! 94169689Skan var obj = {}; 95169689Skan Object.bindProperties(obj, node); 96169689Skan 97169689Skan // we don't want every property, method of java object 98169689Skan for (var prop in obj) { 99169689Skan var val = obj[prop]; 100169689Skan var type = typeof val; 101169689Skan // ignore 'method' members 102169689Skan if (type == 'function' || type == 'undefined') { 103169689Skan continue; 104169689Skan } 105169689Skan 106169689Skan // ignore properties from Javac implementation 107169689Skan // classes - hack by name!! 108169689Skan if (ignoredProps.contains(prop)) { 109169689Skan continue; 110169689Skan } 111169689Skan 112169689Skan // subtree - recurse it 113169689Skan if (val instanceof Tree) { 114169689Skan resultObj[prop] = visitor.visit(val, p); 115169689Skan } else if (val instanceof List) { 116169689Skan // List of trees - recurse each and make an array 117169689Skan var len = val.size(); 118169689Skan if (len != 0) { 119169689Skan var arr = []; 120169689Skan for (var j = 0; j < len; j++) { 121169689Skan var e = val[j]; 122169689Skan if (e instanceof Tree) { 123169689Skan arr.push(visitor.visit(e, p)); 124169689Skan } 125169689Skan } 126169689Skan resultObj[prop] = arr; 127169689Skan } 128169689Skan } else if (val instanceof Set) { 129169689Skan // Set - used for modifier flags 130169689Skan // make array 131169689Skan var len = val.size(); 132169689Skan if (len != 0) { 133169689Skan var arr = []; 134169689Skan for each (var e in val) { 135169689Skan if (e instanceof Enum || typeof e == 'string') { 136169689Skan arr.push(e.toString()); 137169689Skan } 138169689Skan } 139169689Skan resultObj[prop] = arr; 140169689Skan } 141169689Skan } else if (val instanceof Enum || val instanceof Name) { 142169689Skan // make string for any Enum or Name 143169689Skan resultObj[prop] = val.toString(); 144169689Skan } else if (type != 'object') { 145169689Skan // primitives 'as is' 146169689Skan resultObj[prop] = val; 147169689Skan } 148169689Skan } 149169689Skan return resultObj; 150169689Skan } 151169689Skan } 152169689Skan 153169689Skan // top level object with one property for each compilation unit 154169689Skan var scriptObj = {}; 155169689Skan for each (var cu in task.parse()) { 156169689Skan scriptObj[cu.sourceFile.name] = cu.accept(visitor, null); 157169689Skan } 158169689Skan 159169689Skan return scriptObj; 160169689Skan} 161169689Skan 162169689Skan// JavaFX classes used 163169689Skanvar StackPane = Java.type("javafx.scene.layout.StackPane"); 164169689Skanvar Scene = Java.type("javafx.scene.Scene"); 165169689Skanvar TreeItem = Java.type("javafx.scene.control.TreeItem"); 166169689Skanvar TreeView = Java.type("javafx.scene.control.TreeView"); 167169689Skan 168169689Skan// Create a javafx TreeItem to view a script object 169169689Skanfunction treeItemForObject(obj, name) { 170169689Skan var item = new TreeItem(name); 171169689Skan for (var prop in obj) { 172169689Skan var node = obj[prop]; 173169689Skan if (typeof node == 'object') { 174169689Skan if (node == null) { 175169689Skan // skip nulls 176169689Skan continue; 177169689Skan } 178169689Skan var subitem = treeItemForObject(node, prop); 179169689Skan } else { 180169689Skan var subitem = new TreeItem(prop + ": " + node); 181169689Skan } 182169689Skan item.children.add(subitem); 183169689Skan } 184169689Skan 185169689Skan item.expanded = true; 186169689Skan return item; 187169689Skan} 188169689Skan 189169689Skanvar commandArgs = arguments; 190169689Skan 191169689Skan// JavaFX start method 192169689Skanfunction start(stage) { 193169689Skan var obj = javaASTToScriptObject(commandArgs); 194169689Skan stage.title = "Java AST Viewer" 195169689Skan var rootItem = treeItemForObject(obj, "AST"); 196169689Skan rootItem.expanded = true; 197169689Skan var tree = new TreeView(rootItem); 198169689Skan var root = new StackPane(); 199169689Skan root.children.add(tree); 200169689Skan stage.scene = new Scene(root, 300, 450); 201169689Skan stage.show(); 202169689Skan} 203169689Skan