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