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