1 /*
   2  * Copyright (c) 2003, 2010, 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 
  26 package com.sun.tools.javac.comp;
  27 
  28 import com.sun.tools.javac.util.*;
  29 import com.sun.tools.javac.code.*;
  30 import com.sun.tools.javac.code.Symbol.*;
  31 import com.sun.tools.javac.tree.*;
  32 import com.sun.tools.javac.tree.JCTree.*;
  33 
  34 import static com.sun.tools.javac.tree.JCTree.Tag.*;
  35 
  36 /** Enter annotations on symbols.  Annotations accumulate in a queue,
  37  *  which is processed at the top level of any set of recursive calls
  38  *  requesting it be processed.
  39  *
  40  *  <p><b>This is NOT part of any supported API.
  41  *  If you write code that depends on this, you do so at your own risk.
  42  *  This code and its internal interfaces are subject to change or
  43  *  deletion without notice.</b>
  44  */
  45 public class Annotate {
  46     protected static final Context.Key<Annotate> annotateKey =
  47         new Context.Key<Annotate>();
  48 
  49     public static Annotate instance(Context context) {
  50         Annotate instance = context.get(annotateKey);
  51         if (instance == null)
  52             instance = new Annotate(context);
  53         return instance;
  54     }
  55 
  56     final Attr attr;
  57     final TreeMaker make;
  58     final Log log;
  59     final Symtab syms;
  60     final Names names;
  61     final Resolve rs;
  62     final Types types;
  63     final ConstFold cfolder;
  64     final Check chk;
  65 
  66     protected Annotate(Context context) {
  67         context.put(annotateKey, this);
  68         attr = Attr.instance(context);
  69         make = TreeMaker.instance(context);
  70         log = Log.instance(context);
  71         syms = Symtab.instance(context);
  72         names = Names.instance(context);
  73         rs = Resolve.instance(context);
  74         types = Types.instance(context);
  75         cfolder = ConstFold.instance(context);
  76         chk = Check.instance(context);
  77     }
  78 
  79 /* ********************************************************************
  80  * Queue maintenance
  81  *********************************************************************/
  82 
  83     private int enterCount = 0;
  84 
  85     ListBuffer<Annotator> q = new ListBuffer<Annotator>();
  86 
  87     public void later(Annotator a) {
  88         q.append(a);
  89     }
  90 
  91     public void earlier(Annotator a) {
  92         q.prepend(a);
  93     }
  94 
  95     /** Called when the Enter phase starts. */
  96     public void enterStart() {
  97         enterCount++;
  98     }
  99 
 100     /** Called after the Enter phase completes. */
 101     public void enterDone() {
 102         enterCount--;
 103         flush();
 104     }
 105 
 106     public void flush() {
 107         if (enterCount != 0) return;
 108         enterCount++;
 109         try {
 110             while (q.nonEmpty())
 111                 q.next().enterAnnotation();
 112         } finally {
 113             enterCount--;
 114         }
 115     }
 116 
 117     /** A client that has annotations to add registers an annotator,
 118      *  the method it will use to add the annotation.  There are no
 119      *  parameters; any needed data should be captured by the
 120      *  Annotator.
 121      */
 122     public interface Annotator {
 123         void enterAnnotation();
 124         String toString();
 125     }
 126 
 127 
 128 /* ********************************************************************
 129  * Compute an attribute from its annotation.
 130  *********************************************************************/
 131 
 132     /** Process a single compound annotation, returning its
 133      *  Attribute. Used from MemberEnter for attaching the attributes
 134      *  to the annotated symbol.
 135      */
 136     Attribute.Compound enterAnnotation(JCAnnotation a,
 137                                        Type expected,
 138                                        Env<AttrContext> env) {
 139         // The annotation might have had its type attributed (but not checked)
 140         // by attr.attribAnnotationTypes during MemberEnter, in which case we do not
 141         // need to do it again.
 142         Type at = (a.annotationType.type != null ? a.annotationType.type
 143                   : attr.attribType(a.annotationType, env));
 144         a.type = chk.checkType(a.annotationType.pos(), at, expected);
 145         if (a.type.isErroneous())
 146             return new Attribute.Compound(a.type, List.<Pair<MethodSymbol,Attribute>>nil());
 147         if ((a.type.tsym.flags() & Flags.ANNOTATION) == 0) {
 148             log.error(a.annotationType.pos(),
 149                       "not.annotation.type", a.type.toString());
 150             return new Attribute.Compound(a.type, List.<Pair<MethodSymbol,Attribute>>nil());
 151         }
 152         List<JCExpression> args = a.args;
 153         if (args.length() == 1 && !args.head.hasTag(ASSIGN)) {
 154             // special case: elided "value=" assumed
 155             args.head = make.at(args.head.pos).
 156                 Assign(make.Ident(names.value), args.head);
 157         }
 158         ListBuffer<Pair<MethodSymbol,Attribute>> buf =
 159             new ListBuffer<Pair<MethodSymbol,Attribute>>();
 160         for (List<JCExpression> tl = args; tl.nonEmpty(); tl = tl.tail) {
 161             JCExpression t = tl.head;
 162             if (!t.hasTag(ASSIGN)) {
 163                 log.error(t.pos(), "annotation.value.must.be.name.value");
 164                 continue;
 165             }
 166             JCAssign assign = (JCAssign)t;
 167             if (!assign.lhs.hasTag(IDENT)) {
 168                 log.error(t.pos(), "annotation.value.must.be.name.value");
 169                 continue;
 170             }
 171             JCIdent left = (JCIdent)assign.lhs;
 172             Symbol method = rs.resolveQualifiedMethod(left.pos(),
 173                                                           env,
 174                                                           a.type,
 175                                                           left.name,
 176                                                           List.<Type>nil(),
 177                                                           null);
 178             left.sym = method;
 179             left.type = method.type;
 180             if (method.owner != a.type.tsym)
 181                 log.error(left.pos(), "no.annotation.member", left.name, a.type);
 182             Type result = method.type.getReturnType();
 183             Attribute value = enterAttributeValue(result, assign.rhs, env);
 184             if (!method.type.isErroneous())
 185                 buf.append(new Pair<MethodSymbol,Attribute>
 186                            ((MethodSymbol)method, value));
 187             t.type = result;
 188         }
 189         return new Attribute.Compound(a.type, buf.toList());
 190     }
 191 
 192     Attribute enterAttributeValue(Type expected,
 193                                   JCExpression tree,
 194                                   Env<AttrContext> env) {
 195         //first, try completing the attribution value sym - if a completion
 196         //error is thrown, we should recover gracefully, and display an
 197         //ordinary resolution diagnostic.
 198         try {
 199             expected.tsym.complete();
 200         } catch(CompletionFailure e) {
 201             log.error(tree.pos(), "cant.resolve", Kinds.kindName(e.sym), e.sym);
 202             return new Attribute.Error(expected);
 203         }
 204         if (expected.isPrimitive() || types.isSameType(expected, syms.stringType)) {
 205             Type result = attr.attribExpr(tree, env, expected);
 206             if (result.isErroneous())
 207                 return new Attribute.Error(expected);
 208             if (result.constValue() == null) {
 209                 log.error(tree.pos(), "attribute.value.must.be.constant");
 210                 return new Attribute.Error(expected);
 211             }
 212             result = cfolder.coerce(result, expected);
 213             return new Attribute.Constant(expected, result.constValue());
 214         }
 215         if (expected.tsym == syms.classType.tsym) {
 216             Type result = attr.attribExpr(tree, env, expected);
 217             if (result.isErroneous())
 218                 return new Attribute.Error(expected);
 219             if (TreeInfo.name(tree) != names._class) {
 220                 log.error(tree.pos(), "annotation.value.must.be.class.literal");
 221                 return new Attribute.Error(expected);
 222             }
 223             return new Attribute.Class(types,
 224                                        (((JCFieldAccess) tree).selected).type);
 225         }
 226         if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) {
 227             if (!tree.hasTag(ANNOTATION)) {
 228                 log.error(tree.pos(), "annotation.value.must.be.annotation");
 229                 expected = syms.errorType;
 230             }
 231             return enterAnnotation((JCAnnotation)tree, expected, env);
 232         }
 233         if (expected.tag == TypeTags.ARRAY) { // should really be isArray()
 234             if (!tree.hasTag(NEWARRAY)) {
 235                 tree = make.at(tree.pos).
 236                     NewArray(null, List.<JCExpression>nil(), List.of(tree));
 237             }
 238             JCNewArray na = (JCNewArray)tree;
 239             if (na.elemtype != null) {
 240                 log.error(na.elemtype.pos(), "new.not.allowed.in.annotation");
 241                 return new Attribute.Error(expected);
 242             }
 243             ListBuffer<Attribute> buf = new ListBuffer<Attribute>();
 244             for (List<JCExpression> l = na.elems; l.nonEmpty(); l=l.tail) {
 245                 buf.append(enterAttributeValue(types.elemtype(expected),
 246                                                l.head,
 247                                                env));
 248             }
 249             na.type = expected;
 250             return new Attribute.
 251                 Array(expected, buf.toArray(new Attribute[buf.length()]));
 252         }
 253         if (expected.tag == TypeTags.CLASS &&
 254             (expected.tsym.flags() & Flags.ENUM) != 0) {
 255             attr.attribExpr(tree, env, expected);
 256             Symbol sym = TreeInfo.symbol(tree);
 257             if (sym == null ||
 258                 TreeInfo.nonstaticSelect(tree) ||
 259                 sym.kind != Kinds.VAR ||
 260                 (sym.flags() & Flags.ENUM) == 0) {
 261                 log.error(tree.pos(), "enum.annotation.must.be.enum.constant");
 262                 return new Attribute.Error(expected);
 263             }
 264             VarSymbol enumerator = (VarSymbol) sym;
 265             return new Attribute.Enum(expected, enumerator);
 266         }
 267         if (!expected.isErroneous())
 268             log.error(tree.pos(), "annotation.value.not.allowable.type");
 269         return new Attribute.Error(attr.attribExpr(tree, env, expected));
 270     }
 271 }