src/share/classes/com/sun/tools/javac/comp/Annotate.java

Fri, 28 Sep 2012 11:39:29 -0700

author
jfranck
date
Fri, 28 Sep 2012 11:39:29 -0700
changeset 1344
73312ec2cf7c
parent 1322
324b98626f58
child 1374
c002fdee76fd
permissions
-rw-r--r--

7199925: Separate compilation breaks check that elements have a default for the containing annotation
Reviewed-by: jjg, mcimadamore

     1 /*
     2  * Copyright (c) 2003, 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.comp;
    28 import java.util.Map;
    30 import com.sun.tools.javac.util.*;
    31 import com.sun.tools.javac.util.JCDiagnostic.DiagnosticPosition;
    32 import com.sun.tools.javac.code.*;
    33 import com.sun.tools.javac.code.Symbol.*;
    34 import com.sun.tools.javac.tree.*;
    35 import com.sun.tools.javac.tree.JCTree.*;
    37 import static com.sun.tools.javac.tree.JCTree.Tag.*;
    39 /** Enter annotations on symbols.  Annotations accumulate in a queue,
    40  *  which is processed at the top level of any set of recursive calls
    41  *  requesting it be processed.
    42  *
    43  *  <p><b>This is NOT part of any supported API.
    44  *  If you write code that depends on this, you do so at your own risk.
    45  *  This code and its internal interfaces are subject to change or
    46  *  deletion without notice.</b>
    47  */
    48 public class Annotate {
    49     protected static final Context.Key<Annotate> annotateKey =
    50         new Context.Key<Annotate>();
    52     public static Annotate instance(Context context) {
    53         Annotate instance = context.get(annotateKey);
    54         if (instance == null)
    55             instance = new Annotate(context);
    56         return instance;
    57     }
    59     final Attr attr;
    60     final TreeMaker make;
    61     final Log log;
    62     final Symtab syms;
    63     final Names names;
    64     final Resolve rs;
    65     final Types types;
    66     final ConstFold cfolder;
    67     final Check chk;
    69     protected Annotate(Context context) {
    70         context.put(annotateKey, this);
    71         attr = Attr.instance(context);
    72         make = TreeMaker.instance(context);
    73         log = Log.instance(context);
    74         syms = Symtab.instance(context);
    75         names = Names.instance(context);
    76         rs = Resolve.instance(context);
    77         types = Types.instance(context);
    78         cfolder = ConstFold.instance(context);
    79         chk = Check.instance(context);
    80     }
    82 /* ********************************************************************
    83  * Queue maintenance
    84  *********************************************************************/
    86     private int enterCount = 0;
    88     ListBuffer<Annotator> q = new ListBuffer<Annotator>();
    89     ListBuffer<Annotator> repeatedQ = new ListBuffer<Annotator>();
    91     public void normal(Annotator a) {
    92         q.append(a);
    93     }
    95     public void earlier(Annotator a) {
    96         q.prepend(a);
    97     }
    99     public void repeated(Annotator a) {
   100         repeatedQ.append(a);
   101     }
   103     /** Called when the Enter phase starts. */
   104     public void enterStart() {
   105         enterCount++;
   106     }
   108     /** Called after the Enter phase completes. */
   109     public void enterDone() {
   110         enterCount--;
   111         flush();
   112     }
   114     public void flush() {
   115         if (enterCount != 0) return;
   116         enterCount++;
   117         try {
   118             while (q.nonEmpty())
   119                 q.next().enterAnnotation();
   121             while (repeatedQ.nonEmpty()) {
   122                 repeatedQ.next().enterAnnotation();
   123             }
   124         } finally {
   125             enterCount--;
   126         }
   127     }
   129     /** A client that has annotations to add registers an annotator,
   130      *  the method it will use to add the annotation.  There are no
   131      *  parameters; any needed data should be captured by the
   132      *  Annotator.
   133      */
   134     public interface Annotator {
   135         void enterAnnotation();
   136         String toString();
   137     }
   139     /**
   140      * This context contains all the information needed to synthesize new
   141      * annotations trees by the completer for repeating annotations.
   142      */
   143     public class AnnotateRepeatedContext {
   144         public final Env<AttrContext> env;
   145         public final Map<Symbol.TypeSymbol, ListBuffer<Attribute.Compound>> annotated;
   146         public final Map<Attribute.Compound, JCDiagnostic.DiagnosticPosition> pos;
   147         public final Log log;
   149         public AnnotateRepeatedContext(Env<AttrContext> env,
   150                                        Map<Symbol.TypeSymbol, ListBuffer<Attribute.Compound>> annotated,
   151                                        Map<Attribute.Compound, JCDiagnostic.DiagnosticPosition> pos,
   152                                        Log log) {
   153             Assert.checkNonNull(env);
   154             Assert.checkNonNull(annotated);
   155             Assert.checkNonNull(pos);
   156             Assert.checkNonNull(log);
   158             this.env = env;
   159             this.annotated = annotated;
   160             this.pos = pos;
   161             this.log = log;
   162         }
   164         /**
   165          * Process a list of repeating annotations returning a new
   166          * Attribute.Compound that is the attribute for the synthesized tree
   167          * for the container.
   168          *
   169          * @param repeatingAnnotations a List of repeating annotations
   170          * @return a new Attribute.Compound that is the container for the repeatingAnnotations
   171          */
   172         public Attribute.Compound processRepeatedAnnotations(List<Attribute.Compound> repeatingAnnotations) {
   173             return Annotate.this.processRepeatedAnnotations(repeatingAnnotations, this);
   174         }
   176         /**
   177          * Queue the Annotator a on the repeating annotations queue of the
   178          * Annotate instance this context belongs to.
   179          *
   180          * @param a the Annotator to enqueue for repeating annotation annotating
   181          */
   182         public void annotateRepeated(Annotator a) {
   183             Annotate.this.repeated(a);
   184         }
   185     }
   187 /* ********************************************************************
   188  * Compute an attribute from its annotation.
   189  *********************************************************************/
   191     /** Process a single compound annotation, returning its
   192      *  Attribute. Used from MemberEnter for attaching the attributes
   193      *  to the annotated symbol.
   194      */
   195     Attribute.Compound enterAnnotation(JCAnnotation a,
   196                                        Type expected,
   197                                        Env<AttrContext> env) {
   198         // The annotation might have had its type attributed (but not checked)
   199         // by attr.attribAnnotationTypes during MemberEnter, in which case we do not
   200         // need to do it again.
   201         Type at = (a.annotationType.type != null ? a.annotationType.type
   202                   : attr.attribType(a.annotationType, env));
   203         a.type = chk.checkType(a.annotationType.pos(), at, expected);
   204         if (a.type.isErroneous())
   205             return new Attribute.Compound(a.type, List.<Pair<MethodSymbol,Attribute>>nil());
   206         if ((a.type.tsym.flags() & Flags.ANNOTATION) == 0) {
   207             log.error(a.annotationType.pos(),
   208                       "not.annotation.type", a.type.toString());
   209             return new Attribute.Compound(a.type, List.<Pair<MethodSymbol,Attribute>>nil());
   210         }
   211         List<JCExpression> args = a.args;
   212         if (args.length() == 1 && !args.head.hasTag(ASSIGN)) {
   213             // special case: elided "value=" assumed
   214             args.head = make.at(args.head.pos).
   215                 Assign(make.Ident(names.value), args.head);
   216         }
   217         ListBuffer<Pair<MethodSymbol,Attribute>> buf =
   218             new ListBuffer<Pair<MethodSymbol,Attribute>>();
   219         for (List<JCExpression> tl = args; tl.nonEmpty(); tl = tl.tail) {
   220             JCExpression t = tl.head;
   221             if (!t.hasTag(ASSIGN)) {
   222                 log.error(t.pos(), "annotation.value.must.be.name.value");
   223                 continue;
   224             }
   225             JCAssign assign = (JCAssign)t;
   226             if (!assign.lhs.hasTag(IDENT)) {
   227                 log.error(t.pos(), "annotation.value.must.be.name.value");
   228                 continue;
   229             }
   230             JCIdent left = (JCIdent)assign.lhs;
   231             Symbol method = rs.resolveQualifiedMethod(left.pos(),
   232                                                           env,
   233                                                           a.type,
   234                                                           left.name,
   235                                                           List.<Type>nil(),
   236                                                           null);
   237             left.sym = method;
   238             left.type = method.type;
   239             if (method.owner != a.type.tsym)
   240                 log.error(left.pos(), "no.annotation.member", left.name, a.type);
   241             Type result = method.type.getReturnType();
   242             Attribute value = enterAttributeValue(result, assign.rhs, env);
   243             if (!method.type.isErroneous())
   244                 buf.append(new Pair<MethodSymbol,Attribute>
   245                            ((MethodSymbol)method, value));
   246             t.type = result;
   247         }
   248         return new Attribute.Compound(a.type, buf.toList());
   249     }
   251     Attribute enterAttributeValue(Type expected,
   252                                   JCExpression tree,
   253                                   Env<AttrContext> env) {
   254         //first, try completing the attribution value sym - if a completion
   255         //error is thrown, we should recover gracefully, and display an
   256         //ordinary resolution diagnostic.
   257         try {
   258             expected.tsym.complete();
   259         } catch(CompletionFailure e) {
   260             log.error(tree.pos(), "cant.resolve", Kinds.kindName(e.sym), e.sym);
   261             return new Attribute.Error(expected);
   262         }
   263         if (expected.isPrimitive() || types.isSameType(expected, syms.stringType)) {
   264             Type result = attr.attribExpr(tree, env, expected);
   265             if (result.isErroneous())
   266                 return new Attribute.Error(expected);
   267             if (result.constValue() == null) {
   268                 log.error(tree.pos(), "attribute.value.must.be.constant");
   269                 return new Attribute.Error(expected);
   270             }
   271             result = cfolder.coerce(result, expected);
   272             return new Attribute.Constant(expected, result.constValue());
   273         }
   274         if (expected.tsym == syms.classType.tsym) {
   275             Type result = attr.attribExpr(tree, env, expected);
   276             if (result.isErroneous())
   277                 return new Attribute.Error(expected);
   278             if (TreeInfo.name(tree) != names._class) {
   279                 log.error(tree.pos(), "annotation.value.must.be.class.literal");
   280                 return new Attribute.Error(expected);
   281             }
   282             return new Attribute.Class(types,
   283                                        (((JCFieldAccess) tree).selected).type);
   284         }
   285         if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) {
   286             if (!tree.hasTag(ANNOTATION)) {
   287                 log.error(tree.pos(), "annotation.value.must.be.annotation");
   288                 expected = syms.errorType;
   289             }
   290             return enterAnnotation((JCAnnotation)tree, expected, env);
   291         }
   292         if (expected.tag == TypeTags.ARRAY) { // should really be isArray()
   293             if (!tree.hasTag(NEWARRAY)) {
   294                 tree = make.at(tree.pos).
   295                     NewArray(null, List.<JCExpression>nil(), List.of(tree));
   296             }
   297             JCNewArray na = (JCNewArray)tree;
   298             if (na.elemtype != null) {
   299                 log.error(na.elemtype.pos(), "new.not.allowed.in.annotation");
   300                 return new Attribute.Error(expected);
   301             }
   302             ListBuffer<Attribute> buf = new ListBuffer<Attribute>();
   303             for (List<JCExpression> l = na.elems; l.nonEmpty(); l=l.tail) {
   304                 buf.append(enterAttributeValue(types.elemtype(expected),
   305                                                l.head,
   306                                                env));
   307             }
   308             na.type = expected;
   309             return new Attribute.
   310                 Array(expected, buf.toArray(new Attribute[buf.length()]));
   311         }
   312         if (expected.tag == TypeTags.CLASS &&
   313             (expected.tsym.flags() & Flags.ENUM) != 0) {
   314             attr.attribExpr(tree, env, expected);
   315             Symbol sym = TreeInfo.symbol(tree);
   316             if (sym == null ||
   317                 TreeInfo.nonstaticSelect(tree) ||
   318                 sym.kind != Kinds.VAR ||
   319                 (sym.flags() & Flags.ENUM) == 0) {
   320                 log.error(tree.pos(), "enum.annotation.must.be.enum.constant");
   321                 return new Attribute.Error(expected);
   322             }
   323             VarSymbol enumerator = (VarSymbol) sym;
   324             return new Attribute.Enum(expected, enumerator);
   325         }
   326         if (!expected.isErroneous())
   327             log.error(tree.pos(), "annotation.value.not.allowable.type");
   328         return new Attribute.Error(attr.attribExpr(tree, env, expected));
   329     }
   331     /* *********************************
   332      * Support for repeating annotations
   333      ***********************************/
   335     /* Process repeated annotations. This method returns the
   336      * synthesized container annotation or null IFF all repeating
   337      * annotation are invalid.  This method reports errors/warnings.
   338      */
   339     private Attribute.Compound processRepeatedAnnotations(List<Attribute.Compound> annotations,
   340             AnnotateRepeatedContext ctx) {
   341         Attribute.Compound firstOccurrence = annotations.head;
   342         List<Attribute> repeated = List.nil();
   343         Type origAnnoType;
   344         Type arrayOfOrigAnnoType = null;
   345         Type targetContainerType = null;
   346         MethodSymbol containerValueSymbol = null;
   348         Assert.check(!annotations.isEmpty() &&
   349                      !annotations.tail.isEmpty()); // i.e. size() > 1
   351         for (List<Attribute.Compound> al = annotations;
   352              !al.isEmpty();
   353              al = al.tail)
   354         {
   355             Attribute.Compound currentAnno = al.head;
   357             origAnnoType = currentAnno.type;
   358             if (arrayOfOrigAnnoType == null) {
   359                 arrayOfOrigAnnoType = types.makeArrayType(origAnnoType);
   360 }
   362             Type currentContainerType = getContainingType(currentAnno, ctx.pos.get(currentAnno));
   363             if (currentContainerType == null) {
   364                 continue;
   365             }
   366             // Assert that the target Container is == for all repeated
   367             // annos of the same annotation type, the types should
   368             // come from the same Symbol, i.e. be '=='
   369             Assert.check(targetContainerType == null || currentContainerType == targetContainerType);
   370             targetContainerType = currentContainerType;
   372             containerValueSymbol = validateContainer(targetContainerType, origAnnoType, ctx.pos.get(currentAnno));
   374             if (containerValueSymbol == null) { // Check of CA type failed
   375                 // errors are already reported
   376                 continue;
   377             }
   379             repeated = repeated.prepend(currentAnno);
   380         }
   382         if (!repeated.isEmpty()) {
   383             repeated = repeated.reverse();
   384             JCAnnotation annoTree;
   385             TreeMaker m = make.at(ctx.pos.get(firstOccurrence));
   386             Pair<MethodSymbol, Attribute> p =
   387                     new Pair<MethodSymbol, Attribute>(containerValueSymbol,
   388                                                       new Attribute.Array(arrayOfOrigAnnoType, repeated));
   389             annoTree = m.Annotation(new Attribute.Compound(targetContainerType,
   390                     List.of(p)));
   391             Attribute.Compound c = enterAnnotation(annoTree,
   392                                                    targetContainerType,
   393                                                    ctx.env);
   394             return c;
   395         } else {
   396             return null; // errors should have been reported elsewhere
   397         }
   398     }
   400     /** Fetches the actual Type that should be the containing annotation. */
   401     private Type getContainingType(Attribute.Compound currentAnno,
   402             DiagnosticPosition pos)
   403     {
   404         Type origAnnoType = currentAnno.type;
   405         TypeSymbol origAnnoDecl = origAnnoType.tsym;
   407         // Fetch the ContainedBy annotation from the current
   408         // annotation's declaration, or null if it has none
   409         Attribute.Compound ca = origAnnoDecl.attribute(syms.containedByType.tsym);
   410         if (ca == null) { // has no ContainedBy annotation
   411             log.error(pos, "duplicate.annotation.missing.container", origAnnoType);
   412             return null;
   413         }
   415         return filterSame(extractContainingType(ca, pos, origAnnoDecl),
   416                           origAnnoType);
   417     }
   419     // returns null if t is same as 's', returns 't' otherwise
   420     private Type filterSame(Type t, Type s) {
   421         if (t == null || s == null) {
   422             return t;
   423         }
   425         return types.isSameType(t, s) ? null : t;
   426     }
   428     /** Extract the actual Type to be used for a containing annotation. */
   429     private Type extractContainingType(Attribute.Compound ca,
   430             DiagnosticPosition pos,
   431             TypeSymbol annoDecl)
   432     {
   433         // The next three checks check that the ContainedBy annotation
   434         // on the declaration of the annotation type that is repeating is
   435         // valid.
   437         // ContainedBy must have at least one element
   438         if (ca.values.isEmpty()) {
   439             log.error(pos, "invalid.containedby.annotation", annoDecl);
   440             return null;
   441         }
   442         Pair<MethodSymbol,Attribute> p = ca.values.head;
   443         Name name = p.fst.name;
   444         if (name != names.value) { // should contain only one element, named "value"
   445             log.error(pos, "invalid.containedby.annotation", annoDecl);
   446             return null;
   447         }
   448         if (!(p.snd instanceof Attribute.Class)) { // check that the value of "value" is an Attribute.Class
   449             log.error(pos, "invalid.containedby.annotation", annoDecl);
   450             return null;
   451         }
   453         return ((Attribute.Class)p.snd).getValue();
   454     }
   456     /* Validate that the suggested targetContainerType Type is a valid
   457      * container type for repeated instances of originalAnnoType
   458      * annotations. Return null and report errors if this is not the
   459      * case, return the MethodSymbol of the value element in
   460      * targetContainerType if it is suitable (this is needed to
   461      * synthesize the container). */
   462     private MethodSymbol validateContainer(Type targetContainerType,
   463                                            Type originalAnnoType,
   464                                            DiagnosticPosition pos) {
   465         MethodSymbol containerValueSymbol = null;
   466         boolean fatalError = false;
   468         // Validate that there is a (and only 1) value method
   469         Scope scope = targetContainerType.tsym.members();
   470         int nr_value_elems = 0;
   471         boolean error = false;
   472         for(Symbol elm : scope.getElementsByName(names.value)) {
   473             nr_value_elems++;
   475             if (nr_value_elems == 1 &&
   476                 elm.kind == Kinds.MTH) {
   477                 containerValueSymbol = (MethodSymbol)elm;
   478             } else {
   479                 error = true;
   480             }
   481         }
   482         if (error) {
   483             log.error(pos,
   484                       "invalid.containedby.annotation.multiple.values",
   485                       targetContainerType,
   486                       nr_value_elems);
   487             return null;
   488         } else if (nr_value_elems == 0) {
   489             log.error(pos,
   490                       "invalid.containedby.annotation.no.value",
   491                       targetContainerType);
   492             return null;
   493         }
   495         // validate that the 'value' element is a method
   496         // probably "impossible" to fail this
   497         if (containerValueSymbol.kind != Kinds.MTH) {
   498             log.error(pos,
   499                       "invalid.containedby.annotation.invalid.value",
   500                       targetContainerType);
   501             fatalError = true;
   502         }
   504         // validate that the 'value' element has the correct return type
   505         // i.e. array of original anno
   506         Type valueRetType = containerValueSymbol.type.getReturnType();
   507         Type expectedType = types.makeArrayType(originalAnnoType);
   508         if (!(types.isArray(valueRetType) &&
   509               types.isSameType(expectedType, valueRetType))) {
   510             log.error(pos,
   511                       "invalid.containedby.annotation.value.return",
   512                       targetContainerType,
   513                       valueRetType,
   514                       expectedType);
   515             fatalError = true;
   516         }
   517         if (error) {
   518             fatalError = true;
   519         }
   521         // Explicitly no check for/validity of @ContainerFor. That is
   522         // done on declaration of the container, and at reflect time.
   524         // The rest of the conditions for a valid containing annotation are made
   525         // in Check.validateRepeatedAnnotaton();
   527         return fatalError ? null : containerValueSymbol;
   528     }
   529 }

mercurial