1 /* |
|
2 * Copyright (c) 2012, 2014, 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. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 |
|
24 import java.lang.annotation.*; |
|
25 import java.io.*; |
|
26 import java.net.URL; |
|
27 import java.util.List; |
|
28 |
|
29 import com.sun.tools.classfile.*; |
|
30 |
|
31 /** |
|
32 * A class providing utilities for writing tests that inspect class |
|
33 * files directly, looking for specific type annotations. |
|
34 * |
|
35 * Note: this framework does not currently handle repeating |
|
36 * annotations. |
|
37 */ |
|
38 public class ClassfileInspector { |
|
39 |
|
40 /** |
|
41 * A group of expected annotations to be found in a given class. |
|
42 * If the class name is null, then the template will be applied to |
|
43 * every class. |
|
44 */ |
|
45 public static class Expected { |
|
46 /** |
|
47 * The name of the class. If {@code null} this template will |
|
48 * apply to every class; otherwise, it will only be applied to |
|
49 * the named class. |
|
50 */ |
|
51 public final String classname; |
|
52 |
|
53 /** |
|
54 * The expected class annotations. These will be checked |
|
55 * against the class' attributes. |
|
56 */ |
|
57 public final ExpectedTypeAnnotation[] classAnnos; |
|
58 |
|
59 /** |
|
60 * The expected method annotations. These will be checked |
|
61 * against all methods in the class. |
|
62 */ |
|
63 public final ExpectedMethodTypeAnnotation[] methodAnnos; |
|
64 |
|
65 /** |
|
66 * The expected field annotations. These will be checked |
|
67 * against all fields in the class. |
|
68 */ |
|
69 public final ExpectedFieldTypeAnnotation[] fieldAnnos; |
|
70 |
|
71 /** |
|
72 * Create an {@code Expected} from its components. |
|
73 * |
|
74 * @param classname The name of the class to match, or {@code |
|
75 * null} for all classes. |
|
76 * @param classAnnos The expected class annotations. |
|
77 * @param methodAnnos The expected method annotations. |
|
78 * @param fieldAnnos The expected field annotations. |
|
79 */ |
|
80 public Expected(String classname, |
|
81 ExpectedTypeAnnotation[] classAnnos, |
|
82 ExpectedMethodTypeAnnotation[] methodAnnos, |
|
83 ExpectedFieldTypeAnnotation[] fieldAnnos) { |
|
84 this.classname = classname; |
|
85 this.classAnnos = classAnnos; |
|
86 this.methodAnnos = methodAnnos; |
|
87 this.fieldAnnos = fieldAnnos; |
|
88 } |
|
89 |
|
90 public String toString() { |
|
91 final StringBuilder sb = new StringBuilder(); |
|
92 final String newline = System.lineSeparator(); |
|
93 sb.append("Expected on class ").append(classname); |
|
94 if (null != classAnnos) { |
|
95 sb.append(newline).append("Class annotations:").append(newline); |
|
96 for(ExpectedTypeAnnotation anno : classAnnos) { |
|
97 sb.append(anno).append(newline); |
|
98 } |
|
99 } |
|
100 if (null != methodAnnos) { |
|
101 sb.append(newline).append("Method annotations:").append(newline); |
|
102 for(ExpectedTypeAnnotation anno : methodAnnos) { |
|
103 sb.append(anno).append(newline); |
|
104 } |
|
105 } |
|
106 if (null != fieldAnnos) { |
|
107 sb.append(newline).append("Field annotations:").append(newline); |
|
108 for(ExpectedTypeAnnotation anno : fieldAnnos) { |
|
109 sb.append(anno).append(newline); |
|
110 } |
|
111 } |
|
112 return sb.toString(); |
|
113 } |
|
114 |
|
115 /** |
|
116 * See if this template applies to a class. |
|
117 * |
|
118 * @param classname The classname to check. |
|
119 * @return Whether or not this template should apply. |
|
120 */ |
|
121 public boolean matchClassName(String classname) { |
|
122 return this.classname == null || this.classname.equals(classname); |
|
123 } |
|
124 |
|
125 /** |
|
126 * After applying the template to all classes, check to see if |
|
127 * any of the expected annotations weren't matched. |
|
128 * |
|
129 * @return The number of missed matches. |
|
130 */ |
|
131 public int check() { |
|
132 int count = 0; |
|
133 if (classAnnos != null) { |
|
134 for(ExpectedTypeAnnotation expected : classAnnos) { |
|
135 if (!expected.check()) { |
|
136 count++; |
|
137 } |
|
138 } |
|
139 } |
|
140 if (methodAnnos != null) { |
|
141 for(ExpectedMethodTypeAnnotation expected : methodAnnos) { |
|
142 if (!expected.check()) { |
|
143 count++; |
|
144 } |
|
145 } |
|
146 } |
|
147 if (fieldAnnos != null) { |
|
148 for(ExpectedFieldTypeAnnotation expected : fieldAnnos) { |
|
149 if (!expected.check()) { |
|
150 count++; |
|
151 } |
|
152 } |
|
153 } |
|
154 return count; |
|
155 } |
|
156 } |
|
157 |
|
158 /** |
|
159 * An expected type annotation. This is both a superclass for |
|
160 * method and field type annotations, as well as a class for type |
|
161 * annotations on a class. |
|
162 */ |
|
163 public static class ExpectedTypeAnnotation { |
|
164 private int count = 0; |
|
165 protected final String expectedName; |
|
166 protected final int expectedCount; |
|
167 protected final TypeAnnotation.TargetType targetType; |
|
168 protected final int bound_index; |
|
169 protected final int parameter_index; |
|
170 protected final int type_index; |
|
171 protected final int exception_index; |
|
172 protected final TypeAnnotation.Position.TypePathEntry[] typePath; |
|
173 protected final boolean visibility; |
|
174 |
|
175 /** |
|
176 * Create an {@code ExpectedTypeAnnotation} from its |
|
177 * components. It is usually a better idea to use a {@code |
|
178 * Builder} to do this. |
|
179 * |
|
180 * @param expectedName The expected annotation name. |
|
181 * @param visibility Whether this annotation should be runtime-visible. |
|
182 * @param expectedCount The number of annotations that should |
|
183 * be seen. If 0, this asserts that the |
|
184 * described annotation is not present. |
|
185 * @param targetType The expected target type. |
|
186 * @param bound_index The expected bound index, or {@code Integer.MIN_VALUE}. |
|
187 * @param parameter_index The expected parameter index, or |
|
188 * {@code Integer.MIN_VALUE}. |
|
189 * @param type_index The expected type index, or {@code Integer.MIN_VALUE}. |
|
190 * @param exception_index The expected exception index, or |
|
191 * {@code Integer.MIN_VALUE}. |
|
192 * @param typePath The expected type path. |
|
193 */ |
|
194 public ExpectedTypeAnnotation(String expectedName, |
|
195 boolean visibility, |
|
196 int expectedCount, |
|
197 TypeAnnotation.TargetType targetType, |
|
198 int bound_index, |
|
199 int parameter_index, |
|
200 int type_index, |
|
201 int exception_index, |
|
202 TypeAnnotation.Position.TypePathEntry... typePath) { |
|
203 this.expectedName = expectedName; |
|
204 this.visibility = visibility; |
|
205 this.expectedCount = expectedCount; |
|
206 this.targetType = targetType; |
|
207 this.bound_index = bound_index; |
|
208 this.parameter_index = parameter_index; |
|
209 this.type_index = type_index; |
|
210 this.exception_index = exception_index; |
|
211 this.typePath = typePath; |
|
212 } |
|
213 |
|
214 public String toString() { |
|
215 final StringBuilder sb = new StringBuilder(); |
|
216 sb.append("Expected "); |
|
217 sb.append(expectedCount); |
|
218 sb.append(" annotation "); |
|
219 sb.append(expectedName); |
|
220 sb.append(visibility ? ", runtime visibile " : ", runtime invisibile "); |
|
221 sb.append(targetType); |
|
222 sb.append(", bound_index = "); |
|
223 sb.append(bound_index); |
|
224 sb.append(", parameter_index = "); |
|
225 sb.append(parameter_index); |
|
226 sb.append(", type_index = "); |
|
227 sb.append(type_index); |
|
228 sb.append(", exception_index = "); |
|
229 sb.append(exception_index); |
|
230 sb.append(", type_path = ["); |
|
231 for(int i = 0; i < typePath.length; i++) { |
|
232 if (i != 0) { |
|
233 sb.append(", "); |
|
234 } |
|
235 sb.append(typePath[i]); |
|
236 } |
|
237 sb.append("]"); |
|
238 return sb.toString(); |
|
239 } |
|
240 |
|
241 /** |
|
242 * See if this template matches the given visibility. |
|
243 * |
|
244 * @param Whether or not the annotation is visible at runtime. |
|
245 * @return Whether or not this template matches the visibility. |
|
246 */ |
|
247 public boolean matchVisibility(boolean visibility) { |
|
248 return this.visibility == visibility; |
|
249 } |
|
250 |
|
251 /** |
|
252 * Attempty to match this template against an annotation. If |
|
253 * it does match, then the match count for the template will |
|
254 * be incremented. Otherwise, nothing will be done. |
|
255 * |
|
256 * @param anno The annotation to attempt to match. |
|
257 */ |
|
258 public void matchAnnotation(TypeAnnotation anno) { |
|
259 boolean matches = true; |
|
260 |
|
261 try { |
|
262 matches = anno.constant_pool.getUTF8Info(anno.annotation.type_index).value.equals("L" + expectedName + ";"); |
|
263 } catch(Exception e) { |
|
264 matches = false; |
|
265 } |
|
266 |
|
267 matches = matches && anno.position.type == targetType; |
|
268 matches = matches && anno.position.bound_index == bound_index; |
|
269 matches = matches && anno.position.parameter_index == parameter_index; |
|
270 matches = matches && anno.position.type_index == type_index; |
|
271 matches = matches && anno.position.exception_index == exception_index; |
|
272 matches = matches && anno.position.location.size() == typePath.length; |
|
273 |
|
274 if (matches) { |
|
275 int i = 0; |
|
276 for(TypeAnnotation.Position.TypePathEntry entry : |
|
277 anno.position.location) { |
|
278 matches = matches && typePath[i++].equals(entry); |
|
279 } |
|
280 } |
|
281 |
|
282 if (matches) { |
|
283 count++; |
|
284 } |
|
285 } |
|
286 |
|
287 /** |
|
288 * After all matching, check to see if the expected number of |
|
289 * matches equals the actual number. If not, then print a |
|
290 * failure message and return {@code false}. |
|
291 * |
|
292 * @return Whether or not the expected number of matched |
|
293 * equals the actual number. |
|
294 */ |
|
295 public boolean check() { |
|
296 if (count != expectedCount) { |
|
297 System.err.println(this + ", but saw " + count); |
|
298 return false; |
|
299 } else { |
|
300 return true; |
|
301 } |
|
302 } |
|
303 |
|
304 /** |
|
305 * A builder class for creating {@code |
|
306 * ExpectedTypeAnnotation}s in a more convenient fashion. The |
|
307 * constructor for {@code ExpectedTypeAnnotation} takes a |
|
308 * large number of parameters (by necessity). This class |
|
309 * allows users to construct a {@code ExpectedTypeAnnotation}s |
|
310 * using only the ones they need. |
|
311 */ |
|
312 public static class Builder { |
|
313 protected final String expectedName; |
|
314 protected final int expectedCount; |
|
315 protected final TypeAnnotation.TargetType targetType; |
|
316 protected final boolean visibility; |
|
317 protected int bound_index = Integer.MIN_VALUE; |
|
318 protected int parameter_index = Integer.MIN_VALUE; |
|
319 protected int type_index = Integer.MIN_VALUE; |
|
320 protected int exception_index = Integer.MIN_VALUE; |
|
321 protected TypeAnnotation.Position.TypePathEntry[] typePath = |
|
322 new TypeAnnotation.Position.TypePathEntry[0]; |
|
323 |
|
324 /** |
|
325 * Create a {@code Builder} from the mandatory parameters. |
|
326 * |
|
327 * @param expectedName The expected annotation name. |
|
328 * @param targetType The expected target type. |
|
329 * @param visibility Whether this annotation should be runtime-visible. |
|
330 * @param expectedCount The number of annotations that should be seen. |
|
331 */ |
|
332 public Builder(String expectedName, |
|
333 TypeAnnotation.TargetType targetType, |
|
334 boolean visibility, |
|
335 int expectedCount) { |
|
336 this.expectedName = expectedName; |
|
337 this.visibility = visibility; |
|
338 this.expectedCount = expectedCount; |
|
339 this.targetType = targetType; |
|
340 } |
|
341 |
|
342 /** |
|
343 * Create an {@code ExpectedTypeAnnotation} from all |
|
344 * parameters that have been provided. The default values |
|
345 * will be used for those that have not. |
|
346 * |
|
347 * @return The cretaed {@code ExpectedTypeAnnotation}. |
|
348 */ |
|
349 public ExpectedTypeAnnotation build() { |
|
350 return new ExpectedTypeAnnotation(expectedName, visibility, |
|
351 expectedCount, targetType, |
|
352 bound_index, parameter_index, |
|
353 type_index, exception_index, |
|
354 typePath); |
|
355 } |
|
356 |
|
357 /** |
|
358 * Provide a bound index parameter. |
|
359 * |
|
360 * @param bound_index The bound_index value. |
|
361 */ |
|
362 public Builder setBoundIndex(int bound_index) { |
|
363 this.bound_index = bound_index; |
|
364 return this; |
|
365 } |
|
366 |
|
367 /** |
|
368 * Provide a parameter index parameter. |
|
369 * |
|
370 * @param bound_index The parameter_index value. |
|
371 */ |
|
372 public Builder setParameterIndex(int parameter_index) { |
|
373 this.parameter_index = parameter_index; |
|
374 return this; |
|
375 } |
|
376 |
|
377 /** |
|
378 * Provide a type index parameter. |
|
379 * |
|
380 * @param type_index The type_index value. |
|
381 */ |
|
382 public Builder setTypeIndex(int type_index) { |
|
383 this.type_index = type_index; |
|
384 return this; |
|
385 } |
|
386 |
|
387 /** |
|
388 * Provide an exception index parameter. |
|
389 * |
|
390 * @param exception_index The exception_index value. |
|
391 */ |
|
392 public Builder setExceptionIndex(int exception_index) { |
|
393 this.exception_index = exception_index; |
|
394 return this; |
|
395 } |
|
396 |
|
397 /** |
|
398 * Provide a type path parameter. |
|
399 * |
|
400 * @param typePath The type path value. |
|
401 */ |
|
402 public Builder setTypePath(TypeAnnotation.Position.TypePathEntry[] typePath) { |
|
403 this.typePath = typePath; |
|
404 return this; |
|
405 } |
|
406 } |
|
407 } |
|
408 |
|
409 /** |
|
410 * A type annotation found on a method. |
|
411 */ |
|
412 public static class ExpectedMethodTypeAnnotation extends ExpectedTypeAnnotation { |
|
413 private final String methodname; |
|
414 |
|
415 /** |
|
416 * Create an {@code ExpectedMethodTypeAnnotation} from its |
|
417 * components. It is usually a better idea to use a {@code |
|
418 * Builder} to do this. |
|
419 * |
|
420 * @param methodname The expected method name. |
|
421 * @param expectedName The expected annotation name. |
|
422 * @param visibility Whether this annotation should be runtime-visible. |
|
423 * @param expectedCount The number of annotations that should be seen. |
|
424 * @param targetType The expected target type. |
|
425 * @param bound_index The expected bound index, or {@code Integer.MIN_VALUE}. |
|
426 * @param parameter_index The expected parameter index, or |
|
427 * {@code Integer.MIN_VALUE}. |
|
428 * @param type_index The expected type index, or {@code Integer.MIN_VALUE}. |
|
429 * @param exception_index The expected exception index, or |
|
430 * {@code Integer.MIN_VALUE}. |
|
431 * @param typePath The expected type path. |
|
432 */ |
|
433 public ExpectedMethodTypeAnnotation(String methodname, |
|
434 String expectedName, |
|
435 boolean visibility, |
|
436 int expectedCount, |
|
437 TypeAnnotation.TargetType targetType, |
|
438 int bound_index, |
|
439 int parameter_index, |
|
440 int type_index, |
|
441 int exception_index, |
|
442 TypeAnnotation.Position.TypePathEntry... typePath) { |
|
443 super(expectedName, visibility, expectedCount, targetType, bound_index, |
|
444 parameter_index, type_index, exception_index, typePath); |
|
445 this.methodname = methodname; |
|
446 } |
|
447 |
|
448 public String toString() { |
|
449 final StringBuilder sb = new StringBuilder(); |
|
450 sb.append("Expected "); |
|
451 sb.append(expectedCount); |
|
452 sb.append(" annotation "); |
|
453 sb.append(expectedName); |
|
454 sb.append(visibility ? ", runtime visibile " : ", runtime invisibile "); |
|
455 sb.append(targetType); |
|
456 sb.append(", bound_index = "); |
|
457 sb.append(bound_index); |
|
458 sb.append(", parameter_index = "); |
|
459 sb.append(parameter_index); |
|
460 sb.append(", type_index = "); |
|
461 sb.append(type_index); |
|
462 sb.append(", exception_index = "); |
|
463 sb.append(exception_index); |
|
464 sb.append(", type_path = ["); |
|
465 for(int i = 0; i < typePath.length; i++) { |
|
466 if (i != 0) { |
|
467 sb.append(", "); |
|
468 } |
|
469 sb.append(typePath[i]); |
|
470 } |
|
471 sb.append("]"); |
|
472 sb.append(" on method "); |
|
473 sb.append(methodname); |
|
474 return sb.toString(); |
|
475 } |
|
476 |
|
477 /** |
|
478 * See if this template applies to a method. |
|
479 * |
|
480 * @param methodname The method name to check. |
|
481 * @return Whether or not this template should apply. |
|
482 */ |
|
483 public boolean matchMethodName(String methodname) { |
|
484 return this.methodname.equals(methodname); |
|
485 } |
|
486 |
|
487 /** |
|
488 * A builder class for creating {@code |
|
489 * ExpectedMethodTypeAnnotation}s in a more convenient fashion. The |
|
490 * constructor for {@code ExpectedMethodTypeAnnotation} takes a |
|
491 * large number of parameters (by necessity). This class |
|
492 * allows users to construct a {@code ExpectedMethodTypeAnnotation}s |
|
493 * using only the ones they need. |
|
494 */ |
|
495 public static class Builder extends ExpectedTypeAnnotation.Builder { |
|
496 protected final String methodname; |
|
497 |
|
498 /** |
|
499 * Create a {@code Builder} from the mandatory parameters. |
|
500 * |
|
501 * @param methodname The expected method name. |
|
502 * @param expectedName The expected annotation name. |
|
503 * @param targetType The expected target type. |
|
504 * @param visibility Whether this annotation should be runtime-visible. |
|
505 * @param expectedCount The number of annotations that should be seen. |
|
506 */ |
|
507 public Builder(String methodname, |
|
508 String expectedName, |
|
509 TypeAnnotation.TargetType targetType, |
|
510 boolean visibility, |
|
511 int expectedCount) { |
|
512 super(expectedName, targetType, visibility, expectedCount); |
|
513 this.methodname = methodname; |
|
514 } |
|
515 |
|
516 /** |
|
517 * Create an {@code ExpectedMethodTypeAnnotation} from all |
|
518 * parameters that have been provided. The default values |
|
519 * will be used for those that have not. |
|
520 * |
|
521 * @return The cretaed {@code ExpectedMethodTypeAnnotation}. |
|
522 */ |
|
523 public ExpectedMethodTypeAnnotation build() { |
|
524 return new ExpectedMethodTypeAnnotation(methodname, expectedName, |
|
525 visibility, expectedCount, |
|
526 targetType, bound_index, |
|
527 parameter_index, type_index, |
|
528 exception_index, typePath); |
|
529 } |
|
530 } |
|
531 } |
|
532 |
|
533 /** |
|
534 * A type annotation found on a field. |
|
535 */ |
|
536 public static class ExpectedFieldTypeAnnotation extends ExpectedTypeAnnotation { |
|
537 private final String fieldname; |
|
538 |
|
539 /** |
|
540 * Create an {@code ExpectedFieldTypeAnnotation} from its |
|
541 * components. It is usually a better idea to use a {@code |
|
542 * Builder} to do this. |
|
543 * |
|
544 * @param fieldname The expected field name. |
|
545 * @param expectedName The expected annotation name. |
|
546 * @param visibility Whether this annotation should be runtime-visible. |
|
547 * @param expectedCount The number of annotations that should be seen. |
|
548 * @param targetType The expected target type. |
|
549 * @param bound_index The expected bound index, or {@code Integer.MIN_VALUE}. |
|
550 * @param parameter_index The expected parameter index, or |
|
551 * {@code Integer.MIN_VALUE}. |
|
552 * @param type_index The expected type index, or {@code Integer.MIN_VALUE}. |
|
553 * @param exception_index The expected exception index, or |
|
554 * {@code Integer.MIN_VALUE}. |
|
555 * @param typePath The expected type path. |
|
556 */ |
|
557 public ExpectedFieldTypeAnnotation(String fieldname, |
|
558 String expectedName, |
|
559 boolean visibility, |
|
560 int expectedCount, |
|
561 TypeAnnotation.TargetType targetType, |
|
562 int bound_index, |
|
563 int parameter_index, |
|
564 int type_index, |
|
565 int exception_index, |
|
566 TypeAnnotation.Position.TypePathEntry... typePath) { |
|
567 super(expectedName, visibility, expectedCount, targetType, bound_index, |
|
568 parameter_index, type_index, exception_index, typePath); |
|
569 this.fieldname = fieldname; |
|
570 } |
|
571 |
|
572 public String toString() { |
|
573 final StringBuilder sb = new StringBuilder(); |
|
574 sb.append("Expected ").append(expectedCount) |
|
575 .append(" annotation ").append(expectedName) |
|
576 .append(visibility ? ", runtime visibile " : ", runtime invisibile ") |
|
577 .append(targetType) |
|
578 .append(", bound_index = ").append(bound_index) |
|
579 .append(", parameter_index = ").append(parameter_index) |
|
580 .append(", type_index = ").append(type_index) |
|
581 .append(", exception_index = ").append(exception_index) |
|
582 .append(", type_path = ["); |
|
583 |
|
584 for(int i = 0; i < typePath.length; i++) { |
|
585 if (i != 0) { |
|
586 sb.append(", "); |
|
587 } |
|
588 sb.append(typePath[i]); |
|
589 } |
|
590 sb.append("]") |
|
591 .append(" on field ").append(fieldname); |
|
592 return sb.toString(); |
|
593 } |
|
594 |
|
595 /** |
|
596 * See if this template applies to a field. |
|
597 * |
|
598 * @param fieldname The field name to check. |
|
599 * @return Whether or not this template should apply. |
|
600 */ |
|
601 public boolean matchFieldName(String fieldname) { |
|
602 return this.fieldname.equals(fieldname); |
|
603 } |
|
604 |
|
605 /** |
|
606 * A builder class for creating {@code |
|
607 * ExpectedFieldTypeAnnotation}s in a more convenient fashion. The |
|
608 * constructor for {@code ExpectedFieldTypeAnnotation} takes a |
|
609 * large number of parameters (by necessity). This class |
|
610 * allows users to construct a {@code ExpectedFieldTypeAnnotation}s |
|
611 * using only the ones they need. |
|
612 */ |
|
613 public static class Builder extends ExpectedTypeAnnotation.Builder { |
|
614 protected final String fieldname; |
|
615 |
|
616 /** |
|
617 * Create a {@code Builder} from the mandatory parameters. |
|
618 * |
|
619 * @param fieldname The expected field name. |
|
620 * @param expectedName The expected annotation name. |
|
621 * @param targetType The expected target type. |
|
622 * @param visibility Whether this annotation should be runtime-visible. |
|
623 * @param expectedCount The number of annotations that should be seen. |
|
624 */ |
|
625 public Builder(String fieldname, |
|
626 String expectedName, |
|
627 TypeAnnotation.TargetType targetType, |
|
628 boolean visibility, |
|
629 int expectedCount) { |
|
630 super(expectedName, targetType, visibility, expectedCount); |
|
631 this.fieldname = fieldname; |
|
632 } |
|
633 |
|
634 /** |
|
635 * Create an {@code ExpectedFieldTypeAnnotation} from all |
|
636 * parameters that have been provided. The default values |
|
637 * will be used for those that have not. |
|
638 * |
|
639 * @return The cretaed {@code ExpectedFieldTypeAnnotation}. |
|
640 */ |
|
641 public ExpectedFieldTypeAnnotation build() { |
|
642 return new ExpectedFieldTypeAnnotation(fieldname, expectedName, |
|
643 visibility, expectedCount, |
|
644 targetType, bound_index, |
|
645 parameter_index, type_index, |
|
646 exception_index, typePath); |
|
647 } |
|
648 } |
|
649 } |
|
650 |
|
651 private void matchClassTypeAnnotation(ClassFile classfile, |
|
652 ExpectedTypeAnnotation expected) |
|
653 throws ConstantPoolException { |
|
654 for(Attribute attr : classfile.attributes) { |
|
655 attr.accept(typeAnnoMatcher, expected); |
|
656 } |
|
657 } |
|
658 |
|
659 private void matchMethodTypeAnnotation(ClassFile classfile, |
|
660 ExpectedMethodTypeAnnotation expected) |
|
661 throws ConstantPoolException { |
|
662 for(Method meth : classfile.methods) { |
|
663 if (expected.matchMethodName(meth.getName(classfile.constant_pool))) { |
|
664 for(Attribute attr : meth.attributes) { |
|
665 attr.accept(typeAnnoMatcher, expected); |
|
666 } |
|
667 } |
|
668 } |
|
669 } |
|
670 |
|
671 private void matchFieldTypeAnnotation(ClassFile classfile, |
|
672 ExpectedFieldTypeAnnotation expected) |
|
673 throws ConstantPoolException { |
|
674 for(Field field : classfile.fields) { |
|
675 if (expected.matchFieldName(field.getName(classfile.constant_pool))) { |
|
676 for(Attribute attr : field.attributes) { |
|
677 attr.accept(typeAnnoMatcher, expected); |
|
678 } |
|
679 } |
|
680 } |
|
681 } |
|
682 |
|
683 private void matchClassTypeAnnotations(ClassFile classfile, |
|
684 ExpectedTypeAnnotation[] expected) |
|
685 throws ConstantPoolException { |
|
686 for(ExpectedTypeAnnotation one : expected) { |
|
687 matchClassTypeAnnotation(classfile, one); |
|
688 } |
|
689 } |
|
690 |
|
691 private void matchMethodTypeAnnotations(ClassFile classfile, |
|
692 ExpectedMethodTypeAnnotation[] expected) |
|
693 throws ConstantPoolException { |
|
694 for(ExpectedMethodTypeAnnotation one : expected) { |
|
695 matchMethodTypeAnnotation(classfile, one); |
|
696 } |
|
697 } |
|
698 |
|
699 private void matchFieldTypeAnnotations(ClassFile classfile, |
|
700 ExpectedFieldTypeAnnotation[] expected) |
|
701 throws ConstantPoolException { |
|
702 for(ExpectedFieldTypeAnnotation one : expected) { |
|
703 matchFieldTypeAnnotation(classfile, one); |
|
704 } |
|
705 } |
|
706 |
|
707 /** |
|
708 * Run a template on a single {@code ClassFile}. |
|
709 * |
|
710 * @param classfile The {@code ClassFile} on which to run tests. |
|
711 * @param expected The expected annotation template. |
|
712 */ |
|
713 public void run(ClassFile classfile, |
|
714 Expected... expected) |
|
715 throws ConstantPoolException { |
|
716 run(new ClassFile[] { classfile }, expected); |
|
717 } |
|
718 |
|
719 /** |
|
720 * Run a template on multiple {@code ClassFile}s. |
|
721 * |
|
722 * @param classfile The {@code ClassFile}s on which to run tests. |
|
723 * @param expected The expected annotation template. |
|
724 */ |
|
725 public void run(ClassFile[] classfiles, |
|
726 Expected... expected) |
|
727 throws ConstantPoolException { |
|
728 for(ClassFile classfile : classfiles) { |
|
729 for(Expected one : expected) { |
|
730 if (one.matchClassName(classfile.getName())) { |
|
731 if (one.classAnnos != null) |
|
732 matchClassTypeAnnotations(classfile, one.classAnnos); |
|
733 if (one.methodAnnos != null) |
|
734 matchMethodTypeAnnotations(classfile, one.methodAnnos); |
|
735 if (one.fieldAnnos != null) |
|
736 matchFieldTypeAnnotations(classfile, one.fieldAnnos); |
|
737 } |
|
738 } |
|
739 } |
|
740 int count = 0; |
|
741 for (Expected one : expected) { |
|
742 count += one.check(); |
|
743 } |
|
744 |
|
745 if (count != 0) { |
|
746 throw new RuntimeException(count + " errors occurred in test"); |
|
747 } |
|
748 } |
|
749 |
|
750 /** |
|
751 * Get a {@code ClassFile} from its file name. |
|
752 * |
|
753 * @param name The class' file name. |
|
754 * @return The {@code ClassFile} |
|
755 */ |
|
756 public static ClassFile getClassFile(String name) |
|
757 throws IOException, ConstantPoolException { |
|
758 final URL url = ClassfileInspector.class.getResource(name); |
|
759 final InputStream in = url.openStream(); |
|
760 try { |
|
761 return ClassFile.read(in); |
|
762 } finally { |
|
763 in.close(); |
|
764 } |
|
765 } |
|
766 |
|
767 private static final Attribute.Visitor<Void, ExpectedTypeAnnotation> typeAnnoMatcher = |
|
768 new Attribute.Visitor<Void, ExpectedTypeAnnotation>() { |
|
769 |
|
770 @Override |
|
771 public Void visitBootstrapMethods(BootstrapMethods_attribute attr, |
|
772 ExpectedTypeAnnotation expected) { |
|
773 return null; |
|
774 } |
|
775 |
|
776 @Override |
|
777 public Void visitDefault(DefaultAttribute attr, |
|
778 ExpectedTypeAnnotation expected) { |
|
779 return null; |
|
780 } |
|
781 |
|
782 @Override |
|
783 public Void visitAnnotationDefault(AnnotationDefault_attribute attr, |
|
784 ExpectedTypeAnnotation expected) { |
|
785 return null; |
|
786 } |
|
787 |
|
788 @Override |
|
789 public Void visitCharacterRangeTable(CharacterRangeTable_attribute attr, |
|
790 ExpectedTypeAnnotation expected) { |
|
791 return null; |
|
792 } |
|
793 |
|
794 @Override |
|
795 public Void visitCode(Code_attribute attr, |
|
796 ExpectedTypeAnnotation expected) { |
|
797 return null; |
|
798 } |
|
799 |
|
800 @Override |
|
801 public Void visitCompilationID(CompilationID_attribute attr, |
|
802 ExpectedTypeAnnotation expected) { |
|
803 return null; |
|
804 } |
|
805 |
|
806 @Override |
|
807 public Void visitConstantValue(ConstantValue_attribute attr, |
|
808 ExpectedTypeAnnotation expected) { |
|
809 return null; |
|
810 } |
|
811 |
|
812 @Override |
|
813 public Void visitDeprecated(Deprecated_attribute attr, |
|
814 ExpectedTypeAnnotation expected) { |
|
815 return null; |
|
816 } |
|
817 |
|
818 @Override |
|
819 public Void visitEnclosingMethod(EnclosingMethod_attribute attr, |
|
820 ExpectedTypeAnnotation expected) { |
|
821 return null; |
|
822 } |
|
823 |
|
824 @Override |
|
825 public Void visitExceptions(Exceptions_attribute attr, |
|
826 ExpectedTypeAnnotation expected) { |
|
827 return null; |
|
828 } |
|
829 |
|
830 @Override |
|
831 public Void visitInnerClasses(InnerClasses_attribute attr, |
|
832 ExpectedTypeAnnotation expected) { |
|
833 return null; |
|
834 } |
|
835 |
|
836 @Override |
|
837 public Void visitLineNumberTable(LineNumberTable_attribute attr, |
|
838 ExpectedTypeAnnotation expected) { |
|
839 return null; |
|
840 } |
|
841 |
|
842 @Override |
|
843 public Void visitLocalVariableTable(LocalVariableTable_attribute attr, |
|
844 ExpectedTypeAnnotation expected) { |
|
845 return null; |
|
846 } |
|
847 |
|
848 @Override |
|
849 public Void visitLocalVariableTypeTable(LocalVariableTypeTable_attribute attr, |
|
850 ExpectedTypeAnnotation expected) { |
|
851 return null; |
|
852 } |
|
853 |
|
854 @Override |
|
855 public Void visitMethodParameters(MethodParameters_attribute attr, |
|
856 ExpectedTypeAnnotation expected) { |
|
857 return null; |
|
858 } |
|
859 |
|
860 @Override |
|
861 public Void visitRuntimeVisibleAnnotations(RuntimeVisibleAnnotations_attribute attr, |
|
862 ExpectedTypeAnnotation expected) { |
|
863 return null; |
|
864 } |
|
865 |
|
866 @Override |
|
867 public Void visitRuntimeInvisibleAnnotations(RuntimeInvisibleAnnotations_attribute attr, |
|
868 ExpectedTypeAnnotation expected) { |
|
869 return null; |
|
870 } |
|
871 |
|
872 @Override |
|
873 public Void visitRuntimeVisibleParameterAnnotations(RuntimeVisibleParameterAnnotations_attribute attr, |
|
874 ExpectedTypeAnnotation expected) { |
|
875 return null; |
|
876 } |
|
877 |
|
878 @Override |
|
879 public Void visitRuntimeInvisibleParameterAnnotations(RuntimeInvisibleParameterAnnotations_attribute attr, |
|
880 ExpectedTypeAnnotation expected) { |
|
881 return null; |
|
882 } |
|
883 |
|
884 @Override |
|
885 public Void visitSignature(Signature_attribute attr, |
|
886 ExpectedTypeAnnotation expected) { |
|
887 return null; |
|
888 } |
|
889 |
|
890 @Override |
|
891 public Void visitSourceDebugExtension(SourceDebugExtension_attribute attr, |
|
892 ExpectedTypeAnnotation expected) { |
|
893 return null; |
|
894 } |
|
895 |
|
896 @Override |
|
897 public Void visitSourceFile(SourceFile_attribute attr, |
|
898 ExpectedTypeAnnotation expected) { |
|
899 return null; |
|
900 } |
|
901 |
|
902 @Override |
|
903 public Void visitSourceID(SourceID_attribute attr, |
|
904 ExpectedTypeAnnotation expected) { |
|
905 return null; |
|
906 } |
|
907 |
|
908 @Override |
|
909 public Void visitStackMap(StackMap_attribute attr, |
|
910 ExpectedTypeAnnotation expected) { |
|
911 return null; |
|
912 } |
|
913 |
|
914 @Override |
|
915 public Void visitStackMapTable(StackMapTable_attribute attr, |
|
916 ExpectedTypeAnnotation expected) { |
|
917 return null; |
|
918 } |
|
919 |
|
920 @Override |
|
921 public Void visitSynthetic(Synthetic_attribute attr, |
|
922 ExpectedTypeAnnotation expected) { |
|
923 return null; |
|
924 } |
|
925 |
|
926 @Override |
|
927 public Void visitRuntimeVisibleTypeAnnotations(RuntimeVisibleTypeAnnotations_attribute attr, |
|
928 ExpectedTypeAnnotation expected) { |
|
929 if (expected.matchVisibility(true)) { |
|
930 for(TypeAnnotation anno : attr.annotations) { |
|
931 expected.matchAnnotation(anno); |
|
932 } |
|
933 } |
|
934 |
|
935 return null; |
|
936 } |
|
937 |
|
938 @Override |
|
939 public Void visitRuntimeInvisibleTypeAnnotations(RuntimeInvisibleTypeAnnotations_attribute attr, |
|
940 ExpectedTypeAnnotation expected) { |
|
941 if (expected.matchVisibility(false)) { |
|
942 for(TypeAnnotation anno : attr.annotations) { |
|
943 expected.matchAnnotation(anno); |
|
944 } |
|
945 } |
|
946 |
|
947 return null; |
|
948 } |
|
949 }; |
|
950 } |
|