1 /***
2  * ASM: a very small and fast Java bytecode manipulation framework
3  * Copyright (c) 2000-2007 INRIA, France Telecom
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the copyright holders nor the names of its
15  *    contributors may be used to endorse or promote products derived from
16  *    this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
28  * THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 package org.mockito.asm;
31 
32 /**
33  * An {@link AnnotationVisitor} that generates annotations in bytecode form.
34  *
35  * @author Eric Bruneton
36  * @author Eugene Kuleshov
37  */
38 final class AnnotationWriter implements AnnotationVisitor {
39 
40     /**
41      * The class writer to which this annotation must be added.
42      */
43     private final ClassWriter cw;
44 
45     /**
46      * The number of values in this annotation.
47      */
48     private int size;
49 
50     /**
51      * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation
52      * writers used for annotation default and annotation arrays use unnamed
53      * values.
54      */
55     private final boolean named;
56 
57     /**
58      * The annotation values in bytecode form. This byte vector only contains
59      * the values themselves, i.e. the number of values must be stored as a
60      * unsigned short just before these bytes.
61      */
62     private final ByteVector bv;
63 
64     /**
65      * The byte vector to be used to store the number of values of this
66      * annotation. See {@link #bv}.
67      */
68     private final ByteVector parent;
69 
70     /**
71      * Where the number of values of this annotation must be stored in
72      * {@link #parent}.
73      */
74     private final int offset;
75 
76     /**
77      * Next annotation writer. This field is used to store annotation lists.
78      */
79     AnnotationWriter next;
80 
81     /**
82      * Previous annotation writer. This field is used to store annotation lists.
83      */
84     AnnotationWriter prev;
85 
86     // ------------------------------------------------------------------------
87     // Constructor
88     // ------------------------------------------------------------------------
89 
90     /**
91      * Constructs a new {@link AnnotationWriter}.
92      *
93      * @param cw the class writer to which this annotation must be added.
94      * @param named <tt>true<tt> if values are named, <tt>false</tt> otherwise.
95      * @param bv where the annotation values must be stored.
96      * @param parent where the number of annotation values must be stored.
97      * @param offset where in <tt>parent</tt> the number of annotation values must
98      *      be stored.
99      */
AnnotationWriter( final ClassWriter cw, final boolean named, final ByteVector bv, final ByteVector parent, final int offset)100     AnnotationWriter(
101         final ClassWriter cw,
102         final boolean named,
103         final ByteVector bv,
104         final ByteVector parent,
105         final int offset)
106     {
107         this.cw = cw;
108         this.named = named;
109         this.bv = bv;
110         this.parent = parent;
111         this.offset = offset;
112     }
113 
114     // ------------------------------------------------------------------------
115     // Implementation of the AnnotationVisitor interface
116     // ------------------------------------------------------------------------
117 
visit(final String name, final Object value)118     public void visit(final String name, final Object value) {
119         ++size;
120         if (named) {
121             bv.putShort(cw.newUTF8(name));
122         }
123         if (value instanceof String) {
124             bv.put12('s', cw.newUTF8((String) value));
125         } else if (value instanceof Byte) {
126             bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index);
127         } else if (value instanceof Boolean) {
128             int v = ((Boolean) value).booleanValue() ? 1 : 0;
129             bv.put12('Z', cw.newInteger(v).index);
130         } else if (value instanceof Character) {
131             bv.put12('C', cw.newInteger(((Character) value).charValue()).index);
132         } else if (value instanceof Short) {
133             bv.put12('S', cw.newInteger(((Short) value).shortValue()).index);
134         } else if (value instanceof Type) {
135             bv.put12('c', cw.newUTF8(((Type) value).getDescriptor()));
136         } else if (value instanceof byte[]) {
137             byte[] v = (byte[]) value;
138             bv.put12('[', v.length);
139             for (int i = 0; i < v.length; i++) {
140                 bv.put12('B', cw.newInteger(v[i]).index);
141             }
142         } else if (value instanceof boolean[]) {
143             boolean[] v = (boolean[]) value;
144             bv.put12('[', v.length);
145             for (int i = 0; i < v.length; i++) {
146                 bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index);
147             }
148         } else if (value instanceof short[]) {
149             short[] v = (short[]) value;
150             bv.put12('[', v.length);
151             for (int i = 0; i < v.length; i++) {
152                 bv.put12('S', cw.newInteger(v[i]).index);
153             }
154         } else if (value instanceof char[]) {
155             char[] v = (char[]) value;
156             bv.put12('[', v.length);
157             for (int i = 0; i < v.length; i++) {
158                 bv.put12('C', cw.newInteger(v[i]).index);
159             }
160         } else if (value instanceof int[]) {
161             int[] v = (int[]) value;
162             bv.put12('[', v.length);
163             for (int i = 0; i < v.length; i++) {
164                 bv.put12('I', cw.newInteger(v[i]).index);
165             }
166         } else if (value instanceof long[]) {
167             long[] v = (long[]) value;
168             bv.put12('[', v.length);
169             for (int i = 0; i < v.length; i++) {
170                 bv.put12('J', cw.newLong(v[i]).index);
171             }
172         } else if (value instanceof float[]) {
173             float[] v = (float[]) value;
174             bv.put12('[', v.length);
175             for (int i = 0; i < v.length; i++) {
176                 bv.put12('F', cw.newFloat(v[i]).index);
177             }
178         } else if (value instanceof double[]) {
179             double[] v = (double[]) value;
180             bv.put12('[', v.length);
181             for (int i = 0; i < v.length; i++) {
182                 bv.put12('D', cw.newDouble(v[i]).index);
183             }
184         } else {
185             Item i = cw.newConstItem(value);
186             bv.put12(".s.IFJDCS".charAt(i.type), i.index);
187         }
188     }
189 
visitEnum( final String name, final String desc, final String value)190     public void visitEnum(
191         final String name,
192         final String desc,
193         final String value)
194     {
195         ++size;
196         if (named) {
197             bv.putShort(cw.newUTF8(name));
198         }
199         bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value));
200     }
201 
visitAnnotation( final String name, final String desc)202     public AnnotationVisitor visitAnnotation(
203         final String name,
204         final String desc)
205     {
206         ++size;
207         if (named) {
208             bv.putShort(cw.newUTF8(name));
209         }
210         // write tag and type, and reserve space for values count
211         bv.put12('@', cw.newUTF8(desc)).putShort(0);
212         return new AnnotationWriter(cw, true, bv, bv, bv.length - 2);
213     }
214 
visitArray(final String name)215     public AnnotationVisitor visitArray(final String name) {
216         ++size;
217         if (named) {
218             bv.putShort(cw.newUTF8(name));
219         }
220         // write tag, and reserve space for array size
221         bv.put12('[', 0);
222         return new AnnotationWriter(cw, false, bv, bv, bv.length - 2);
223     }
224 
visitEnd()225     public void visitEnd() {
226         if (parent != null) {
227             byte[] data = parent.data;
228             data[offset] = (byte) (size >>> 8);
229             data[offset + 1] = (byte) size;
230         }
231     }
232 
233     // ------------------------------------------------------------------------
234     // Utility methods
235     // ------------------------------------------------------------------------
236 
237     /**
238      * Returns the size of this annotation writer list.
239      *
240      * @return the size of this annotation writer list.
241      */
getSize()242     int getSize() {
243         int size = 0;
244         AnnotationWriter aw = this;
245         while (aw != null) {
246             size += aw.bv.length;
247             aw = aw.next;
248         }
249         return size;
250     }
251 
252     /**
253      * Puts the annotations of this annotation writer list into the given byte
254      * vector.
255      *
256      * @param out where the annotations must be put.
257      */
put(final ByteVector out)258     void put(final ByteVector out) {
259         int n = 0;
260         int size = 2;
261         AnnotationWriter aw = this;
262         AnnotationWriter last = null;
263         while (aw != null) {
264             ++n;
265             size += aw.bv.length;
266             aw.visitEnd(); // in case user forgot to call visitEnd
267             aw.prev = last;
268             last = aw;
269             aw = aw.next;
270         }
271         out.putInt(size);
272         out.putShort(n);
273         aw = last;
274         while (aw != null) {
275             out.putByteArray(aw.bv.data, 0, aw.bv.length);
276             aw = aw.prev;
277         }
278     }
279 
280     /**
281      * Puts the given annotation lists into the given byte vector.
282      *
283      * @param panns an array of annotation writer lists.
284      * @param off index of the first annotation to be written.
285      * @param out where the annotations must be put.
286      */
put( final AnnotationWriter[] panns, final int off, final ByteVector out)287     static void put(
288         final AnnotationWriter[] panns,
289         final int off,
290         final ByteVector out)
291     {
292         int size = 1 + 2 * (panns.length - off);
293         for (int i = off; i < panns.length; ++i) {
294             size += panns[i] == null ? 0 : panns[i].getSize();
295         }
296         out.putInt(size).putByte(panns.length - off);
297         for (int i = off; i < panns.length; ++i) {
298             AnnotationWriter aw = panns[i];
299             AnnotationWriter last = null;
300             int n = 0;
301             while (aw != null) {
302                 ++n;
303                 aw.visitEnd(); // in case user forgot to call visitEnd
304                 aw.prev = last;
305                 last = aw;
306                 aw = aw.next;
307             }
308             out.putShort(n);
309             aw = last;
310             while (aw != null) {
311                 out.putByteArray(aw.bv.data, 0, aw.bv.length);
312                 aw = aw.prev;
313             }
314         }
315     }
316 }
317