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