Tue, 08 Nov 2011 11:51:05 -0800
6921494: provide way to print javac tree tag values
Reviewed-by: jjg, mcimadamore
Contributed-by: vicenterz@yahoo.es
1 /*
2 * Copyright (c) 2005, 2011, 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.tools.javac.model;
28 import java.lang.annotation.Annotation;
29 import java.lang.annotation.Inherited;
30 import java.util.Map;
31 import javax.lang.model.SourceVersion;
32 import javax.lang.model.element.*;
33 import javax.lang.model.type.DeclaredType;
34 import javax.lang.model.util.Elements;
35 import javax.tools.JavaFileObject;
36 import com.sun.tools.javac.code.*;
37 import com.sun.tools.javac.code.Symbol.*;
38 import com.sun.tools.javac.code.TypeTags;
39 import com.sun.tools.javac.comp.AttrContext;
40 import com.sun.tools.javac.comp.Enter;
41 import com.sun.tools.javac.comp.Env;
42 import com.sun.tools.javac.main.JavaCompiler;
43 import com.sun.tools.javac.processing.PrintingProcessor;
44 import com.sun.tools.javac.tree.JCTree;
45 import com.sun.tools.javac.tree.JCTree.*;
46 import com.sun.tools.javac.tree.TreeInfo;
47 import com.sun.tools.javac.tree.TreeScanner;
48 import com.sun.tools.javac.util.*;
49 import com.sun.tools.javac.util.Name;
51 import static com.sun.tools.javac.tree.JCTree.Tag.*;
52 import static javax.lang.model.util.ElementFilter.methodsIn;
54 /**
55 * Utility methods for operating on program elements.
56 *
57 * <p><b>This is NOT part of any supported API.
58 * If you write code that depends on this, you do so at your own
59 * risk. This code and its internal interfaces are subject to change
60 * or deletion without notice.</b></p>
61 */
62 public class JavacElements implements Elements {
64 private JavaCompiler javaCompiler;
65 private Symtab syms;
66 private Names names;
67 private Types types;
68 private Enter enter;
70 public static JavacElements instance(Context context) {
71 JavacElements instance = context.get(JavacElements.class);
72 if (instance == null)
73 instance = new JavacElements(context);
74 return instance;
75 }
77 /**
78 * Public for use only by JavacProcessingEnvironment
79 */
80 protected JavacElements(Context context) {
81 setContext(context);
82 }
84 /**
85 * Use a new context. May be called from outside to update
86 * internal state for a new annotation-processing round.
87 */
88 public void setContext(Context context) {
89 context.put(JavacElements.class, this);
90 javaCompiler = JavaCompiler.instance(context);
91 syms = Symtab.instance(context);
92 names = Names.instance(context);
93 types = Types.instance(context);
94 enter = Enter.instance(context);
95 }
98 /**
99 * An internal-use utility that creates a reified annotation.
100 */
101 public static <A extends Annotation> A getAnnotation(Symbol annotated,
102 Class<A> annoType) {
103 if (!annoType.isAnnotation())
104 throw new IllegalArgumentException("Not an annotation type: "
105 + annoType);
106 String name = annoType.getName();
107 for (Attribute.Compound anno : annotated.getAnnotationMirrors())
108 if (name.equals(anno.type.tsym.flatName().toString()))
109 return AnnotationProxyMaker.generateAnnotation(anno, annoType);
110 return null;
111 }
113 /**
114 * An internal-use utility that creates a reified annotation.
115 * This overloaded version take annotation inheritance into account.
116 */
117 public static <A extends Annotation> A getAnnotation(ClassSymbol annotated,
118 Class<A> annoType) {
119 boolean inherited = annoType.isAnnotationPresent(Inherited.class);
120 A result = null;
121 while (annotated.name != annotated.name.table.names.java_lang_Object) {
122 result = getAnnotation((Symbol)annotated, annoType);
123 if (result != null || !inherited)
124 break;
125 Type sup = annotated.getSuperclass();
126 if (sup.tag != TypeTags.CLASS || sup.isErroneous())
127 break;
128 annotated = (ClassSymbol) sup.tsym;
129 }
130 return result;
131 }
134 public PackageSymbol getPackageElement(CharSequence name) {
135 String strName = name.toString();
136 if (strName.equals(""))
137 return syms.unnamedPackage;
138 return SourceVersion.isName(strName)
139 ? nameToSymbol(strName, PackageSymbol.class)
140 : null;
141 }
143 public ClassSymbol getTypeElement(CharSequence name) {
144 String strName = name.toString();
145 return SourceVersion.isName(strName)
146 ? nameToSymbol(strName, ClassSymbol.class)
147 : null;
148 }
150 /**
151 * Returns a symbol given the type's or packages's canonical name,
152 * or null if the name isn't found.
153 */
154 private <S extends Symbol> S nameToSymbol(String nameStr, Class<S> clazz) {
155 Name name = names.fromString(nameStr);
156 // First check cache.
157 Symbol sym = (clazz == ClassSymbol.class)
158 ? syms.classes.get(name)
159 : syms.packages.get(name);
161 try {
162 if (sym == null)
163 sym = javaCompiler.resolveIdent(nameStr);
165 sym.complete();
167 return (sym.kind != Kinds.ERR &&
168 sym.exists() &&
169 clazz.isInstance(sym) &&
170 name.equals(sym.getQualifiedName()))
171 ? clazz.cast(sym)
172 : null;
173 } catch (CompletionFailure e) {
174 return null;
175 }
176 }
178 public JavacSourcePosition getSourcePosition(Element e) {
179 Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e);
180 if (treeTop == null)
181 return null;
182 JCTree tree = treeTop.fst;
183 JCCompilationUnit toplevel = treeTop.snd;
184 JavaFileObject sourcefile = toplevel.sourcefile;
185 if (sourcefile == null)
186 return null;
187 return new JavacSourcePosition(sourcefile, tree.pos, toplevel.lineMap);
188 }
190 public JavacSourcePosition getSourcePosition(Element e, AnnotationMirror a) {
191 Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e);
192 if (treeTop == null)
193 return null;
194 JCTree tree = treeTop.fst;
195 JCCompilationUnit toplevel = treeTop.snd;
196 JavaFileObject sourcefile = toplevel.sourcefile;
197 if (sourcefile == null)
198 return null;
200 JCTree annoTree = matchAnnoToTree(a, e, tree);
201 if (annoTree == null)
202 return null;
203 return new JavacSourcePosition(sourcefile, annoTree.pos,
204 toplevel.lineMap);
205 }
207 public JavacSourcePosition getSourcePosition(Element e, AnnotationMirror a,
208 AnnotationValue v) {
209 // TODO: better accuracy in getSourcePosition(... AnnotationValue)
210 return getSourcePosition(e, a);
211 }
213 /**
214 * Returns the tree for an annotation given the annotated element
215 * and the element's own tree. Returns null if the tree cannot be found.
216 */
217 private JCTree matchAnnoToTree(AnnotationMirror findme,
218 Element e, JCTree tree) {
219 Symbol sym = cast(Symbol.class, e);
220 class Vis extends JCTree.Visitor {
221 List<JCAnnotation> result = null;
222 public void visitTopLevel(JCCompilationUnit tree) {
223 result = tree.packageAnnotations;
224 }
225 public void visitClassDef(JCClassDecl tree) {
226 result = tree.mods.annotations;
227 }
228 public void visitMethodDef(JCMethodDecl tree) {
229 result = tree.mods.annotations;
230 }
231 public void visitVarDef(JCVariableDecl tree) {
232 result = tree.mods.annotations;
233 }
234 }
235 Vis vis = new Vis();
236 tree.accept(vis);
237 if (vis.result == null)
238 return null;
239 return matchAnnoToTree(cast(Attribute.Compound.class, findme),
240 sym.getAnnotationMirrors(),
241 vis.result);
242 }
244 /**
245 * Returns the tree for an annotation given a list of annotations
246 * in which to search (recursively) and their corresponding trees.
247 * Returns null if the tree cannot be found.
248 */
249 private JCTree matchAnnoToTree(Attribute.Compound findme,
250 List<Attribute.Compound> annos,
251 List<JCAnnotation> trees) {
252 for (Attribute.Compound anno : annos) {
253 for (JCAnnotation tree : trees) {
254 JCTree match = matchAnnoToTree(findme, anno, tree);
255 if (match != null)
256 return match;
257 }
258 }
259 return null;
260 }
262 /**
263 * Returns the tree for an annotation given an Attribute to
264 * search (recursively) and its corresponding tree.
265 * Returns null if the tree cannot be found.
266 */
267 private JCTree matchAnnoToTree(final Attribute.Compound findme,
268 final Attribute attr,
269 final JCTree tree) {
270 if (attr == findme)
271 return (tree.type.tsym == findme.type.tsym) ? tree : null;
273 class Vis implements Attribute.Visitor {
274 JCTree result = null;
275 public void visitConstant(Attribute.Constant value) {
276 }
277 public void visitClass(Attribute.Class clazz) {
278 }
279 public void visitCompound(Attribute.Compound anno) {
280 for (Pair<MethodSymbol, Attribute> pair : anno.values) {
281 JCExpression expr = scanForAssign(pair.fst, tree);
282 if (expr != null) {
283 JCTree match = matchAnnoToTree(findme, pair.snd, expr);
284 if (match != null) {
285 result = match;
286 return;
287 }
288 }
289 }
290 }
291 public void visitArray(Attribute.Array array) {
292 if (tree.hasTag(NEWARRAY) &&
293 types.elemtype(array.type).tsym == findme.type.tsym) {
294 List<JCExpression> elems = ((JCNewArray) tree).elems;
295 for (Attribute value : array.values) {
296 if (value == findme) {
297 result = elems.head;
298 return;
299 }
300 elems = elems.tail;
301 }
302 }
303 }
304 public void visitEnum(Attribute.Enum e) {
305 }
306 public void visitError(Attribute.Error e) {
307 }
308 }
309 Vis vis = new Vis();
310 attr.accept(vis);
311 return vis.result;
312 }
314 /**
315 * Scans for a JCAssign node with a LHS matching a given
316 * symbol, and returns its RHS. Does not scan nested JCAnnotations.
317 */
318 private JCExpression scanForAssign(final MethodSymbol sym,
319 final JCTree tree) {
320 class TS extends TreeScanner {
321 JCExpression result = null;
322 public void scan(JCTree t) {
323 if (t != null && result == null)
324 t.accept(this);
325 }
326 public void visitAnnotation(JCAnnotation t) {
327 if (t == tree)
328 scan(t.args);
329 }
330 public void visitAssign(JCAssign t) {
331 if (t.lhs.hasTag(IDENT)) {
332 JCIdent ident = (JCIdent) t.lhs;
333 if (ident.sym == sym)
334 result = t.rhs;
335 }
336 }
337 }
338 TS scanner = new TS();
339 tree.accept(scanner);
340 return scanner.result;
341 }
343 /**
344 * Returns the tree node corresponding to this element, or null
345 * if none can be found.
346 */
347 public JCTree getTree(Element e) {
348 Pair<JCTree, ?> treeTop = getTreeAndTopLevel(e);
349 return (treeTop != null) ? treeTop.fst : null;
350 }
352 public String getDocComment(Element e) {
353 // Our doc comment is contained in a map in our toplevel,
354 // indexed by our tree. Find our enter environment, which gives
355 // us our toplevel. It also gives us a tree that contains our
356 // tree: walk it to find our tree. This is painful.
357 Pair<JCTree, JCCompilationUnit> treeTop = getTreeAndTopLevel(e);
358 if (treeTop == null)
359 return null;
360 JCTree tree = treeTop.fst;
361 JCCompilationUnit toplevel = treeTop.snd;
362 if (toplevel.docComments == null)
363 return null;
364 return toplevel.docComments.get(tree);
365 }
367 public PackageElement getPackageOf(Element e) {
368 return cast(Symbol.class, e).packge();
369 }
371 public boolean isDeprecated(Element e) {
372 Symbol sym = cast(Symbol.class, e);
373 return (sym.flags() & Flags.DEPRECATED) != 0;
374 }
376 public Name getBinaryName(TypeElement type) {
377 return cast(TypeSymbol.class, type).flatName();
378 }
380 public Map<MethodSymbol, Attribute> getElementValuesWithDefaults(
381 AnnotationMirror a) {
382 Attribute.Compound anno = cast(Attribute.Compound.class, a);
383 DeclaredType annotype = a.getAnnotationType();
384 Map<MethodSymbol, Attribute> valmap = anno.getElementValues();
386 for (ExecutableElement ex :
387 methodsIn(annotype.asElement().getEnclosedElements())) {
388 MethodSymbol meth = (MethodSymbol) ex;
389 Attribute defaultValue = meth.getDefaultValue();
390 if (defaultValue != null && !valmap.containsKey(meth)) {
391 valmap.put(meth, defaultValue);
392 }
393 }
394 return valmap;
395 }
397 /**
398 * {@inheritDoc}
399 */
400 public FilteredMemberList getAllMembers(TypeElement element) {
401 Symbol sym = cast(Symbol.class, element);
402 Scope scope = sym.members().dupUnshared();
403 List<Type> closure = types.closure(sym.asType());
404 for (Type t : closure)
405 addMembers(scope, t);
406 return new FilteredMemberList(scope);
407 }
408 // where
409 private void addMembers(Scope scope, Type type) {
410 members:
411 for (Scope.Entry e = type.asElement().members().elems; e != null; e = e.sibling) {
412 Scope.Entry overrider = scope.lookup(e.sym.getSimpleName());
413 while (overrider.scope != null) {
414 if (overrider.sym.kind == e.sym.kind
415 && (overrider.sym.flags() & Flags.SYNTHETIC) == 0)
416 {
417 if (overrider.sym.getKind() == ElementKind.METHOD
418 && overrides((ExecutableElement)overrider.sym, (ExecutableElement)e.sym, (TypeElement)type.asElement())) {
419 continue members;
420 }
421 }
422 overrider = overrider.next();
423 }
424 boolean derived = e.sym.getEnclosingElement() != scope.owner;
425 ElementKind kind = e.sym.getKind();
426 boolean initializer = kind == ElementKind.CONSTRUCTOR
427 || kind == ElementKind.INSTANCE_INIT
428 || kind == ElementKind.STATIC_INIT;
429 if (!derived || (!initializer && e.sym.isInheritedIn(scope.owner, types)))
430 scope.enter(e.sym);
431 }
432 }
434 /**
435 * Returns all annotations of an element, whether
436 * inherited or directly present.
437 *
438 * @param e the element being examined
439 * @return all annotations of the element
440 */
441 public List<Attribute.Compound> getAllAnnotationMirrors(Element e) {
442 Symbol sym = cast(Symbol.class, e);
443 List<Attribute.Compound> annos = sym.getAnnotationMirrors();
444 while (sym.getKind() == ElementKind.CLASS) {
445 Type sup = ((ClassSymbol) sym).getSuperclass();
446 if (sup.tag != TypeTags.CLASS || sup.isErroneous() ||
447 sup.tsym == syms.objectType.tsym) {
448 break;
449 }
450 sym = sup.tsym;
451 List<Attribute.Compound> oldAnnos = annos;
452 for (Attribute.Compound anno : sym.getAnnotationMirrors()) {
453 if (isInherited(anno.type) &&
454 !containsAnnoOfType(oldAnnos, anno.type)) {
455 annos = annos.prepend(anno);
456 }
457 }
458 }
459 return annos;
460 }
462 /**
463 * Tests whether an annotation type is @Inherited.
464 */
465 private boolean isInherited(Type annotype) {
466 for (Attribute.Compound anno : annotype.tsym.getAnnotationMirrors()) {
467 if (anno.type.tsym == syms.inheritedType.tsym)
468 return true;
469 }
470 return false;
471 }
473 /**
474 * Tests whether a list of annotations contains an annotation
475 * of a given type.
476 */
477 private static boolean containsAnnoOfType(List<Attribute.Compound> annos,
478 Type type) {
479 for (Attribute.Compound anno : annos) {
480 if (anno.type.tsym == type.tsym)
481 return true;
482 }
483 return false;
484 }
486 public boolean hides(Element hiderEl, Element hideeEl) {
487 Symbol hider = cast(Symbol.class, hiderEl);
488 Symbol hidee = cast(Symbol.class, hideeEl);
490 // Fields only hide fields; methods only methods; types only types.
491 // Names must match. Nothing hides itself (just try it).
492 if (hider == hidee ||
493 hider.kind != hidee.kind ||
494 hider.name != hidee.name) {
495 return false;
496 }
498 // Only static methods can hide other methods.
499 // Methods only hide methods with matching signatures.
500 if (hider.kind == Kinds.MTH) {
501 if (!hider.isStatic() ||
502 !types.isSubSignature(hider.type, hidee.type)) {
503 return false;
504 }
505 }
507 // Hider must be in a subclass of hidee's class.
508 // Note that if M1 hides M2, and M2 hides M3, and M3 is accessible
509 // in M1's class, then M1 and M2 both hide M3.
510 ClassSymbol hiderClass = hider.owner.enclClass();
511 ClassSymbol hideeClass = hidee.owner.enclClass();
512 if (hiderClass == null || hideeClass == null ||
513 !hiderClass.isSubClass(hideeClass, types)) {
514 return false;
515 }
517 // Hidee must be accessible in hider's class.
518 // The method isInheritedIn is poorly named: it checks only access.
519 return hidee.isInheritedIn(hiderClass, types);
520 }
522 public boolean overrides(ExecutableElement riderEl,
523 ExecutableElement rideeEl, TypeElement typeEl) {
524 MethodSymbol rider = cast(MethodSymbol.class, riderEl);
525 MethodSymbol ridee = cast(MethodSymbol.class, rideeEl);
526 ClassSymbol origin = cast(ClassSymbol.class, typeEl);
528 return rider.name == ridee.name &&
530 // not reflexive as per JLS
531 rider != ridee &&
533 // we don't care if ridee is static, though that wouldn't
534 // compile
535 !rider.isStatic() &&
537 // Symbol.overrides assumes the following
538 ridee.isMemberOf(origin, types) &&
540 // check access and signatures; don't check return types
541 rider.overrides(ridee, origin, types, false);
542 }
544 public String getConstantExpression(Object value) {
545 return Constants.format(value);
546 }
548 /**
549 * Print a representation of the elements to the given writer in
550 * the specified order. The main purpose of this method is for
551 * diagnostics. The exact format of the output is <em>not</em>
552 * specified and is subject to change.
553 *
554 * @param w the writer to print the output to
555 * @param elements the elements to print
556 */
557 public void printElements(java.io.Writer w, Element... elements) {
558 for (Element element : elements)
559 (new PrintingProcessor.PrintingElementVisitor(w, this)).visit(element).flush();
560 }
562 public Name getName(CharSequence cs) {
563 return names.fromString(cs.toString());
564 }
566 /**
567 * Returns the tree node and compilation unit corresponding to this
568 * element, or null if they can't be found.
569 */
570 private Pair<JCTree, JCCompilationUnit> getTreeAndTopLevel(Element e) {
571 Symbol sym = cast(Symbol.class, e);
572 Env<AttrContext> enterEnv = getEnterEnv(sym);
573 if (enterEnv == null)
574 return null;
575 JCTree tree = TreeInfo.declarationFor(sym, enterEnv.tree);
576 if (tree == null || enterEnv.toplevel == null)
577 return null;
578 return new Pair<JCTree,JCCompilationUnit>(tree, enterEnv.toplevel);
579 }
581 /**
582 * Returns the best approximation for the tree node and compilation unit
583 * corresponding to the given element, annotation and value.
584 * If the element is null, null is returned.
585 * If the annotation is null or cannot be found, the tree node and
586 * compilation unit for the element is returned.
587 * If the annotation value is null or cannot be found, the tree node and
588 * compilation unit for the annotation is returned.
589 */
590 public Pair<JCTree, JCCompilationUnit> getTreeAndTopLevel(
591 Element e, AnnotationMirror a, AnnotationValue v) {
592 if (e == null)
593 return null;
595 Pair<JCTree, JCCompilationUnit> elemTreeTop = getTreeAndTopLevel(e);
596 if (elemTreeTop == null)
597 return null;
599 if (a == null)
600 return elemTreeTop;
602 JCTree annoTree = matchAnnoToTree(a, e, elemTreeTop.fst);
603 if (annoTree == null)
604 return elemTreeTop;
606 // 6388543: if v != null, we should search within annoTree to find
607 // the tree matching v. For now, we ignore v and return the tree of
608 // the annotation.
609 return new Pair<JCTree, JCCompilationUnit>(annoTree, elemTreeTop.snd);
610 }
612 /**
613 * Returns a symbol's enter environment, or null if it has none.
614 */
615 private Env<AttrContext> getEnterEnv(Symbol sym) {
616 // Get enclosing class of sym, or sym itself if it is a class
617 // or package.
618 TypeSymbol ts = (sym.kind != Kinds.PCK)
619 ? sym.enclClass()
620 : (PackageSymbol) sym;
621 return (ts != null)
622 ? enter.getEnv(ts)
623 : null;
624 }
626 /**
627 * Returns an object cast to the specified type.
628 * @throws NullPointerException if the object is {@code null}
629 * @throws IllegalArgumentException if the object is of the wrong type
630 */
631 private static <T> T cast(Class<T> clazz, Object o) {
632 if (! clazz.isInstance(o))
633 throw new IllegalArgumentException(o.toString());
634 return clazz.cast(o);
635 }
636 }