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