1 /*
2  * Javassist, a Java-bytecode translator toolkit.
3  * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved.
4  *
5  * The contents of this file are subject to the Mozilla Public License Version
6  * 1.1 (the "License"); you may not use this file except in compliance with
7  * the License.  Alternatively, the contents of this file may be used under
8  * the terms of the GNU Lesser General Public License Version 2.1 or later.
9  *
10  * Software distributed under the License is distributed on an "AS IS" basis,
11  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12  * for the specific language governing rights and limitations under the
13  * License.
14  */
15 
16 package javassist.bytecode;
17 
18 import java.util.Map;
19 import java.util.HashMap;
20 import java.io.IOException;
21 import java.io.DataInputStream;
22 import java.io.ByteArrayOutputStream;
23 import javassist.bytecode.annotation.*;
24 
25 /**
26  * A class representing
27  * <code>RuntimeVisibleAnnotations_attribute</code> and
28  * <code>RuntimeInvisibleAnnotations_attribute</code>.
29  *
30  * <p>To obtain an AnnotationAttribute object, invoke
31  * <code>getAttribute(AnnotationsAttribute.visibleTag)</code>
32  * in <code>ClassFile</code>, <code>MethodInfo</code>,
33  * or <code>FieldInfo</code>.  The obtained attribute is a
34  * runtime visible annotations attribute.
35  * If the parameter is
36  * <code>AnnotationAttribute.invisibleTag</code>, then the obtained
37  * attribute is a runtime invisible one.
38  *
39  * <p>For example,
40  *
41  * <ul><pre>
42  * import javassist.bytecode.annotation.Annotation;
43  *    :
44  * CtMethod m = ... ;
45  * MethodInfo minfo = m.getMethodInfo();
46  * AnnotationsAttribute attr = (AnnotationsAttribute)
47  *         minfo.getAttribute(AnnotationsAttribute.invisibleTag);
48  * Annotation an = attr.getAnnotation("Author");
49  * String s = ((StringMemberValue)an.getMemberValue("name")).getValue();
50  * System.out.println("@Author(name=" + s + ")");
51  * </pre></ul>
52  *
53  * <p>This code snippet retrieves an annotation of the type <code>Author</code>
54  * from the <code>MethodInfo</code> object specified by <code>minfo</code>.
55  * Then, it prints the value of <code>name</code> in <code>Author</code>.
56  *
57  * <p>If the annotation type <code>Author</code> is annotated by a meta annotation:
58  *
59  * <ul><pre>
60  * &#64;Retention(RetentionPolicy.RUNTIME)
61  * </pre></ul>
62  *
63  * <p>Then <code>Author</code> is visible at runtime.  Therefore, the third
64  * statement of the code snippet above must be changed into:
65  *
66  * <ul><pre>
67  * AnnotationsAttribute attr = (AnnotationsAttribute)
68  *         minfo.getAttribute(AnnotationsAttribute.visibleTag);
69  * </pre></ul>
70  *
71  * <p>The attribute tag must be <code>visibleTag</code> instead of
72  * <code>invisibleTag</code>.
73  *
74  * <p>If the member value of an annotation is not specified, the default value
75  * is used as that member value.  If so, <code>getMemberValue()</code> in
76  * <code>Annotation</code> returns <code>null</code>
77  * since the default value is not included in the
78  * <code>AnnotationsAttribute</code>.  It is included in the
79  * <code>AnnotationDefaultAttribute</code> of the method declared in the
80  * annotation type.
81  *
82  * <p>If you want to record a new AnnotationAttribute object, execute the
83  * following snippet:
84  *
85  * <ul><pre>
86  * ClassFile cf = ... ;
87  * ConstPool cp = cf.getConstPool();
88  * AnnotationsAttribute attr
89  *     = new AnnotationsAttribute(cp, AnnotationsAttribute.visibleTag);
90  * Annotation a = new Annotation("Author", cp);
91  * a.addMemberValue("name", new StringMemberValue("Chiba", cp));
92  * attr.setAnnotation(a);
93  * cf.addAttribute(attr);
94  * cf.setVersionToJava5();
95  * </pre></ul>
96  *
97  * <p>The last statement is necessary if the class file was produced by
98  * Javassist or JDK 1.4.  Otherwise, it is not necessary.
99  *
100  * @see AnnotationDefaultAttribute
101  * @see javassist.bytecode.annotation.Annotation
102  */
103 public class AnnotationsAttribute extends AttributeInfo {
104     /**
105      * The name of the <code>RuntimeVisibleAnnotations</code> attribute.
106      */
107     public static final String visibleTag = "RuntimeVisibleAnnotations";
108 
109     /**
110      * The name of the <code>RuntimeInvisibleAnnotations</code> attribute.
111      */
112     public static final String invisibleTag = "RuntimeInvisibleAnnotations";
113 
114     /**
115      * Constructs a <code>Runtime(In)VisibleAnnotations_attribute</code>.
116      *
117      * @param cp            constant pool
118      * @param attrname      attribute name (<code>visibleTag</code> or
119      *                      <code>invisibleTag</code>).
120      * @param info          the contents of this attribute.  It does not
121      *                      include <code>attribute_name_index</code> or
122      *                      <code>attribute_length</code>.
123      */
AnnotationsAttribute(ConstPool cp, String attrname, byte[] info)124     public AnnotationsAttribute(ConstPool cp, String attrname, byte[] info) {
125         super(cp, attrname, info);
126     }
127 
128     /**
129      * Constructs an empty
130      * <code>Runtime(In)VisibleAnnotations_attribute</code>.
131      * A new annotation can be later added to the created attribute
132      * by <code>setAnnotations()</code>.
133      *
134      * @param cp            constant pool
135      * @param attrname      attribute name (<code>visibleTag</code> or
136      *                      <code>invisibleTag</code>).
137      * @see #setAnnotations(Annotation[])
138      */
AnnotationsAttribute(ConstPool cp, String attrname)139     public AnnotationsAttribute(ConstPool cp, String attrname) {
140         this(cp, attrname, new byte[] { 0, 0 });
141     }
142 
143     /**
144      * @param n     the attribute name.
145      */
AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)146     AnnotationsAttribute(ConstPool cp, int n, DataInputStream in)
147         throws IOException
148     {
149         super(cp, n, in);
150     }
151 
152     /**
153      * Returns <code>num_annotations</code>.
154      */
numAnnotations()155     public int numAnnotations() {
156         return ByteArray.readU16bit(info, 0);
157     }
158 
159     /**
160      * Copies this attribute and returns a new copy.
161      */
copy(ConstPool newCp, Map classnames)162     public AttributeInfo copy(ConstPool newCp, Map classnames) {
163         Copier copier = new Copier(info, constPool, newCp, classnames);
164         try {
165             copier.annotationArray();
166             return new AnnotationsAttribute(newCp, getName(), copier.close());
167         }
168         catch (Exception e) {
169             throw new RuntimeException(e);
170         }
171     }
172 
173     /**
174      * Parses the annotations and returns a data structure representing
175      * the annotation with the specified type.  See also
176      * <code>getAnnotations()</code> as to the returned data structure.
177      *
178      * @param type      the annotation type.
179      * @return null if the specified annotation type is not included.
180      * @see #getAnnotations()
181      */
getAnnotation(String type)182     public Annotation getAnnotation(String type) {
183         Annotation[] annotations = getAnnotations();
184         for (int i = 0; i < annotations.length; i++) {
185             if (annotations[i].getTypeName().equals(type))
186                 return annotations[i];
187         }
188 
189         return null;
190     }
191 
192     /**
193      * Adds an annotation.  If there is an annotation with the same type,
194      * it is removed before the new annotation is added.
195      *
196      * @param annotation        the added annotation.
197      */
addAnnotation(Annotation annotation)198     public void addAnnotation(Annotation annotation) {
199         String type = annotation.getTypeName();
200         Annotation[] annotations = getAnnotations();
201         for (int i = 0; i < annotations.length; i++) {
202             if (annotations[i].getTypeName().equals(type)) {
203                 annotations[i] = annotation;
204                 setAnnotations(annotations);
205                 return;
206             }
207         }
208 
209         Annotation[] newlist = new Annotation[annotations.length + 1];
210         System.arraycopy(annotations, 0, newlist, 0, annotations.length);
211         newlist[annotations.length] = annotation;
212         setAnnotations(newlist);
213     }
214 
215     /**
216      * Parses the annotations and returns a data structure representing
217      * that parsed annotations.  Note that changes of the node values of the
218      * returned tree are not reflected on the annotations represented by
219      * this object unless the tree is copied back to this object by
220      * <code>setAnnotations()</code>.
221      *
222      * @see #setAnnotations(Annotation[])
223      */
getAnnotations()224     public Annotation[] getAnnotations() {
225         try {
226             return new Parser(info, constPool).parseAnnotations();
227         }
228         catch (Exception e) {
229             throw new RuntimeException(e);
230         }
231     }
232 
233     /**
234      * Changes the annotations represented by this object according to
235      * the given array of <code>Annotation</code> objects.
236      *
237      * @param annotations           the data structure representing the
238      *                              new annotations.
239      */
setAnnotations(Annotation[] annotations)240     public void setAnnotations(Annotation[] annotations) {
241         ByteArrayOutputStream output = new ByteArrayOutputStream();
242         AnnotationsWriter writer = new AnnotationsWriter(output, constPool);
243         try {
244             int n = annotations.length;
245             writer.numAnnotations(n);
246             for (int i = 0; i < n; ++i)
247                 annotations[i].write(writer);
248 
249             writer.close();
250         }
251         catch (IOException e) {
252             throw new RuntimeException(e);      // should never reach here.
253         }
254 
255         set(output.toByteArray());
256     }
257 
258     /**
259      * Changes the annotations.  A call to this method is equivalent to:
260      * <ul><pre>setAnnotations(new Annotation[] { annotation })</pre></ul>
261      *
262      * @param annotation    the data structure representing
263      *                      the new annotation.
264      */
setAnnotation(Annotation annotation)265     public void setAnnotation(Annotation annotation) {
266         setAnnotations(new Annotation[] { annotation });
267     }
268 
269     /**
270      * @param oldname       a JVM class name.
271      * @param newname       a JVM class name.
272      */
renameClass(String oldname, String newname)273     void renameClass(String oldname, String newname) {
274         HashMap map = new HashMap();
275         map.put(oldname, newname);
276         renameClass(map);
277     }
278 
renameClass(Map classnames)279     void renameClass(Map classnames) {
280         Renamer renamer = new Renamer(info, getConstPool(), classnames);
281         try {
282             renamer.annotationArray();
283         } catch (Exception e) {
284             throw new RuntimeException(e);
285         }
286     }
287 
getRefClasses(Map classnames)288     void getRefClasses(Map classnames) { renameClass(classnames); }
289 
290     /**
291      * Returns a string representation of this object.
292      */
toString()293     public String toString() {
294         Annotation[] a = getAnnotations();
295         StringBuilder sbuf = new StringBuilder();
296         int i = 0;
297         while (i < a.length) {
298             sbuf.append(a[i++].toString());
299             if (i != a.length)
300                 sbuf.append(", ");
301         }
302 
303         return sbuf.toString();
304     }
305 
306     static class Walker {
307         byte[] info;
308 
Walker(byte[] attrInfo)309         Walker(byte[] attrInfo) {
310             info = attrInfo;
311         }
312 
parameters()313         final void parameters() throws Exception {
314             int numParam = info[0] & 0xff;
315             parameters(numParam, 1);
316         }
317 
parameters(int numParam, int pos)318         void parameters(int numParam, int pos) throws Exception {
319             for (int i = 0; i < numParam; ++i)
320                 pos = annotationArray(pos);
321         }
322 
annotationArray()323         final void annotationArray() throws Exception {
324             annotationArray(0);
325         }
326 
annotationArray(int pos)327         final int annotationArray(int pos) throws Exception {
328             int num = ByteArray.readU16bit(info, pos);
329             return annotationArray(pos + 2, num);
330         }
331 
annotationArray(int pos, int num)332         int annotationArray(int pos, int num) throws Exception {
333             for (int i = 0; i < num; ++i)
334                 pos = annotation(pos);
335 
336             return pos;
337         }
338 
annotation(int pos)339         final int annotation(int pos) throws Exception {
340             int type = ByteArray.readU16bit(info, pos);
341             int numPairs = ByteArray.readU16bit(info, pos + 2);
342             return annotation(pos + 4, type, numPairs);
343         }
344 
annotation(int pos, int type, int numPairs)345         int annotation(int pos, int type, int numPairs) throws Exception {
346             for (int j = 0; j < numPairs; ++j)
347                 pos = memberValuePair(pos);
348 
349             return pos;
350         }
351 
memberValuePair(int pos)352         final int memberValuePair(int pos) throws Exception {
353             int nameIndex = ByteArray.readU16bit(info, pos);
354             return memberValuePair(pos + 2, nameIndex);
355         }
356 
memberValuePair(int pos, int nameIndex)357         int memberValuePair(int pos, int nameIndex) throws Exception {
358             return memberValue(pos);
359         }
360 
memberValue(int pos)361         final int memberValue(int pos) throws Exception {
362             int tag = info[pos] & 0xff;
363             if (tag == 'e') {
364                 int typeNameIndex = ByteArray.readU16bit(info, pos + 1);
365                 int constNameIndex = ByteArray.readU16bit(info, pos + 3);
366                 enumMemberValue(pos, typeNameIndex, constNameIndex);
367                 return pos + 5;
368             }
369             else if (tag == 'c') {
370                 int index = ByteArray.readU16bit(info, pos + 1);
371                 classMemberValue(pos, index);
372                 return pos + 3;
373             }
374             else if (tag == '@')
375                 return annotationMemberValue(pos + 1);
376             else if (tag == '[') {
377                 int num = ByteArray.readU16bit(info, pos + 1);
378                 return arrayMemberValue(pos + 3, num);
379             }
380             else { // primitive types or String.
381                 int index = ByteArray.readU16bit(info, pos + 1);
382                 constValueMember(tag, index);
383                 return pos + 3;
384             }
385         }
386 
constValueMember(int tag, int index)387         void constValueMember(int tag, int index) throws Exception {}
388 
enumMemberValue(int pos, int typeNameIndex, int constNameIndex)389         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
390             throws Exception {
391         }
392 
classMemberValue(int pos, int index)393         void classMemberValue(int pos, int index) throws Exception {}
394 
annotationMemberValue(int pos)395         int annotationMemberValue(int pos) throws Exception {
396             return annotation(pos);
397         }
398 
arrayMemberValue(int pos, int num)399         int arrayMemberValue(int pos, int num) throws Exception {
400             for (int i = 0; i < num; ++i) {
401                 pos = memberValue(pos);
402             }
403 
404             return pos;
405         }
406     }
407 
408     static class Renamer extends Walker {
409         ConstPool cpool;
410         Map classnames;
411 
412         /**
413          * Constructs a renamer.  It renames some class names
414          * into the new names specified by <code>map</code>.
415          *
416          * @param info      the annotations attribute.
417          * @param cp        the constant pool.
418          * @param map       pairs of replaced and substituted class names.
419          *                  It can be null.
420          */
Renamer(byte[] info, ConstPool cp, Map map)421         Renamer(byte[] info, ConstPool cp, Map map) {
422             super(info);
423             cpool = cp;
424             classnames = map;
425         }
426 
annotation(int pos, int type, int numPairs)427         int annotation(int pos, int type, int numPairs) throws Exception {
428             renameType(pos - 4, type);
429             return super.annotation(pos, type, numPairs);
430         }
431 
enumMemberValue(int pos, int typeNameIndex, int constNameIndex)432         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
433             throws Exception
434         {
435             renameType(pos + 1, typeNameIndex);
436             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
437         }
438 
classMemberValue(int pos, int index)439         void classMemberValue(int pos, int index) throws Exception {
440             renameType(pos + 1, index);
441             super.classMemberValue(pos, index);
442         }
443 
renameType(int pos, int index)444         private void renameType(int pos, int index) {
445             String name = cpool.getUtf8Info(index);
446             String newName = Descriptor.rename(name, classnames);
447             if (!name.equals(newName)) {
448                 int index2 = cpool.addUtf8Info(newName);
449                 ByteArray.write16bit(index2, info, pos);
450             }
451         }
452     }
453 
454     static class Copier extends Walker {
455         ByteArrayOutputStream output;
456         AnnotationsWriter writer;
457         ConstPool srcPool, destPool;
458         Map classnames;
459 
460         /**
461          * Constructs a copier.  This copier renames some class names
462          * into the new names specified by <code>map</code> when it copies
463          * an annotation attribute.
464          *
465          * @param info      the source attribute.
466          * @param src       the constant pool of the source class.
467          * @param dest      the constant pool of the destination class.
468          * @param map       pairs of replaced and substituted class names.
469          *                  It can be null.
470          */
Copier(byte[] info, ConstPool src, ConstPool dest, Map map)471         Copier(byte[] info, ConstPool src, ConstPool dest, Map map) {
472             super(info);
473             output = new ByteArrayOutputStream();
474             writer = new AnnotationsWriter(output, dest);
475             srcPool = src;
476             destPool = dest;
477             classnames = map;
478         }
479 
close()480         byte[] close() throws IOException {
481             writer.close();
482             return output.toByteArray();
483         }
484 
parameters(int numParam, int pos)485         void parameters(int numParam, int pos) throws Exception {
486             writer.numParameters(numParam);
487             super.parameters(numParam, pos);
488         }
489 
annotationArray(int pos, int num)490         int annotationArray(int pos, int num) throws Exception {
491             writer.numAnnotations(num);
492             return super.annotationArray(pos, num);
493         }
494 
annotation(int pos, int type, int numPairs)495         int annotation(int pos, int type, int numPairs) throws Exception {
496             writer.annotation(copyType(type), numPairs);
497             return super.annotation(pos, type, numPairs);
498         }
499 
memberValuePair(int pos, int nameIndex)500         int memberValuePair(int pos, int nameIndex) throws Exception {
501             writer.memberValuePair(copy(nameIndex));
502             return super.memberValuePair(pos, nameIndex);
503         }
504 
constValueMember(int tag, int index)505         void constValueMember(int tag, int index) throws Exception {
506             writer.constValueIndex(tag, copy(index));
507             super.constValueMember(tag, index);
508         }
509 
enumMemberValue(int pos, int typeNameIndex, int constNameIndex)510         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
511             throws Exception
512         {
513             writer.enumConstValue(copyType(typeNameIndex), copy(constNameIndex));
514             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
515         }
516 
classMemberValue(int pos, int index)517         void classMemberValue(int pos, int index) throws Exception {
518             writer.classInfoIndex(copyType(index));
519             super.classMemberValue(pos, index);
520         }
521 
annotationMemberValue(int pos)522         int annotationMemberValue(int pos) throws Exception {
523             writer.annotationValue();
524             return super.annotationMemberValue(pos);
525         }
526 
arrayMemberValue(int pos, int num)527         int arrayMemberValue(int pos, int num) throws Exception {
528             writer.arrayValue(num);
529             return super.arrayMemberValue(pos, num);
530         }
531 
532         /**
533          * Copies a constant pool entry into the destination constant pool
534          * and returns the index of the copied entry.
535          *
536          * @param srcIndex      the index of the copied entry into the source
537          *                      constant pool.
538          * @return the index of the copied item into the destination
539          *         constant pool.
540          */
copy(int srcIndex)541         int copy(int srcIndex) {
542             return srcPool.copy(srcIndex, destPool, classnames);
543         }
544 
545         /**
546          * Copies a constant pool entry into the destination constant pool
547          * and returns the index of the copied entry.  That entry must be
548          * a Utf8Info representing a class name in the L<class name>; form.
549          *
550          * @param srcIndex  the index of the copied entry into the source
551          *                  constant pool.
552          * @return          the index of the copied item into the destination
553          *                  constant pool.
554          */
copyType(int srcIndex)555         int copyType(int srcIndex) {
556             String name = srcPool.getUtf8Info(srcIndex);
557             String newName = Descriptor.rename(name, classnames);
558             return destPool.addUtf8Info(newName);
559         }
560     }
561 
562     static class Parser extends Walker {
563         ConstPool pool;
564         Annotation[][] allParams;   // all parameters
565         Annotation[] allAnno;       // all annotations
566         Annotation currentAnno;     // current annotation
567         MemberValue currentMember;  // current member
568 
569         /**
570          * Constructs a parser.  This parser constructs a parse tree of
571          * the annotations.
572          *
573          * @param info      the attribute.
574          * @param src       the constant pool.
575          */
Parser(byte[] info, ConstPool cp)576         Parser(byte[] info, ConstPool cp) {
577             super(info);
578             pool = cp;
579         }
580 
parseParameters()581         Annotation[][] parseParameters() throws Exception {
582             parameters();
583             return allParams;
584         }
585 
parseAnnotations()586         Annotation[] parseAnnotations() throws Exception {
587             annotationArray();
588             return allAnno;
589         }
590 
parseMemberValue()591         MemberValue parseMemberValue() throws Exception {
592             memberValue(0);
593             return currentMember;
594         }
595 
parameters(int numParam, int pos)596         void parameters(int numParam, int pos) throws Exception {
597             Annotation[][] params = new Annotation[numParam][];
598             for (int i = 0; i < numParam; ++i) {
599                 pos = annotationArray(pos);
600                 params[i] = allAnno;
601             }
602 
603             allParams = params;
604         }
605 
annotationArray(int pos, int num)606         int annotationArray(int pos, int num) throws Exception {
607             Annotation[] array = new Annotation[num];
608             for (int i = 0; i < num; ++i) {
609                 pos = annotation(pos);
610                 array[i] = currentAnno;
611             }
612 
613             allAnno = array;
614             return pos;
615         }
616 
annotation(int pos, int type, int numPairs)617         int annotation(int pos, int type, int numPairs) throws Exception {
618             currentAnno = new Annotation(type, pool);
619             return super.annotation(pos, type, numPairs);
620         }
621 
memberValuePair(int pos, int nameIndex)622         int memberValuePair(int pos, int nameIndex) throws Exception {
623             pos = super.memberValuePair(pos, nameIndex);
624             currentAnno.addMemberValue(nameIndex, currentMember);
625             return pos;
626         }
627 
constValueMember(int tag, int index)628         void constValueMember(int tag, int index) throws Exception {
629             MemberValue m;
630             ConstPool cp = pool;
631             switch (tag) {
632             case 'B' :
633                 m = new ByteMemberValue(index, cp);
634                 break;
635             case 'C' :
636                 m = new CharMemberValue(index, cp);
637                 break;
638             case 'D' :
639                 m = new DoubleMemberValue(index, cp);
640                 break;
641             case 'F' :
642                 m = new FloatMemberValue(index, cp);
643                 break;
644             case 'I' :
645                 m = new IntegerMemberValue(index, cp);
646                 break;
647             case 'J' :
648                 m = new LongMemberValue(index, cp);
649                 break;
650             case 'S' :
651                 m = new ShortMemberValue(index, cp);
652                 break;
653             case 'Z' :
654                 m = new BooleanMemberValue(index, cp);
655                 break;
656             case 's' :
657                 m = new StringMemberValue(index, cp);
658                 break;
659             default :
660                 throw new RuntimeException("unknown tag:" + tag);
661             }
662 
663             currentMember = m;
664             super.constValueMember(tag, index);
665         }
666 
enumMemberValue(int pos, int typeNameIndex, int constNameIndex)667         void enumMemberValue(int pos, int typeNameIndex, int constNameIndex)
668             throws Exception
669         {
670             currentMember = new EnumMemberValue(typeNameIndex,
671                                               constNameIndex, pool);
672             super.enumMemberValue(pos, typeNameIndex, constNameIndex);
673         }
674 
classMemberValue(int pos, int index)675         void classMemberValue(int pos, int index) throws Exception {
676             currentMember = new ClassMemberValue(index, pool);
677             super.classMemberValue(pos, index);
678         }
679 
annotationMemberValue(int pos)680         int annotationMemberValue(int pos) throws Exception {
681             Annotation anno = currentAnno;
682             pos = super.annotationMemberValue(pos);
683             currentMember = new AnnotationMemberValue(currentAnno, pool);
684             currentAnno = anno;
685             return pos;
686         }
687 
arrayMemberValue(int pos, int num)688         int arrayMemberValue(int pos, int num) throws Exception {
689             ArrayMemberValue amv = new ArrayMemberValue(pool);
690             MemberValue[] elements = new MemberValue[num];
691             for (int i = 0; i < num; ++i) {
692                 pos = memberValue(pos);
693                 elements[i] = currentMember;
694             }
695 
696             amv.setValue(elements);
697             currentMember = amv;
698             return pos;
699         }
700     }
701 }
702