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