Fri, 28 Sep 2012 11:39:29 -0700
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 }