/* * Copyright (c) 2013, 2016, 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. */ /* @test * @bug 8043484 8007307 * @summary Make sure DPrinter.java compiles * @modules jdk.compiler/com.sun.tools.javac.api * jdk.compiler/com.sun.tools.javac.code * jdk.compiler/com.sun.tools.javac.tree * jdk.compiler/com.sun.tools.javac.util * @compile DPrinter.java */ import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; 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.doctree.*; 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.Convert; 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.logKey).getWriter(Log.WriterKind.STDERR); 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 printDocTree(String label, DocTree tree) { if (tree == null) { printNull(label); } else { indent(); out.print(label); out.println(": " + tree.getClass().getSimpleName() + "," + tree.getKind()); indent(+1); tree.accept(docTreeVisitor, null); 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 printLimitedEscapedString(String label, String text) { String s = Convert.quote(text); if (s.length() > maxSrcLength) { String trim = "[...]"; int head = (maxSrcLength - trim.length()) * 2 / 3; int tail = maxSrcLength - trim.length() - head; s = s.substring(0, head) + trim + s.substring(s.length() - tail); } printString(label, s); } 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 DocTree) { printDocTree(label, (DocTree) item); } else if (item instanceof List) { printList(label, (List) item); } else if (item instanceof Name) { printName(label, (Name) item); } else if (item instanceof Scope) { printScope(label, (Scope) 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.getSymbols()) { out.print(sep); out.print(sym.name); sep = ","; } out.println("]"); break; } case FULL: { indent(); out.println(label); indent(+1); printFullScopeImpl(scope); indent(-1); break; } } } } void printFullScopeImpl(Scope scope) { indent(); out.println(scope.getClass().getName()); printSymbol("owner", scope.owner, Details.SUMMARY); if (SCOPE_IMPL_CLASS.equals(scope.getClass().getName())) { printScope("next", (Scope) getField(scope, scope.getClass(), "next"), Details.SUMMARY); printObject("shared", getField(scope, scope.getClass(), "shared"), Details.SUMMARY); Object[] table = (Object[]) getField(scope, scope.getClass(), "table"); for (int i = 0; i < table.length; i++) { if (i > 0) out.print(", "); else indent(); out.print(i + ":" + entryToString(table[i], table, false)); } out.println(); } else if (FILTER_SCOPE_CLASS.equals(scope.getClass().getName())) { printScope("origin", (Scope) getField(scope, scope.getClass(), "origin"), Details.FULL); } else if (scope instanceof CompoundScope) { printList("delegates", (List<?>) getField(scope, CompoundScope.class, "subScopes")); } else { for (Symbol sym : scope.getSymbols()) { printSymbol(sym.name.toString(), sym, Details.SUMMARY); } } } //where: static final String SCOPE_IMPL_CLASS = "com.sun.tools.javac.code.Scope$ScopeImpl"; static final String FILTER_SCOPE_CLASS = "com.sun.tools.javac.code.Scope$FilterImportScope"; /** * Create a string showing the contents of an entry, using the table * to help identify cross-references to other entries in the table. * @param e the entry to be shown * @param table the table containing the other entries */ String entryToString(Object e, Object[] table, boolean ref) { if (e == null) return "null"; Symbol sym = (Symbol) getField(e, e.getClass(), "sym"); if (sym == null) return "sent"; // sentinel if (ref) { int index = indexOf(table, e); if (index != -1) return String.valueOf(index); } Scope scope = (Scope) getField(e, e.getClass(), "scope"); return "(" + sym.name + ":" + sym + ",shdw:" + entryToString(callMethod(e, e.getClass(), "next"), table, true) + ",sibl:" + entryToString(getField(e, e.getClass(), "sibling"), table, true) + ((sym.owner != scope.owner) ? (",BOGUS[" + sym.owner + "," + scope.owner + "]") : "") + ")"; } <T> int indexOf(T[] array, T item) { for (int i = 0; i < array.length; i++) { if (array[i] == item) return i; } return -1; } 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.ordinal(), 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.getMetadata(), 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; } } protected Object callMethod(Object o, Class<?> clazz, String name) { try { Method m = clazz.getDeclaredMethod(name); boolean prev = m.isAccessible(); m.setAccessible(true); try { return m.invoke(o); } finally { m.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.getPackageAnnotations()); printList("defs", tree.defs); } @Override public void visitPackageDef(JCPackageDecl tree) { printTree("pid", tree.pid); } @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="DocTree visitor"> protected DocTreeVisitor<Void,Void> docTreeVisitor = new DefaultDocTreeVisitor(); /** * Default visitor class for DocTree objects. * Note: each visitXYZ method ends by calling the corresponding * visit method for its superclass. */ class DefaultDocTreeVisitor implements DocTreeVisitor<Void,Void> { public Void visitAttribute(AttributeTree node, Void p) { printName("name", node.getName()); printString("vkind", node.getValueKind().name()); printList("value", node.getValue()); return visitTree(node, null); } public Void visitAuthor(AuthorTree node, Void p) { printList("name", node.getName()); return visitBlockTag(node, null); } public Void visitComment(CommentTree node, Void p) { printLimitedEscapedString("body", node.getBody()); return visitTree(node, null); } public Void visitDeprecated(DeprecatedTree node, Void p) { printList("body", node.getBody()); return visitBlockTag(node, null); } public Void visitDocComment(DocCommentTree node, Void p) { printList("firstSentence", node.getFirstSentence()); printList("body", node.getBody()); printList("tags", node.getBlockTags()); return visitTree(node, null); } public Void visitDocRoot(DocRootTree node, Void p) { return visitInlineTag(node, null); } public Void visitEndElement(EndElementTree node, Void p) { printName("name", node.getName()); return visitTree(node, null); } public Void visitEntity(EntityTree node, Void p) { printName("name", node.getName()); return visitTree(node, null); } public Void visitErroneous(ErroneousTree node, Void p) { printLimitedEscapedString("body", node.getBody()); printString("diag", node.getDiagnostic().getMessage(Locale.getDefault())); return visitTree(node, null); } public Void visitHidden(HiddenTree node, Void p) { printList("body", node.getBody()); return visitBlockTag(node, null); } public Void visitIdentifier(IdentifierTree node, Void p) { printName("name", node.getName()); return visitTree(node, null); } public Void visitIndex(IndexTree node, Void p) { printString("kind", node.getKind().name()); printDocTree("term", node.getSearchTerm()); printList("desc", node.getDescription()); return visitInlineTag(node, p); } public Void visitInheritDoc(InheritDocTree node, Void p) { return visitInlineTag(node, null); } public Void visitLink(LinkTree node, Void p) { printString("kind", node.getKind().name()); printDocTree("ref", node.getReference()); printList("list", node.getLabel()); return visitInlineTag(node, null); } public Void visitLiteral(LiteralTree node, Void p) { printString("kind", node.getKind().name()); printDocTree("body", node.getBody()); return visitInlineTag(node, null); } public Void visitParam(ParamTree node, Void p) { printString("isTypeParameter", String.valueOf(node.isTypeParameter())); printString("kind", node.getKind().name()); printList("desc", node.getDescription()); return visitBlockTag(node, null); } public Void visitReference(ReferenceTree node, Void p) { printString("signature", node.getSignature()); return visitTree(node, null); } public Void visitReturn(ReturnTree node, Void p) { printList("desc", node.getDescription()); return visitBlockTag(node, null); } public Void visitSee(SeeTree node, Void p) { printList("ref", node.getReference()); return visitBlockTag(node, null); } public Void visitSerial(SerialTree node, Void p) { printList("desc", node.getDescription()); return visitBlockTag(node, null); } public Void visitSerialData(SerialDataTree node, Void p) { printList("desc", node.getDescription()); return visitBlockTag(node, null); } public Void visitSerialField(SerialFieldTree node, Void p) { printDocTree("name", node.getName()); printDocTree("type", node.getType()); printList("desc", node.getDescription()); return visitBlockTag(node, null); } public Void visitSince(SinceTree node, Void p) { printList("body", node.getBody()); return visitBlockTag(node, null); } public Void visitStartElement(StartElementTree node, Void p) { printName("name", node.getName()); printList("attrs", node.getAttributes()); printString("selfClosing", String.valueOf(node.isSelfClosing())); return visitBlockTag(node, null); } public Void visitText(TextTree node, Void p) { printLimitedEscapedString("body", node.getBody()); return visitTree(node, null); } public Void visitThrows(ThrowsTree node, Void p) { printDocTree("name", node.getExceptionName()); printList("desc", node.getDescription()); return visitBlockTag(node, null); } public Void visitUnknownBlockTag(UnknownBlockTagTree node, Void p) { printString("name", node.getTagName()); printList("content", node.getContent()); return visitBlockTag(node, null); } public Void visitUnknownInlineTag(UnknownInlineTagTree node, Void p) { printString("name", node.getTagName()); printList("content", node.getContent()); return visitInlineTag(node, null); } public Void visitValue(ValueTree node, Void p) { printDocTree("value", node.getReference()); return visitInlineTag(node, null); } public Void visitVersion(VersionTree node, Void p) { printList("body", node.getBody()); return visitBlockTag(node, null); } public Void visitOther(DocTree node, Void p) { return visitTree(node, null); } public Void visitBlockTag(DocTree node, Void p) { return visitTree(node, null); } public Void visitInlineTag(DocTree node, Void p) { return visitTree(node, null); } public Void visitTree(DocTree node, Void p) { return null; } } // </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 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); printType("recvtype", type.recvtype, Details.FULL); return visitType(type, null); } public Void visitModuleType(ModuleType type, Void ignore) { 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.getInst(), 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) { JCCompilationUnit unit = (JCCompilationUnit) e.getCompilationUnit(); switch (e.getKind()) { case PARSE: case ENTER: h.handle(e.getSourceFile().getName(), unit, unit, dprinter); break; default: TypeElement elem = e.getTypeElement(); h.handle(elem.toString(), unit, (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, JCCompilationUnit unit, 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, JCCompilationUnit unit, JCTree tree, DPrinter dprinter) { dprinter.printTree(name, tree); dprinter.out.println(); } }, new Handler("doctrees") { @Override void handle(final String name, final JCCompilationUnit unit, JCTree tree, final DPrinter dprinter) { TreeScanner ds = new DeclScanner() { public void visitDecl(JCTree tree, Symbol sym) { DocTree dt = unit.docComments.getCommentTree(tree); if (dt != null) { String label = (sym == null) ? Pretty.toSimpleString(tree) : sym.name.toString(); dprinter.printDocTree(label, dt); dprinter.out.println(); } } }; ds.scan(tree); } }, new Handler("symbols") { @Override void handle(String name, JCCompilationUnit unit, JCTree tree, final DPrinter dprinter) { TreeScanner ds = new DeclScanner() { public void visitDecl(JCTree tree, Symbol sym) { String label = (sym == null) ? Pretty.toSimpleString(tree) : sym.name.toString(); dprinter.printSymbol(label, sym); dprinter.out.println(); } }; ds.scan(tree); } }, new Handler("types") { @Override void handle(String name, JCCompilationUnit unit, 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); } } }; } protected static abstract class DeclScanner extends 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); } protected abstract void visitDecl(JCTree tree, Symbol sym); } // </editor-fold> }