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