| /* |
| * Copyright (c) 2013, 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. |
| */ |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.PrintWriter; |
| import java.lang.reflect.Field; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.EnumSet; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.lang.model.element.Name; |
| import javax.lang.model.element.TypeElement; |
| import javax.tools.FileObject; |
| import javax.tools.JavaCompiler; |
| import javax.tools.JavaFileObject; |
| import javax.tools.StandardJavaFileManager; |
| import javax.tools.StandardLocation; |
| import javax.tools.ToolProvider; |
| |
| import com.sun.source.util.JavacTask; |
| import com.sun.source.util.TaskEvent; |
| import com.sun.source.util.TaskListener; |
| import com.sun.source.util.Trees; |
| import com.sun.tools.javac.api.JavacTrees; |
| import com.sun.tools.javac.code.SymbolMetadata; |
| import com.sun.tools.javac.code.Attribute; |
| import com.sun.tools.javac.code.Flags; |
| import com.sun.tools.javac.code.Kinds; |
| import com.sun.tools.javac.code.Printer; |
| import com.sun.tools.javac.code.Scope; |
| import com.sun.tools.javac.code.Scope.CompoundScope; |
| import com.sun.tools.javac.code.Symbol; |
| import com.sun.tools.javac.code.Symbol.*; |
| import com.sun.tools.javac.code.Type; |
| import com.sun.tools.javac.code.Type.*; |
| import com.sun.tools.javac.code.TypeTag; |
| import com.sun.tools.javac.tree.JCTree; |
| import com.sun.tools.javac.tree.JCTree.*; |
| import com.sun.tools.javac.tree.Pretty; |
| import com.sun.tools.javac.tree.TreeInfo; |
| import com.sun.tools.javac.tree.TreeScanner; |
| import com.sun.tools.javac.util.Assert; |
| import com.sun.tools.javac.util.Context; |
| import com.sun.tools.javac.util.Log; |
| |
| |
| /** |
| * Debug printer for javac internals, for when toString() just isn't enough. |
| * |
| * <p> |
| * The printer provides an API to generate structured views of javac objects, |
| * such as AST nodes, symbol, types and annotations. Various aspects of the |
| * output can be configured, such as whether to show nulls, empty lists, or |
| * a compressed representation of the source code. Visitors are used to walk |
| * object hierarchies, and can be replaced with custom visitors if the default |
| * visitors are not flexible enough. |
| * |
| * <p> |
| * In general, nodes are printed with an initial line identifying the node |
| * followed by indented lines for the child nodes. Currently, graphs are |
| * represented by printing a spanning subtree. |
| * |
| * <p> |
| * The printer can be accessed via a simple command-line utility, |
| * which makes it easy to see the internal representation of source code, |
| * such as simple test programs, during the compilation pipeline. |
| * |
| * <p><b>This is NOT part of any supported API. |
| * If you write code that depends on this, you do so at your own risk. |
| * This code and its internal interfaces are subject to change or |
| * deletion without notice.</b> |
| */ |
| |
| public class DPrinter { |
| protected final PrintWriter out; |
| protected final Trees trees; |
| protected Printer printer; |
| protected boolean showEmptyItems = true; |
| protected boolean showNulls = true; |
| protected boolean showPositions = false; |
| protected boolean showSrc; |
| protected boolean showTreeSymbols; |
| protected boolean showTreeTypes; |
| protected int maxSrcLength = 32; |
| protected Locale locale = Locale.getDefault(); |
| protected static final String NULL = "#null"; |
| |
| // <editor-fold defaultstate="collapsed" desc="Configuration"> |
| |
| public static DPrinter instance(Context context) { |
| DPrinter dp = context.get(DPrinter.class); |
| if (dp == null) { |
| dp = new DPrinter(context); |
| } |
| return dp; |
| |
| } |
| |
| protected DPrinter(Context context) { |
| context.put(DPrinter.class, this); |
| out = context.get(Log.outKey); |
| trees = JavacTrees.instance(context); |
| } |
| |
| public DPrinter(PrintWriter out, Trees trees) { |
| this.out = out; |
| this.trees = trees; |
| } |
| |
| public DPrinter emptyItems(boolean showEmptyItems) { |
| this.showEmptyItems = showEmptyItems; |
| return this; |
| } |
| |
| public DPrinter nulls(boolean showNulls) { |
| this.showNulls = showNulls; |
| return this; |
| } |
| |
| public DPrinter positions(boolean showPositions) { |
| this.showPositions = showPositions; |
| return this; |
| } |
| |
| public DPrinter source(boolean showSrc) { |
| this.showSrc = showSrc; |
| return this; |
| } |
| |
| public DPrinter source(int maxSrcLength) { |
| this.showSrc = true; |
| this.maxSrcLength = maxSrcLength; |
| return this; |
| } |
| |
| public DPrinter treeSymbols(boolean showTreeSymbols) { |
| this.showTreeSymbols = showTreeSymbols; |
| return this; |
| } |
| |
| public DPrinter treeTypes(boolean showTreeTypes) { |
| this.showTreeTypes = showTreeTypes; |
| return this; |
| } |
| |
| public DPrinter typeSymbolPrinter(Printer p) { |
| printer = p; |
| return this; |
| } |
| |
| // </editor-fold> |
| |
| // <editor-fold defaultstate="collapsed" desc="Printing"> |
| |
| protected enum Details { |
| /** A one-line non-recursive summary */ |
| SUMMARY, |
| /** Multi-line, possibly recursive. */ |
| FULL |
| }; |
| |
| public void printAnnotations(String label, SymbolMetadata annotations) { |
| printAnnotations(label, annotations, Details.FULL); |
| } |
| |
| protected void printAnnotations(String label, SymbolMetadata annotations, Details details) { |
| if (annotations == null) { |
| printNull(label); |
| } else { |
| // no SUMMARY format currently available to use |
| |
| // use reflection to get at private fields |
| Object DECL_NOT_STARTED = getField(null, SymbolMetadata.class, "DECL_NOT_STARTED"); |
| Object DECL_IN_PROGRESS = getField(null, SymbolMetadata.class, "DECL_IN_PROGRESS"); |
| Object attributes = getField(annotations, SymbolMetadata.class, "attributes"); |
| Object type_attributes = getField(annotations, SymbolMetadata.class, "type_attributes"); |
| |
| if (!showEmptyItems) { |
| if (attributes instanceof List && ((List) attributes).isEmpty() |
| && attributes != DECL_NOT_STARTED |
| && attributes != DECL_IN_PROGRESS |
| && type_attributes instanceof List && ((List) type_attributes).isEmpty()) |
| return; |
| } |
| |
| printString(label, hashString(annotations)); |
| |
| indent(+1); |
| if (attributes == DECL_NOT_STARTED) |
| printString("attributes", "DECL_NOT_STARTED"); |
| else if (attributes == DECL_IN_PROGRESS) |
| printString("attributes", "DECL_IN_PROGRESS"); |
| else if (attributes instanceof List) |
| printList("attributes", (List) attributes); |
| else |
| printObject("attributes", attributes, Details.SUMMARY); |
| |
| if (attributes instanceof List) |
| printList("type_attributes", (List) type_attributes); |
| else |
| printObject("type_attributes", type_attributes, Details.SUMMARY); |
| indent(-1); |
| } |
| } |
| |
| public void printAttribute(String label, Attribute attr) { |
| if (attr == null) { |
| printNull(label); |
| } else { |
| printString(label, attr.getClass().getSimpleName()); |
| |
| indent(+1); |
| attr.accept(attrVisitor); |
| indent(-1); |
| } |
| } |
| |
| public void printFileObject(String label, FileObject fo) { |
| if (fo == null) { |
| printNull(label); |
| } else { |
| printString(label, fo.getName()); |
| } |
| } |
| |
| protected <T> void printImplClass(T item, Class<? extends T> stdImplClass) { |
| if (item.getClass() != stdImplClass) |
| printString("impl", item.getClass().getName()); |
| } |
| |
| public void printInt(String label, int i) { |
| printString(label, String.valueOf(i)); |
| } |
| |
| public void printList(String label, List<?> list) { |
| if (list == null) { |
| printNull(label); |
| } else if (!list.isEmpty() || showEmptyItems) { |
| printString(label, "[" + list.size() + "]"); |
| |
| indent(+1); |
| int i = 0; |
| for (Object item: list) { |
| printObject(String.valueOf(i++), item, Details.FULL); |
| } |
| indent(-1); |
| } |
| } |
| |
| public void printName(String label, Name name) { |
| if (name == null) { |
| printNull(label); |
| } else { |
| printString(label, name.toString()); |
| } |
| } |
| |
| public void printNull(String label) { |
| if (showNulls) |
| printString(label, NULL); |
| } |
| |
| protected void printObject(String label, Object item, Details details) { |
| if (item == null) { |
| printNull(label); |
| } else if (item instanceof Attribute) { |
| printAttribute(label, (Attribute) item); |
| } else if (item instanceof Symbol) { |
| printSymbol(label, (Symbol) item, details); |
| } else if (item instanceof Type) { |
| printType(label, (Type) item, details); |
| } else if (item instanceof JCTree) { |
| printTree(label, (JCTree) item); |
| } else if (item instanceof List) { |
| printList(label, (List) item); |
| } else if (item instanceof Name) { |
| printName(label, (Name) item); |
| } else { |
| printString(label, String.valueOf(item)); |
| } |
| } |
| |
| public void printScope(String label, Scope scope) { |
| printScope(label, scope, Details.FULL); |
| } |
| |
| public void printScope(String label, Scope scope, Details details) { |
| if (scope == null) { |
| printNull(label); |
| } else { |
| switch (details) { |
| case SUMMARY: { |
| indent(); |
| out.print(label); |
| out.print(": ["); |
| String sep = ""; |
| for (Symbol sym: scope.getElements()) { |
| out.print(sep); |
| out.print(sym.name); |
| sep = ","; |
| } |
| out.println("]"); |
| break; |
| } |
| |
| case FULL: { |
| indent(); |
| out.println(label); |
| |
| indent(+1); |
| printImplClass(scope, Scope.class); |
| printSymbol("owner", scope.owner, Details.SUMMARY); |
| printScope("next", scope.next, Details.SUMMARY); |
| printObject("shared", getField(scope, Scope.class, "shared"), Details.SUMMARY); |
| if (scope instanceof CompoundScope) { |
| printObject("subScopes", |
| getField(scope, CompoundScope.class, "subScopes"), |
| Details.FULL); |
| } else { |
| for (Symbol sym : scope.getElements()) { |
| printSymbol(sym.name.toString(), sym, Details.SUMMARY); |
| } |
| } |
| indent(-1); |
| break; |
| } |
| } |
| } |
| } |
| |
| public void printSource(String label, JCTree tree) { |
| printString(label, Pretty.toSimpleString(tree, maxSrcLength)); |
| } |
| |
| public void printString(String label, String text) { |
| indent(); |
| out.print(label); |
| out.print(": "); |
| out.print(text); |
| out.println(); |
| } |
| |
| public void printSymbol(String label, Symbol symbol) { |
| printSymbol(label, symbol, Details.FULL); |
| } |
| |
| protected void printSymbol(String label, Symbol sym, Details details) { |
| if (sym == null) { |
| printNull(label); |
| } else { |
| switch (details) { |
| case SUMMARY: |
| printString(label, toString(sym)); |
| break; |
| |
| case FULL: |
| indent(); |
| out.print(label); |
| out.println(": " + |
| info(sym.getClass(), |
| String.format("0x%x--%s", sym.kind, Kinds.kindName(sym)), |
| sym.getKind()) |
| + " " + sym.name |
| + " " + hashString(sym)); |
| |
| indent(+1); |
| if (showSrc) { |
| JCTree tree = (JCTree) trees.getTree(sym); |
| if (tree != null) |
| printSource("src", tree); |
| } |
| printString("flags", String.format("0x%x--%s", |
| sym.flags_field, Flags.toString(sym.flags_field))); |
| printObject("completer", sym.completer, Details.SUMMARY); // what if too long? |
| printSymbol("owner", sym.owner, Details.SUMMARY); |
| printType("type", sym.type, Details.SUMMARY); |
| printType("erasure", sym.erasure_field, Details.SUMMARY); |
| sym.accept(symVisitor, null); |
| printAnnotations("annotations", sym.getAnnotations(), Details.SUMMARY); |
| indent(-1); |
| } |
| } |
| } |
| |
| protected String toString(Symbol sym) { |
| return (printer != null) ? printer.visit(sym, locale) : String.valueOf(sym); |
| } |
| |
| protected void printTree(String label, JCTree tree) { |
| if (tree == null) { |
| printNull(label); |
| } else { |
| indent(); |
| String ext; |
| try { |
| ext = tree.getKind().name(); |
| } catch (Throwable t) { |
| ext = "n/a"; |
| } |
| out.print(label + ": " + info(tree.getClass(), tree.getTag(), ext)); |
| if (showPositions) { |
| // We can always get start position, but to get end position |
| // and/or line+offset, we would need a JCCompilationUnit |
| out.print(" pos:" + tree.pos); |
| } |
| if (showTreeTypes && tree.type != null) |
| out.print(" type:" + toString(tree.type)); |
| Symbol sym; |
| if (showTreeSymbols && (sym = TreeInfo.symbolFor(tree)) != null) |
| out.print(" sym:" + toString(sym)); |
| out.println(); |
| |
| indent(+1); |
| if (showSrc) { |
| indent(); |
| out.println("src: " + Pretty.toSimpleString(tree, maxSrcLength)); |
| } |
| tree.accept(treeVisitor); |
| indent(-1); |
| } |
| } |
| |
| public void printType(String label, Type type) { |
| printType(label, type, Details.FULL); |
| } |
| |
| protected void printType(String label, Type type, Details details) { |
| if (type == null) |
| printNull(label); |
| else { |
| switch (details) { |
| case SUMMARY: |
| printString(label, toString(type)); |
| break; |
| |
| case FULL: |
| indent(); |
| out.print(label); |
| out.println(": " + info(type.getClass(), type.getTag(), type.getKind()) |
| + " " + hashString(type)); |
| |
| indent(+1); |
| printSymbol("tsym", type.tsym, Details.SUMMARY); |
| printObject("constValue", type.constValue(), Details.SUMMARY); |
| printObject("annotations", type.getAnnotationMirrors(), Details.SUMMARY); |
| type.accept(typeVisitor, null); |
| indent(-1); |
| } |
| } |
| } |
| |
| protected String toString(Type type) { |
| return (printer != null) ? printer.visit(type, locale) : String.valueOf(type); |
| } |
| |
| protected String hashString(Object obj) { |
| return String.format("#%x", obj.hashCode()); |
| } |
| |
| protected String info(Class<?> clazz, Object internal, Object external) { |
| return String.format("%s,%s,%s", clazz.getSimpleName(), internal, external); |
| } |
| |
| private int indent = 0; |
| |
| protected void indent() { |
| for (int i = 0; i < indent; i++) { |
| out.print(" "); |
| } |
| } |
| |
| protected void indent(int n) { |
| indent += n; |
| } |
| |
| protected Object getField(Object o, Class<?> clazz, String name) { |
| try { |
| Field f = clazz.getDeclaredField(name); |
| boolean prev = f.isAccessible(); |
| f.setAccessible(true); |
| try { |
| return f.get(o); |
| } finally { |
| f.setAccessible(prev); |
| } |
| } catch (ReflectiveOperationException e) { |
| return e; |
| } catch (SecurityException e) { |
| return e; |
| } |
| } |
| |
| // </editor-fold> |
| |
| // <editor-fold defaultstate="collapsed" desc="JCTree visitor methods"> |
| |
| protected JCTree.Visitor treeVisitor = new TreeVisitor(); |
| |
| /** |
| * Default visitor class for JCTree (AST) objects. |
| */ |
| public class TreeVisitor extends JCTree.Visitor { |
| @Override |
| public void visitTopLevel(JCCompilationUnit tree) { |
| printList("packageAnnotations", tree.packageAnnotations); |
| printTree("pid", tree.pid); |
| printList("defs", tree.defs); |
| } |
| |
| @Override |
| public void visitImport(JCImport tree) { |
| printTree("qualid", tree.qualid); |
| } |
| |
| @Override |
| public void visitClassDef(JCClassDecl tree) { |
| printName("name", tree.name); |
| printTree("mods", tree.mods); |
| printList("typarams", tree.typarams); |
| printTree("extending", tree.extending); |
| printList("implementing", tree.implementing); |
| printList("defs", tree.defs); |
| } |
| |
| @Override |
| public void visitMethodDef(JCMethodDecl tree) { |
| printName("name", tree.name); |
| printTree("mods", tree.mods); |
| printTree("restype", tree.restype); |
| printList("typarams", tree.typarams); |
| printTree("recvparam", tree.recvparam); |
| printList("params", tree.params); |
| printList("thrown", tree.thrown); |
| printTree("defaultValue", tree.defaultValue); |
| printTree("body", tree.body); |
| } |
| |
| @Override |
| public void visitVarDef(JCVariableDecl tree) { |
| printName("name", tree.name); |
| printTree("mods", tree.mods); |
| printTree("vartype", tree.vartype); |
| printTree("init", tree.init); |
| } |
| |
| @Override |
| public void visitSkip(JCSkip tree) { |
| } |
| |
| @Override |
| public void visitBlock(JCBlock tree) { |
| printList("stats", tree.stats); |
| } |
| |
| @Override |
| public void visitDoLoop(JCDoWhileLoop tree) { |
| printTree("body", tree.body); |
| printTree("cond", tree.cond); |
| } |
| |
| @Override |
| public void visitWhileLoop(JCWhileLoop tree) { |
| printTree("cond", tree.cond); |
| printTree("body", tree.body); |
| } |
| |
| @Override |
| public void visitForLoop(JCForLoop tree) { |
| printList("init", tree.init); |
| printTree("cond", tree.cond); |
| printList("step", tree.step); |
| printTree("body", tree.body); |
| } |
| |
| @Override |
| public void visitForeachLoop(JCEnhancedForLoop tree) { |
| printTree("var", tree.var); |
| printTree("expr", tree.expr); |
| printTree("body", tree.body); |
| } |
| |
| @Override |
| public void visitLabelled(JCLabeledStatement tree) { |
| printTree("body", tree.body); |
| } |
| |
| @Override |
| public void visitSwitch(JCSwitch tree) { |
| printTree("selector", tree.selector); |
| printList("cases", tree.cases); |
| } |
| |
| @Override |
| public void visitCase(JCCase tree) { |
| printTree("pat", tree.pat); |
| printList("stats", tree.stats); |
| } |
| |
| @Override |
| public void visitSynchronized(JCSynchronized tree) { |
| printTree("lock", tree.lock); |
| printTree("body", tree.body); |
| } |
| |
| @Override |
| public void visitTry(JCTry tree) { |
| printList("resources", tree.resources); |
| printTree("body", tree.body); |
| printList("catchers", tree.catchers); |
| printTree("finalizer", tree.finalizer); |
| } |
| |
| @Override |
| public void visitCatch(JCCatch tree) { |
| printTree("param", tree.param); |
| printTree("body", tree.body); |
| } |
| |
| @Override |
| public void visitConditional(JCConditional tree) { |
| printTree("cond", tree.cond); |
| printTree("truepart", tree.truepart); |
| printTree("falsepart", tree.falsepart); |
| } |
| |
| @Override |
| public void visitIf(JCIf tree) { |
| printTree("cond", tree.cond); |
| printTree("thenpart", tree.thenpart); |
| printTree("elsepart", tree.elsepart); |
| } |
| |
| @Override |
| public void visitExec(JCExpressionStatement tree) { |
| printTree("expr", tree.expr); |
| } |
| |
| @Override |
| public void visitBreak(JCBreak tree) { |
| printName("label", tree.label); |
| } |
| |
| @Override |
| public void visitContinue(JCContinue tree) { |
| printName("label", tree.label); |
| } |
| |
| @Override |
| public void visitReturn(JCReturn tree) { |
| printTree("expr", tree.expr); |
| } |
| |
| @Override |
| public void visitThrow(JCThrow tree) { |
| printTree("expr", tree.expr); |
| } |
| |
| @Override |
| public void visitAssert(JCAssert tree) { |
| printTree("cond", tree.cond); |
| printTree("detail", tree.detail); |
| } |
| |
| @Override |
| public void visitApply(JCMethodInvocation tree) { |
| printList("typeargs", tree.typeargs); |
| printTree("meth", tree.meth); |
| printList("args", tree.args); |
| } |
| |
| @Override |
| public void visitNewClass(JCNewClass tree) { |
| printTree("encl", tree.encl); |
| printList("typeargs", tree.typeargs); |
| printTree("clazz", tree.clazz); |
| printList("args", tree.args); |
| printTree("def", tree.def); |
| } |
| |
| @Override |
| public void visitNewArray(JCNewArray tree) { |
| printList("annotations", tree.annotations); |
| printTree("elemtype", tree.elemtype); |
| printList("dims", tree.dims); |
| printList("dimAnnotations", tree.dimAnnotations); |
| printList("elems", tree.elems); |
| } |
| |
| @Override |
| public void visitLambda(JCLambda tree) { |
| printTree("body", tree.body); |
| printList("params", tree.params); |
| } |
| |
| @Override |
| public void visitParens(JCParens tree) { |
| printTree("expr", tree.expr); |
| } |
| |
| @Override |
| public void visitAssign(JCAssign tree) { |
| printTree("lhs", tree.lhs); |
| printTree("rhs", tree.rhs); |
| } |
| |
| @Override |
| public void visitAssignop(JCAssignOp tree) { |
| printTree("lhs", tree.lhs); |
| printTree("rhs", tree.rhs); |
| } |
| |
| @Override |
| public void visitUnary(JCUnary tree) { |
| printTree("arg", tree.arg); |
| } |
| |
| @Override |
| public void visitBinary(JCBinary tree) { |
| printTree("lhs", tree.lhs); |
| printTree("rhs", tree.rhs); |
| } |
| |
| @Override |
| public void visitTypeCast(JCTypeCast tree) { |
| printTree("clazz", tree.clazz); |
| printTree("expr", tree.expr); |
| } |
| |
| @Override |
| public void visitTypeTest(JCInstanceOf tree) { |
| printTree("expr", tree.expr); |
| printTree("clazz", tree.clazz); |
| } |
| |
| @Override |
| public void visitIndexed(JCArrayAccess tree) { |
| printTree("indexed", tree.indexed); |
| printTree("index", tree.index); |
| } |
| |
| @Override |
| public void visitSelect(JCFieldAccess tree) { |
| printTree("selected", tree.selected); |
| } |
| |
| @Override |
| public void visitReference(JCMemberReference tree) { |
| printTree("expr", tree.expr); |
| printList("typeargs", tree.typeargs); |
| } |
| |
| @Override |
| public void visitIdent(JCIdent tree) { |
| printName("name", tree.name); |
| } |
| |
| @Override |
| public void visitLiteral(JCLiteral tree) { |
| printString("value", Pretty.toSimpleString(tree, 32)); |
| } |
| |
| @Override |
| public void visitTypeIdent(JCPrimitiveTypeTree tree) { |
| printString("typetag", tree.typetag.name()); |
| } |
| |
| @Override |
| public void visitTypeArray(JCArrayTypeTree tree) { |
| printTree("elemtype", tree.elemtype); |
| } |
| |
| @Override |
| public void visitTypeApply(JCTypeApply tree) { |
| printTree("clazz", tree.clazz); |
| printList("arguments", tree.arguments); |
| } |
| |
| @Override |
| public void visitTypeUnion(JCTypeUnion tree) { |
| printList("alternatives", tree.alternatives); |
| } |
| |
| @Override |
| public void visitTypeIntersection(JCTypeIntersection tree) { |
| printList("bounds", tree.bounds); |
| } |
| |
| @Override |
| public void visitTypeParameter(JCTypeParameter tree) { |
| printName("name", tree.name); |
| printList("annotations", tree.annotations); |
| printList("bounds", tree.bounds); |
| } |
| |
| @Override |
| public void visitWildcard(JCWildcard tree) { |
| printTree("kind", tree.kind); |
| printTree("inner", tree.inner); |
| } |
| |
| @Override |
| public void visitTypeBoundKind(TypeBoundKind tree) { |
| printString("kind", tree.kind.name()); |
| } |
| |
| @Override |
| public void visitModifiers(JCModifiers tree) { |
| printList("annotations", tree.annotations); |
| printString("flags", String.valueOf(Flags.asFlagSet(tree.flags))); |
| } |
| |
| @Override |
| public void visitAnnotation(JCAnnotation tree) { |
| printTree("annotationType", tree.annotationType); |
| printList("args", tree.args); |
| } |
| |
| @Override |
| public void visitAnnotatedType(JCAnnotatedType tree) { |
| printList("annotations", tree.annotations); |
| printTree("underlyingType", tree.underlyingType); |
| } |
| |
| @Override |
| public void visitErroneous(JCErroneous tree) { |
| printList("errs", tree.errs); |
| } |
| |
| @Override |
| public void visitLetExpr(LetExpr tree) { |
| printList("defs", tree.defs); |
| printTree("expr", tree.expr); |
| } |
| |
| @Override |
| public void visitTree(JCTree tree) { |
| Assert.error(); |
| } |
| } |
| |
| // </editor-fold> |
| |
| // <editor-fold defaultstate="collapsed" desc="Symbol visitor"> |
| |
| protected Symbol.Visitor<Void,Void> symVisitor = new SymbolVisitor(); |
| |
| /** |
| * Default visitor class for Symbol objects. |
| * Note: each visitXYZ method ends by calling the corresponding |
| * visit method for its superclass. |
| */ |
| class SymbolVisitor implements Symbol.Visitor<Void,Void> { |
| @Override |
| public Void visitClassSymbol(ClassSymbol sym, Void ignore) { |
| printName("fullname", sym.fullname); |
| printName("flatname", sym.flatname); |
| printScope("members", sym.members_field); |
| printFileObject("sourcefile", sym.sourcefile); |
| printFileObject("classfile", sym.classfile); |
| // trans-local? |
| // pool? |
| return visitTypeSymbol(sym, null); |
| } |
| |
| @Override |
| public Void visitMethodSymbol(MethodSymbol sym, Void ignore) { |
| // code |
| printList("params", sym.params); |
| printList("savedParameterNames", sym.savedParameterNames); |
| return visitSymbol(sym, null); |
| } |
| |
| @Override |
| public Void visitPackageSymbol(PackageSymbol sym, Void ignore) { |
| printName("fullname", sym.fullname); |
| printScope("members", sym.members_field); |
| printSymbol("package-info", sym.package_info, Details.SUMMARY); |
| return visitTypeSymbol(sym, null); |
| } |
| |
| @Override |
| public Void visitOperatorSymbol(OperatorSymbol sym, Void ignore) { |
| printInt("opcode", sym.opcode); |
| return visitMethodSymbol(sym, null); |
| } |
| |
| @Override |
| public Void visitVarSymbol(VarSymbol sym, Void ignore) { |
| printInt("pos", sym.pos); |
| printInt("adm", sym.adr); |
| // data is a private field, and the standard accessors may |
| // mutate it as part of lazy evaluation. Therefore, use |
| // reflection to get the raw data. |
| printObject("data", getField(sym, VarSymbol.class, "data"), Details.SUMMARY); |
| return visitSymbol(sym, null); |
| } |
| |
| @Override |
| public Void visitTypeSymbol(TypeSymbol sym, Void ignore) { |
| return visitSymbol(sym, null); |
| } |
| |
| @Override |
| public Void visitSymbol(Symbol sym, Void ignore) { |
| return null; |
| } |
| } |
| |
| // </editor-fold> |
| |
| // <editor-fold defaultstate="collapsed" desc="Type visitor"> |
| |
| protected Type.Visitor<Void,Void> typeVisitor = new TypeVisitor(); |
| |
| /** |
| * Default visitor class for Type objects. |
| * Note: each visitXYZ method ends by calling the corresponding |
| * visit method for its superclass. |
| */ |
| public class TypeVisitor implements Type.Visitor<Void,Void> { |
| public Void visitAnnotatedType(AnnotatedType type, Void ignore) { |
| printList("typeAnnotations", type.getAnnotationMirrors()); |
| printType("underlyingType", type.unannotatedType(), Details.FULL); |
| return visitType(type, null); |
| } |
| |
| public Void visitArrayType(ArrayType type, Void ignore) { |
| printType("elemType", type.elemtype, Details.FULL); |
| return visitType(type, null); |
| } |
| |
| public Void visitCapturedType(CapturedType type, Void ignore) { |
| printType("wildcard", type.wildcard, Details.FULL); |
| return visitTypeVar(type, null); |
| } |
| |
| public Void visitClassType(ClassType type, Void ignore) { |
| printType("outer", type.getEnclosingType(), Details.SUMMARY); |
| printList("typarams", type.typarams_field); |
| printList("allparams", type.allparams_field); |
| printType("supertype", type.supertype_field, Details.SUMMARY); |
| printList("interfaces", type.interfaces_field); |
| printList("allinterfaces", type.all_interfaces_field); |
| return visitType(type, null); |
| } |
| |
| public Void visitErrorType(ErrorType type, Void ignore) { |
| printType("originalType", type.getOriginalType(), Details.FULL); |
| return visitClassType(type, null); |
| } |
| |
| public Void visitForAll(ForAll type, Void ignore) { |
| printList("tvars", type.tvars); |
| return visitDelegatedType(type); |
| } |
| |
| public Void visitMethodType(MethodType type, Void ignore) { |
| printList("argtypes", type.argtypes); |
| printType("restype", type.restype, Details.FULL); |
| printList("thrown", type.thrown); |
| return visitType(type, null); |
| } |
| |
| public Void visitPackageType(PackageType type, Void ignore) { |
| return visitType(type, null); |
| } |
| |
| public Void visitTypeVar(TypeVar type, Void ignore) { |
| // For TypeVars (and not subtypes), the bound should always be |
| // null or bot. So, only print the bound for subtypes of TypeVar, |
| // or if the bound is (erroneously) not null or bot. |
| if (!type.hasTag(TypeTag.TYPEVAR) |
| || !(type.bound == null || type.bound.hasTag(TypeTag.BOT))) { |
| printType("bound", type.bound, Details.FULL); |
| } |
| printType("lower", type.lower, Details.FULL); |
| return visitType(type, null); |
| } |
| |
| public Void visitUndetVar(UndetVar type, Void ignore) { |
| for (UndetVar.InferenceBound ib: UndetVar.InferenceBound.values()) |
| printList("bounds." + ib, type.getBounds(ib)); |
| printInt("declaredCount", type.declaredCount); |
| printType("inst", type.inst, Details.SUMMARY); |
| return visitDelegatedType(type); |
| } |
| |
| public Void visitWildcardType(WildcardType type, Void ignore) { |
| printType("type", type.type, Details.SUMMARY); |
| printString("kind", type.kind.name()); |
| printType("bound", type.bound, Details.SUMMARY); |
| return visitType(type, null); |
| } |
| |
| protected Void visitDelegatedType(DelegatedType type) { |
| printType("qtype", type.qtype, Details.FULL); |
| return visitType(type, null); |
| } |
| |
| public Void visitType(Type type, Void ignore) { |
| return null; |
| } |
| } |
| |
| // </editor-fold> |
| |
| // <editor-fold defaultstate="collapsed" desc="Attribute (annotations) visitor"> |
| |
| protected Attribute.Visitor attrVisitor = new AttributeVisitor(); |
| |
| /** |
| * Default visitor class for Attribute (annotation) objects. |
| */ |
| public class AttributeVisitor implements Attribute.Visitor { |
| |
| public void visitConstant(Attribute.Constant a) { |
| printObject("value", a.value, Details.SUMMARY); |
| visitAttribute(a); |
| } |
| |
| public void visitClass(Attribute.Class a) { |
| printObject("classType", a.classType, Details.SUMMARY); |
| visitAttribute(a); |
| } |
| |
| public void visitCompound(Attribute.Compound a) { |
| if (a instanceof Attribute.TypeCompound) { |
| Attribute.TypeCompound ta = (Attribute.TypeCompound) a; |
| // consider a custom printer? |
| printObject("position", ta.position, Details.SUMMARY); |
| } |
| printObject("synthesized", a.isSynthesized(), Details.SUMMARY); |
| printList("values", a.values); |
| visitAttribute(a); |
| } |
| |
| public void visitArray(Attribute.Array a) { |
| printList("values", Arrays.asList(a.values)); |
| visitAttribute(a); |
| } |
| |
| public void visitEnum(Attribute.Enum a) { |
| printSymbol("value", a.value, Details.SUMMARY); |
| visitAttribute(a); |
| } |
| |
| public void visitError(Attribute.Error a) { |
| visitAttribute(a); |
| } |
| |
| public void visitAttribute(Attribute a) { |
| printType("type", a.type, Details.SUMMARY); |
| } |
| |
| } |
| // </editor-fold> |
| |
| // <editor-fold defaultstate="collapsed" desc="Utility front end"> |
| |
| /** |
| * Utility class to invoke DPrinter from the command line. |
| */ |
| static class Main { |
| public static void main(String... args) throws IOException { |
| Main m = new Main(); |
| PrintWriter out = new PrintWriter(System.out); |
| try { |
| if (args.length == 0) |
| m.usage(out); |
| else |
| m.run(out, args); |
| } finally { |
| out.flush(); |
| } |
| } |
| |
| void usage(PrintWriter out) { |
| out.println("Usage:"); |
| out.println(" java " + Main.class.getName() + " mode [options] [javac-options]"); |
| out.print("where mode is one of: "); |
| String sep = ""; |
| for (Handler h: getHandlers().values()) { |
| out.print(sep); |
| out.print(h.name); |
| sep = ", "; |
| } |
| out.println(); |
| out.println("and where options include:"); |
| out.println(" -before PARSE|ENTER|ANALYZE|GENERATE|ANNOTATION_PROCESSING|ANNOTATION_PROCESSING_ROUND"); |
| out.println(" -after PARSE|ENTER|ANALYZE|GENERATE|ANNOTATION_PROCESSING|ANNOTATION_PROCESSING_ROUND"); |
| out.println(" -showPositions"); |
| out.println(" -showSource"); |
| out.println(" -showTreeSymbols"); |
| out.println(" -showTreeTypes"); |
| out.println(" -hideEmptyItems"); |
| out.println(" -hideNulls"); |
| } |
| |
| void run(PrintWriter out, String... args) throws IOException { |
| JavaCompiler c = ToolProvider.getSystemJavaCompiler(); |
| StandardJavaFileManager fm = c.getStandardFileManager(null, null, null); |
| |
| // DPrinter options |
| final Set<TaskEvent.Kind> before = EnumSet.noneOf(TaskEvent.Kind.class); |
| final Set<TaskEvent.Kind> after = EnumSet.noneOf(TaskEvent.Kind.class); |
| boolean showPositions = false; |
| boolean showSource = false; |
| boolean showTreeSymbols = false; |
| boolean showTreeTypes = false; |
| boolean showEmptyItems = true; |
| boolean showNulls = true; |
| |
| // javac options |
| Collection<String> options = new ArrayList<String>(); |
| Collection<File> files = new ArrayList<File>(); |
| String classpath = null; |
| String classoutdir = null; |
| |
| final Handler h = getHandlers().get(args[0]); |
| if (h == null) |
| throw new IllegalArgumentException(args[0]); |
| |
| for (int i = 1; i < args.length; i++) { |
| String arg = args[i]; |
| if (arg.equals("-before") && i + 1 < args.length) { |
| before.add(getKind(args[++i])); |
| } else if (arg.equals("-after") && i + 1 < args.length) { |
| after.add(getKind(args[++i])); |
| } else if (arg.equals("-showPositions")) { |
| showPositions = true; |
| } else if (arg.equals("-showSource")) { |
| showSource = true; |
| } else if (arg.equals("-showTreeSymbols")) { |
| showTreeSymbols = true; |
| } else if (arg.equals("-showTreeTypes")) { |
| showTreeTypes = true; |
| } else if (arg.equals("-hideEmptyLists")) { |
| showEmptyItems = false; |
| } else if (arg.equals("-hideNulls")) { |
| showNulls = false; |
| } else if (arg.equals("-classpath") && i + 1 < args.length) { |
| classpath = args[++i]; |
| } else if (arg.equals("-d") && i + 1 < args.length) { |
| classoutdir = args[++i]; |
| } else if (arg.startsWith("-")) { |
| int n = c.isSupportedOption(arg); |
| if (n < 0) throw new IllegalArgumentException(arg); |
| options.add(arg); |
| while (n > 0) options.add(args[++i]); |
| } else if (arg.endsWith(".java")) { |
| files.add(new File(arg)); |
| } |
| } |
| |
| if (classoutdir != null) { |
| fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File(classoutdir))); |
| } |
| |
| if (classpath != null) { |
| Collection<File> path = new ArrayList<File>(); |
| for (String p: classpath.split(File.pathSeparator)) { |
| if (p.isEmpty()) continue; |
| File f = new File(p); |
| if (f.exists()) path.add(f); |
| } |
| fm.setLocation(StandardLocation.CLASS_PATH, path); |
| } |
| Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files); |
| |
| JavacTask task = (JavacTask) c.getTask(out, fm, null, options, null, fos); |
| final Trees trees = Trees.instance(task); |
| |
| final DPrinter dprinter = new DPrinter(out, trees); |
| dprinter.source(showSource) |
| .emptyItems(showEmptyItems) |
| .nulls(showNulls) |
| .positions(showPositions) |
| .treeSymbols(showTreeSymbols) |
| .treeTypes(showTreeTypes); |
| |
| if (before.isEmpty() && after.isEmpty()) { |
| if (h.name.equals("trees") && !showTreeSymbols && !showTreeTypes) |
| after.add(TaskEvent.Kind.PARSE); |
| else |
| after.add(TaskEvent.Kind.ANALYZE); |
| } |
| |
| task.addTaskListener(new TaskListener() { |
| public void started(TaskEvent e) { |
| if (before.contains(e.getKind())) |
| handle(e); |
| } |
| |
| public void finished(TaskEvent e) { |
| if (after.contains(e.getKind())) |
| handle(e); |
| } |
| |
| private void handle(TaskEvent e) { |
| switch (e.getKind()) { |
| case PARSE: |
| case ENTER: |
| h.handle(e.getSourceFile().getName(), |
| (JCTree) e.getCompilationUnit(), |
| dprinter); |
| break; |
| |
| default: |
| TypeElement elem = e.getTypeElement(); |
| h.handle(elem.toString(), |
| (JCTree) trees.getTree(elem), |
| dprinter); |
| break; |
| } |
| } |
| }); |
| |
| task.call(); |
| } |
| |
| TaskEvent.Kind getKind(String s) { |
| return TaskEvent.Kind.valueOf(s.toUpperCase()); |
| } |
| |
| static protected abstract class Handler { |
| final String name; |
| Handler(String name) { |
| this.name = name; |
| } |
| abstract void handle(String label, JCTree tree, DPrinter dprinter); |
| } |
| |
| Map<String,Handler> getHandlers() { |
| Map<String,Handler> map = new HashMap<String, Handler>(); |
| for (Handler h: defaultHandlers) { |
| map.put(h.name, h); |
| } |
| return map; |
| } |
| |
| protected final Handler[] defaultHandlers = { |
| new Handler("trees") { |
| @Override |
| void handle(String name, JCTree tree, DPrinter dprinter) { |
| dprinter.printTree(name, tree); |
| dprinter.out.println(); |
| } |
| }, |
| |
| new Handler("symbols") { |
| @Override |
| void handle(String name, JCTree tree, final DPrinter dprinter) { |
| TreeScanner ds = new TreeScanner() { |
| @Override |
| public void visitClassDef(JCClassDecl tree) { |
| visitDecl(tree, tree.sym); |
| super.visitClassDef(tree); |
| } |
| |
| @Override |
| public void visitMethodDef(JCMethodDecl tree) { |
| visitDecl(tree, tree.sym); |
| super.visitMethodDef(tree); |
| } |
| |
| @Override |
| public void visitVarDef(JCVariableDecl tree) { |
| visitDecl(tree, tree.sym); |
| super.visitVarDef(tree); |
| } |
| |
| void visitDecl(JCTree tree, Symbol sym) { |
| dprinter.printSymbol(sym.name.toString(), sym); |
| dprinter.out.println(); |
| } |
| }; |
| ds.scan(tree); |
| } |
| }, |
| |
| new Handler("types") { |
| @Override |
| void handle(String name, JCTree tree, final DPrinter dprinter) { |
| TreeScanner ts = new TreeScanner() { |
| @Override |
| public void scan(JCTree tree) { |
| if (tree == null) { |
| return; |
| } |
| if (tree.type != null) { |
| String label = Pretty.toSimpleString(tree); |
| dprinter.printType(label, tree.type); |
| dprinter.out.println(); |
| } |
| super.scan(tree); |
| } |
| }; |
| ts.scan(tree); |
| } |
| } |
| }; |
| } |
| |
| // </editor-fold> |
| |
| } |