1// This is a wrapper class around an TypeAnnotationVisitor that can be used
2// to verify the information it visits specifies a valid [extended] annotation.
3
4package annotations.io.classfile;
5
6/*>>>
7import org.checkerframework.checker.nullness.qual.*;
8*/
9
10import java.util.ArrayList;
11import java.util.List;
12
13import org.objectweb.asm.AnnotationVisitor;
14import org.objectweb.asm.TypeAnnotationVisitor;
15
16import com.sun.tools.javac.code.TargetType;
17import com.sun.tools.javac.code.TypeAnnotationPosition.TypePathEntry;
18
19/**
20 * A <code>SafeTypeAnnotationVisitor</code> wraps around an
21 * TypeAnnotationVisitor and delegates all calls to it.  However, it
22 * maintains a record of all methods that have been called and on
23 * calling {@link SafeTypeAnnotationVisitor#visitEnd},
24 * performs a check to verify that all the data passed
25 * to this visitor specifies a legal annotation or extended annotation.  Its
26 * intended use is to wrap around an <code>TypeAnnotationWriter</code> and
27 * thus ensure that no illegal class files are written, although it will work
28 * more generally on any visitor.
29 *
30 * <p>
31 * Also note that nothing special needs to be done about subannotations
32 * and regular arrays, since their lengths are not passed in to this visitor.
33 *
34 * <p>
35 * If none of the <code>visitX*</code> methods has been called, it is
36 * automatically a legal annotation.  Else, if some of the <code>visitX*</code>
37 * methods have been called, the check will ensure that the data passed to this
38 * specifies a legal extended annotation, as defined by its target type.
39 */
40public class SafeTypeAnnotationVisitor
41implements TypeAnnotationVisitor {
42
43  // The visitor this delegates all calls to.
44  private final TypeAnnotationVisitor xav;
45
46  // Each list keeps a record of what was passed in to the similarly-named
47  // method, and except for xLocationArgs, should all contain at most 1 element.
48  private final List<Integer> xIndexArgs;
49  private final List<Integer> xLengthArgs;
50  private final List<TypePathEntry> xLocationArgs;
51  private final List<Integer> xLocationLengthArgs;
52  private final List<Integer> xOffsetArgs;
53  private final List<Integer> xStartPcArgs;
54  private final List<Integer> xTargetTypeArgs;
55  private final List<Integer> xParamIndexArgs;
56  private final List<Integer> xBoundIndexArgs;
57  private final List<Integer> xTypeIndexArgs;
58
59  // Counts the number of times visitXNameAndArgsSize is called.
60  private int xNameAndArgsCount;
61
62  /**
63   * Constructs a new <code> SafeTypeAnnotationVisitor </code> that
64   *  delegates all calls to the given visitor.
65   *
66   * @param xav the visitor to delegate all method calls to
67   */
68  public SafeTypeAnnotationVisitor(TypeAnnotationVisitor xav) {
69    this.xav = xav;
70    // Start most of these with a capacity of one, since for legal annotations
71    // they should not contain more than one element.
72    xIndexArgs = new ArrayList<Integer>(1);
73    xLengthArgs = new ArrayList<Integer>(1);
74    xLocationArgs = new ArrayList<TypePathEntry>();
75    xLocationLengthArgs = new ArrayList<Integer>(1);
76    xOffsetArgs = new ArrayList<Integer>(1);
77    xStartPcArgs = new ArrayList<Integer>(1);
78    xTargetTypeArgs = new ArrayList<Integer>(1);
79    xParamIndexArgs = new ArrayList<Integer>(1);
80    xBoundIndexArgs = new ArrayList<Integer>(1);
81    xTypeIndexArgs = new ArrayList<Integer>(1);
82    xNameAndArgsCount = 0;
83  }
84
85  /**
86   * {@inheritDoc}
87   * @see org.objectweb.asm.AnnotationVisitor#visit(java.lang.String, java.lang.Object)
88   */
89  @Override
90  public void visit(String name, Object value) {
91    xav.visit(name, value);
92  }
93
94  /**
95   * {@inheritDoc}
96   * @see org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang.String, java.lang.String)
97   */
98  @Override
99  public AnnotationVisitor visitAnnotation(String name, String desc) {
100    return xav.visitAnnotation(name, desc);
101  }
102
103  /**
104   * {@inheritDoc}
105   * @see org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
106   */
107  @Override
108  public AnnotationVisitor visitArray(String name) {
109    return xav.visitArray(name);
110  }
111
112  /**
113   * {@inheritDoc}
114   * @see org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String, java.lang.String, java.lang.String)
115   */
116  @Override
117  public void visitEnum(String name, String desc, String value) {
118    xav.visitEnum(name, desc, value);
119  }
120
121  /**
122   * {@inheritDoc}
123   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXIndex(int)
124   */
125  @Override
126  public void visitXIndex(int index) {
127    xIndexArgs.add(index);
128    xav.visitXIndex(index);
129  }
130
131  /**
132   * {@inheritDoc}
133   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLength(int)
134   */
135  @Override
136  public void visitXLength(int length) {
137    xLengthArgs.add(length);
138    xav.visitXLength(length);
139  }
140
141  /**
142   * {@inheritDoc}
143   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocation(TypePathEntry)
144   */
145  @Override
146  public void visitXLocation(TypePathEntry location) {
147    xLocationArgs.add(location);
148    xav.visitXLocation(location);
149  }
150
151  /**
152   * {@inheritDoc}
153   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXLocationLength(int)
154   */
155  @Override
156  public void visitXLocationLength(int location_length) {
157    xLocationLengthArgs.add(location_length);
158    xav.visitXLocationLength(location_length);
159  }
160
161  /**
162   * {@inheritDoc}
163   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXOffset(int)
164   */
165  @Override
166  public void visitXOffset(int offset) {
167    xOffsetArgs.add(offset);
168    xav.visitXOffset(offset);
169  }
170
171  @Override
172  public void visitXNumEntries(int num_entries) {
173  }
174
175  /**
176   * {@inheritDoc}
177   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXStartPc(int)
178   */
179  @Override
180  public void visitXStartPc(int start_pc) {
181    xStartPcArgs.add(start_pc);
182    xav.visitXStartPc(start_pc);
183  }
184
185  /**
186   * {@inheritDoc}
187   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXTargetType(int)
188   */
189  @Override
190  public void visitXTargetType(int target_type) {
191    xTargetTypeArgs.add(target_type);
192    xav.visitXTargetType(target_type);
193  }
194
195  /**
196   * {@inheritDoc}
197   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXParamIndex(int)
198   */
199  @Override
200  public void visitXParamIndex(int param_index) {
201    xParamIndexArgs.add(param_index);
202    xav.visitXParamIndex(param_index);
203  }
204
205  /**
206   * {@inheritDoc}
207   * @see org.objectweb.asm.TypeAnnotationVisitor#visitXBoundIndex(int)
208   */
209  @Override
210  public void visitXBoundIndex(int bound_index) {
211    if (bound_index != -1) {
212      xBoundIndexArgs.add(bound_index);
213      xav.visitXBoundIndex(bound_index);
214    }
215  }
216
217  @Override
218  public void visitXTypeIndex(int type_index) {
219    xTypeIndexArgs.add(type_index);
220    xav.visitXTypeIndex(type_index);
221  }
222
223  @Override
224  public void visitXExceptionIndex(int exception_index) {
225    // TODO
226  }
227
228  @Override
229  public void visitXNameAndArgsSize() {
230    xNameAndArgsCount++;
231    xav.visitXNameAndArgsSize();
232  }
233
234  /**
235   * Visits the end of the annotation, and also performs a check
236   *  to ensure that the information this has visited specifies a legal
237   *  annotation.  If the information does not specify a legal annotation,
238   *  throws an exception.
239   *
240   * {@inheritDoc}
241   * @throws InvalidTypeAnnotationException if the information this
242   *  has visited does not specify a legal extended annotation
243   * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
244   */
245  @Override
246  public void visitEnd() {
247    if (xTargetTypeArgs.size() > 0) {
248      checkX();
249    } else {
250      // This has not visited an the target type of an extended annotation, so
251      //  must ensure that all other extended information lists are empty.
252      if (xIndexArgs.size() != 0 ||
253          xLengthArgs.size() != 0 ||
254          xLocationArgs.size() != 0 ||
255          xLocationLengthArgs.size() != 0 ||
256          xOffsetArgs.size() != 0 ||
257          xStartPcArgs.size() != 0) {
258        throw new InvalidTypeAnnotationException(
259        "No target type was specified, yet other visitX* methods were still called.");
260      }
261    }
262    xav.visitEnd();
263  }
264
265  /**
266   * Checks that the extended information this has visited is valid.
267   *
268   * @throws InvalidTypeAnnotationException if extended information is
269   *  not valid
270   */
271  private void checkX() {
272    // First, check to see that only one target type was specified, and
273    // then dispatch to checkListSize() based on that target type.
274    if (xTargetTypeArgs.size() != 1) {
275      throw new
276      InvalidTypeAnnotationException("More than one target type visited.");
277    }
278
279    if (xNameAndArgsCount != 1) {
280      throw new InvalidTypeAnnotationException("Name and args count should "
281          + " be visited 1 time, actually visited " + xNameAndArgsCount
282          + " times.");
283    }
284
285    // Since the correct size of xLocationArgs is specified by
286    // xLocationLengthArgs, this information must be looked up first.
287    int c = 0;
288    if (xLocationLengthArgs.size() > 0) {
289      c = xLocationLengthArgs.get(0);
290    }
291
292    switch(TargetType.fromTargetTypeValue(xTargetTypeArgs.get(0))) {
293    case CAST:
294      checkListSize(0, 0, c, 1, 1, 0, 0, 0, 1,
295          "Invalid typecast annotation:");
296      break;
297    case INSTANCEOF:
298      checkListSize(0, 0, c, 1, 1, 0, 0, 0, 0,
299      "Invalid type test annotation:");
300      break;
301    case NEW:
302      checkListSize(0, 0, c, 1, 1, 0, 0, 0, 0,
303      "Invalid object creation annotation:");
304      break;
305    case METHOD_RECEIVER:
306      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0,
307      "Invalid method receiver annotation:");
308      break;
309    case LOCAL_VARIABLE:
310      checkListSize(1, 1, c, 1, 0, 1, 0, 0, 0,
311      "Invalid local variable annotation:");
312      break;
313    case METHOD_RETURN:
314      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0,
315      "Invalid method return type annotation:");
316      break;
317    case METHOD_FORMAL_PARAMETER:
318      checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0,
319      "Invalid method parameter annotation:");
320      break;
321    case FIELD:
322      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 0,
323      "Invalid field annotation:");
324      break;
325    case CLASS_TYPE_PARAMETER:
326      checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0,
327      "Invalid class type parameter annotation:");
328      break;
329    case CLASS_TYPE_PARAMETER_BOUND:
330      checkListSize(0, 0, c, 1, 0, 0, 1, 1, 0,
331      "Invalid class type parameter bound annotation:");
332      break;
333    case METHOD_TYPE_PARAMETER:
334      checkListSize(0, 0, c, 1, 0, 0, 1, 0, 0,
335      "Invalid method type parameter annotation:");
336      break;
337    case METHOD_TYPE_PARAMETER_BOUND:
338      checkListSize(0, 0, c, 1, 0, 0, 1, 1, 0,
339      "Invalid method type parameter bound annotation:");
340      break;
341    case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
342    case METHOD_INVOCATION_TYPE_ARGUMENT:
343    case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
344    case METHOD_REFERENCE_TYPE_ARGUMENT:
345      // TODO
346      break;
347    case CLASS_EXTENDS:
348      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 1,
349      "Invalid class extends/implements annotation:");
350      break;
351    case THROWS:
352      checkListSize(0, 0, c, 1, 0, 0, 0, 0, 1,
353      "Invalid exception type in throws annotation:");
354      break;
355    default:
356      throw new InvalidTypeAnnotationException(
357          "Unknown target type given: " + xTargetTypeArgs.get(0));
358    }
359  }
360
361  /**
362   * If list.size() != correctLength, appends a descriptive error message to sb
363   *  specifying how many times methodName was supposed to be called and how
364   *  many times it was actually called.
365   * Else, has no effect.
366   *
367   * @param list the list of arguments actually visited
368   * @param correctLength the correct length of list
369   * @param methodName the name of the method whose arguments went into list
370   * @param sb the StringBuilder to append error messages to
371   */
372  private void appendMessage(List<?> list, int idealLength,
373      String methodName, StringBuilder sb) {
374    if (list.size() != idealLength) {
375      sb.append("\nInvalid method calls: ");
376      sb.append(methodName);
377      sb.append(" was called ");
378      sb.append(list.size());
379      sb.append(" times, but should have only been called ");
380      sb.append(idealLength);
381      sb.append(" times");
382    }
383  }
384
385  /**
386   * Checks that the seven lists containing extended annotation information are
387   *  of the specified sizes.  If at least one of the lists is not of the
388   *  correct length, this throws an exception with a message equal to msg, plus
389   *  information describing which lists were incorrect.
390   *
391   * @throws InvalidTypeAnnotationException if the extended information
392   *  lists are not of the correct length
393   */
394  private void checkListSize(
395      int correctLengthIndex,
396      int correctLengthLength,
397      int correctLengthLocation,
398      int correctLengthLocationLength,
399      int correctLengthOffset,
400      int correctLengthStartPc,
401      int correctLengthParamIndex,
402      int correctLengthBoundIndex,
403      int correctLengthTypeIndex,
404      String msg) {
405    StringBuilder sb = new StringBuilder();
406    appendMessage(xIndexArgs, correctLengthIndex, "visitXIndex", sb);
407    appendMessage(xLengthArgs, correctLengthLength, "visitXLength", sb);
408    appendMessage(xLocationArgs, correctLengthLocation, "visitXLocation", sb);
409    appendMessage(xLocationLengthArgs, correctLengthLocationLength,
410        "visitXLocationLength", sb);
411    appendMessage(xOffsetArgs, correctLengthOffset, "visitXOffset", sb);
412    appendMessage(xStartPcArgs, correctLengthStartPc, "visitXStartPc", sb);
413    appendMessage(xParamIndexArgs, correctLengthParamIndex, "visitXParamIndex", sb);
414    appendMessage(xBoundIndexArgs, correctLengthBoundIndex, "visitXBoundIndex", sb);
415    appendMessage(xTypeIndexArgs, correctLengthTypeIndex, "VisitXTypeIndex", sb);
416
417    // At this point, sb will contain Strings iff there is an error
418    //  in the extended annotation information.
419    String s = sb.toString();
420    if (s.length() > 0) {
421      throw new InvalidTypeAnnotationException(msg + s);
422    }
423  }
424}
425