1 /*
2  * Copyright (c) 2013, 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 
24 import java.io.File;
25 import java.io.IOException;
26 import java.io.PrintWriter;
27 import java.lang.reflect.Field;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.EnumSet;
32 import java.util.HashMap;
33 import java.util.List;
34 import java.util.Locale;
35 import java.util.Map;
36 import java.util.Set;
37 
38 import javax.lang.model.element.Name;
39 import javax.lang.model.element.TypeElement;
40 import javax.tools.FileObject;
41 import javax.tools.JavaCompiler;
42 import javax.tools.JavaFileObject;
43 import javax.tools.StandardJavaFileManager;
44 import javax.tools.StandardLocation;
45 import javax.tools.ToolProvider;
46 
47 import com.sun.source.util.JavacTask;
48 import com.sun.source.util.TaskEvent;
49 import com.sun.source.util.TaskListener;
50 import com.sun.source.util.Trees;
51 import com.sun.tools.javac.api.JavacTrees;
52 import com.sun.tools.javac.code.SymbolMetadata;
53 import com.sun.tools.javac.code.Attribute;
54 import com.sun.tools.javac.code.Flags;
55 import com.sun.tools.javac.code.Kinds;
56 import com.sun.tools.javac.code.Printer;
57 import com.sun.tools.javac.code.Scope;
58 import com.sun.tools.javac.code.Scope.CompoundScope;
59 import com.sun.tools.javac.code.Symbol;
60 import com.sun.tools.javac.code.Symbol.*;
61 import com.sun.tools.javac.code.Type;
62 import com.sun.tools.javac.code.Type.*;
63 import com.sun.tools.javac.code.TypeTag;
64 import com.sun.tools.javac.tree.JCTree;
65 import com.sun.tools.javac.tree.JCTree.*;
66 import com.sun.tools.javac.tree.Pretty;
67 import com.sun.tools.javac.tree.TreeInfo;
68 import com.sun.tools.javac.tree.TreeScanner;
69 import com.sun.tools.javac.util.Assert;
70 import com.sun.tools.javac.util.Context;
71 import com.sun.tools.javac.util.Log;
72 
73 
74 /**
75  * Debug printer for javac internals, for when toString() just isn't enough.
76  *
77  * <p>
78  * The printer provides an API to generate structured views of javac objects,
79  * such as AST nodes, symbol, types and annotations. Various aspects of the
80  * output can be configured, such as whether to show nulls, empty lists, or
81  * a compressed representation of the source code. Visitors are used to walk
82  * object hierarchies, and can be replaced with custom visitors if the default
83  * visitors are not flexible enough.
84  *
85  * <p>
86  * In general, nodes are printed with an initial line identifying the node
87  * followed by indented lines for the child nodes. Currently, graphs are
88  * represented by printing a spanning subtree.
89  *
90  * <p>
91  * The printer can be accessed via a simple command-line utility,
92  * which makes it easy to see the internal representation of source code,
93  * such as simple test programs, during the compilation pipeline.
94  *
95  *  <p><b>This is NOT part of any supported API.
96  *  If you write code that depends on this, you do so at your own risk.
97  *  This code and its internal interfaces are subject to change or
98  *  deletion without notice.</b>
99  */
100 
101 public class DPrinter {
102     protected final PrintWriter out;
103     protected final Trees trees;
104     protected Printer printer;
105     protected boolean showEmptyItems = true;
106     protected boolean showNulls = true;
107     protected boolean showPositions = false;
108     protected boolean showSrc;
109     protected boolean showTreeSymbols;
110     protected boolean showTreeTypes;
111     protected int maxSrcLength = 32;
112     protected Locale locale = Locale.getDefault();
113     protected static final String NULL = "#null";
114 
115     // <editor-fold defaultstate="collapsed" desc="Configuration">
116 
instance(Context context)117     public static DPrinter instance(Context context) {
118         DPrinter dp = context.get(DPrinter.class);
119         if (dp == null) {
120             dp = new DPrinter(context);
121         }
122         return dp;
123 
124     }
125 
DPrinter(Context context)126     protected DPrinter(Context context) {
127         context.put(DPrinter.class, this);
128         out = context.get(Log.outKey);
129         trees = JavacTrees.instance(context);
130     }
131 
DPrinter(PrintWriter out, Trees trees)132     public DPrinter(PrintWriter out, Trees trees) {
133         this.out = out;
134         this.trees = trees;
135     }
136 
emptyItems(boolean showEmptyItems)137     public DPrinter emptyItems(boolean showEmptyItems) {
138         this.showEmptyItems = showEmptyItems;
139         return this;
140     }
141 
nulls(boolean showNulls)142     public DPrinter nulls(boolean showNulls) {
143         this.showNulls = showNulls;
144         return this;
145     }
146 
positions(boolean showPositions)147     public DPrinter positions(boolean showPositions) {
148         this.showPositions = showPositions;
149         return this;
150     }
151 
source(boolean showSrc)152     public DPrinter source(boolean showSrc) {
153         this.showSrc = showSrc;
154         return this;
155     }
156 
source(int maxSrcLength)157     public DPrinter source(int maxSrcLength) {
158         this.showSrc = true;
159         this.maxSrcLength = maxSrcLength;
160         return this;
161     }
162 
treeSymbols(boolean showTreeSymbols)163     public DPrinter treeSymbols(boolean showTreeSymbols) {
164         this.showTreeSymbols = showTreeSymbols;
165         return this;
166     }
167 
treeTypes(boolean showTreeTypes)168     public DPrinter treeTypes(boolean showTreeTypes) {
169         this.showTreeTypes = showTreeTypes;
170         return this;
171     }
172 
typeSymbolPrinter(Printer p)173     public DPrinter typeSymbolPrinter(Printer p) {
174         printer = p;
175         return this;
176     }
177 
178     // </editor-fold>
179 
180     // <editor-fold defaultstate="collapsed" desc="Printing">
181 
182     protected enum Details {
183         /** A one-line non-recursive summary */
184         SUMMARY,
185         /** Multi-line, possibly recursive. */
186         FULL
187     };
188 
printAnnotations(String label, SymbolMetadata annotations)189     public void printAnnotations(String label, SymbolMetadata annotations) {
190         printAnnotations(label, annotations, Details.FULL);
191     }
192 
printAnnotations(String label, SymbolMetadata annotations, Details details)193     protected void printAnnotations(String label, SymbolMetadata annotations, Details details) {
194         if (annotations == null) {
195             printNull(label);
196         } else {
197             // no SUMMARY format currently available to use
198 
199             // use reflection to get at private fields
200             Object DECL_NOT_STARTED = getField(null, SymbolMetadata.class, "DECL_NOT_STARTED");
201             Object DECL_IN_PROGRESS = getField(null, SymbolMetadata.class, "DECL_IN_PROGRESS");
202             Object attributes = getField(annotations, SymbolMetadata.class, "attributes");
203             Object type_attributes = getField(annotations, SymbolMetadata.class, "type_attributes");
204 
205             if (!showEmptyItems) {
206                 if (attributes instanceof List && ((List) attributes).isEmpty()
207                         && attributes != DECL_NOT_STARTED
208                         && attributes != DECL_IN_PROGRESS
209                         && type_attributes instanceof List && ((List) type_attributes).isEmpty())
210                     return;
211             }
212 
213             printString(label, hashString(annotations));
214 
215             indent(+1);
216             if (attributes == DECL_NOT_STARTED)
217                 printString("attributes", "DECL_NOT_STARTED");
218             else if (attributes == DECL_IN_PROGRESS)
219                 printString("attributes", "DECL_IN_PROGRESS");
220             else if (attributes instanceof List)
221                 printList("attributes", (List) attributes);
222             else
223                 printObject("attributes", attributes, Details.SUMMARY);
224 
225             if (attributes instanceof List)
226                 printList("type_attributes", (List) type_attributes);
227             else
228                 printObject("type_attributes", type_attributes, Details.SUMMARY);
229             indent(-1);
230         }
231     }
232 
printAttribute(String label, Attribute attr)233     public void printAttribute(String label, Attribute attr) {
234         if (attr == null) {
235             printNull(label);
236         } else {
237             printString(label, attr.getClass().getSimpleName());
238 
239             indent(+1);
240             attr.accept(attrVisitor);
241             indent(-1);
242         }
243     }
244 
printFileObject(String label, FileObject fo)245     public void printFileObject(String label, FileObject fo) {
246         if (fo == null) {
247             printNull(label);
248         } else {
249             printString(label, fo.getName());
250         }
251     }
252 
printImplClass(T item, Class<? extends T> stdImplClass)253     protected <T> void printImplClass(T item, Class<? extends T> stdImplClass) {
254         if (item.getClass() != stdImplClass)
255             printString("impl", item.getClass().getName());
256     }
257 
printInt(String label, int i)258     public void printInt(String label, int i) {
259         printString(label, String.valueOf(i));
260     }
261 
printList(String label, List<?> list)262     public void printList(String label, List<?> list) {
263         if (list == null) {
264              printNull(label);
265         } else if (!list.isEmpty() || showEmptyItems) {
266             printString(label, "[" + list.size() + "]");
267 
268             indent(+1);
269             int i = 0;
270             for (Object item: list) {
271                 printObject(String.valueOf(i++), item, Details.FULL);
272             }
273             indent(-1);
274         }
275     }
276 
printName(String label, Name name)277     public void printName(String label, Name name) {
278         if (name == null) {
279             printNull(label);
280         } else {
281             printString(label, name.toString());
282         }
283     }
284 
printNull(String label)285     public void printNull(String label) {
286         if (showNulls)
287             printString(label, NULL);
288     }
289 
printObject(String label, Object item, Details details)290     protected void printObject(String label, Object item, Details details) {
291         if (item == null) {
292             printNull(label);
293         } else if (item instanceof Attribute) {
294             printAttribute(label, (Attribute) item);
295         } else if (item instanceof Symbol) {
296             printSymbol(label, (Symbol) item, details);
297         } else if (item instanceof Type) {
298             printType(label, (Type) item, details);
299         } else if (item instanceof JCTree) {
300             printTree(label, (JCTree) item);
301         } else if (item instanceof List) {
302             printList(label, (List) item);
303         } else if (item instanceof Name) {
304             printName(label, (Name) item);
305         } else {
306             printString(label, String.valueOf(item));
307         }
308     }
309 
printScope(String label, Scope scope)310     public void printScope(String label, Scope scope) {
311         printScope(label, scope, Details.FULL);
312     }
313 
printScope(String label, Scope scope, Details details)314     public void printScope(String label, Scope scope, Details details) {
315         if (scope == null) {
316             printNull(label);
317         } else {
318             switch (details) {
319                 case SUMMARY: {
320                     indent();
321                     out.print(label);
322                     out.print(": [");
323                     String sep = "";
324                     for (Symbol sym: scope.getElements()) {
325                         out.print(sep);
326                         out.print(sym.name);
327                         sep = ",";
328                     }
329                     out.println("]");
330                     break;
331                 }
332 
333                 case FULL: {
334                     indent();
335                     out.println(label);
336 
337                     indent(+1);
338                     printImplClass(scope, Scope.class);
339                     printSymbol("owner", scope.owner, Details.SUMMARY);
340                     printScope("next", scope.next, Details.SUMMARY);
341                     printObject("shared", getField(scope, Scope.class, "shared"), Details.SUMMARY);
342                     if (scope instanceof CompoundScope) {
343                         printObject("subScopes",
344                                 getField(scope, CompoundScope.class, "subScopes"),
345                                 Details.FULL);
346                     } else {
347                         for (Symbol sym : scope.getElements()) {
348                             printSymbol(sym.name.toString(), sym, Details.SUMMARY);
349                         }
350                     }
351                     indent(-1);
352                     break;
353                 }
354             }
355         }
356     }
357 
printSource(String label, JCTree tree)358     public void printSource(String label, JCTree tree) {
359         printString(label, Pretty.toSimpleString(tree, maxSrcLength));
360     }
361 
printString(String label, String text)362     public void printString(String label, String text) {
363         indent();
364         out.print(label);
365         out.print(": ");
366         out.print(text);
367         out.println();
368     }
369 
printSymbol(String label, Symbol symbol)370     public void printSymbol(String label, Symbol symbol) {
371         printSymbol(label, symbol, Details.FULL);
372     }
373 
printSymbol(String label, Symbol sym, Details details)374     protected void printSymbol(String label, Symbol sym, Details details) {
375         if (sym == null) {
376             printNull(label);
377         } else {
378             switch (details) {
379             case SUMMARY:
380                 printString(label, toString(sym));
381                 break;
382 
383             case FULL:
384                 indent();
385                 out.print(label);
386                 out.println(": " +
387                         info(sym.getClass(),
388                             String.format("0x%x--%s", sym.kind, Kinds.kindName(sym)),
389                             sym.getKind())
390                         + " " + sym.name
391                         + " " + hashString(sym));
392 
393                 indent(+1);
394                 if (showSrc) {
395                     JCTree tree = (JCTree) trees.getTree(sym);
396                     if (tree != null)
397                         printSource("src", tree);
398                 }
399                 printString("flags", String.format("0x%x--%s",
400                         sym.flags_field, Flags.toString(sym.flags_field)));
401                 printObject("completer", sym.completer, Details.SUMMARY); // what if too long?
402                 printSymbol("owner", sym.owner, Details.SUMMARY);
403                 printType("type", sym.type, Details.SUMMARY);
404                 printType("erasure", sym.erasure_field, Details.SUMMARY);
405                 sym.accept(symVisitor, null);
406                 printAnnotations("annotations", sym.getAnnotations(), Details.SUMMARY);
407                 indent(-1);
408             }
409         }
410     }
411 
toString(Symbol sym)412     protected String toString(Symbol sym) {
413         return (printer != null) ? printer.visit(sym, locale) : String.valueOf(sym);
414     }
415 
printTree(String label, JCTree tree)416     protected void printTree(String label, JCTree tree) {
417         if (tree == null) {
418             printNull(label);
419         } else {
420             indent();
421             String ext;
422             try {
423                 ext = tree.getKind().name();
424             } catch (Throwable t) {
425                 ext = "n/a";
426             }
427             out.print(label + ": " + info(tree.getClass(), tree.getTag(), ext));
428             if (showPositions) {
429                 // We can always get start position, but to get end position
430                 // and/or line+offset, we would need a JCCompilationUnit
431                 out.print(" pos:" + tree.pos);
432             }
433             if (showTreeTypes && tree.type != null)
434                 out.print(" type:" + toString(tree.type));
435             Symbol sym;
436             if (showTreeSymbols && (sym = TreeInfo.symbolFor(tree)) != null)
437                 out.print(" sym:" + toString(sym));
438             out.println();
439 
440             indent(+1);
441             if (showSrc) {
442                 indent();
443                 out.println("src: " + Pretty.toSimpleString(tree, maxSrcLength));
444             }
445             tree.accept(treeVisitor);
446             indent(-1);
447         }
448     }
449 
printType(String label, Type type)450     public void printType(String label, Type type) {
451         printType(label, type, Details.FULL);
452     }
453 
printType(String label, Type type, Details details)454     protected void printType(String label, Type type, Details details) {
455         if (type == null)
456             printNull(label);
457         else {
458             switch (details) {
459                 case SUMMARY:
460                     printString(label, toString(type));
461                     break;
462 
463                 case FULL:
464                     indent();
465                     out.print(label);
466                     out.println(": " + info(type.getClass(), type.getTag(), type.getKind())
467                             + " " + hashString(type));
468 
469                     indent(+1);
470                     printSymbol("tsym", type.tsym, Details.SUMMARY);
471                     printObject("constValue", type.constValue(), Details.SUMMARY);
472                     printObject("annotations", type.getAnnotationMirrors(), Details.SUMMARY);
473                     type.accept(typeVisitor, null);
474                     indent(-1);
475             }
476         }
477     }
478 
toString(Type type)479     protected String toString(Type type) {
480         return (printer != null) ? printer.visit(type, locale) : String.valueOf(type);
481     }
482 
hashString(Object obj)483     protected String hashString(Object obj) {
484         return String.format("#%x", obj.hashCode());
485     }
486 
info(Class<?> clazz, Object internal, Object external)487     protected String info(Class<?> clazz, Object internal, Object external) {
488         return String.format("%s,%s,%s", clazz.getSimpleName(), internal, external);
489     }
490 
491     private int indent = 0;
492 
indent()493     protected void indent() {
494         for (int i = 0; i < indent; i++) {
495             out.print("  ");
496         }
497     }
498 
indent(int n)499     protected void indent(int n) {
500         indent += n;
501     }
502 
getField(Object o, Class<?> clazz, String name)503     protected Object getField(Object o, Class<?> clazz, String name) {
504         try {
505             Field f = clazz.getDeclaredField(name);
506             boolean prev = f.isAccessible();
507             f.setAccessible(true);
508             try {
509                 return f.get(o);
510             } finally {
511                 f.setAccessible(prev);
512             }
513         } catch (ReflectiveOperationException e) {
514             return e;
515         } catch (SecurityException e) {
516             return e;
517         }
518     }
519 
520     // </editor-fold>
521 
522     // <editor-fold defaultstate="collapsed" desc="JCTree visitor methods">
523 
524     protected JCTree.Visitor treeVisitor = new TreeVisitor();
525 
526     /**
527      * Default visitor class for JCTree (AST) objects.
528      */
529     public class TreeVisitor extends JCTree.Visitor {
530         @Override
visitTopLevel(JCCompilationUnit tree)531         public void visitTopLevel(JCCompilationUnit tree) {
532             printList("packageAnnotations", tree.packageAnnotations);
533             printTree("pid", tree.pid);
534             printList("defs", tree.defs);
535         }
536 
537         @Override
visitImport(JCImport tree)538         public void visitImport(JCImport tree) {
539             printTree("qualid", tree.qualid);
540         }
541 
542         @Override
visitClassDef(JCClassDecl tree)543         public void visitClassDef(JCClassDecl tree) {
544             printName("name", tree.name);
545             printTree("mods", tree.mods);
546             printList("typarams", tree.typarams);
547             printTree("extending", tree.extending);
548             printList("implementing", tree.implementing);
549             printList("defs", tree.defs);
550         }
551 
552         @Override
visitMethodDef(JCMethodDecl tree)553         public void visitMethodDef(JCMethodDecl tree) {
554             printName("name", tree.name);
555             printTree("mods", tree.mods);
556             printTree("restype", tree.restype);
557             printList("typarams", tree.typarams);
558             printTree("recvparam", tree.recvparam);
559             printList("params", tree.params);
560             printList("thrown", tree.thrown);
561             printTree("defaultValue", tree.defaultValue);
562             printTree("body", tree.body);
563         }
564 
565         @Override
visitVarDef(JCVariableDecl tree)566         public void visitVarDef(JCVariableDecl tree) {
567             printName("name", tree.name);
568             printTree("mods", tree.mods);
569             printTree("vartype", tree.vartype);
570             printTree("init", tree.init);
571         }
572 
573         @Override
visitSkip(JCSkip tree)574         public void visitSkip(JCSkip tree) {
575         }
576 
577         @Override
visitBlock(JCBlock tree)578         public void visitBlock(JCBlock tree) {
579             printList("stats", tree.stats);
580         }
581 
582         @Override
visitDoLoop(JCDoWhileLoop tree)583         public void visitDoLoop(JCDoWhileLoop tree) {
584             printTree("body", tree.body);
585             printTree("cond", tree.cond);
586         }
587 
588         @Override
visitWhileLoop(JCWhileLoop tree)589         public void visitWhileLoop(JCWhileLoop tree) {
590             printTree("cond", tree.cond);
591             printTree("body", tree.body);
592         }
593 
594         @Override
visitForLoop(JCForLoop tree)595         public void visitForLoop(JCForLoop tree) {
596             printList("init", tree.init);
597             printTree("cond", tree.cond);
598             printList("step", tree.step);
599             printTree("body", tree.body);
600         }
601 
602         @Override
visitForeachLoop(JCEnhancedForLoop tree)603         public void visitForeachLoop(JCEnhancedForLoop tree) {
604             printTree("var", tree.var);
605             printTree("expr", tree.expr);
606             printTree("body", tree.body);
607         }
608 
609         @Override
visitLabelled(JCLabeledStatement tree)610         public void visitLabelled(JCLabeledStatement tree) {
611             printTree("body", tree.body);
612         }
613 
614         @Override
visitSwitch(JCSwitch tree)615         public void visitSwitch(JCSwitch tree) {
616             printTree("selector", tree.selector);
617             printList("cases", tree.cases);
618         }
619 
620         @Override
visitCase(JCCase tree)621         public void visitCase(JCCase tree) {
622             printTree("pat", tree.pat);
623             printList("stats", tree.stats);
624         }
625 
626         @Override
visitSynchronized(JCSynchronized tree)627         public void visitSynchronized(JCSynchronized tree) {
628             printTree("lock", tree.lock);
629             printTree("body", tree.body);
630         }
631 
632         @Override
visitTry(JCTry tree)633         public void visitTry(JCTry tree) {
634             printList("resources", tree.resources);
635             printTree("body", tree.body);
636             printList("catchers", tree.catchers);
637             printTree("finalizer", tree.finalizer);
638         }
639 
640         @Override
visitCatch(JCCatch tree)641         public void visitCatch(JCCatch tree) {
642             printTree("param", tree.param);
643             printTree("body", tree.body);
644         }
645 
646         @Override
visitConditional(JCConditional tree)647         public void visitConditional(JCConditional tree) {
648             printTree("cond", tree.cond);
649             printTree("truepart", tree.truepart);
650             printTree("falsepart", tree.falsepart);
651         }
652 
653         @Override
visitIf(JCIf tree)654         public void visitIf(JCIf tree) {
655             printTree("cond", tree.cond);
656             printTree("thenpart", tree.thenpart);
657             printTree("elsepart", tree.elsepart);
658         }
659 
660         @Override
visitExec(JCExpressionStatement tree)661         public void visitExec(JCExpressionStatement tree) {
662             printTree("expr", tree.expr);
663         }
664 
665         @Override
visitBreak(JCBreak tree)666         public void visitBreak(JCBreak tree) {
667             printName("label", tree.label);
668         }
669 
670         @Override
visitContinue(JCContinue tree)671         public void visitContinue(JCContinue tree) {
672             printName("label", tree.label);
673         }
674 
675         @Override
visitReturn(JCReturn tree)676         public void visitReturn(JCReturn tree) {
677             printTree("expr", tree.expr);
678         }
679 
680         @Override
visitThrow(JCThrow tree)681         public void visitThrow(JCThrow tree) {
682             printTree("expr", tree.expr);
683         }
684 
685         @Override
visitAssert(JCAssert tree)686         public void visitAssert(JCAssert tree) {
687             printTree("cond", tree.cond);
688             printTree("detail", tree.detail);
689         }
690 
691         @Override
visitApply(JCMethodInvocation tree)692         public void visitApply(JCMethodInvocation tree) {
693             printList("typeargs", tree.typeargs);
694             printTree("meth", tree.meth);
695             printList("args", tree.args);
696         }
697 
698         @Override
visitNewClass(JCNewClass tree)699         public void visitNewClass(JCNewClass tree) {
700             printTree("encl", tree.encl);
701             printList("typeargs", tree.typeargs);
702             printTree("clazz", tree.clazz);
703             printList("args", tree.args);
704             printTree("def", tree.def);
705         }
706 
707         @Override
visitNewArray(JCNewArray tree)708         public void visitNewArray(JCNewArray tree) {
709             printList("annotations", tree.annotations);
710             printTree("elemtype", tree.elemtype);
711             printList("dims", tree.dims);
712             printList("dimAnnotations", tree.dimAnnotations);
713             printList("elems", tree.elems);
714         }
715 
716         @Override
visitLambda(JCLambda tree)717         public void visitLambda(JCLambda tree) {
718             printTree("body", tree.body);
719             printList("params", tree.params);
720         }
721 
722         @Override
visitParens(JCParens tree)723         public void visitParens(JCParens tree) {
724             printTree("expr", tree.expr);
725         }
726 
727         @Override
visitAssign(JCAssign tree)728         public void visitAssign(JCAssign tree) {
729             printTree("lhs", tree.lhs);
730             printTree("rhs", tree.rhs);
731         }
732 
733         @Override
visitAssignop(JCAssignOp tree)734         public void visitAssignop(JCAssignOp tree) {
735             printTree("lhs", tree.lhs);
736             printTree("rhs", tree.rhs);
737         }
738 
739         @Override
visitUnary(JCUnary tree)740         public void visitUnary(JCUnary tree) {
741             printTree("arg", tree.arg);
742         }
743 
744         @Override
visitBinary(JCBinary tree)745         public void visitBinary(JCBinary tree) {
746             printTree("lhs", tree.lhs);
747             printTree("rhs", tree.rhs);
748         }
749 
750         @Override
visitTypeCast(JCTypeCast tree)751         public void visitTypeCast(JCTypeCast tree) {
752             printTree("clazz", tree.clazz);
753             printTree("expr", tree.expr);
754         }
755 
756         @Override
visitTypeTest(JCInstanceOf tree)757         public void visitTypeTest(JCInstanceOf tree) {
758             printTree("expr", tree.expr);
759             printTree("clazz", tree.clazz);
760         }
761 
762         @Override
visitIndexed(JCArrayAccess tree)763         public void visitIndexed(JCArrayAccess tree) {
764             printTree("indexed", tree.indexed);
765             printTree("index", tree.index);
766         }
767 
768         @Override
visitSelect(JCFieldAccess tree)769         public void visitSelect(JCFieldAccess tree) {
770             printTree("selected", tree.selected);
771         }
772 
773         @Override
visitReference(JCMemberReference tree)774         public void visitReference(JCMemberReference tree) {
775             printTree("expr", tree.expr);
776             printList("typeargs", tree.typeargs);
777         }
778 
779         @Override
visitIdent(JCIdent tree)780         public void visitIdent(JCIdent tree) {
781             printName("name", tree.name);
782         }
783 
784         @Override
visitLiteral(JCLiteral tree)785         public void visitLiteral(JCLiteral tree) {
786             printString("value", Pretty.toSimpleString(tree, 32));
787         }
788 
789         @Override
visitTypeIdent(JCPrimitiveTypeTree tree)790         public void visitTypeIdent(JCPrimitiveTypeTree tree) {
791             printString("typetag", tree.typetag.name());
792         }
793 
794         @Override
visitTypeArray(JCArrayTypeTree tree)795         public void visitTypeArray(JCArrayTypeTree tree) {
796             printTree("elemtype", tree.elemtype);
797         }
798 
799         @Override
visitTypeApply(JCTypeApply tree)800         public void visitTypeApply(JCTypeApply tree) {
801             printTree("clazz", tree.clazz);
802             printList("arguments", tree.arguments);
803         }
804 
805         @Override
visitTypeUnion(JCTypeUnion tree)806         public void visitTypeUnion(JCTypeUnion tree) {
807             printList("alternatives", tree.alternatives);
808         }
809 
810         @Override
visitTypeIntersection(JCTypeIntersection tree)811         public void visitTypeIntersection(JCTypeIntersection tree) {
812             printList("bounds", tree.bounds);
813         }
814 
815         @Override
visitTypeParameter(JCTypeParameter tree)816         public void visitTypeParameter(JCTypeParameter tree) {
817             printName("name", tree.name);
818             printList("annotations", tree.annotations);
819             printList("bounds", tree.bounds);
820         }
821 
822         @Override
visitWildcard(JCWildcard tree)823         public void visitWildcard(JCWildcard tree) {
824             printTree("kind", tree.kind);
825             printTree("inner", tree.inner);
826         }
827 
828         @Override
visitTypeBoundKind(TypeBoundKind tree)829         public void visitTypeBoundKind(TypeBoundKind tree) {
830             printString("kind", tree.kind.name());
831         }
832 
833         @Override
visitModifiers(JCModifiers tree)834         public void visitModifiers(JCModifiers tree) {
835             printList("annotations", tree.annotations);
836             printString("flags", String.valueOf(Flags.asFlagSet(tree.flags)));
837         }
838 
839         @Override
visitAnnotation(JCAnnotation tree)840         public void visitAnnotation(JCAnnotation tree) {
841             printTree("annotationType", tree.annotationType);
842             printList("args", tree.args);
843         }
844 
845         @Override
visitAnnotatedType(JCAnnotatedType tree)846         public void visitAnnotatedType(JCAnnotatedType tree) {
847             printList("annotations", tree.annotations);
848             printTree("underlyingType", tree.underlyingType);
849         }
850 
851         @Override
visitErroneous(JCErroneous tree)852         public void visitErroneous(JCErroneous tree) {
853             printList("errs", tree.errs);
854         }
855 
856         @Override
visitLetExpr(LetExpr tree)857         public void visitLetExpr(LetExpr tree) {
858             printList("defs", tree.defs);
859             printTree("expr", tree.expr);
860         }
861 
862         @Override
visitTree(JCTree tree)863         public void visitTree(JCTree tree) {
864             Assert.error();
865         }
866     }
867 
868     // </editor-fold>
869 
870     // <editor-fold defaultstate="collapsed" desc="Symbol visitor">
871 
872     protected Symbol.Visitor<Void,Void> symVisitor = new SymbolVisitor();
873 
874     /**
875      * Default visitor class for Symbol objects.
876      * Note: each visitXYZ method ends by calling the corresponding
877      * visit method for its superclass.
878      */
879     class SymbolVisitor implements Symbol.Visitor<Void,Void> {
880         @Override
visitClassSymbol(ClassSymbol sym, Void ignore)881         public Void visitClassSymbol(ClassSymbol sym, Void ignore) {
882             printName("fullname", sym.fullname);
883             printName("flatname", sym.flatname);
884             printScope("members", sym.members_field);
885             printFileObject("sourcefile", sym.sourcefile);
886             printFileObject("classfile", sym.classfile);
887             // trans-local?
888             // pool?
889             return visitTypeSymbol(sym, null);
890         }
891 
892         @Override
visitMethodSymbol(MethodSymbol sym, Void ignore)893         public Void visitMethodSymbol(MethodSymbol sym, Void ignore) {
894             // code
895             printList("params", sym.params);
896             printList("savedParameterNames", sym.savedParameterNames);
897             return visitSymbol(sym, null);
898         }
899 
900         @Override
visitPackageSymbol(PackageSymbol sym, Void ignore)901         public Void visitPackageSymbol(PackageSymbol sym, Void ignore) {
902             printName("fullname", sym.fullname);
903             printScope("members", sym.members_field);
904             printSymbol("package-info", sym.package_info, Details.SUMMARY);
905             return visitTypeSymbol(sym, null);
906         }
907 
908         @Override
visitOperatorSymbol(OperatorSymbol sym, Void ignore)909         public Void visitOperatorSymbol(OperatorSymbol sym, Void ignore) {
910             printInt("opcode", sym.opcode);
911             return visitMethodSymbol(sym, null);
912         }
913 
914         @Override
visitVarSymbol(VarSymbol sym, Void ignore)915         public Void visitVarSymbol(VarSymbol sym, Void ignore) {
916             printInt("pos", sym.pos);
917             printInt("adm", sym.adr);
918             // data is a private field, and the standard accessors may
919             // mutate it as part of lazy evaluation. Therefore, use
920             // reflection to get the raw data.
921             printObject("data", getField(sym, VarSymbol.class, "data"), Details.SUMMARY);
922             return visitSymbol(sym, null);
923         }
924 
925         @Override
visitTypeSymbol(TypeSymbol sym, Void ignore)926         public Void visitTypeSymbol(TypeSymbol sym, Void ignore) {
927             return visitSymbol(sym, null);
928         }
929 
930         @Override
visitSymbol(Symbol sym, Void ignore)931         public Void visitSymbol(Symbol sym, Void ignore) {
932             return null;
933         }
934     }
935 
936     // </editor-fold>
937 
938     // <editor-fold defaultstate="collapsed" desc="Type visitor">
939 
940     protected Type.Visitor<Void,Void> typeVisitor = new TypeVisitor();
941 
942     /**
943      * Default visitor class for Type objects.
944      * Note: each visitXYZ method ends by calling the corresponding
945      * visit method for its superclass.
946      */
947     public class TypeVisitor implements Type.Visitor<Void,Void> {
visitAnnotatedType(AnnotatedType type, Void ignore)948         public Void visitAnnotatedType(AnnotatedType type, Void ignore) {
949             printList("typeAnnotations", type.getAnnotationMirrors());
950             printType("underlyingType", type.unannotatedType(), Details.FULL);
951             return visitType(type, null);
952         }
953 
visitArrayType(ArrayType type, Void ignore)954         public Void visitArrayType(ArrayType type, Void ignore) {
955             printType("elemType", type.elemtype, Details.FULL);
956             return visitType(type, null);
957         }
958 
visitCapturedType(CapturedType type, Void ignore)959         public Void visitCapturedType(CapturedType type, Void ignore) {
960             printType("wildcard", type.wildcard, Details.FULL);
961             return visitTypeVar(type, null);
962         }
963 
visitClassType(ClassType type, Void ignore)964         public Void visitClassType(ClassType type, Void ignore) {
965             printType("outer", type.getEnclosingType(), Details.SUMMARY);
966             printList("typarams", type.typarams_field);
967             printList("allparams", type.allparams_field);
968             printType("supertype", type.supertype_field, Details.SUMMARY);
969             printList("interfaces", type.interfaces_field);
970             printList("allinterfaces", type.all_interfaces_field);
971             return visitType(type, null);
972         }
973 
visitErrorType(ErrorType type, Void ignore)974         public Void visitErrorType(ErrorType type, Void ignore) {
975             printType("originalType", type.getOriginalType(), Details.FULL);
976             return visitClassType(type, null);
977         }
978 
visitForAll(ForAll type, Void ignore)979         public Void visitForAll(ForAll type, Void ignore) {
980             printList("tvars", type.tvars);
981             return visitDelegatedType(type);
982         }
983 
visitMethodType(MethodType type, Void ignore)984         public Void visitMethodType(MethodType type, Void ignore) {
985             printList("argtypes", type.argtypes);
986             printType("restype", type.restype, Details.FULL);
987             printList("thrown", type.thrown);
988             return visitType(type, null);
989         }
990 
visitPackageType(PackageType type, Void ignore)991         public Void visitPackageType(PackageType type, Void ignore) {
992             return visitType(type, null);
993         }
994 
visitTypeVar(TypeVar type, Void ignore)995         public Void visitTypeVar(TypeVar type, Void ignore) {
996             // For TypeVars (and not subtypes), the bound should always be
997             // null or bot. So, only print the bound for subtypes of TypeVar,
998             // or if the bound is (erroneously) not null or bot.
999             if (!type.hasTag(TypeTag.TYPEVAR)
1000                     || !(type.bound == null || type.bound.hasTag(TypeTag.BOT))) {
1001                 printType("bound", type.bound, Details.FULL);
1002             }
1003             printType("lower", type.lower, Details.FULL);
1004             return visitType(type, null);
1005         }
1006 
visitUndetVar(UndetVar type, Void ignore)1007         public Void visitUndetVar(UndetVar type, Void ignore) {
1008             for (UndetVar.InferenceBound ib: UndetVar.InferenceBound.values())
1009                 printList("bounds." + ib, type.getBounds(ib));
1010             printInt("declaredCount", type.declaredCount);
1011             printType("inst", type.inst, Details.SUMMARY);
1012             return visitDelegatedType(type);
1013         }
1014 
visitWildcardType(WildcardType type, Void ignore)1015         public Void visitWildcardType(WildcardType type, Void ignore) {
1016             printType("type", type.type, Details.SUMMARY);
1017             printString("kind", type.kind.name());
1018             printType("bound", type.bound, Details.SUMMARY);
1019             return visitType(type, null);
1020         }
1021 
visitDelegatedType(DelegatedType type)1022         protected Void visitDelegatedType(DelegatedType type) {
1023             printType("qtype", type.qtype, Details.FULL);
1024             return visitType(type, null);
1025         }
1026 
visitType(Type type, Void ignore)1027         public Void visitType(Type type, Void ignore) {
1028             return null;
1029         }
1030     }
1031 
1032     // </editor-fold>
1033 
1034     // <editor-fold defaultstate="collapsed" desc="Attribute (annotations) visitor">
1035 
1036     protected Attribute.Visitor attrVisitor = new AttributeVisitor();
1037 
1038     /**
1039      * Default visitor class for Attribute (annotation) objects.
1040      */
1041     public class AttributeVisitor implements Attribute.Visitor {
1042 
visitConstant(Attribute.Constant a)1043         public void visitConstant(Attribute.Constant a) {
1044             printObject("value", a.value, Details.SUMMARY);
1045             visitAttribute(a);
1046         }
1047 
visitClass(Attribute.Class a)1048         public void visitClass(Attribute.Class a) {
1049             printObject("classType", a.classType, Details.SUMMARY);
1050             visitAttribute(a);
1051         }
1052 
visitCompound(Attribute.Compound a)1053         public void visitCompound(Attribute.Compound a) {
1054             if (a instanceof Attribute.TypeCompound) {
1055                 Attribute.TypeCompound ta = (Attribute.TypeCompound) a;
1056                 // consider a custom printer?
1057                 printObject("position", ta.position, Details.SUMMARY);
1058             }
1059             printObject("synthesized", a.isSynthesized(), Details.SUMMARY);
1060             printList("values", a.values);
1061             visitAttribute(a);
1062         }
1063 
visitArray(Attribute.Array a)1064         public void visitArray(Attribute.Array a) {
1065             printList("values", Arrays.asList(a.values));
1066             visitAttribute(a);
1067         }
1068 
visitEnum(Attribute.Enum a)1069         public void visitEnum(Attribute.Enum a) {
1070             printSymbol("value", a.value, Details.SUMMARY);
1071             visitAttribute(a);
1072         }
1073 
visitError(Attribute.Error a)1074         public void visitError(Attribute.Error a) {
1075             visitAttribute(a);
1076         }
1077 
visitAttribute(Attribute a)1078         public void visitAttribute(Attribute a) {
1079             printType("type", a.type, Details.SUMMARY);
1080         }
1081 
1082     }
1083     // </editor-fold>
1084 
1085     // <editor-fold defaultstate="collapsed" desc="Utility front end">
1086 
1087     /**
1088      * Utility class to invoke DPrinter from the command line.
1089      */
1090     static class Main {
main(String... args)1091         public static void main(String... args) throws IOException {
1092             Main m = new Main();
1093             PrintWriter out = new PrintWriter(System.out);
1094             try {
1095                 if (args.length == 0)
1096                     m.usage(out);
1097                 else
1098                     m.run(out, args);
1099             } finally {
1100                 out.flush();
1101             }
1102         }
1103 
usage(PrintWriter out)1104         void usage(PrintWriter out) {
1105             out.println("Usage:");
1106             out.println("  java " + Main.class.getName() + " mode [options] [javac-options]");
1107             out.print("where mode is one of: ");
1108             String sep = "";
1109             for (Handler h: getHandlers().values()) {
1110                 out.print(sep);
1111                 out.print(h.name);
1112                 sep = ", ";
1113             }
1114             out.println();
1115             out.println("and where options include:");
1116             out.println("  -before PARSE|ENTER|ANALYZE|GENERATE|ANNOTATION_PROCESSING|ANNOTATION_PROCESSING_ROUND");
1117             out.println("  -after PARSE|ENTER|ANALYZE|GENERATE|ANNOTATION_PROCESSING|ANNOTATION_PROCESSING_ROUND");
1118             out.println("  -showPositions");
1119             out.println("  -showSource");
1120             out.println("  -showTreeSymbols");
1121             out.println("  -showTreeTypes");
1122             out.println("  -hideEmptyItems");
1123             out.println("  -hideNulls");
1124         }
1125 
run(PrintWriter out, String... args)1126         void run(PrintWriter out, String... args) throws IOException {
1127             JavaCompiler c = ToolProvider.getSystemJavaCompiler();
1128             StandardJavaFileManager fm = c.getStandardFileManager(null, null, null);
1129 
1130             // DPrinter options
1131             final Set<TaskEvent.Kind> before = EnumSet.noneOf(TaskEvent.Kind.class);
1132             final Set<TaskEvent.Kind> after = EnumSet.noneOf(TaskEvent.Kind.class);
1133             boolean showPositions = false;
1134             boolean showSource = false;
1135             boolean showTreeSymbols = false;
1136             boolean showTreeTypes = false;
1137             boolean showEmptyItems = true;
1138             boolean showNulls = true;
1139 
1140             // javac options
1141             Collection<String> options = new ArrayList<String>();
1142             Collection<File> files = new ArrayList<File>();
1143             String classpath = null;
1144             String classoutdir = null;
1145 
1146             final Handler h = getHandlers().get(args[0]);
1147             if (h == null)
1148                 throw new IllegalArgumentException(args[0]);
1149 
1150             for (int i = 1; i < args.length; i++) {
1151                 String arg = args[i];
1152                 if (arg.equals("-before") && i + 1 < args.length) {
1153                     before.add(getKind(args[++i]));
1154                 } else if (arg.equals("-after") && i + 1 < args.length) {
1155                     after.add(getKind(args[++i]));
1156                 } else if (arg.equals("-showPositions")) {
1157                     showPositions = true;
1158                 } else if (arg.equals("-showSource")) {
1159                     showSource = true;
1160                 } else if (arg.equals("-showTreeSymbols")) {
1161                     showTreeSymbols = true;
1162                 } else if (arg.equals("-showTreeTypes")) {
1163                     showTreeTypes = true;
1164                 } else if (arg.equals("-hideEmptyLists")) {
1165                     showEmptyItems = false;
1166                 } else if (arg.equals("-hideNulls")) {
1167                     showNulls = false;
1168                 } else if (arg.equals("-classpath") && i + 1 < args.length) {
1169                     classpath = args[++i];
1170                 } else if (arg.equals("-d") && i + 1 < args.length) {
1171                     classoutdir = args[++i];
1172                 } else if (arg.startsWith("-")) {
1173                     int n = c.isSupportedOption(arg);
1174                     if (n < 0) throw new IllegalArgumentException(arg);
1175                     options.add(arg);
1176                     while (n > 0) options.add(args[++i]);
1177                 } else if (arg.endsWith(".java")) {
1178                     files.add(new File(arg));
1179                 }
1180             }
1181 
1182             if (classoutdir != null) {
1183                 fm.setLocation(StandardLocation.CLASS_OUTPUT, Arrays.asList(new File(classoutdir)));
1184             }
1185 
1186             if (classpath != null) {
1187                 Collection<File> path = new ArrayList<File>();
1188                 for (String p: classpath.split(File.pathSeparator)) {
1189                     if (p.isEmpty()) continue;
1190                     File f = new File(p);
1191                     if (f.exists()) path.add(f);
1192                 }
1193                 fm.setLocation(StandardLocation.CLASS_PATH, path);
1194             }
1195             Iterable<? extends JavaFileObject> fos = fm.getJavaFileObjectsFromFiles(files);
1196 
1197             JavacTask task = (JavacTask) c.getTask(out, fm, null, options, null, fos);
1198             final Trees trees = Trees.instance(task);
1199 
1200             final DPrinter dprinter = new DPrinter(out, trees);
1201             dprinter.source(showSource)
1202                     .emptyItems(showEmptyItems)
1203                     .nulls(showNulls)
1204                     .positions(showPositions)
1205                     .treeSymbols(showTreeSymbols)
1206                     .treeTypes(showTreeTypes);
1207 
1208             if (before.isEmpty() && after.isEmpty()) {
1209                 if (h.name.equals("trees") && !showTreeSymbols && !showTreeTypes)
1210                     after.add(TaskEvent.Kind.PARSE);
1211                 else
1212                     after.add(TaskEvent.Kind.ANALYZE);
1213             }
1214 
1215             task.addTaskListener(new TaskListener() {
1216                 public void started(TaskEvent e) {
1217                     if (before.contains(e.getKind()))
1218                         handle(e);
1219                 }
1220 
1221                 public void finished(TaskEvent e) {
1222                     if (after.contains(e.getKind()))
1223                         handle(e);
1224                 }
1225 
1226                 private void handle(TaskEvent e) {
1227                      switch (e.getKind()) {
1228                          case PARSE:
1229                          case ENTER:
1230                              h.handle(e.getSourceFile().getName(),
1231                                      (JCTree) e.getCompilationUnit(),
1232                                      dprinter);
1233                              break;
1234 
1235                          default:
1236                              TypeElement elem = e.getTypeElement();
1237                              h.handle(elem.toString(),
1238                                      (JCTree) trees.getTree(elem),
1239                                      dprinter);
1240                              break;
1241                      }
1242                 }
1243             });
1244 
1245             task.call();
1246         }
1247 
getKind(String s)1248         TaskEvent.Kind getKind(String s) {
1249             return TaskEvent.Kind.valueOf(s.toUpperCase());
1250         }
1251 
1252         static protected abstract class Handler {
1253             final String name;
Handler(String name)1254             Handler(String name) {
1255                 this.name = name;
1256             }
handle(String label, JCTree tree, DPrinter dprinter)1257             abstract void handle(String label, JCTree tree, DPrinter dprinter);
1258         }
1259 
getHandlers()1260         Map<String,Handler> getHandlers() {
1261             Map<String,Handler> map = new HashMap<String, Handler>();
1262             for (Handler h: defaultHandlers) {
1263                 map.put(h.name, h);
1264             }
1265             return map;
1266         }
1267 
1268         protected final Handler[] defaultHandlers = {
1269             new Handler("trees") {
1270                 @Override
1271                 void handle(String name, JCTree tree, DPrinter dprinter) {
1272                     dprinter.printTree(name, tree);
1273                     dprinter.out.println();
1274                 }
1275             },
1276 
1277             new Handler("symbols") {
1278                 @Override
1279                 void handle(String name, JCTree tree, final DPrinter dprinter) {
1280                     TreeScanner ds = new TreeScanner() {
1281                         @Override
1282                         public void visitClassDef(JCClassDecl tree) {
1283                             visitDecl(tree, tree.sym);
1284                             super.visitClassDef(tree);
1285                         }
1286 
1287                         @Override
1288                         public void visitMethodDef(JCMethodDecl tree) {
1289                             visitDecl(tree, tree.sym);
1290                             super.visitMethodDef(tree);
1291                         }
1292 
1293                         @Override
1294                         public void visitVarDef(JCVariableDecl tree) {
1295                             visitDecl(tree, tree.sym);
1296                             super.visitVarDef(tree);
1297                         }
1298 
1299                         void visitDecl(JCTree tree, Symbol sym) {
1300                             dprinter.printSymbol(sym.name.toString(), sym);
1301                             dprinter.out.println();
1302                         }
1303                     };
1304                     ds.scan(tree);
1305                 }
1306             },
1307 
1308             new Handler("types") {
1309                 @Override
1310                 void handle(String name, JCTree tree, final DPrinter dprinter) {
1311                     TreeScanner ts = new TreeScanner() {
1312                         @Override
1313                         public void scan(JCTree tree) {
1314                             if (tree == null) {
1315                                 return;
1316                             }
1317                             if (tree.type != null) {
1318                                 String label = Pretty.toSimpleString(tree);
1319                                 dprinter.printType(label, tree.type);
1320                                 dprinter.out.println();
1321                             }
1322                             super.scan(tree);
1323                         }
1324                     };
1325                     ts.scan(tree);
1326                 }
1327             }
1328         };
1329     }
1330 
1331     // </editor-fold>
1332 
1333 }
1334