Home | History | Annotate | Download | only in executable
      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 {
     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      */
     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!
     84     AScene newScene() {
     85         return new AScene();
     86     }
     87 
     88     void doParseTest(String index,
     89                      /*@NonNull*/ AScene expectScene) {
     90         AScene s = newScene();
     91         doParseTest(index, s, expectScene);
     92     }
     93 
     94     private Annotation createEmptyAnnotation(AnnotationDef def) {
     95         return new Annotation(def, Collections.<String, Object> emptyMap());
     96     }
     97 
     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 
    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 
    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 
    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 
    183         public TestDefCollector(AScene s) throws DefException {
    184             super(s);
    185         }
    186 
    187         @Override
    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 
    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 
    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 
    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 
    283     public void testRewriteOne() throws Exception {
    284         LineNumberReader fr = openPackagedIndexFile("test1.jaif");
    285         doRewriteTest(fr);
    286     }
    287 
    288     public void testRewriteTwo() throws Exception {
    289         LineNumberReader fr = openPackagedIndexFile("test2.jaif");
    290         doRewriteTest(fr);
    291     }
    292 
    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 
    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 
    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 
    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 
    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 
    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 
    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;
    508         MyTAST(int id) {
    509             this.id = id;
    510         }
    511 
    512         MyTAST element = null;
    513         MyTAST[] typeArgs = null;
    514 
    515         static MyTAST arrayOf(int id, MyTAST element) {
    516             MyTAST t = new MyTAST(id);
    517             t.element = element;
    518             return t;
    519         }
    520 
    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 
    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
    547         protected MyTAST getElementType(MyTAST n) {
    548             return n.element;
    549         }
    550 
    551         @Override
    552         protected MyTAST getTypeArgument(MyTAST n, int index) {
    553             return n.typeArgs[index];
    554         }
    555 
    556         @Override
    557         protected int numTypeArguments(MyTAST n) {
    558             return n.typeArgs == null ? 0 : n.typeArgs.length;
    559         }
    560 
    561         @Override
    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 
    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 
    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