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