1 package annotations.tests.executable;
2 
3 /*>>>
4 import org.checkerframework.checker.nullness.qual.*;
5 */
6 
7 import java.io.*;
8 import java.util.*;
9 import java.lang.annotation.RetentionPolicy;
10 
11 import com.sun.tools.classfile.TypeAnnotation.Position.TypePathEntryKind;
12 import com.sun.tools.javac.code.TypeAnnotationPosition;
13 
14 import junit.framework.*;
15 import annotations.*;
16 import annotations.el.*;
17 import annotations.field.*;
18 import annotations.io.*;
19 
20 import plume.FileIOException;
21 
22 public class TestSceneLib extends TestCase {
openPackagedIndexFile(String name)23     LineNumberReader openPackagedIndexFile(String name) {
24         return new LineNumberReader(new InputStreamReader(
25                 (InputStream) TestSceneLib.class.getResourceAsStream(name)));
26     }
27 
28     static final String fooIndexContents =
29             "package:\n" +
30             "annotation @Ready: @Retention(RUNTIME)\n" +
31             "annotation @Author: @Retention(CLASS)\n" +
32             "String value\n" +
33             "class Foo:\n" +
34             "field x: @Ready\n" +
35             "method y()Z:\n" +
36             "parameter #5:\n" +
37             "type:\n" +
38             "inner-type 0, 0, 3, 2:\n" +
39             "@Author(value=\"Matt M.\")\n";
40 
41     public static AnnotationDef adAuthor
42       = Annotations.createValueAnnotationDef("Author",
43                                              Annotations.asRetentionClass,
44                                              BasicAFT.forType(String.class));
45 
46     static final AnnotationDef ready =
47                     new AnnotationDef(
48                             "Ready",
49                             Annotations.asRetentionRuntime,
50                             Annotations.noFieldTypes);
51     static final AnnotationDef readyClassRetention =
52                     new AnnotationDef(
53                             "Ready",
54                             Annotations.asRetentionClass,
55                             Annotations.noFieldTypes);
56 
57 
58     /**
59      * Parse indexFileContents as an annotation file, merging the results
60      * into s; the final state of s should equal expectScene.
61      */
doParseTest(String indexFileContents, AScene s, AScene expectScene)62     void doParseTest(String indexFileContents,
63                      AScene s,
64                      AScene expectScene) {
65         try {
66             IndexFileParser.parseString(indexFileContents, s);
67         } catch (IOException e) {
68             throw new RuntimeException(e);
69         }
70         if (! expectScene.equals(s)) {
71           System.err.println("expectScene does not equal s");
72           String esu = expectScene.unparse();
73           String su = s.unparse();
74           if (esu.equals(su)) {
75             System.err.println("(but their printed representations are the same)");
76           }
77           System.err.println(esu);
78           System.err.println(su);
79         }
80         assertEquals(expectScene, s);
81     }
82 
83     // lazy typist!
newScene()84     AScene newScene() {
85         return new AScene();
86     }
87 
doParseTest(String index, AScene expectScene)88     void doParseTest(String index,
89                      /*@NonNull*/ AScene expectScene) {
90         AScene s = newScene();
91         doParseTest(index, s, expectScene);
92     }
93 
createEmptyAnnotation(AnnotationDef def)94     private Annotation createEmptyAnnotation(AnnotationDef def) {
95         return new Annotation(def, Collections.<String, Object> emptyMap());
96     }
97 
testEquals()98     public void testEquals() {
99         AScene s1 = newScene(), s2 = newScene();
100 
101         s1.classes.vivify("Foo");
102         s1.classes.vivify("Foo").fields.vivify("x");
103         s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
104                 .add(createEmptyAnnotation(ready));
105 
106         s2.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
107                 .add(Annotations.aNonNull);
108         s2.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
109                 .add(createEmptyAnnotation(ready));
110 
111         assertEquals(false, s1.equals(s2));  // FIXME: why does assertion fail?
112 
113         s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
114                 .add(Annotations.aNonNull);
115 
116         assertEquals(true, s1.equals(s2));
117     }
118 
testStoreParse1()119     public void testStoreParse1() {
120         AScene s1 = newScene();
121 
122         s1.classes.vivify("Foo").fields.vivify("x").tlAnnotationsHere
123                 .add(createEmptyAnnotation(ready));
124         Annotation myAuthor = Annotations.createValueAnnotation(adAuthor, "Matt M.");
125         s1.classes.vivify("Foo");
126         s1.classes.vivify("Foo").methods.vivify("y()Z");
127         s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5);
128         @SuppressWarnings("unused")
129         Object dummy =
130           s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type;
131         @SuppressWarnings("unused")
132         Object dummy2 =
133           s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type.innerTypes;
134         s1.classes.vivify("Foo").methods.vivify("y()Z").parameters.vivify(5).type.innerTypes
135                 .vivify(new InnerTypeLocation(
136                         TypeAnnotationPosition.getTypePathFromBinary(
137                                 Arrays.asList(new Integer[] { 0, 0, 3, 2 })))).tlAnnotationsHere
138                 .add(myAuthor);
139 
140         doParseTest(fooIndexContents, s1);
141     }
142 
checkConstructor(AMethod constructor)143     private void checkConstructor(AMethod constructor) {
144         Annotation ann = ((Annotation) constructor.receiver.type.lookup("p2.D"));
145         assertEquals(Collections.singletonMap("value", "spam"), ann.fieldValues);
146         ATypeElement l = (ATypeElement) constructor.body.locals
147                         .get(new LocalLocation(1, 3, 5)).type;
148         AElement i = (AElement) l.innerTypes.get(new InnerTypeLocation(
149                 TypeAnnotationPosition.getTypePathFromBinary(
150                                 Arrays.asList(new Integer[] { 0, 0 }))));
151         assertNotNull(i.lookup("p2.C"));
152         AField l2 =
153                 constructor.body.locals.get(new LocalLocation(1, 3, 6));
154         assertNull(l2);
155     }
156 
testParseRetrieve1()157     public void testParseRetrieve1() throws Exception {
158         LineNumberReader fr = openPackagedIndexFile("test1.jaif");
159         AScene s1 = newScene();
160         IndexFileParser.parse(fr, s1);
161 
162         AClass foo1 = s1.classes.get("p1.Foo");
163         assertNotNull("Didn't find foo1", foo1);
164         AClass foo = (AClass) foo1;
165         boolean sawConstructor = false;
166         for (Map.Entry<String, AMethod> me : foo.methods.entrySet()) {
167             if (me.getKey().equals("<init>(Ljava/util/Set;)V")) {
168                 assertFalse(sawConstructor);
169                 AMethod constructor = me.getValue();
170                 assertNotNull(constructor);
171                 checkConstructor((AMethod) constructor);
172                 sawConstructor = true;
173             }
174         }
175         assertTrue(sawConstructor);
176     }
177 
178     class TestDefCollector extends DefCollector {
179         AnnotationDef a, b, c, d, e;
180 
181         AnnotationDef f;
182 
TestDefCollector(AScene s)183         public TestDefCollector(AScene s) throws DefException {
184             super(s);
185         }
186 
187         @Override
visitAnnotationDef(AnnotationDef tldef)188         protected void visitAnnotationDef(AnnotationDef tldef) {
189             if (tldef.name.equals("p2.A")) {
190                 assertNull(a);
191                 a = tldef;
192             } else if (tldef.name.equals("p2.B")) {
193                 assertNull(b);
194                 b = tldef;
195             } else if (tldef.name.equals("p2.C")) {
196                 assertNull(c);
197                 c = tldef;
198             } else if (tldef.name.equals("p2.D")) {
199                 assertNull(d);
200                 d = tldef;
201             } else if (tldef.name.equals("p2.E")) {
202                 assertNotNull(f); // should give us fields first
203                 assertNull(e);
204                 e = tldef;
205             } else if (tldef.name.equals("p2.F")) {
206                 f = tldef;
207             } else {
208                 fail();
209             }
210         }
211     }
212 
testParseRetrieveTypes()213     public void testParseRetrieveTypes() throws Exception {
214         LineNumberReader fr = openPackagedIndexFile("test1.jaif");
215         AScene s1 = newScene();
216         IndexFileParser.parse(fr, s1);
217 
218         TestDefCollector tdc = new TestDefCollector(s1);
219         tdc.visit();
220         assertNotNull(tdc.a);
221         assertNotNull(tdc.b);
222         assertNotNull(tdc.c);
223         assertNotNull(tdc.d);
224         assertNotNull(tdc.e);
225         assertNotNull(tdc.f);
226 
227         // now look at p2.E because it has some rather complex types
228         AnnotationDef tle = (AnnotationDef) tdc.e;
229         assertEquals(RetentionPolicy.CLASS, tle.retention());
230         AnnotationDef e = tle;
231         assertEquals(new ArrayAFT(new AnnotationAFT(tdc.a)), e.fieldTypes
232                 .get("first"));
233         assertEquals(new AnnotationAFT(tdc.f), e.fieldTypes.get("second"));
234         assertEquals(new EnumAFT("Foo"), e.fieldTypes.get("third"));
235         assertEquals(
236                 ClassTokenAFT.ctaft,
237                 e.fieldTypes.get("fourth"));
238         assertEquals(ClassTokenAFT.ctaft, e.fieldTypes.get("fifth"));
239 
240         AnnotationDef tla = (AnnotationDef) tdc.a;
241         assertEquals(RetentionPolicy.RUNTIME, tla.retention());
242         AnnotationDef a = tla;
243         assertEquals(BasicAFT.forType(int.class), a.fieldTypes.get("value"));
244         AnnotationDef d = tdc.d;
245         assertEquals(BasicAFT.forType(String.class), d.fieldTypes.get("value"));
246     }
247 
testParseRetrieveValues()248     public void testParseRetrieveValues() throws Exception {
249         LineNumberReader fr = openPackagedIndexFile("test1.jaif");
250         AScene s1 = newScene();
251         IndexFileParser.parse(fr, s1);
252 
253         // now look at Bar because it has some rather complex values
254         Annotation a = s1.classes.get("p1.Bar").lookup("p2.E");
255 
256         assertEquals("fooconstant", a.fieldValues.get("third"));
257         assertEquals("interface java.util.Map", a.fieldValues.get("fourth").toString());
258         assertEquals("class [[I", a.fieldValues.get("fifth").toString());
259 
260         List<?> first =
261                 (List<?>) a.fieldValues.get("first");
262         assertEquals(2, first.size(), 2);
263         Annotation aa = (Annotation) first.get(0);
264         assertEquals("p2.A", aa.def().name);
265         assertEquals(-1, aa.fieldValues.get("value"));
266 
267         Annotation a2 =
268                 s1.classes.get("p1.Baz").lookup("p2.E");
269         assertEquals("FOO_FOO", a2.fieldValues.get("third"));
270         assertEquals("class java.util.LinkedHashMap", a2.fieldValues.get("fourth").toString());
271         assertEquals("void", a2.fieldValues.get("fifth").toString());
272     }
273 
doRewriteTest(LineNumberReader r)274     void doRewriteTest(LineNumberReader r) throws Exception {
275         AScene s1 = newScene(), s2 = newScene();
276         IndexFileParser.parse(r, s1);
277         StringWriter sbw = new StringWriter();
278         IndexFileWriter.write(s1, sbw);
279         IndexFileParser.parseString(sbw.toString(), s2);
280         assertEquals(s1, s2);
281     }
282 
testRewriteOne()283     public void testRewriteOne() throws Exception {
284         LineNumberReader fr = openPackagedIndexFile("test1.jaif");
285         doRewriteTest(fr);
286     }
287 
testRewriteTwo()288     public void testRewriteTwo() throws Exception {
289         LineNumberReader fr = openPackagedIndexFile("test2.jaif");
290         doRewriteTest(fr);
291     }
292 
testConflictedDefinition()293     public void testConflictedDefinition() throws Exception {
294         AScene s1 = newScene();
295         s1.classes.vivify("Foo").tlAnnotationsHere
296                 .add(createEmptyAnnotation(ready));
297         s1.classes.vivify("Bar").tlAnnotationsHere
298                 .add(createEmptyAnnotation(readyClassRetention));
299         StringWriter sbw = new StringWriter();
300         try {
301             IndexFileWriter.write(s1, sbw);
302             fail("an exception should have been thrown");
303         } catch (DefException de) {
304             assertEquals("Ready", de.annotationType);
305             // success
306         }
307 
308     }
309 
testParseErrorMissingColon()310     public void testParseErrorMissingColon() throws Exception {
311         AScene s1 = newScene();
312         String fileContents = "package p1:\n" + "annotation @A:\n"
313                               + "class Foo @A";
314         try {
315             IndexFileParser.parseString(fileContents, s1);
316             fail(); // an exception should have been thrown
317         } catch (FileIOException e) {
318             // TODO:  check line number
319             // assertEquals(3, e.line);
320             // success
321         }
322     }
323 
testParseErrorMissingDefinition()324     public void testParseErrorMissingDefinition() throws Exception {
325         AScene s1 = newScene();
326         String fileContents
327           = "package p1:\n" + "annotation @AIsDefined:\n"
328           + "class Foo:\n" + "@AIsDefined\n" + "@BIsNotDefined\n";
329         try {
330             IndexFileParser.parseString(fileContents, s1);
331             fail(); // an exception should have been thrown
332         } catch (FileIOException e) {
333             // TODO: check line number
334             // assertEquals(5, e.line);
335             // success
336         }
337     }
338 
getAnnotation(Set<Annotation> annos, String name)339     private static Annotation getAnnotation(Set<Annotation> annos, String name) {
340         for (Annotation anno : annos) {
341             if (anno.def.name.equals(name)) {
342                 return anno;
343             }
344         }
345         return null;
346     }
347 
testEmptyArrayHack()348     public void testEmptyArrayHack() throws Exception {
349         AScene scene = newScene();
350         AClass clazz =
351             scene.classes.vivify("bar.Test");
352 
353         // One annotation with an empty array of unknown type...
354         AnnotationBuilder ab1 =
355           AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass);
356         ab1.addEmptyArrayField("array");
357         Annotation a1 = ab1.finish();
358         Annotation tla1 = a1;
359 
360         // ... and another with an empty array of known type
361         AnnotationBuilder ab2 =
362           AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass);
363         ArrayBuilder ab2ab = ab2.beginArrayField("array",
364                 new ArrayAFT(BasicAFT.forType(int.class)));
365         ab2ab.finish();
366         Annotation a2 = ab2.finish();
367         Annotation tla2 = a2;
368 
369         // And they're both fields of another annotation to make sure that
370         // unification works recursively.
371         AnnotationBuilder ab3 =
372           AnnotationFactory.saf.beginAnnotation("foo.CombinedAnno", Annotations.asRetentionRuntime);
373         ab3.addScalarField("fieldOne", new AnnotationAFT(a1.def()), a1);
374         ab3.addScalarField("fieldTwo", new AnnotationAFT(a2.def()), a2);
375         Annotation a3 = ab3.finish();
376         Annotation tla3 = a3;
377 
378         clazz.tlAnnotationsHere.add(tla3);
379 
380         StringWriter sw = new StringWriter();
381         IndexFileWriter.write(scene, sw);
382 
383         AScene sceneRead = newScene();
384         IndexFileParser.parseString(sw.toString(), sceneRead);
385 
386         // the anomaly: see second "consequence" on IndexFileWriter#write
387         assertFalse(scene.equals(sceneRead));
388 
389         AClass clazz2 = sceneRead.classes.get("bar.Test");
390         assertNotNull(clazz2);
391         Annotation a3_2 = getAnnotation(clazz2.tlAnnotationsHere, "foo.CombinedAnno");
392         Annotation a1_2 = (Annotation) a3_2.getFieldValue("fieldOne");
393         Annotation a2_2 = (Annotation) a3_2.getFieldValue("fieldTwo");
394         // now that the defs were merged, the annotations should be equal
395         assertEquals(a1_2, a2_2);
396 
397         // Yet another annotation with an array of a different known type
398         AnnotationBuilder ab4 =
399           AnnotationFactory.saf.beginAnnotation("foo.ArrayAnno", Annotations.asRetentionClass);
400         ArrayBuilder ab4ab = ab4.beginArrayField("array",
401                 new ArrayAFT(BasicAFT.forType(double.class)));
402         ab4ab.appendElement(5.0);
403         ab4ab.finish();
404         Annotation a4 = ab4.finish();
405         Annotation tla4 = a4;
406 
407         // try combining unifiable _top-level_ annotations
408         AScene secondScene = newScene();
409         AClass secondSceneClazz =
410             secondScene.classes.vivify("bar.Test");
411         secondSceneClazz.tlAnnotationsHere.add(tla1);
412         // Oops--the keyed set gives us an exception if we try to put two
413         // different foo.ArrayAnnos on the same class!
414         AClass secondSceneClazz2 =
415             secondScene.classes.vivify("bar.Test2");
416         secondSceneClazz2.tlAnnotationsHere.add(tla4);
417 
418         // it should be legal to write this
419         StringWriter secondSW = new StringWriter();
420         IndexFileWriter.write(secondScene, secondSW);
421 
422         // add an incompatible annotation
423         AClass secondSceneClazz3 =
424             secondScene.classes.vivify("bar.Test3");
425         secondSceneClazz3.tlAnnotationsHere.add(tla2);
426 
427         // now we should get a DefException
428         StringWriter secondSceneSW2 = new StringWriter();
429         try {
430             IndexFileWriter.write(secondScene, secondSceneSW2);
431             // we should have gotten an exception
432             fail();
433         } catch (DefException de) {
434             assertEquals("Conflicting definition of annotation type foo.ArrayAnno",
435                     de.getMessage());
436             // success
437         }
438     }
439 
testEmptyArrayIO()440     public void testEmptyArrayIO() throws Exception {
441         // should succeed
442         String index1 = "package: annotation @Foo: @Retention(CLASS)\n  unknown[] arr\n" +
443             "class Bar: @Foo(arr={})";
444         AScene scene1 = newScene();
445         IndexFileParser.parseString(index1, scene1);
446 
447         // should reject nonempty array
448         String index2 = "package: annotation @Foo:  @Retention(CLASS)\n unknown[] arr\n" +
449             "class Bar: @Foo(arr={1})";
450         AScene scene2 = newScene();
451         try {
452             IndexFileParser.parseString(index2, scene2);
453             // should have gotten an exception
454             fail();
455         } catch (FileIOException e) {
456             // success
457         }
458 
459         // construct a scene programmatically
460         AScene scene3 = newScene();
461         AClass clazz3 =
462             scene3.classes.vivify("Bar");
463         AnnotationBuilder ab =
464           AnnotationFactory.saf.beginAnnotation("Foo", Annotations.asRetentionClass);
465         ab.addEmptyArrayField("arr");
466         Annotation a = ab.finish();
467         Annotation tla = a;
468         clazz3.tlAnnotationsHere.add(tla);
469 
470         assertEquals(scene1, scene3);
471 
472         // when we write the scene out, the index file should contain the
473         // special unknown[] field type
474         StringWriter sw3 = new StringWriter();
475         IndexFileWriter.write(scene3, sw3);
476         String index3 = sw3.toString();
477         assertTrue(index3.indexOf("unknown[]") >= 0);
478 
479         // can we read it back in and get the same thing?
480         AScene scene4 = newScene();
481         IndexFileParser.parseString(index3, scene4);
482         assertEquals(scene3, scene4);
483     }
484 
testPrune()485     public void testPrune() {
486         AScene s1 = newScene(), s2 = newScene();
487         assertTrue(s1.equals(s2));
488 
489         s1.classes.vivify("Foo");
490         assertFalse(s1.equals(s2));
491 
492         assertTrue(s1.prune());
493         assertTrue(s1.equals(s2));
494 
495         Annotation sa = AnnotationFactory.saf.beginAnnotation("Anno", Annotations.asRetentionClass).finish();
496         Annotation tla = sa;
497 
498         AClass clazz2 = s2.classes.vivify("Bar");
499         clazz2.tlAnnotationsHere.add(tla);
500 
501         assertFalse(s1.equals(s2));
502         assertFalse(s2.prune());
503         assertFalse(s1.equals(s2));
504     }
505 
506     static class MyTAST {
507         final int id;
MyTAST(int id)508         MyTAST(int id) {
509             this.id = id;
510         }
511 
512         MyTAST element = null;
513         MyTAST[] typeArgs = null;
514 
arrayOf(int id, MyTAST element)515         static MyTAST arrayOf(int id, MyTAST element) {
516             MyTAST t = new MyTAST(id);
517             t.element = element;
518             return t;
519         }
520 
parameterization(int id, MyTAST... args)521         static MyTAST parameterization(int id, MyTAST... args) {
522             MyTAST t = new MyTAST(id);
523             t.typeArgs = args;
524             return t;
525         }
526     }
527 
528     /*
529     private static final AnnotationDef idAnnoDef =
530         new AnnotationDef("IdAnno", null, Collections.singletonMap(
531                 "id", BasicAFT.forType(int.class)));
532     */
533     private static final AnnotationDef idAnnoTLDef =
534       new AnnotationDef("IdAnno", Annotations.asRetentionClass,
535                           Collections.singletonMap(
536                 "id", BasicAFT.forType(int.class)));
537 
makeTLIdAnno(int id)538     static Annotation makeTLIdAnno(int id) {
539         return new Annotation(idAnnoTLDef,
540                               Collections.singletonMap("id", Integer.valueOf(id)));
541     }
542 
543     static class MyTASTMapper extends TypeASTMapper<MyTAST> {
544         boolean[] saw = new boolean[11];
545 
546         @Override
getElementType(MyTAST n)547         protected MyTAST getElementType(MyTAST n) {
548             return n.element;
549         }
550 
551         @Override
getTypeArgument(MyTAST n, int index)552         protected MyTAST getTypeArgument(MyTAST n, int index) {
553             return n.typeArgs[index];
554         }
555 
556         @Override
numTypeArguments(MyTAST n)557         protected int numTypeArguments(MyTAST n) {
558             return n.typeArgs == null ? 0 : n.typeArgs.length;
559         }
560 
561         @Override
map(MyTAST n, ATypeElement e)562         protected void map(MyTAST n, ATypeElement e) {
563             int nodeID = n.id;
564             if (nodeID == 10) {
565                 assertTrue(e.lookup("IdAnno") == null);
566                 e.tlAnnotationsHere.add(makeTLIdAnno(10));
567             } else {
568                 int annoID = (Integer) e.lookup("IdAnno").getFieldValue("id");
569                 assertEquals(nodeID, annoID);
570             }
571             assertFalse(saw[nodeID]);
572             saw[nodeID] = true;
573         }
574     }
575 
assignId(ATypeElement myField, int id, Integer... ls)576     private void assignId(ATypeElement myField, int id,
577             Integer... ls) {
578         AElement el = myField.innerTypes.vivify(
579                 new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Arrays.asList(ls))));
580         el.tlAnnotationsHere.add(makeTLIdAnno(id));
581     }
582 
testTypeASTMapper()583     public void testTypeASTMapper() {
584         // Construct a TAST for the type structure:
585         // 0< 3<4>[1][2], 5<6, 8[7], 9, 10> >
586         MyTAST tast = MyTAST.parameterization(0,
587                 MyTAST.arrayOf(1,
588                         MyTAST.arrayOf(2,
589                                 MyTAST.parameterization(3,
590                                         new MyTAST(4)
591                                 )
592                         )
593                 ),
594                 MyTAST.parameterization(5,
595                         new MyTAST(6),
596                         MyTAST.arrayOf(7,
597                                 new MyTAST(8)),
598                         new MyTAST(9),
599                         new MyTAST(10)
600                 )
601         );
602 
603         // Pretend myField represents a field of the type represented by tast.
604         // We have to do this because clients are no longer allowed to create
605         // AElements directly; instead, they must vivify.
606         AElement myAField =
607             new AScene().classes.vivify("someclass").fields.vivify("somefield");
608         ATypeElement myAFieldType = myAField.type;
609         // load it with annotations we can check against IDs
610         myAFieldType.tlAnnotationsHere.add(makeTLIdAnno(0));
611 
612         final int ARRAY = TypePathEntryKind.ARRAY.tag;
613         final int TYPE_ARGUMENT = TypePathEntryKind.TYPE_ARGUMENT.tag;
614 
615         assignId(myAFieldType, 1, TYPE_ARGUMENT, 0);
616         assignId(myAFieldType, 2, TYPE_ARGUMENT, 0, ARRAY, 0);
617         assignId(myAFieldType, 3, TYPE_ARGUMENT, 0, ARRAY, 0, ARRAY, 0);
618         assignId(myAFieldType, 4, TYPE_ARGUMENT, 0, ARRAY, 0, ARRAY, 0, TYPE_ARGUMENT, 0);
619         assignId(myAFieldType, 5, TYPE_ARGUMENT, 1);
620         assignId(myAFieldType, 6, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 0);
621         assignId(myAFieldType, 7, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 1);
622         assignId(myAFieldType, 8, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 1, ARRAY, 0);
623         assignId(myAFieldType, 9, TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 2);
624         // to test vivification, we don't assign 10
625 
626         // now visit and make sure the ID numbers match up
627         MyTASTMapper mapper = new MyTASTMapper();
628         mapper.traverse(tast, myAFieldType);
629 
630         for (int i = 0; i < 11; i++) {
631             assertTrue(mapper.saw[i]);
632         }
633         // make sure it vivified #10 and our annotation stuck
634         AElement e10 = myAFieldType.innerTypes.get(
635                 new InnerTypeLocation(TypeAnnotationPosition.getTypePathFromBinary(Arrays.asList(TYPE_ARGUMENT, 1, TYPE_ARGUMENT, 3))));
636         assertNotNull(e10);
637         int e10aid = (Integer) e10.lookup("IdAnno").getFieldValue("id");
638         assertEquals(e10aid, 10);
639     }
640 }
641