Removed unused files
Daigo Moriwaki
12 years ago
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A visitor to visit a Java annotation. The methods of this interface must be | |
33 | * called in the following order: (<tt>visit<tt> | <tt>visitEnum<tt> | | |
34 | * <tt>visitAnnotation<tt> | <tt>visitArray<tt>)* <tt>visitEnd<tt>. | |
35 | * | |
36 | * @author Eric Bruneton | |
37 | * @author Eugene Kuleshov | |
38 | */ | |
39 | public interface AnnotationVisitor{ | |
40 | ||
41 | /** | |
42 | * Visits a primitive value of the annotation. | |
43 | * | |
44 | * @param name the value name. | |
45 | * @param value the actual value, whose type must be {@link Byte}, | |
46 | * {@link Boolean}, {@link Character}, {@link Short}, | |
47 | * {@link Integer}, {@link Long}, {@link Float}, {@link Double}, | |
48 | * {@link String} or {@link Type}. This value can also be an array | |
49 | * of byte, boolean, short, char, int, long, float or double values | |
50 | * (this is equivalent to using {@link #visitArray visitArray} and | |
51 | * visiting each array element in turn, but is more convenient). | |
52 | */ | |
53 | void visit(String name, Object value); | |
54 | ||
55 | /** | |
56 | * Visits an enumeration value of the annotation. | |
57 | * | |
58 | * @param name the value name. | |
59 | * @param desc the class descriptor of the enumeration class. | |
60 | * @param value the actual enumeration value. | |
61 | */ | |
62 | void visitEnum(String name, String desc, String value); | |
63 | ||
64 | /** | |
65 | * Visits a nested annotation value of the annotation. | |
66 | * | |
67 | * @param name the value name. | |
68 | * @param desc the class descriptor of the nested annotation class. | |
69 | * @return a visitor to visit the actual nested annotation value, or | |
70 | * <tt>null</tt> if this visitor is not interested in visiting | |
71 | * this nested annotation. <i>The nested annotation value must be | |
72 | * fully visited before calling other methods on this annotation | |
73 | * visitor</i>. | |
74 | */ | |
75 | AnnotationVisitor visitAnnotation(String name, String desc); | |
76 | ||
77 | /** | |
78 | * Visits an array value of the annotation. Note that arrays of primitive | |
79 | * types (such as byte, boolean, short, char, int, long, float or double) | |
80 | * can be passed as value to {@link #visit visit}. This is what | |
81 | * {@link ClassReader} does. | |
82 | * | |
83 | * @param name the value name. | |
84 | * @return a visitor to visit the actual array value elements, or | |
85 | * <tt>null</tt> if this visitor is not interested in visiting | |
86 | * these values. The 'name' parameters passed to the methods of this | |
87 | * visitor are ignored. <i>All the array values must be visited | |
88 | * before calling other methods on this annotation visitor</i>. | |
89 | */ | |
90 | AnnotationVisitor visitArray(String name); | |
91 | ||
92 | /** | |
93 | * Visits the end of the annotation. | |
94 | */ | |
95 | void visitEnd(); | |
96 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * An {@link AnnotationVisitor} that generates annotations in bytecode form. | |
33 | * | |
34 | * @author Eric Bruneton | |
35 | * @author Eugene Kuleshov | |
36 | */ | |
37 | final class AnnotationWriter implements AnnotationVisitor{ | |
38 | ||
39 | /** | |
40 | * The class writer to which this annotation must be added. | |
41 | */ | |
42 | private final ClassWriter cw; | |
43 | ||
44 | /** | |
45 | * The number of values in this annotation. | |
46 | */ | |
47 | private int size; | |
48 | ||
49 | /** | |
50 | * <tt>true<tt> if values are named, <tt>false</tt> otherwise. Annotation | |
51 | * writers used for annotation default and annotation arrays use unnamed | |
52 | * values. | |
53 | */ | |
54 | private final boolean named; | |
55 | ||
56 | /** | |
57 | * The annotation values in bytecode form. This byte vector only contains | |
58 | * the values themselves, i.e. the number of values must be stored as a | |
59 | * unsigned short just before these bytes. | |
60 | */ | |
61 | private final ByteVector bv; | |
62 | ||
63 | /** | |
64 | * The byte vector to be used to store the number of values of this | |
65 | * annotation. See {@link #bv}. | |
66 | */ | |
67 | private final ByteVector parent; | |
68 | ||
69 | /** | |
70 | * Where the number of values of this annotation must be stored in | |
71 | * {@link #parent}. | |
72 | */ | |
73 | private final int offset; | |
74 | ||
75 | /** | |
76 | * Next annotation writer. This field is used to store annotation lists. | |
77 | */ | |
78 | AnnotationWriter next; | |
79 | ||
80 | /** | |
81 | * Previous annotation writer. This field is used to store annotation lists. | |
82 | */ | |
83 | AnnotationWriter prev; | |
84 | ||
85 | // ------------------------------------------------------------------------ | |
86 | // Constructor | |
87 | // ------------------------------------------------------------------------ | |
88 | ||
89 | /** | |
90 | * Constructs a new {@link AnnotationWriter}. | |
91 | * | |
92 | * @param cw the class writer to which this annotation must be added. | |
93 | * @param named <tt>true<tt> if values are named, <tt>false</tt> otherwise. | |
94 | * @param bv where the annotation values must be stored. | |
95 | * @param parent where the number of annotation values must be stored. | |
96 | * @param offset where in <tt>parent</tt> the number of annotation values must | |
97 | * be stored. | |
98 | */ | |
99 | AnnotationWriter( | |
100 | final ClassWriter cw, | |
101 | final boolean named, | |
102 | final ByteVector bv, | |
103 | final ByteVector parent, | |
104 | final int offset){ | |
105 | this.cw = cw; | |
106 | this.named = named; | |
107 | this.bv = bv; | |
108 | this.parent = parent; | |
109 | this.offset = offset; | |
110 | } | |
111 | ||
112 | // ------------------------------------------------------------------------ | |
113 | // Implementation of the AnnotationVisitor interface | |
114 | // ------------------------------------------------------------------------ | |
115 | ||
116 | public void visit(final String name, final Object value){ | |
117 | ++size; | |
118 | if(named) | |
119 | { | |
120 | bv.putShort(cw.newUTF8(name)); | |
121 | } | |
122 | if(value instanceof String) | |
123 | { | |
124 | bv.put12('s', cw.newUTF8((String) value)); | |
125 | } | |
126 | else if(value instanceof Byte) | |
127 | { | |
128 | bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); | |
129 | } | |
130 | else if(value instanceof Boolean) | |
131 | { | |
132 | int v = ((Boolean) value).booleanValue() ? 1 : 0; | |
133 | bv.put12('Z', cw.newInteger(v).index); | |
134 | } | |
135 | else if(value instanceof Character) | |
136 | { | |
137 | bv.put12('C', cw.newInteger(((Character) value).charValue()).index); | |
138 | } | |
139 | else if(value instanceof Short) | |
140 | { | |
141 | bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); | |
142 | } | |
143 | else if(value instanceof Type) | |
144 | { | |
145 | bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); | |
146 | } | |
147 | else if(value instanceof byte[]) | |
148 | { | |
149 | byte[] v = (byte[]) value; | |
150 | bv.put12('[', v.length); | |
151 | for(int i = 0; i < v.length; i++) | |
152 | { | |
153 | bv.put12('B', cw.newInteger(v[i]).index); | |
154 | } | |
155 | } | |
156 | else if(value instanceof boolean[]) | |
157 | { | |
158 | boolean[] v = (boolean[]) value; | |
159 | bv.put12('[', v.length); | |
160 | for(int i = 0; i < v.length; i++) | |
161 | { | |
162 | bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); | |
163 | } | |
164 | } | |
165 | else if(value instanceof short[]) | |
166 | { | |
167 | short[] v = (short[]) value; | |
168 | bv.put12('[', v.length); | |
169 | for(int i = 0; i < v.length; i++) | |
170 | { | |
171 | bv.put12('S', cw.newInteger(v[i]).index); | |
172 | } | |
173 | } | |
174 | else if(value instanceof char[]) | |
175 | { | |
176 | char[] v = (char[]) value; | |
177 | bv.put12('[', v.length); | |
178 | for(int i = 0; i < v.length; i++) | |
179 | { | |
180 | bv.put12('C', cw.newInteger(v[i]).index); | |
181 | } | |
182 | } | |
183 | else if(value instanceof int[]) | |
184 | { | |
185 | int[] v = (int[]) value; | |
186 | bv.put12('[', v.length); | |
187 | for(int i = 0; i < v.length; i++) | |
188 | { | |
189 | bv.put12('I', cw.newInteger(v[i]).index); | |
190 | } | |
191 | } | |
192 | else if(value instanceof long[]) | |
193 | { | |
194 | long[] v = (long[]) value; | |
195 | bv.put12('[', v.length); | |
196 | for(int i = 0; i < v.length; i++) | |
197 | { | |
198 | bv.put12('J', cw.newLong(v[i]).index); | |
199 | } | |
200 | } | |
201 | else if(value instanceof float[]) | |
202 | { | |
203 | float[] v = (float[]) value; | |
204 | bv.put12('[', v.length); | |
205 | for(int i = 0; i < v.length; i++) | |
206 | { | |
207 | bv.put12('F', cw.newFloat(v[i]).index); | |
208 | } | |
209 | } | |
210 | else if(value instanceof double[]) | |
211 | { | |
212 | double[] v = (double[]) value; | |
213 | bv.put12('[', v.length); | |
214 | for(int i = 0; i < v.length; i++) | |
215 | { | |
216 | bv.put12('D', cw.newDouble(v[i]).index); | |
217 | } | |
218 | } | |
219 | else | |
220 | { | |
221 | Item i = cw.newConstItem(value); | |
222 | bv.put12(".s.IFJDCS".charAt(i.type), i.index); | |
223 | } | |
224 | } | |
225 | ||
226 | public void visitEnum( | |
227 | final String name, | |
228 | final String desc, | |
229 | final String value){ | |
230 | ++size; | |
231 | if(named) | |
232 | { | |
233 | bv.putShort(cw.newUTF8(name)); | |
234 | } | |
235 | bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); | |
236 | } | |
237 | ||
238 | public AnnotationVisitor visitAnnotation( | |
239 | final String name, | |
240 | final String desc){ | |
241 | ++size; | |
242 | if(named) | |
243 | { | |
244 | bv.putShort(cw.newUTF8(name)); | |
245 | } | |
246 | // write tag and type, and reserve space for values count | |
247 | bv.put12('@', cw.newUTF8(desc)).putShort(0); | |
248 | return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); | |
249 | } | |
250 | ||
251 | public AnnotationVisitor visitArray(final String name){ | |
252 | ++size; | |
253 | if(named) | |
254 | { | |
255 | bv.putShort(cw.newUTF8(name)); | |
256 | } | |
257 | // write tag, and reserve space for array size | |
258 | bv.put12('[', 0); | |
259 | return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); | |
260 | } | |
261 | ||
262 | public void visitEnd(){ | |
263 | if(parent != null) | |
264 | { | |
265 | byte[] data = parent.data; | |
266 | data[offset] = (byte) (size >>> 8); | |
267 | data[offset + 1] = (byte) size; | |
268 | } | |
269 | } | |
270 | ||
271 | // ------------------------------------------------------------------------ | |
272 | // Utility methods | |
273 | // ------------------------------------------------------------------------ | |
274 | ||
275 | /** | |
276 | * Returns the size of this annotation writer list. | |
277 | * | |
278 | * @return the size of this annotation writer list. | |
279 | */ | |
280 | int getSize(){ | |
281 | int size = 0; | |
282 | AnnotationWriter aw = this; | |
283 | while(aw != null) | |
284 | { | |
285 | size += aw.bv.length; | |
286 | aw = aw.next; | |
287 | } | |
288 | return size; | |
289 | } | |
290 | ||
291 | /** | |
292 | * Puts the annotations of this annotation writer list into the given byte | |
293 | * vector. | |
294 | * | |
295 | * @param out where the annotations must be put. | |
296 | */ | |
297 | void put(final ByteVector out){ | |
298 | int n = 0; | |
299 | int size = 2; | |
300 | AnnotationWriter aw = this; | |
301 | AnnotationWriter last = null; | |
302 | while(aw != null) | |
303 | { | |
304 | ++n; | |
305 | size += aw.bv.length; | |
306 | aw.visitEnd(); // in case user forgot to call visitEnd | |
307 | aw.prev = last; | |
308 | last = aw; | |
309 | aw = aw.next; | |
310 | } | |
311 | out.putInt(size); | |
312 | out.putShort(n); | |
313 | aw = last; | |
314 | while(aw != null) | |
315 | { | |
316 | out.putByteArray(aw.bv.data, 0, aw.bv.length); | |
317 | aw = aw.prev; | |
318 | } | |
319 | } | |
320 | ||
321 | /** | |
322 | * Puts the given annotation lists into the given byte vector. | |
323 | * | |
324 | * @param panns an array of annotation writer lists. | |
325 | * @param out where the annotations must be put. | |
326 | */ | |
327 | static void put(final AnnotationWriter[] panns, final ByteVector out){ | |
328 | int size = 1 + 2 * panns.length; | |
329 | for(int i = 0; i < panns.length; ++i) | |
330 | { | |
331 | size += panns[i] == null ? 0 : panns[i].getSize(); | |
332 | } | |
333 | out.putInt(size).putByte(panns.length); | |
334 | for(int i = 0; i < panns.length; ++i) | |
335 | { | |
336 | AnnotationWriter aw = panns[i]; | |
337 | AnnotationWriter last = null; | |
338 | int n = 0; | |
339 | while(aw != null) | |
340 | { | |
341 | ++n; | |
342 | aw.visitEnd(); // in case user forgot to call visitEnd | |
343 | aw.prev = last; | |
344 | last = aw; | |
345 | aw = aw.next; | |
346 | } | |
347 | out.putShort(n); | |
348 | aw = last; | |
349 | while(aw != null) | |
350 | { | |
351 | out.putByteArray(aw.bv.data, 0, aw.bv.length); | |
352 | aw = aw.prev; | |
353 | } | |
354 | } | |
355 | } | |
356 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A non standard class, field, method or code attribute. | |
33 | * | |
34 | * @author Eric Bruneton | |
35 | * @author Eugene Kuleshov | |
36 | */ | |
37 | public class Attribute{ | |
38 | ||
39 | /** | |
40 | * The type of this attribute. | |
41 | */ | |
42 | public final String type; | |
43 | ||
44 | /** | |
45 | * The raw value of this attribute, used only for unknown attributes. | |
46 | */ | |
47 | byte[] value; | |
48 | ||
49 | /** | |
50 | * The next attribute in this attribute list. May be <tt>null</tt>. | |
51 | */ | |
52 | Attribute next; | |
53 | ||
54 | /** | |
55 | * Constructs a new empty attribute. | |
56 | * | |
57 | * @param type the type of the attribute. | |
58 | */ | |
59 | protected Attribute(final String type){ | |
60 | this.type = type; | |
61 | } | |
62 | ||
63 | /** | |
64 | * Returns <tt>true</tt> if this type of attribute is unknown. The default | |
65 | * implementation of this method always returns <tt>true</tt>. | |
66 | * | |
67 | * @return <tt>true</tt> if this type of attribute is unknown. | |
68 | */ | |
69 | public boolean isUnknown(){ | |
70 | return true; | |
71 | } | |
72 | ||
73 | /** | |
74 | * Returns <tt>true</tt> if this type of attribute is a code attribute. | |
75 | * | |
76 | * @return <tt>true</tt> if this type of attribute is a code attribute. | |
77 | */ | |
78 | public boolean isCodeAttribute(){ | |
79 | return false; | |
80 | } | |
81 | ||
82 | /** | |
83 | * Returns the labels corresponding to this attribute. | |
84 | * | |
85 | * @return the labels corresponding to this attribute, or <tt>null</tt> if | |
86 | * this attribute is not a code attribute that contains labels. | |
87 | */ | |
88 | protected Label[] getLabels(){ | |
89 | return null; | |
90 | } | |
91 | ||
92 | /** | |
93 | * Reads a {@link #type type} attribute. This method must return a <i>new</i> | |
94 | * {@link Attribute} object, of type {@link #type type}, corresponding to | |
95 | * the <tt>len</tt> bytes starting at the given offset, in the given class | |
96 | * reader. | |
97 | * | |
98 | * @param cr the class that contains the attribute to be read. | |
99 | * @param off index of the first byte of the attribute's content in {@link | |
100 | * ClassReader#b cr.b}. The 6 attribute header bytes, containing the | |
101 | * type and the length of the attribute, are not taken into account | |
102 | * here. | |
103 | * @param len the length of the attribute's content. | |
104 | * @param buf buffer to be used to call | |
105 | * {@link ClassReader#readUTF8 readUTF8}, | |
106 | * {@link ClassReader#readClass(int,char[]) readClass} or | |
107 | * {@link ClassReader#readConst readConst}. | |
108 | * @param codeOff index of the first byte of code's attribute content in | |
109 | * {@link ClassReader#b cr.b}, or -1 if the attribute to be read is | |
110 | * not a code attribute. The 6 attribute header bytes, containing the | |
111 | * type and the length of the attribute, are not taken into account | |
112 | * here. | |
113 | * @param labels the labels of the method's code, or <tt>null</tt> if the | |
114 | * attribute to be read is not a code attribute. | |
115 | * @return a <i>new</i> {@link Attribute} object corresponding to the given | |
116 | * bytes. | |
117 | */ | |
118 | protected Attribute read( | |
119 | final ClassReader cr, | |
120 | final int off, | |
121 | final int len, | |
122 | final char[] buf, | |
123 | final int codeOff, | |
124 | final Label[] labels){ | |
125 | Attribute attr = new Attribute(type); | |
126 | attr.value = new byte[len]; | |
127 | System.arraycopy(cr.b, off, attr.value, 0, len); | |
128 | return attr; | |
129 | } | |
130 | ||
131 | /** | |
132 | * Returns the byte array form of this attribute. | |
133 | * | |
134 | * @param cw the class to which this attribute must be added. This parameter | |
135 | * can be used to add to the constant pool of this class the items | |
136 | * that corresponds to this attribute. | |
137 | * @param code the bytecode of the method corresponding to this code | |
138 | * attribute, or <tt>null</tt> if this attribute is not a code | |
139 | * attributes. | |
140 | * @param len the length of the bytecode of the method corresponding to this | |
141 | * code attribute, or <tt>null</tt> if this attribute is not a code | |
142 | * attribute. | |
143 | * @param maxStack the maximum stack size of the method corresponding to | |
144 | * this code attribute, or -1 if this attribute is not a code | |
145 | * attribute. | |
146 | * @param maxLocals the maximum number of local variables of the method | |
147 | * corresponding to this code attribute, or -1 if this attribute is | |
148 | * not a code attribute. | |
149 | * @return the byte array form of this attribute. | |
150 | */ | |
151 | protected ByteVector write( | |
152 | final ClassWriter cw, | |
153 | final byte[] code, | |
154 | final int len, | |
155 | final int maxStack, | |
156 | final int maxLocals){ | |
157 | ByteVector v = new ByteVector(); | |
158 | v.data = value; | |
159 | v.length = value.length; | |
160 | return v; | |
161 | } | |
162 | ||
163 | /** | |
164 | * Returns the length of the attribute list that begins with this attribute. | |
165 | * | |
166 | * @return the length of the attribute list that begins with this attribute. | |
167 | */ | |
168 | final int getCount(){ | |
169 | int count = 0; | |
170 | Attribute attr = this; | |
171 | while(attr != null) | |
172 | { | |
173 | count += 1; | |
174 | attr = attr.next; | |
175 | } | |
176 | return count; | |
177 | } | |
178 | ||
179 | /** | |
180 | * Returns the size of all the attributes in this attribute list. | |
181 | * | |
182 | * @param cw the class writer to be used to convert the attributes into byte | |
183 | * arrays, with the {@link #write write} method. | |
184 | * @param code the bytecode of the method corresponding to these code | |
185 | * attributes, or <tt>null</tt> if these attributes are not code | |
186 | * attributes. | |
187 | * @param len the length of the bytecode of the method corresponding to | |
188 | * these code attributes, or <tt>null</tt> if these attributes are | |
189 | * not code attributes. | |
190 | * @param maxStack the maximum stack size of the method corresponding to | |
191 | * these code attributes, or -1 if these attributes are not code | |
192 | * attributes. | |
193 | * @param maxLocals the maximum number of local variables of the method | |
194 | * corresponding to these code attributes, or -1 if these attributes | |
195 | * are not code attributes. | |
196 | * @return the size of all the attributes in this attribute list. This size | |
197 | * includes the size of the attribute headers. | |
198 | */ | |
199 | final int getSize( | |
200 | final ClassWriter cw, | |
201 | final byte[] code, | |
202 | final int len, | |
203 | final int maxStack, | |
204 | final int maxLocals){ | |
205 | Attribute attr = this; | |
206 | int size = 0; | |
207 | while(attr != null) | |
208 | { | |
209 | cw.newUTF8(attr.type); | |
210 | size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; | |
211 | attr = attr.next; | |
212 | } | |
213 | return size; | |
214 | } | |
215 | ||
216 | /** | |
217 | * Writes all the attributes of this attribute list in the given byte | |
218 | * vector. | |
219 | * | |
220 | * @param cw the class writer to be used to convert the attributes into byte | |
221 | * arrays, with the {@link #write write} method. | |
222 | * @param code the bytecode of the method corresponding to these code | |
223 | * attributes, or <tt>null</tt> if these attributes are not code | |
224 | * attributes. | |
225 | * @param len the length of the bytecode of the method corresponding to | |
226 | * these code attributes, or <tt>null</tt> if these attributes are | |
227 | * not code attributes. | |
228 | * @param maxStack the maximum stack size of the method corresponding to | |
229 | * these code attributes, or -1 if these attributes are not code | |
230 | * attributes. | |
231 | * @param maxLocals the maximum number of local variables of the method | |
232 | * corresponding to these code attributes, or -1 if these attributes | |
233 | * are not code attributes. | |
234 | * @param out where the attributes must be written. | |
235 | */ | |
236 | final void put( | |
237 | final ClassWriter cw, | |
238 | final byte[] code, | |
239 | final int len, | |
240 | final int maxStack, | |
241 | final int maxLocals, | |
242 | final ByteVector out){ | |
243 | Attribute attr = this; | |
244 | while(attr != null) | |
245 | { | |
246 | ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); | |
247 | out.putShort(cw.newUTF8(attr.type)).putInt(b.length); | |
248 | out.putByteArray(b.data, 0, b.length); | |
249 | attr = attr.next; | |
250 | } | |
251 | } | |
252 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A dynamically extensible vector of bytes. This class is roughly equivalent to | |
33 | * a DataOutputStream on top of a ByteArrayOutputStream, but is more efficient. | |
34 | * | |
35 | * @author Eric Bruneton | |
36 | */ | |
37 | public class ByteVector{ | |
38 | ||
39 | /** | |
40 | * The content of this vector. | |
41 | */ | |
42 | byte[] data; | |
43 | ||
44 | /** | |
45 | * Actual number of bytes in this vector. | |
46 | */ | |
47 | int length; | |
48 | ||
49 | /** | |
50 | * Constructs a new {@link ByteVector ByteVector} with a default initial | |
51 | * size. | |
52 | */ | |
53 | public ByteVector(){ | |
54 | data = new byte[64]; | |
55 | } | |
56 | ||
57 | /** | |
58 | * Constructs a new {@link ByteVector ByteVector} with the given initial | |
59 | * size. | |
60 | * | |
61 | * @param initialSize the initial size of the byte vector to be constructed. | |
62 | */ | |
63 | public ByteVector(final int initialSize){ | |
64 | data = new byte[initialSize]; | |
65 | } | |
66 | ||
67 | /** | |
68 | * Puts a byte into this byte vector. The byte vector is automatically | |
69 | * enlarged if necessary. | |
70 | * | |
71 | * @param b a byte. | |
72 | * @return this byte vector. | |
73 | */ | |
74 | public ByteVector putByte(final int b){ | |
75 | int length = this.length; | |
76 | if(length + 1 > data.length) | |
77 | { | |
78 | enlarge(1); | |
79 | } | |
80 | data[length++] = (byte) b; | |
81 | this.length = length; | |
82 | return this; | |
83 | } | |
84 | ||
85 | /** | |
86 | * Puts two bytes into this byte vector. The byte vector is automatically | |
87 | * enlarged if necessary. | |
88 | * | |
89 | * @param b1 a byte. | |
90 | * @param b2 another byte. | |
91 | * @return this byte vector. | |
92 | */ | |
93 | ByteVector put11(final int b1, final int b2){ | |
94 | int length = this.length; | |
95 | if(length + 2 > data.length) | |
96 | { | |
97 | enlarge(2); | |
98 | } | |
99 | byte[] data = this.data; | |
100 | data[length++] = (byte) b1; | |
101 | data[length++] = (byte) b2; | |
102 | this.length = length; | |
103 | return this; | |
104 | } | |
105 | ||
106 | /** | |
107 | * Puts a short into this byte vector. The byte vector is automatically | |
108 | * enlarged if necessary. | |
109 | * | |
110 | * @param s a short. | |
111 | * @return this byte vector. | |
112 | */ | |
113 | public ByteVector putShort(final int s){ | |
114 | int length = this.length; | |
115 | if(length + 2 > data.length) | |
116 | { | |
117 | enlarge(2); | |
118 | } | |
119 | byte[] data = this.data; | |
120 | data[length++] = (byte) (s >>> 8); | |
121 | data[length++] = (byte) s; | |
122 | this.length = length; | |
123 | return this; | |
124 | } | |
125 | ||
126 | /** | |
127 | * Puts a byte and a short into this byte vector. The byte vector is | |
128 | * automatically enlarged if necessary. | |
129 | * | |
130 | * @param b a byte. | |
131 | * @param s a short. | |
132 | * @return this byte vector. | |
133 | */ | |
134 | ByteVector put12(final int b, final int s){ | |
135 | int length = this.length; | |
136 | if(length + 3 > data.length) | |
137 | { | |
138 | enlarge(3); | |
139 | } | |
140 | byte[] data = this.data; | |
141 | data[length++] = (byte) b; | |
142 | data[length++] = (byte) (s >>> 8); | |
143 | data[length++] = (byte) s; | |
144 | this.length = length; | |
145 | return this; | |
146 | } | |
147 | ||
148 | /** | |
149 | * Puts an int into this byte vector. The byte vector is automatically | |
150 | * enlarged if necessary. | |
151 | * | |
152 | * @param i an int. | |
153 | * @return this byte vector. | |
154 | */ | |
155 | public ByteVector putInt(final int i){ | |
156 | int length = this.length; | |
157 | if(length + 4 > data.length) | |
158 | { | |
159 | enlarge(4); | |
160 | } | |
161 | byte[] data = this.data; | |
162 | data[length++] = (byte) (i >>> 24); | |
163 | data[length++] = (byte) (i >>> 16); | |
164 | data[length++] = (byte) (i >>> 8); | |
165 | data[length++] = (byte) i; | |
166 | this.length = length; | |
167 | return this; | |
168 | } | |
169 | ||
170 | /** | |
171 | * Puts a long into this byte vector. The byte vector is automatically | |
172 | * enlarged if necessary. | |
173 | * | |
174 | * @param l a long. | |
175 | * @return this byte vector. | |
176 | */ | |
177 | public ByteVector putLong(final long l){ | |
178 | int length = this.length; | |
179 | if(length + 8 > data.length) | |
180 | { | |
181 | enlarge(8); | |
182 | } | |
183 | byte[] data = this.data; | |
184 | int i = (int) (l >>> 32); | |
185 | data[length++] = (byte) (i >>> 24); | |
186 | data[length++] = (byte) (i >>> 16); | |
187 | data[length++] = (byte) (i >>> 8); | |
188 | data[length++] = (byte) i; | |
189 | i = (int) l; | |
190 | data[length++] = (byte) (i >>> 24); | |
191 | data[length++] = (byte) (i >>> 16); | |
192 | data[length++] = (byte) (i >>> 8); | |
193 | data[length++] = (byte) i; | |
194 | this.length = length; | |
195 | return this; | |
196 | } | |
197 | ||
198 | /** | |
199 | * Puts an UTF8 string into this byte vector. The byte vector is | |
200 | * automatically enlarged if necessary. | |
201 | * | |
202 | * @param s a String. | |
203 | * @return this byte vector. | |
204 | */ | |
205 | public ByteVector putUTF8(final String s){ | |
206 | int charLength = s.length(); | |
207 | if(length + 2 + charLength > data.length) | |
208 | { | |
209 | enlarge(2 + charLength); | |
210 | } | |
211 | int len = length; | |
212 | byte[] data = this.data; | |
213 | // optimistic algorithm: instead of computing the byte length and then | |
214 | // serializing the string (which requires two loops), we assume the byte | |
215 | // length is equal to char length (which is the most frequent case), and | |
216 | // we start serializing the string right away. During the serialization, | |
217 | // if we find that this assumption is wrong, we continue with the | |
218 | // general method. | |
219 | data[len++] = (byte) (charLength >>> 8); | |
220 | data[len++] = (byte) charLength; | |
221 | for(int i = 0; i < charLength; ++i) | |
222 | { | |
223 | char c = s.charAt(i); | |
224 | if(c >= '\001' && c <= '\177') | |
225 | { | |
226 | data[len++] = (byte) c; | |
227 | } | |
228 | else | |
229 | { | |
230 | int byteLength = i; | |
231 | for(int j = i; j < charLength; ++j) | |
232 | { | |
233 | c = s.charAt(j); | |
234 | if(c >= '\001' && c <= '\177') | |
235 | { | |
236 | byteLength++; | |
237 | } | |
238 | else if(c > '\u07FF') | |
239 | { | |
240 | byteLength += 3; | |
241 | } | |
242 | else | |
243 | { | |
244 | byteLength += 2; | |
245 | } | |
246 | } | |
247 | data[length] = (byte) (byteLength >>> 8); | |
248 | data[length + 1] = (byte) byteLength; | |
249 | if(length + 2 + byteLength > data.length) | |
250 | { | |
251 | length = len; | |
252 | enlarge(2 + byteLength); | |
253 | data = this.data; | |
254 | } | |
255 | for(int j = i; j < charLength; ++j) | |
256 | { | |
257 | c = s.charAt(j); | |
258 | if(c >= '\001' && c <= '\177') | |
259 | { | |
260 | data[len++] = (byte) c; | |
261 | } | |
262 | else if(c > '\u07FF') | |
263 | { | |
264 | data[len++] = (byte) (0xE0 | c >> 12 & 0xF); | |
265 | data[len++] = (byte) (0x80 | c >> 6 & 0x3F); | |
266 | data[len++] = (byte) (0x80 | c & 0x3F); | |
267 | } | |
268 | else | |
269 | { | |
270 | data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); | |
271 | data[len++] = (byte) (0x80 | c & 0x3F); | |
272 | } | |
273 | } | |
274 | break; | |
275 | } | |
276 | } | |
277 | length = len; | |
278 | return this; | |
279 | } | |
280 | ||
281 | /** | |
282 | * Puts an array of bytes into this byte vector. The byte vector is | |
283 | * automatically enlarged if necessary. | |
284 | * | |
285 | * @param b an array of bytes. May be <tt>null</tt> to put <tt>len</tt> | |
286 | * null bytes into this byte vector. | |
287 | * @param off index of the fist byte of b that must be copied. | |
288 | * @param len number of bytes of b that must be copied. | |
289 | * @return this byte vector. | |
290 | */ | |
291 | public ByteVector putByteArray(final byte[] b, final int off, final int len){ | |
292 | if(length + len > data.length) | |
293 | { | |
294 | enlarge(len); | |
295 | } | |
296 | if(b != null) | |
297 | { | |
298 | System.arraycopy(b, off, data, length, len); | |
299 | } | |
300 | length += len; | |
301 | return this; | |
302 | } | |
303 | ||
304 | /** | |
305 | * Enlarge this byte vector so that it can receive n more bytes. | |
306 | * | |
307 | * @param size number of additional bytes that this byte vector should be | |
308 | * able to receive. | |
309 | */ | |
310 | private void enlarge(final int size){ | |
311 | int length1 = 2 * data.length; | |
312 | int length2 = length + size; | |
313 | byte[] newData = new byte[length1 > length2 ? length1 : length2]; | |
314 | System.arraycopy(data, 0, newData, 0, length); | |
315 | data = newData; | |
316 | } | |
317 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * An empty {@link ClassVisitor} that delegates to another {@link ClassVisitor}. | |
33 | * This class can be used as a super class to quickly implement usefull class | |
34 | * adapter classes, just by overriding the necessary methods. | |
35 | * | |
36 | * @author Eric Bruneton | |
37 | */ | |
38 | public class ClassAdapter implements ClassVisitor{ | |
39 | ||
40 | /** | |
41 | * The {@link ClassVisitor} to which this adapter delegates calls. | |
42 | */ | |
43 | protected ClassVisitor cv; | |
44 | ||
45 | /** | |
46 | * Constructs a new {@link ClassAdapter} object. | |
47 | * | |
48 | * @param cv the class visitor to which this adapter must delegate calls. | |
49 | */ | |
50 | public ClassAdapter(final ClassVisitor cv){ | |
51 | this.cv = cv; | |
52 | } | |
53 | ||
54 | public void visit( | |
55 | final int version, | |
56 | final int access, | |
57 | final String name, | |
58 | final String signature, | |
59 | final String superName, | |
60 | final String[] interfaces){ | |
61 | cv.visit(version, access, name, signature, superName, interfaces); | |
62 | } | |
63 | ||
64 | public void visitSource(final String source, final String debug){ | |
65 | cv.visitSource(source, debug); | |
66 | } | |
67 | ||
68 | public void visitOuterClass( | |
69 | final String owner, | |
70 | final String name, | |
71 | final String desc){ | |
72 | cv.visitOuterClass(owner, name, desc); | |
73 | } | |
74 | ||
75 | public AnnotationVisitor visitAnnotation( | |
76 | final String desc, | |
77 | final boolean visible){ | |
78 | return cv.visitAnnotation(desc, visible); | |
79 | } | |
80 | ||
81 | public void visitAttribute(final Attribute attr){ | |
82 | cv.visitAttribute(attr); | |
83 | } | |
84 | ||
85 | public void visitInnerClass( | |
86 | final String name, | |
87 | final String outerName, | |
88 | final String innerName, | |
89 | final int access){ | |
90 | cv.visitInnerClass(name, outerName, innerName, access); | |
91 | } | |
92 | ||
93 | public FieldVisitor visitField( | |
94 | final int access, | |
95 | final String name, | |
96 | final String desc, | |
97 | final String signature, | |
98 | final Object value){ | |
99 | return cv.visitField(access, name, desc, signature, value); | |
100 | } | |
101 | ||
102 | public MethodVisitor visitMethod( | |
103 | final int access, | |
104 | final String name, | |
105 | final String desc, | |
106 | final String signature, | |
107 | final String[] exceptions){ | |
108 | return cv.visitMethod(access, name, desc, signature, exceptions); | |
109 | } | |
110 | ||
111 | public void visitEnd(){ | |
112 | cv.visitEnd(); | |
113 | } | |
114 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | import java.io.InputStream; | |
32 | import java.io.IOException; | |
33 | ||
34 | /** | |
35 | * A Java class parser to make a {@link ClassVisitor} visit an existing class. | |
36 | * This class parses a byte array conforming to the Java class file format and | |
37 | * calls the appropriate visit methods of a given class visitor for each field, | |
38 | * method and bytecode instruction encountered. | |
39 | * | |
40 | * @author Eric Bruneton | |
41 | * @author Eugene Kuleshov | |
42 | */ | |
43 | public class ClassReader{ | |
44 | ||
45 | /** | |
46 | * Flag to skip method code. If this class is set <code>CODE</code> | |
47 | * attribute won't be visited. This can be used, for example, to retrieve | |
48 | * annotations for methods and method parameters. | |
49 | */ | |
50 | public final static int SKIP_CODE = 1; | |
51 | ||
52 | /** | |
53 | * Flag to skip the debug information in the class. If this flag is set the | |
54 | * debug information of the class is not visited, i.e. the | |
55 | * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and | |
56 | * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be | |
57 | * called. | |
58 | */ | |
59 | public final static int SKIP_DEBUG = 2; | |
60 | ||
61 | /** | |
62 | * Flag to skip the stack map frames in the class. If this flag is set the | |
63 | * stack map frames of the class is not visited, i.e. the | |
64 | * {@link MethodVisitor#visitFrame visitFrame} method will not be called. | |
65 | * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is | |
66 | * used: it avoids visiting frames that will be ignored and recomputed from | |
67 | * scratch in the class writer. | |
68 | */ | |
69 | public final static int SKIP_FRAMES = 4; | |
70 | ||
71 | /** | |
72 | * Flag to expand the stack map frames. By default stack map frames are | |
73 | * visited in their original format (i.e. "expanded" for classes whose | |
74 | * version is less than V1_6, and "compressed" for the other classes). If | |
75 | * this flag is set, stack map frames are always visited in expanded format | |
76 | * (this option adds a decompression/recompression step in ClassReader and | |
77 | * ClassWriter which degrades performances quite a lot). | |
78 | */ | |
79 | public final static int EXPAND_FRAMES = 8; | |
80 | ||
81 | /** | |
82 | * The class to be parsed. <i>The content of this array must not be | |
83 | * modified. This field is intended for {@link Attribute} sub classes, and | |
84 | * is normally not needed by class generators or adapters.</i> | |
85 | */ | |
86 | public final byte[] b; | |
87 | ||
88 | /** | |
89 | * The start index of each constant pool item in {@link #b b}, plus one. | |
90 | * The one byte offset skips the constant pool item tag that indicates its | |
91 | * type. | |
92 | */ | |
93 | private final int[] items; | |
94 | ||
95 | /** | |
96 | * The String objects corresponding to the CONSTANT_Utf8 items. This cache | |
97 | * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, | |
98 | * which GREATLY improves performances (by a factor 2 to 3). This caching | |
99 | * strategy could be extended to all constant pool items, but its benefit | |
100 | * would not be so great for these items (because they are much less | |
101 | * expensive to parse than CONSTANT_Utf8 items). | |
102 | */ | |
103 | private final String[] strings; | |
104 | ||
105 | /** | |
106 | * Maximum length of the strings contained in the constant pool of the | |
107 | * class. | |
108 | */ | |
109 | private final int maxStringLength; | |
110 | ||
111 | /** | |
112 | * Start index of the class header information (access, name...) in | |
113 | * {@link #b b}. | |
114 | */ | |
115 | public final int header; | |
116 | ||
117 | // ------------------------------------------------------------------------ | |
118 | // Constructors | |
119 | // ------------------------------------------------------------------------ | |
120 | ||
121 | /** | |
122 | * Constructs a new {@link ClassReader} object. | |
123 | * | |
124 | * @param b the bytecode of the class to be read. | |
125 | */ | |
126 | public ClassReader(final byte[] b){ | |
127 | this(b, 0, b.length); | |
128 | } | |
129 | ||
130 | /** | |
131 | * Constructs a new {@link ClassReader} object. | |
132 | * | |
133 | * @param b the bytecode of the class to be read. | |
134 | * @param off the start offset of the class data. | |
135 | * @param len the length of the class data. | |
136 | */ | |
137 | public ClassReader(final byte[] b, final int off, final int len){ | |
138 | this.b = b; | |
139 | // parses the constant pool | |
140 | items = new int[readUnsignedShort(off + 8)]; | |
141 | int n = items.length; | |
142 | strings = new String[n]; | |
143 | int max = 0; | |
144 | int index = off + 10; | |
145 | for(int i = 1; i < n; ++i) | |
146 | { | |
147 | items[i] = index + 1; | |
148 | int size; | |
149 | switch(b[index]) | |
150 | { | |
151 | case ClassWriter.FIELD: | |
152 | case ClassWriter.METH: | |
153 | case ClassWriter.IMETH: | |
154 | case ClassWriter.INT: | |
155 | case ClassWriter.FLOAT: | |
156 | case ClassWriter.NAME_TYPE: | |
157 | size = 5; | |
158 | break; | |
159 | case ClassWriter.LONG: | |
160 | case ClassWriter.DOUBLE: | |
161 | size = 9; | |
162 | ++i; | |
163 | break; | |
164 | case ClassWriter.UTF8: | |
165 | size = 3 + readUnsignedShort(index + 1); | |
166 | if(size > max) | |
167 | { | |
168 | max = size; | |
169 | } | |
170 | break; | |
171 | // case ClassWriter.CLASS: | |
172 | // case ClassWriter.STR: | |
173 | default: | |
174 | size = 3; | |
175 | break; | |
176 | } | |
177 | index += size; | |
178 | } | |
179 | maxStringLength = max; | |
180 | // the class header information starts just after the constant pool | |
181 | header = index; | |
182 | } | |
183 | ||
184 | /** | |
185 | * Returns the class's access flags (see {@link Opcodes}). This value may | |
186 | * not reflect Deprecated and Synthetic flags when bytecode is before 1.5 | |
187 | * and those flags are represented by attributes. | |
188 | * | |
189 | * @return the class access flags | |
190 | * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
191 | */ | |
192 | public int getAccess(){ | |
193 | return readUnsignedShort(header); | |
194 | } | |
195 | ||
196 | /** | |
197 | * Returns the internal name of the class (see | |
198 | * {@link Type#getInternalName() getInternalName}). | |
199 | * | |
200 | * @return the internal class name | |
201 | * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
202 | */ | |
203 | public String getClassName(){ | |
204 | return readClass(header + 2, new char[maxStringLength]); | |
205 | } | |
206 | ||
207 | /** | |
208 | * Returns the internal of name of the super class (see | |
209 | * {@link Type#getInternalName() getInternalName}). For interfaces, the | |
210 | * super class is {@link Object}. | |
211 | * | |
212 | * @return the internal name of super class, or <tt>null</tt> for | |
213 | * {@link Object} class. | |
214 | * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
215 | */ | |
216 | public String getSuperName(){ | |
217 | int n = items[readUnsignedShort(header + 4)]; | |
218 | return n == 0 ? null : readUTF8(n, new char[maxStringLength]); | |
219 | } | |
220 | ||
221 | /** | |
222 | * Returns the internal names of the class's interfaces (see | |
223 | * {@link Type#getInternalName() getInternalName}). | |
224 | * | |
225 | * @return the array of internal names for all implemented interfaces or | |
226 | * <tt>null</tt>. | |
227 | * @see ClassVisitor#visit(int,int,String,String,String,String[]) | |
228 | */ | |
229 | public String[] getInterfaces(){ | |
230 | int index = header + 6; | |
231 | int n = readUnsignedShort(index); | |
232 | String[] interfaces = new String[n]; | |
233 | if(n > 0) | |
234 | { | |
235 | char[] buf = new char[maxStringLength]; | |
236 | for(int i = 0; i < n; ++i) | |
237 | { | |
238 | index += 2; | |
239 | interfaces[i] = readClass(index, buf); | |
240 | } | |
241 | } | |
242 | return interfaces; | |
243 | } | |
244 | ||
245 | /** | |
246 | * Copies the constant pool data into the given {@link ClassWriter}. Should | |
247 | * be called before the {@link #accept(ClassVisitor,int)} method. | |
248 | * | |
249 | * @param classWriter the {@link ClassWriter} to copy constant pool into. | |
250 | */ | |
251 | void copyPool(final ClassWriter classWriter){ | |
252 | char[] buf = new char[maxStringLength]; | |
253 | int ll = items.length; | |
254 | Item[] items2 = new Item[ll]; | |
255 | for(int i = 1; i < ll; i++) | |
256 | { | |
257 | int index = items[i]; | |
258 | int tag = b[index - 1]; | |
259 | Item item = new Item(i); | |
260 | int nameType; | |
261 | switch(tag) | |
262 | { | |
263 | case ClassWriter.FIELD: | |
264 | case ClassWriter.METH: | |
265 | case ClassWriter.IMETH: | |
266 | nameType = items[readUnsignedShort(index + 2)]; | |
267 | item.set(tag, | |
268 | readClass(index, buf), | |
269 | readUTF8(nameType, buf), | |
270 | readUTF8(nameType + 2, buf)); | |
271 | break; | |
272 | ||
273 | case ClassWriter.INT: | |
274 | item.set(readInt(index)); | |
275 | break; | |
276 | ||
277 | case ClassWriter.FLOAT: | |
278 | item.set(Float.intBitsToFloat(readInt(index))); | |
279 | break; | |
280 | ||
281 | case ClassWriter.NAME_TYPE: | |
282 | item.set(tag, | |
283 | readUTF8(index, buf), | |
284 | readUTF8(index + 2, buf), | |
285 | null); | |
286 | break; | |
287 | ||
288 | case ClassWriter.LONG: | |
289 | item.set(readLong(index)); | |
290 | ++i; | |
291 | break; | |
292 | ||
293 | case ClassWriter.DOUBLE: | |
294 | item.set(Double.longBitsToDouble(readLong(index))); | |
295 | ++i; | |
296 | break; | |
297 | ||
298 | case ClassWriter.UTF8: | |
299 | { | |
300 | String s = strings[i]; | |
301 | if(s == null) | |
302 | { | |
303 | index = items[i]; | |
304 | s = strings[i] = readUTF(index + 2, | |
305 | readUnsignedShort(index), | |
306 | buf); | |
307 | } | |
308 | item.set(tag, s, null, null); | |
309 | } | |
310 | break; | |
311 | ||
312 | // case ClassWriter.STR: | |
313 | // case ClassWriter.CLASS: | |
314 | default: | |
315 | item.set(tag, readUTF8(index, buf), null, null); | |
316 | break; | |
317 | } | |
318 | ||
319 | int index2 = item.hashCode % items2.length; | |
320 | item.next = items2[index2]; | |
321 | items2[index2] = item; | |
322 | } | |
323 | ||
324 | int off = items[1] - 1; | |
325 | classWriter.pool.putByteArray(b, off, header - off); | |
326 | classWriter.items = items2; | |
327 | classWriter.threshold = (int) (0.75d * ll); | |
328 | classWriter.index = ll; | |
329 | } | |
330 | ||
331 | /** | |
332 | * Constructs a new {@link ClassReader} object. | |
333 | * | |
334 | * @param is an input stream from which to read the class. | |
335 | * @throws IOException if a problem occurs during reading. | |
336 | */ | |
337 | public ClassReader(final InputStream is) throws IOException{ | |
338 | this(readClass(is)); | |
339 | } | |
340 | ||
341 | /** | |
342 | * Constructs a new {@link ClassReader} object. | |
343 | * | |
344 | * @param name the fully qualified name of the class to be read. | |
345 | * @throws IOException if an exception occurs during reading. | |
346 | */ | |
347 | public ClassReader(final String name) throws IOException{ | |
348 | this(ClassLoader.getSystemResourceAsStream(name.replace('.', '/') | |
349 | + ".class")); | |
350 | } | |
351 | ||
352 | /** | |
353 | * Reads the bytecode of a class. | |
354 | * | |
355 | * @param is an input stream from which to read the class. | |
356 | * @return the bytecode read from the given input stream. | |
357 | * @throws IOException if a problem occurs during reading. | |
358 | */ | |
359 | private static byte[] readClass(final InputStream is) throws IOException{ | |
360 | if(is == null) | |
361 | { | |
362 | throw new IOException("Class not found"); | |
363 | } | |
364 | byte[] b = new byte[is.available()]; | |
365 | int len = 0; | |
366 | while(true) | |
367 | { | |
368 | int n = is.read(b, len, b.length - len); | |
369 | if(n == -1) | |
370 | { | |
371 | if(len < b.length) | |
372 | { | |
373 | byte[] c = new byte[len]; | |
374 | System.arraycopy(b, 0, c, 0, len); | |
375 | b = c; | |
376 | } | |
377 | return b; | |
378 | } | |
379 | len += n; | |
380 | if(len == b.length) | |
381 | { | |
382 | byte[] c = new byte[b.length + 1000]; | |
383 | System.arraycopy(b, 0, c, 0, len); | |
384 | b = c; | |
385 | } | |
386 | } | |
387 | } | |
388 | ||
389 | // ------------------------------------------------------------------------ | |
390 | // Public methods | |
391 | // ------------------------------------------------------------------------ | |
392 | ||
393 | /** | |
394 | * Makes the given visitor visit the Java class of this {@link ClassReader}. | |
395 | * This class is the one specified in the constructor (see | |
396 | * {@link #ClassReader(byte[]) ClassReader}). | |
397 | * | |
398 | * @param classVisitor the visitor that must visit this class. | |
399 | * @param flags option flags that can be used to modify the default behavior | |
400 | * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. | |
401 | */ | |
402 | public void accept(final ClassVisitor classVisitor, final int flags){ | |
403 | accept(classVisitor, new Attribute[0], flags); | |
404 | } | |
405 | ||
406 | /** | |
407 | * Makes the given visitor visit the Java class of this {@link ClassReader}. | |
408 | * This class is the one specified in the constructor (see | |
409 | * {@link #ClassReader(byte[]) ClassReader}). | |
410 | * | |
411 | * @param classVisitor the visitor that must visit this class. | |
412 | * @param attrs prototypes of the attributes that must be parsed during the | |
413 | * visit of the class. Any attribute whose type is not equal to the | |
414 | * type of one the prototypes will not be parsed: its byte array | |
415 | * value will be passed unchanged to the ClassWriter. <i>This may | |
416 | * corrupt it if this value contains references to the constant pool, | |
417 | * or has syntactic or semantic links with a class element that has | |
418 | * been transformed by a class adapter between the reader and the | |
419 | * writer</i>. | |
420 | * @param flags option flags that can be used to modify the default behavior | |
421 | * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES}. | |
422 | */ | |
423 | public void accept( | |
424 | final ClassVisitor classVisitor, | |
425 | final Attribute[] attrs, | |
426 | final int flags){ | |
427 | byte[] b = this.b; // the bytecode array | |
428 | char[] c = new char[maxStringLength]; // buffer used to read strings | |
429 | int i, j, k; // loop variables | |
430 | int u, v, w; // indexes in b | |
431 | Attribute attr; | |
432 | ||
433 | int access; | |
434 | String name; | |
435 | String desc; | |
436 | String attrName; | |
437 | String signature; | |
438 | int anns = 0; | |
439 | int ianns = 0; | |
440 | Attribute cattrs = null; | |
441 | ||
442 | // visits the header | |
443 | u = header; | |
444 | access = readUnsignedShort(u); | |
445 | name = readClass(u + 2, c); | |
446 | v = items[readUnsignedShort(u + 4)]; | |
447 | String superClassName = v == 0 ? null : readUTF8(v, c); | |
448 | String[] implementedItfs = new String[readUnsignedShort(u + 6)]; | |
449 | w = 0; | |
450 | u += 8; | |
451 | for(i = 0; i < implementedItfs.length; ++i) | |
452 | { | |
453 | implementedItfs[i] = readClass(u, c); | |
454 | u += 2; | |
455 | } | |
456 | ||
457 | boolean skipCode = (flags & SKIP_CODE) != 0; | |
458 | boolean skipDebug = (flags & SKIP_DEBUG) != 0; | |
459 | boolean unzip = (flags & EXPAND_FRAMES) != 0; | |
460 | ||
461 | // skips fields and methods | |
462 | v = u; | |
463 | i = readUnsignedShort(v); | |
464 | v += 2; | |
465 | for(; i > 0; --i) | |
466 | { | |
467 | j = readUnsignedShort(v + 6); | |
468 | v += 8; | |
469 | for(; j > 0; --j) | |
470 | { | |
471 | v += 6 + readInt(v + 2); | |
472 | } | |
473 | } | |
474 | i = readUnsignedShort(v); | |
475 | v += 2; | |
476 | for(; i > 0; --i) | |
477 | { | |
478 | j = readUnsignedShort(v + 6); | |
479 | v += 8; | |
480 | for(; j > 0; --j) | |
481 | { | |
482 | v += 6 + readInt(v + 2); | |
483 | } | |
484 | } | |
485 | // reads the class's attributes | |
486 | signature = null; | |
487 | String sourceFile = null; | |
488 | String sourceDebug = null; | |
489 | String enclosingOwner = null; | |
490 | String enclosingName = null; | |
491 | String enclosingDesc = null; | |
492 | ||
493 | i = readUnsignedShort(v); | |
494 | v += 2; | |
495 | for(; i > 0; --i) | |
496 | { | |
497 | attrName = readUTF8(v, c); | |
498 | // tests are sorted in decreasing frequency order | |
499 | // (based on frequencies observed on typical classes) | |
500 | if(attrName.equals("SourceFile")) | |
501 | { | |
502 | sourceFile = readUTF8(v + 6, c); | |
503 | } | |
504 | else if(attrName.equals("InnerClasses")) | |
505 | { | |
506 | w = v + 6; | |
507 | } | |
508 | else if(attrName.equals("EnclosingMethod")) | |
509 | { | |
510 | enclosingOwner = readClass(v + 6, c); | |
511 | int item = readUnsignedShort(v + 8); | |
512 | if(item != 0) | |
513 | { | |
514 | enclosingName = readUTF8(items[item], c); | |
515 | enclosingDesc = readUTF8(items[item] + 2, c); | |
516 | } | |
517 | } | |
518 | else if(attrName.equals("Signature")) | |
519 | { | |
520 | signature = readUTF8(v + 6, c); | |
521 | } | |
522 | else if(attrName.equals("RuntimeVisibleAnnotations")) | |
523 | { | |
524 | anns = v + 6; | |
525 | } | |
526 | else if(attrName.equals("Deprecated")) | |
527 | { | |
528 | access |= Opcodes.ACC_DEPRECATED; | |
529 | } | |
530 | else if(attrName.equals("Synthetic")) | |
531 | { | |
532 | access |= Opcodes.ACC_SYNTHETIC; | |
533 | } | |
534 | else if(attrName.equals("SourceDebugExtension")) | |
535 | { | |
536 | int len = readInt(v + 2); | |
537 | sourceDebug = readUTF(v + 6, len, new char[len]); | |
538 | } | |
539 | else if(attrName.equals("RuntimeInvisibleAnnotations")) | |
540 | { | |
541 | ianns = v + 6; | |
542 | } | |
543 | else | |
544 | { | |
545 | attr = readAttribute(attrs, | |
546 | attrName, | |
547 | v + 6, | |
548 | readInt(v + 2), | |
549 | c, | |
550 | -1, | |
551 | null); | |
552 | if(attr != null) | |
553 | { | |
554 | attr.next = cattrs; | |
555 | cattrs = attr; | |
556 | } | |
557 | } | |
558 | v += 6 + readInt(v + 2); | |
559 | } | |
560 | // calls the visit method | |
561 | classVisitor.visit(readInt(4), | |
562 | access, | |
563 | name, | |
564 | signature, | |
565 | superClassName, | |
566 | implementedItfs); | |
567 | ||
568 | // calls the visitSource method | |
569 | if(!skipDebug && (sourceFile != null || sourceDebug != null)) | |
570 | { | |
571 | classVisitor.visitSource(sourceFile, sourceDebug); | |
572 | } | |
573 | ||
574 | // calls the visitOuterClass method | |
575 | if(enclosingOwner != null) | |
576 | { | |
577 | classVisitor.visitOuterClass(enclosingOwner, | |
578 | enclosingName, | |
579 | enclosingDesc); | |
580 | } | |
581 | ||
582 | // visits the class annotations | |
583 | for(i = 1; i >= 0; --i) | |
584 | { | |
585 | v = i == 0 ? ianns : anns; | |
586 | if(v != 0) | |
587 | { | |
588 | j = readUnsignedShort(v); | |
589 | v += 2; | |
590 | for(; j > 0; --j) | |
591 | { | |
592 | v = readAnnotationValues(v + 2, | |
593 | c, | |
594 | true, | |
595 | classVisitor.visitAnnotation(readUTF8(v, c), i != 0)); | |
596 | } | |
597 | } | |
598 | } | |
599 | ||
600 | // visits the class attributes | |
601 | while(cattrs != null) | |
602 | { | |
603 | attr = cattrs.next; | |
604 | cattrs.next = null; | |
605 | classVisitor.visitAttribute(cattrs); | |
606 | cattrs = attr; | |
607 | } | |
608 | ||
609 | // calls the visitInnerClass method | |
610 | if(w != 0) | |
611 | { | |
612 | i = readUnsignedShort(w); | |
613 | w += 2; | |
614 | for(; i > 0; --i) | |
615 | { | |
616 | classVisitor.visitInnerClass(readUnsignedShort(w) == 0 | |
617 | ? null | |
618 | : readClass(w, c), readUnsignedShort(w + 2) == 0 | |
619 | ? null | |
620 | : readClass(w + 2, c), readUnsignedShort(w + 4) == 0 | |
621 | ? null | |
622 | : readUTF8(w + 4, c), | |
623 | readUnsignedShort(w + 6)); | |
624 | w += 8; | |
625 | } | |
626 | } | |
627 | ||
628 | // visits the fields | |
629 | i = readUnsignedShort(u); | |
630 | u += 2; | |
631 | for(; i > 0; --i) | |
632 | { | |
633 | access = readUnsignedShort(u); | |
634 | name = readUTF8(u + 2, c); | |
635 | desc = readUTF8(u + 4, c); | |
636 | // visits the field's attributes and looks for a ConstantValue | |
637 | // attribute | |
638 | int fieldValueItem = 0; | |
639 | signature = null; | |
640 | anns = 0; | |
641 | ianns = 0; | |
642 | cattrs = null; | |
643 | ||
644 | j = readUnsignedShort(u + 6); | |
645 | u += 8; | |
646 | for(; j > 0; --j) | |
647 | { | |
648 | attrName = readUTF8(u, c); | |
649 | // tests are sorted in decreasing frequency order | |
650 | // (based on frequencies observed on typical classes) | |
651 | if(attrName.equals("ConstantValue")) | |
652 | { | |
653 | fieldValueItem = readUnsignedShort(u + 6); | |
654 | } | |
655 | else if(attrName.equals("Signature")) | |
656 | { | |
657 | signature = readUTF8(u + 6, c); | |
658 | } | |
659 | else if(attrName.equals("Deprecated")) | |
660 | { | |
661 | access |= Opcodes.ACC_DEPRECATED; | |
662 | } | |
663 | else if(attrName.equals("Synthetic")) | |
664 | { | |
665 | access |= Opcodes.ACC_SYNTHETIC; | |
666 | } | |
667 | else if(attrName.equals("RuntimeVisibleAnnotations")) | |
668 | { | |
669 | anns = u + 6; | |
670 | } | |
671 | else if(attrName.equals("RuntimeInvisibleAnnotations")) | |
672 | { | |
673 | ianns = u + 6; | |
674 | } | |
675 | else | |
676 | { | |
677 | attr = readAttribute(attrs, | |
678 | attrName, | |
679 | u + 6, | |
680 | readInt(u + 2), | |
681 | c, | |
682 | -1, | |
683 | null); | |
684 | if(attr != null) | |
685 | { | |
686 | attr.next = cattrs; | |
687 | cattrs = attr; | |
688 | } | |
689 | } | |
690 | u += 6 + readInt(u + 2); | |
691 | } | |
692 | // visits the field | |
693 | FieldVisitor fv = classVisitor.visitField(access, | |
694 | name, | |
695 | desc, | |
696 | signature, | |
697 | fieldValueItem == 0 ? null : readConst(fieldValueItem, c)); | |
698 | // visits the field annotations and attributes | |
699 | if(fv != null) | |
700 | { | |
701 | for(j = 1; j >= 0; --j) | |
702 | { | |
703 | v = j == 0 ? ianns : anns; | |
704 | if(v != 0) | |
705 | { | |
706 | k = readUnsignedShort(v); | |
707 | v += 2; | |
708 | for(; k > 0; --k) | |
709 | { | |
710 | v = readAnnotationValues(v + 2, | |
711 | c, | |
712 | true, | |
713 | fv.visitAnnotation(readUTF8(v, c), j != 0)); | |
714 | } | |
715 | } | |
716 | } | |
717 | while(cattrs != null) | |
718 | { | |
719 | attr = cattrs.next; | |
720 | cattrs.next = null; | |
721 | fv.visitAttribute(cattrs); | |
722 | cattrs = attr; | |
723 | } | |
724 | fv.visitEnd(); | |
725 | } | |
726 | } | |
727 | ||
728 | // visits the methods | |
729 | i = readUnsignedShort(u); | |
730 | u += 2; | |
731 | for(; i > 0; --i) | |
732 | { | |
733 | int u0 = u + 6; | |
734 | access = readUnsignedShort(u); | |
735 | name = readUTF8(u + 2, c); | |
736 | desc = readUTF8(u + 4, c); | |
737 | signature = null; | |
738 | anns = 0; | |
739 | ianns = 0; | |
740 | int dann = 0; | |
741 | int mpanns = 0; | |
742 | int impanns = 0; | |
743 | cattrs = null; | |
744 | v = 0; | |
745 | w = 0; | |
746 | ||
747 | // looks for Code and Exceptions attributes | |
748 | j = readUnsignedShort(u + 6); | |
749 | u += 8; | |
750 | for(; j > 0; --j) | |
751 | { | |
752 | attrName = readUTF8(u, c); | |
753 | int attrSize = readInt(u + 2); | |
754 | u += 6; | |
755 | // tests are sorted in decreasing frequency order | |
756 | // (based on frequencies observed on typical classes) | |
757 | if(attrName.equals("Code")) | |
758 | { | |
759 | if(!skipCode) | |
760 | { | |
761 | v = u; | |
762 | } | |
763 | } | |
764 | else if(attrName.equals("Exceptions")) | |
765 | { | |
766 | w = u; | |
767 | } | |
768 | else if(attrName.equals("Signature")) | |
769 | { | |
770 | signature = readUTF8(u, c); | |
771 | } | |
772 | else if(attrName.equals("Deprecated")) | |
773 | { | |
774 | access |= Opcodes.ACC_DEPRECATED; | |
775 | } | |
776 | else if(attrName.equals("RuntimeVisibleAnnotations")) | |
777 | { | |
778 | anns = u; | |
779 | } | |
780 | else if(attrName.equals("AnnotationDefault")) | |
781 | { | |
782 | dann = u; | |
783 | } | |
784 | else if(attrName.equals("Synthetic")) | |
785 | { | |
786 | access |= Opcodes.ACC_SYNTHETIC; | |
787 | } | |
788 | else if(attrName.equals("RuntimeInvisibleAnnotations")) | |
789 | { | |
790 | ianns = u; | |
791 | } | |
792 | else if(attrName.equals("RuntimeVisibleParameterAnnotations")) | |
793 | { | |
794 | mpanns = u; | |
795 | } | |
796 | else if(attrName.equals("RuntimeInvisibleParameterAnnotations")) | |
797 | { | |
798 | impanns = u; | |
799 | } | |
800 | else | |
801 | { | |
802 | attr = readAttribute(attrs, | |
803 | attrName, | |
804 | u, | |
805 | attrSize, | |
806 | c, | |
807 | -1, | |
808 | null); | |
809 | if(attr != null) | |
810 | { | |
811 | attr.next = cattrs; | |
812 | cattrs = attr; | |
813 | } | |
814 | } | |
815 | u += attrSize; | |
816 | } | |
817 | // reads declared exceptions | |
818 | String[] exceptions; | |
819 | if(w == 0) | |
820 | { | |
821 | exceptions = null; | |
822 | } | |
823 | else | |
824 | { | |
825 | exceptions = new String[readUnsignedShort(w)]; | |
826 | w += 2; | |
827 | for(j = 0; j < exceptions.length; ++j) | |
828 | { | |
829 | exceptions[j] = readClass(w, c); | |
830 | w += 2; | |
831 | } | |
832 | } | |
833 | ||
834 | // visits the method's code, if any | |
835 | MethodVisitor mv = classVisitor.visitMethod(access, | |
836 | name, | |
837 | desc, | |
838 | signature, | |
839 | exceptions); | |
840 | ||
841 | if(mv != null) | |
842 | { | |
843 | /* | |
844 | * if the returned MethodVisitor is in fact a MethodWriter, it | |
845 | * means there is no method adapter between the reader and the | |
846 | * writer. If, in addition, the writer's constant pool was | |
847 | * copied from this reader (mw.cw.cr == this), and the signature | |
848 | * and exceptions of the method have not been changed, then it | |
849 | * is possible to skip all visit events and just copy the | |
850 | * original code of the method to the writer (the access, name | |
851 | * and descriptor can have been changed, this is not important | |
852 | * since they are not copied as is from the reader). | |
853 | */ | |
854 | if(mv instanceof MethodWriter) | |
855 | { | |
856 | MethodWriter mw = (MethodWriter) mv; | |
857 | if(mw.cw.cr == this) | |
858 | { | |
859 | if(signature == mw.signature) | |
860 | { | |
861 | boolean sameExceptions = false; | |
862 | if(exceptions == null) | |
863 | { | |
864 | sameExceptions = mw.exceptionCount == 0; | |
865 | } | |
866 | else | |
867 | { | |
868 | if(exceptions.length == mw.exceptionCount) | |
869 | { | |
870 | sameExceptions = true; | |
871 | for(j = exceptions.length - 1; j >= 0; --j) | |
872 | { | |
873 | w -= 2; | |
874 | if(mw.exceptions[j] != readUnsignedShort(w)) | |
875 | { | |
876 | sameExceptions = false; | |
877 | break; | |
878 | } | |
879 | } | |
880 | } | |
881 | } | |
882 | if(sameExceptions) | |
883 | { | |
884 | /* | |
885 | * we do not copy directly the code into | |
886 | * MethodWriter to save a byte array copy | |
887 | * operation. The real copy will be done in | |
888 | * ClassWriter.toByteArray(). | |
889 | */ | |
890 | mw.classReaderOffset = u0; | |
891 | mw.classReaderLength = u - u0; | |
892 | continue; | |
893 | } | |
894 | } | |
895 | } | |
896 | } | |
897 | ||
898 | if(dann != 0) | |
899 | { | |
900 | AnnotationVisitor dv = mv.visitAnnotationDefault(); | |
901 | readAnnotationValue(dann, c, null, dv); | |
902 | if(dv != null) | |
903 | { | |
904 | dv.visitEnd(); | |
905 | } | |
906 | } | |
907 | for(j = 1; j >= 0; --j) | |
908 | { | |
909 | w = j == 0 ? ianns : anns; | |
910 | if(w != 0) | |
911 | { | |
912 | k = readUnsignedShort(w); | |
913 | w += 2; | |
914 | for(; k > 0; --k) | |
915 | { | |
916 | w = readAnnotationValues(w + 2, | |
917 | c, | |
918 | true, | |
919 | mv.visitAnnotation(readUTF8(w, c), j != 0)); | |
920 | } | |
921 | } | |
922 | } | |
923 | if(mpanns != 0) | |
924 | { | |
925 | readParameterAnnotations(mpanns, c, true, mv); | |
926 | } | |
927 | if(impanns != 0) | |
928 | { | |
929 | readParameterAnnotations(impanns, c, false, mv); | |
930 | } | |
931 | while(cattrs != null) | |
932 | { | |
933 | attr = cattrs.next; | |
934 | cattrs.next = null; | |
935 | mv.visitAttribute(cattrs); | |
936 | cattrs = attr; | |
937 | } | |
938 | } | |
939 | ||
940 | if(mv != null && v != 0) | |
941 | { | |
942 | int maxStack = readUnsignedShort(v); | |
943 | int maxLocals = readUnsignedShort(v + 2); | |
944 | int codeLength = readInt(v + 4); | |
945 | v += 8; | |
946 | ||
947 | int codeStart = v; | |
948 | int codeEnd = v + codeLength; | |
949 | ||
950 | mv.visitCode(); | |
951 | ||
952 | // 1st phase: finds the labels | |
953 | int label; | |
954 | Label[] labels = new Label[codeLength + 1]; | |
955 | while(v < codeEnd) | |
956 | { | |
957 | int opcode = b[v] & 0xFF; | |
958 | switch(ClassWriter.TYPE[opcode]) | |
959 | { | |
960 | case ClassWriter.NOARG_INSN: | |
961 | case ClassWriter.IMPLVAR_INSN: | |
962 | v += 1; | |
963 | break; | |
964 | case ClassWriter.LABEL_INSN: | |
965 | label = v - codeStart + readShort(v + 1); | |
966 | if(labels[label] == null) | |
967 | { | |
968 | labels[label] = new Label(); | |
969 | } | |
970 | v += 3; | |
971 | break; | |
972 | case ClassWriter.LABELW_INSN: | |
973 | label = v - codeStart + readInt(v + 1); | |
974 | if(labels[label] == null) | |
975 | { | |
976 | labels[label] = new Label(); | |
977 | } | |
978 | v += 5; | |
979 | break; | |
980 | case ClassWriter.WIDE_INSN: | |
981 | opcode = b[v + 1] & 0xFF; | |
982 | if(opcode == Opcodes.IINC) | |
983 | { | |
984 | v += 6; | |
985 | } | |
986 | else | |
987 | { | |
988 | v += 4; | |
989 | } | |
990 | break; | |
991 | case ClassWriter.TABL_INSN: | |
992 | // skips 0 to 3 padding bytes | |
993 | w = v - codeStart; | |
994 | v = v + 4 - (w & 3); | |
995 | // reads instruction | |
996 | label = w + readInt(v); | |
997 | if(labels[label] == null) | |
998 | { | |
999 | labels[label] = new Label(); | |
1000 | } | |
1001 | j = readInt(v + 8) - readInt(v + 4) + 1; | |
1002 | v += 12; | |
1003 | for(; j > 0; --j) | |
1004 | { | |
1005 | label = w + readInt(v); | |
1006 | v += 4; | |
1007 | if(labels[label] == null) | |
1008 | { | |
1009 | labels[label] = new Label(); | |
1010 | } | |
1011 | } | |
1012 | break; | |
1013 | case ClassWriter.LOOK_INSN: | |
1014 | // skips 0 to 3 padding bytes | |
1015 | w = v - codeStart; | |
1016 | v = v + 4 - (w & 3); | |
1017 | // reads instruction | |
1018 | label = w + readInt(v); | |
1019 | if(labels[label] == null) | |
1020 | { | |
1021 | labels[label] = new Label(); | |
1022 | } | |
1023 | j = readInt(v + 4); | |
1024 | v += 8; | |
1025 | for(; j > 0; --j) | |
1026 | { | |
1027 | label = w + readInt(v + 4); | |
1028 | v += 8; | |
1029 | if(labels[label] == null) | |
1030 | { | |
1031 | labels[label] = new Label(); | |
1032 | } | |
1033 | } | |
1034 | break; | |
1035 | case ClassWriter.VAR_INSN: | |
1036 | case ClassWriter.SBYTE_INSN: | |
1037 | case ClassWriter.LDC_INSN: | |
1038 | v += 2; | |
1039 | break; | |
1040 | case ClassWriter.SHORT_INSN: | |
1041 | case ClassWriter.LDCW_INSN: | |
1042 | case ClassWriter.FIELDORMETH_INSN: | |
1043 | case ClassWriter.TYPE_INSN: | |
1044 | case ClassWriter.IINC_INSN: | |
1045 | v += 3; | |
1046 | break; | |
1047 | case ClassWriter.ITFMETH_INSN: | |
1048 | v += 5; | |
1049 | break; | |
1050 | // case MANA_INSN: | |
1051 | default: | |
1052 | v += 4; | |
1053 | break; | |
1054 | } | |
1055 | } | |
1056 | // parses the try catch entries | |
1057 | j = readUnsignedShort(v); | |
1058 | v += 2; | |
1059 | for(; j > 0; --j) | |
1060 | { | |
1061 | label = readUnsignedShort(v); | |
1062 | Label start = labels[label]; | |
1063 | if(start == null) | |
1064 | { | |
1065 | labels[label] = start = new Label(); | |
1066 | } | |
1067 | label = readUnsignedShort(v + 2); | |
1068 | Label end = labels[label]; | |
1069 | if(end == null) | |
1070 | { | |
1071 | labels[label] = end = new Label(); | |
1072 | } | |
1073 | label = readUnsignedShort(v + 4); | |
1074 | Label handler = labels[label]; | |
1075 | if(handler == null) | |
1076 | { | |
1077 | labels[label] = handler = new Label(); | |
1078 | } | |
1079 | int type = readUnsignedShort(v + 6); | |
1080 | if(type == 0) | |
1081 | { | |
1082 | mv.visitTryCatchBlock(start, end, handler, null); | |
1083 | } | |
1084 | else | |
1085 | { | |
1086 | mv.visitTryCatchBlock(start, | |
1087 | end, | |
1088 | handler, | |
1089 | readUTF8(items[type], c)); | |
1090 | } | |
1091 | v += 8; | |
1092 | } | |
1093 | // parses the local variable, line number tables, and code | |
1094 | // attributes | |
1095 | int varTable = 0; | |
1096 | int varTypeTable = 0; | |
1097 | int stackMap = 0; | |
1098 | int frameCount = 0; | |
1099 | int frameMode = 0; | |
1100 | int frameOffset = 0; | |
1101 | int frameLocalCount = 0; | |
1102 | int frameLocalDiff = 0; | |
1103 | int frameStackCount = 0; | |
1104 | Object[] frameLocal = null; | |
1105 | Object[] frameStack = null; | |
1106 | boolean zip = true; | |
1107 | cattrs = null; | |
1108 | j = readUnsignedShort(v); | |
1109 | v += 2; | |
1110 | for(; j > 0; --j) | |
1111 | { | |
1112 | attrName = readUTF8(v, c); | |
1113 | if(attrName.equals("LocalVariableTable")) | |
1114 | { | |
1115 | if(!skipDebug) | |
1116 | { | |
1117 | varTable = v + 6; | |
1118 | k = readUnsignedShort(v + 6); | |
1119 | w = v + 8; | |
1120 | for(; k > 0; --k) | |
1121 | { | |
1122 | label = readUnsignedShort(w); | |
1123 | if(labels[label] == null) | |
1124 | { | |
1125 | labels[label] = new Label(true); | |
1126 | } | |
1127 | label += readUnsignedShort(w + 2); | |
1128 | if(labels[label] == null) | |
1129 | { | |
1130 | labels[label] = new Label(true); | |
1131 | } | |
1132 | w += 10; | |
1133 | } | |
1134 | } | |
1135 | } | |
1136 | else if(attrName.equals("LocalVariableTypeTable")) | |
1137 | { | |
1138 | varTypeTable = v + 6; | |
1139 | } | |
1140 | else if(attrName.equals("LineNumberTable")) | |
1141 | { | |
1142 | if(!skipDebug) | |
1143 | { | |
1144 | k = readUnsignedShort(v + 6); | |
1145 | w = v + 8; | |
1146 | for(; k > 0; --k) | |
1147 | { | |
1148 | label = readUnsignedShort(w); | |
1149 | if(labels[label] == null) | |
1150 | { | |
1151 | labels[label] = new Label(true); | |
1152 | } | |
1153 | labels[label].line = readUnsignedShort(w + 2); | |
1154 | w += 4; | |
1155 | } | |
1156 | } | |
1157 | } | |
1158 | else if(attrName.equals("StackMapTable")) | |
1159 | { | |
1160 | if((flags & SKIP_FRAMES) == 0) | |
1161 | { | |
1162 | stackMap = v + 8; | |
1163 | frameCount = readUnsignedShort(v + 6); | |
1164 | } | |
1165 | /* | |
1166 | * here we do not extract the labels corresponding to | |
1167 | * the attribute content. This would require a full | |
1168 | * parsing of the attribute, which would need to be | |
1169 | * repeated in the second phase (see below). Instead the | |
1170 | * content of the attribute is read one frame at a time | |
1171 | * (i.e. after a frame has been visited, the next frame | |
1172 | * is read), and the labels it contains are also | |
1173 | * extracted one frame at a time. Thanks to the ordering | |
1174 | * of frames, having only a "one frame lookahead" is not | |
1175 | * a problem, i.e. it is not possible to see an offset | |
1176 | * smaller than the offset of the current insn and for | |
1177 | * which no Label exist. | |
1178 | */ | |
1179 | // TODO true for frame offsets, | |
1180 | // but for UNINITIALIZED type offsets? | |
1181 | } | |
1182 | else if(attrName.equals("StackMap")) | |
1183 | { | |
1184 | if((flags & SKIP_FRAMES) == 0) | |
1185 | { | |
1186 | stackMap = v + 8; | |
1187 | frameCount = readUnsignedShort(v + 6); | |
1188 | zip = false; | |
1189 | } | |
1190 | /* | |
1191 | * IMPORTANT! here we assume that the frames are | |
1192 | * ordered, as in the StackMapTable attribute, although | |
1193 | * this is not guaranteed by the attribute format. | |
1194 | */ | |
1195 | } | |
1196 | else | |
1197 | { | |
1198 | for(k = 0; k < attrs.length; ++k) | |
1199 | { | |
1200 | if(attrs[k].type.equals(attrName)) | |
1201 | { | |
1202 | attr = attrs[k].read(this, | |
1203 | v + 6, | |
1204 | readInt(v + 2), | |
1205 | c, | |
1206 | codeStart - 8, | |
1207 | labels); | |
1208 | if(attr != null) | |
1209 | { | |
1210 | attr.next = cattrs; | |
1211 | cattrs = attr; | |
1212 | } | |
1213 | } | |
1214 | } | |
1215 | } | |
1216 | v += 6 + readInt(v + 2); | |
1217 | } | |
1218 | ||
1219 | // 2nd phase: visits each instruction | |
1220 | if(stackMap != 0) | |
1221 | { | |
1222 | // creates the very first (implicit) frame from the method | |
1223 | // descriptor | |
1224 | frameLocal = new Object[maxLocals]; | |
1225 | frameStack = new Object[maxStack]; | |
1226 | if(unzip) | |
1227 | { | |
1228 | int local = 0; | |
1229 | if((access & Opcodes.ACC_STATIC) == 0) | |
1230 | { | |
1231 | if(name.equals("<init>")) | |
1232 | { | |
1233 | frameLocal[local++] = Opcodes.UNINITIALIZED_THIS; | |
1234 | } | |
1235 | else | |
1236 | { | |
1237 | frameLocal[local++] = readClass(header + 2, c); | |
1238 | } | |
1239 | } | |
1240 | j = 1; | |
1241 | loop: | |
1242 | while(true) | |
1243 | { | |
1244 | k = j; | |
1245 | switch(desc.charAt(j++)) | |
1246 | { | |
1247 | case'Z': | |
1248 | case'C': | |
1249 | case'B': | |
1250 | case'S': | |
1251 | case'I': | |
1252 | frameLocal[local++] = Opcodes.INTEGER; | |
1253 | break; | |
1254 | case'F': | |
1255 | frameLocal[local++] = Opcodes.FLOAT; | |
1256 | break; | |
1257 | case'J': | |
1258 | frameLocal[local++] = Opcodes.LONG; | |
1259 | break; | |
1260 | case'D': | |
1261 | frameLocal[local++] = Opcodes.DOUBLE; | |
1262 | break; | |
1263 | case'[': | |
1264 | while(desc.charAt(j) == '[') | |
1265 | { | |
1266 | ++j; | |
1267 | } | |
1268 | if(desc.charAt(j) == 'L') | |
1269 | { | |
1270 | ++j; | |
1271 | while(desc.charAt(j) != ';') | |
1272 | { | |
1273 | ++j; | |
1274 | } | |
1275 | } | |
1276 | frameLocal[local++] = desc.substring(k, ++j); | |
1277 | break; | |
1278 | case'L': | |
1279 | while(desc.charAt(j) != ';') | |
1280 | { | |
1281 | ++j; | |
1282 | } | |
1283 | frameLocal[local++] = desc.substring(k + 1, | |
1284 | j++); | |
1285 | break; | |
1286 | default: | |
1287 | break loop; | |
1288 | } | |
1289 | } | |
1290 | frameLocalCount = local; | |
1291 | } | |
1292 | /* | |
1293 | * for the first explicit frame the offset is not | |
1294 | * offset_delta + 1 but only offset_delta; setting the | |
1295 | * implicit frame offset to -1 allow the use of the | |
1296 | * "offset_delta + 1" rule in all cases | |
1297 | */ | |
1298 | frameOffset = -1; | |
1299 | } | |
1300 | v = codeStart; | |
1301 | Label l; | |
1302 | while(v < codeEnd) | |
1303 | { | |
1304 | w = v - codeStart; | |
1305 | ||
1306 | l = labels[w]; | |
1307 | if(l != null) | |
1308 | { | |
1309 | mv.visitLabel(l); | |
1310 | if(!skipDebug && l.line > 0) | |
1311 | { | |
1312 | mv.visitLineNumber(l.line, l); | |
1313 | } | |
1314 | } | |
1315 | ||
1316 | while(frameLocal != null | |
1317 | && (frameOffset == w || frameOffset == -1)) | |
1318 | { | |
1319 | // if there is a frame for this offset, | |
1320 | // makes the visitor visit it, | |
1321 | // and reads the next frame if there is one. | |
1322 | if(!zip || unzip) | |
1323 | { | |
1324 | mv.visitFrame(Opcodes.F_NEW, | |
1325 | frameLocalCount, | |
1326 | frameLocal, | |
1327 | frameStackCount, | |
1328 | frameStack); | |
1329 | } | |
1330 | else if(frameOffset != -1) | |
1331 | { | |
1332 | mv.visitFrame(frameMode, | |
1333 | frameLocalDiff, | |
1334 | frameLocal, | |
1335 | frameStackCount, | |
1336 | frameStack); | |
1337 | } | |
1338 | ||
1339 | if(frameCount > 0) | |
1340 | { | |
1341 | int tag, delta, n; | |
1342 | if(zip) | |
1343 | { | |
1344 | tag = b[stackMap++] & 0xFF; | |
1345 | } | |
1346 | else | |
1347 | { | |
1348 | tag = MethodWriter.FULL_FRAME; | |
1349 | frameOffset = -1; | |
1350 | } | |
1351 | frameLocalDiff = 0; | |
1352 | if(tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) | |
1353 | { | |
1354 | delta = tag; | |
1355 | frameMode = Opcodes.F_SAME; | |
1356 | frameStackCount = 0; | |
1357 | } | |
1358 | else if(tag < MethodWriter.RESERVED) | |
1359 | { | |
1360 | delta = tag | |
1361 | - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; | |
1362 | stackMap = readFrameType(frameStack, | |
1363 | 0, | |
1364 | stackMap, | |
1365 | c, | |
1366 | labels); | |
1367 | frameMode = Opcodes.F_SAME1; | |
1368 | frameStackCount = 1; | |
1369 | } | |
1370 | else | |
1371 | { | |
1372 | delta = readUnsignedShort(stackMap); | |
1373 | stackMap += 2; | |
1374 | if(tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) | |
1375 | { | |
1376 | stackMap = readFrameType(frameStack, | |
1377 | 0, | |
1378 | stackMap, | |
1379 | c, | |
1380 | labels); | |
1381 | frameMode = Opcodes.F_SAME1; | |
1382 | frameStackCount = 1; | |
1383 | } | |
1384 | else if(tag >= MethodWriter.CHOP_FRAME | |
1385 | && tag < MethodWriter.SAME_FRAME_EXTENDED) | |
1386 | { | |
1387 | frameMode = Opcodes.F_CHOP; | |
1388 | frameLocalDiff = MethodWriter.SAME_FRAME_EXTENDED | |
1389 | - tag; | |
1390 | frameLocalCount -= frameLocalDiff; | |
1391 | frameStackCount = 0; | |
1392 | } | |
1393 | else if(tag == MethodWriter.SAME_FRAME_EXTENDED) | |
1394 | { | |
1395 | frameMode = Opcodes.F_SAME; | |
1396 | frameStackCount = 0; | |
1397 | } | |
1398 | else if(tag < MethodWriter.FULL_FRAME) | |
1399 | { | |
1400 | j = unzip ? frameLocalCount : 0; | |
1401 | for(k = tag | |
1402 | - MethodWriter.SAME_FRAME_EXTENDED; k > 0; k--) | |
1403 | { | |
1404 | stackMap = readFrameType(frameLocal, | |
1405 | j++, | |
1406 | stackMap, | |
1407 | c, | |
1408 | labels); | |
1409 | } | |
1410 | frameMode = Opcodes.F_APPEND; | |
1411 | frameLocalDiff = tag | |
1412 | - MethodWriter.SAME_FRAME_EXTENDED; | |
1413 | frameLocalCount += frameLocalDiff; | |
1414 | frameStackCount = 0; | |
1415 | } | |
1416 | else | |
1417 | { // if (tag == FULL_FRAME) { | |
1418 | frameMode = Opcodes.F_FULL; | |
1419 | n = frameLocalDiff = frameLocalCount = readUnsignedShort(stackMap); | |
1420 | stackMap += 2; | |
1421 | for(j = 0; n > 0; n--) | |
1422 | { | |
1423 | stackMap = readFrameType(frameLocal, | |
1424 | j++, | |
1425 | stackMap, | |
1426 | c, | |
1427 | labels); | |
1428 | } | |
1429 | n = frameStackCount = readUnsignedShort(stackMap); | |
1430 | stackMap += 2; | |
1431 | for(j = 0; n > 0; n--) | |
1432 | { | |
1433 | stackMap = readFrameType(frameStack, | |
1434 | j++, | |
1435 | stackMap, | |
1436 | c, | |
1437 | labels); | |
1438 | } | |
1439 | } | |
1440 | } | |
1441 | frameOffset += delta + 1; | |
1442 | if(labels[frameOffset] == null) | |
1443 | { | |
1444 | labels[frameOffset] = new Label(); | |
1445 | } | |
1446 | ||
1447 | --frameCount; | |
1448 | } | |
1449 | else | |
1450 | { | |
1451 | frameLocal = null; | |
1452 | } | |
1453 | } | |
1454 | ||
1455 | int opcode = b[v] & 0xFF; | |
1456 | switch(ClassWriter.TYPE[opcode]) | |
1457 | { | |
1458 | case ClassWriter.NOARG_INSN: | |
1459 | mv.visitInsn(opcode); | |
1460 | v += 1; | |
1461 | break; | |
1462 | case ClassWriter.IMPLVAR_INSN: | |
1463 | if(opcode > Opcodes.ISTORE) | |
1464 | { | |
1465 | opcode -= 59; // ISTORE_0 | |
1466 | mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), | |
1467 | opcode & 0x3); | |
1468 | } | |
1469 | else | |
1470 | { | |
1471 | opcode -= 26; // ILOAD_0 | |
1472 | mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), | |
1473 | opcode & 0x3); | |
1474 | } | |
1475 | v += 1; | |
1476 | break; | |
1477 | case ClassWriter.LABEL_INSN: | |
1478 | mv.visitJumpInsn(opcode, labels[w | |
1479 | + readShort(v + 1)]); | |
1480 | v += 3; | |
1481 | break; | |
1482 | case ClassWriter.LABELW_INSN: | |
1483 | mv.visitJumpInsn(opcode - 33, labels[w | |
1484 | + readInt(v + 1)]); | |
1485 | v += 5; | |
1486 | break; | |
1487 | case ClassWriter.WIDE_INSN: | |
1488 | opcode = b[v + 1] & 0xFF; | |
1489 | if(opcode == Opcodes.IINC) | |
1490 | { | |
1491 | mv.visitIincInsn(readUnsignedShort(v + 2), | |
1492 | readShort(v + 4)); | |
1493 | v += 6; | |
1494 | } | |
1495 | else | |
1496 | { | |
1497 | mv.visitVarInsn(opcode, | |
1498 | readUnsignedShort(v + 2)); | |
1499 | v += 4; | |
1500 | } | |
1501 | break; | |
1502 | case ClassWriter.TABL_INSN: | |
1503 | // skips 0 to 3 padding bytes | |
1504 | v = v + 4 - (w & 3); | |
1505 | // reads instruction | |
1506 | label = w + readInt(v); | |
1507 | int min = readInt(v + 4); | |
1508 | int max = readInt(v + 8); | |
1509 | v += 12; | |
1510 | Label[] table = new Label[max - min + 1]; | |
1511 | for(j = 0; j < table.length; ++j) | |
1512 | { | |
1513 | table[j] = labels[w + readInt(v)]; | |
1514 | v += 4; | |
1515 | } | |
1516 | mv.visitTableSwitchInsn(min, | |
1517 | max, | |
1518 | labels[label], | |
1519 | table); | |
1520 | break; | |
1521 | case ClassWriter.LOOK_INSN: | |
1522 | // skips 0 to 3 padding bytes | |
1523 | v = v + 4 - (w & 3); | |
1524 | // reads instruction | |
1525 | label = w + readInt(v); | |
1526 | j = readInt(v + 4); | |
1527 | v += 8; | |
1528 | int[] keys = new int[j]; | |
1529 | Label[] values = new Label[j]; | |
1530 | for(j = 0; j < keys.length; ++j) | |
1531 | { | |
1532 | keys[j] = readInt(v); | |
1533 | values[j] = labels[w + readInt(v + 4)]; | |
1534 | v += 8; | |
1535 | } | |
1536 | mv.visitLookupSwitchInsn(labels[label], | |
1537 | keys, | |
1538 | values); | |
1539 | break; | |
1540 | case ClassWriter.VAR_INSN: | |
1541 | mv.visitVarInsn(opcode, b[v + 1] & 0xFF); | |
1542 | v += 2; | |
1543 | break; | |
1544 | case ClassWriter.SBYTE_INSN: | |
1545 | mv.visitIntInsn(opcode, b[v + 1]); | |
1546 | v += 2; | |
1547 | break; | |
1548 | case ClassWriter.SHORT_INSN: | |
1549 | mv.visitIntInsn(opcode, readShort(v + 1)); | |
1550 | v += 3; | |
1551 | break; | |
1552 | case ClassWriter.LDC_INSN: | |
1553 | mv.visitLdcInsn(readConst(b[v + 1] & 0xFF, c)); | |
1554 | v += 2; | |
1555 | break; | |
1556 | case ClassWriter.LDCW_INSN: | |
1557 | mv.visitLdcInsn(readConst(readUnsignedShort(v + 1), | |
1558 | c)); | |
1559 | v += 3; | |
1560 | break; | |
1561 | case ClassWriter.FIELDORMETH_INSN: | |
1562 | case ClassWriter.ITFMETH_INSN: | |
1563 | int cpIndex = items[readUnsignedShort(v + 1)]; | |
1564 | String iowner = readClass(cpIndex, c); | |
1565 | cpIndex = items[readUnsignedShort(cpIndex + 2)]; | |
1566 | String iname = readUTF8(cpIndex, c); | |
1567 | String idesc = readUTF8(cpIndex + 2, c); | |
1568 | if(opcode < Opcodes.INVOKEVIRTUAL) | |
1569 | { | |
1570 | mv.visitFieldInsn(opcode, iowner, iname, idesc); | |
1571 | } | |
1572 | else | |
1573 | { | |
1574 | mv.visitMethodInsn(opcode, iowner, iname, idesc); | |
1575 | } | |
1576 | if(opcode == Opcodes.INVOKEINTERFACE) | |
1577 | { | |
1578 | v += 5; | |
1579 | } | |
1580 | else | |
1581 | { | |
1582 | v += 3; | |
1583 | } | |
1584 | break; | |
1585 | case ClassWriter.TYPE_INSN: | |
1586 | mv.visitTypeInsn(opcode, readClass(v + 1, c)); | |
1587 | v += 3; | |
1588 | break; | |
1589 | case ClassWriter.IINC_INSN: | |
1590 | mv.visitIincInsn(b[v + 1] & 0xFF, b[v + 2]); | |
1591 | v += 3; | |
1592 | break; | |
1593 | // case MANA_INSN: | |
1594 | default: | |
1595 | mv.visitMultiANewArrayInsn(readClass(v + 1, c), | |
1596 | b[v + 3] & 0xFF); | |
1597 | v += 4; | |
1598 | break; | |
1599 | } | |
1600 | } | |
1601 | l = labels[codeEnd - codeStart]; | |
1602 | if(l != null) | |
1603 | { | |
1604 | mv.visitLabel(l); | |
1605 | } | |
1606 | // visits the local variable tables | |
1607 | if(!skipDebug && varTable != 0) | |
1608 | { | |
1609 | int[] typeTable = null; | |
1610 | if(varTypeTable != 0) | |
1611 | { | |
1612 | k = readUnsignedShort(varTypeTable) * 3; | |
1613 | w = varTypeTable + 2; | |
1614 | typeTable = new int[k]; | |
1615 | while(k > 0) | |
1616 | { | |
1617 | typeTable[--k] = w + 6; // signature | |
1618 | typeTable[--k] = readUnsignedShort(w + 8); // index | |
1619 | typeTable[--k] = readUnsignedShort(w); // start | |
1620 | w += 10; | |
1621 | } | |
1622 | } | |
1623 | k = readUnsignedShort(varTable); | |
1624 | w = varTable + 2; | |
1625 | for(; k > 0; --k) | |
1626 | { | |
1627 | int start = readUnsignedShort(w); | |
1628 | int length = readUnsignedShort(w + 2); | |
1629 | int index = readUnsignedShort(w + 8); | |
1630 | String vsignature = null; | |
1631 | if(typeTable != null) | |
1632 | { | |
1633 | for(int a = 0; a < typeTable.length; a += 3) | |
1634 | { | |
1635 | if(typeTable[a] == start | |
1636 | && typeTable[a + 1] == index) | |
1637 | { | |
1638 | vsignature = readUTF8(typeTable[a + 2], c); | |
1639 | break; | |
1640 | } | |
1641 | } | |
1642 | } | |
1643 | mv.visitLocalVariable(readUTF8(w + 4, c), | |
1644 | readUTF8(w + 6, c), | |
1645 | vsignature, | |
1646 | labels[start], | |
1647 | labels[start + length], | |
1648 | index); | |
1649 | w += 10; | |
1650 | } | |
1651 | } | |
1652 | // visits the other attributes | |
1653 | while(cattrs != null) | |
1654 | { | |
1655 | attr = cattrs.next; | |
1656 | cattrs.next = null; | |
1657 | mv.visitAttribute(cattrs); | |
1658 | cattrs = attr; | |
1659 | } | |
1660 | // visits the max stack and max locals values | |
1661 | mv.visitMaxs(maxStack, maxLocals); | |
1662 | } | |
1663 | ||
1664 | if(mv != null) | |
1665 | { | |
1666 | mv.visitEnd(); | |
1667 | } | |
1668 | } | |
1669 | ||
1670 | // visits the end of the class | |
1671 | classVisitor.visitEnd(); | |
1672 | } | |
1673 | ||
1674 | /** | |
1675 | * Reads parameter annotations and makes the given visitor visit them. | |
1676 | * | |
1677 | * @param v start offset in {@link #b b} of the annotations to be read. | |
1678 | * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1679 | * {@link #readClass(int,char[]) readClass} or | |
1680 | * {@link #readConst readConst}. | |
1681 | * @param visible <tt>true</tt> if the annotations to be read are visible | |
1682 | * at runtime. | |
1683 | * @param mv the visitor that must visit the annotations. | |
1684 | */ | |
1685 | private void readParameterAnnotations( | |
1686 | int v, | |
1687 | final char[] buf, | |
1688 | final boolean visible, | |
1689 | final MethodVisitor mv){ | |
1690 | int n = b[v++] & 0xFF; | |
1691 | for(int i = 0; i < n; ++i) | |
1692 | { | |
1693 | int j = readUnsignedShort(v); | |
1694 | v += 2; | |
1695 | for(; j > 0; --j) | |
1696 | { | |
1697 | v = readAnnotationValues(v + 2, | |
1698 | buf, | |
1699 | true, | |
1700 | mv.visitParameterAnnotation(i, | |
1701 | readUTF8(v, buf), | |
1702 | visible)); | |
1703 | } | |
1704 | } | |
1705 | } | |
1706 | ||
1707 | /** | |
1708 | * Reads the values of an annotation and makes the given visitor visit them. | |
1709 | * | |
1710 | * @param v the start offset in {@link #b b} of the values to be read | |
1711 | * (including the unsigned short that gives the number of values). | |
1712 | * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1713 | * {@link #readClass(int,char[]) readClass} or | |
1714 | * {@link #readConst readConst}. | |
1715 | * @param named if the annotation values are named or not. | |
1716 | * @param av the visitor that must visit the values. | |
1717 | * @return the end offset of the annotation values. | |
1718 | */ | |
1719 | private int readAnnotationValues( | |
1720 | int v, | |
1721 | final char[] buf, | |
1722 | final boolean named, | |
1723 | final AnnotationVisitor av){ | |
1724 | int i = readUnsignedShort(v); | |
1725 | v += 2; | |
1726 | if(named) | |
1727 | { | |
1728 | for(; i > 0; --i) | |
1729 | { | |
1730 | v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); | |
1731 | } | |
1732 | } | |
1733 | else | |
1734 | { | |
1735 | for(; i > 0; --i) | |
1736 | { | |
1737 | v = readAnnotationValue(v, buf, null, av); | |
1738 | } | |
1739 | } | |
1740 | if(av != null) | |
1741 | { | |
1742 | av.visitEnd(); | |
1743 | } | |
1744 | return v; | |
1745 | } | |
1746 | ||
1747 | /** | |
1748 | * Reads a value of an annotation and makes the given visitor visit it. | |
1749 | * | |
1750 | * @param v the start offset in {@link #b b} of the value to be read (<i>not | |
1751 | * including the value name constant pool index</i>). | |
1752 | * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1753 | * {@link #readClass(int,char[]) readClass} or | |
1754 | * {@link #readConst readConst}. | |
1755 | * @param name the name of the value to be read. | |
1756 | * @param av the visitor that must visit the value. | |
1757 | * @return the end offset of the annotation value. | |
1758 | */ | |
1759 | private int readAnnotationValue( | |
1760 | int v, | |
1761 | final char[] buf, | |
1762 | final String name, | |
1763 | final AnnotationVisitor av){ | |
1764 | int i; | |
1765 | if(av == null) | |
1766 | { | |
1767 | switch(b[v] & 0xFF) | |
1768 | { | |
1769 | case'e': // enum_const_value | |
1770 | return v + 5; | |
1771 | case'@': // annotation_value | |
1772 | return readAnnotationValues(v + 3, buf, true, null); | |
1773 | case'[': // array_value | |
1774 | return readAnnotationValues(v + 1, buf, false, null); | |
1775 | default: | |
1776 | return v + 3; | |
1777 | } | |
1778 | } | |
1779 | switch(b[v++] & 0xFF) | |
1780 | { | |
1781 | case'I': // pointer to CONSTANT_Integer | |
1782 | case'J': // pointer to CONSTANT_Long | |
1783 | case'F': // pointer to CONSTANT_Float | |
1784 | case'D': // pointer to CONSTANT_Double | |
1785 | av.visit(name, readConst(readUnsignedShort(v), buf)); | |
1786 | v += 2; | |
1787 | break; | |
1788 | case'B': // pointer to CONSTANT_Byte | |
1789 | av.visit(name, | |
1790 | new Byte((byte) readInt(items[readUnsignedShort(v)]))); | |
1791 | v += 2; | |
1792 | break; | |
1793 | case'Z': // pointer to CONSTANT_Boolean | |
1794 | av.visit(name, readInt(items[readUnsignedShort(v)]) == 0 | |
1795 | ? Boolean.FALSE | |
1796 | : Boolean.TRUE); | |
1797 | v += 2; | |
1798 | break; | |
1799 | case'S': // pointer to CONSTANT_Short | |
1800 | av.visit(name, | |
1801 | new Short((short) readInt(items[readUnsignedShort(v)]))); | |
1802 | v += 2; | |
1803 | break; | |
1804 | case'C': // pointer to CONSTANT_Char | |
1805 | av.visit(name, | |
1806 | new Character((char) readInt(items[readUnsignedShort(v)]))); | |
1807 | v += 2; | |
1808 | break; | |
1809 | case's': // pointer to CONSTANT_Utf8 | |
1810 | av.visit(name, readUTF8(v, buf)); | |
1811 | v += 2; | |
1812 | break; | |
1813 | case'e': // enum_const_value | |
1814 | av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); | |
1815 | v += 4; | |
1816 | break; | |
1817 | case'c': // class_info | |
1818 | av.visit(name, Type.getType(readUTF8(v, buf))); | |
1819 | v += 2; | |
1820 | break; | |
1821 | case'@': // annotation_value | |
1822 | v = readAnnotationValues(v + 2, | |
1823 | buf, | |
1824 | true, | |
1825 | av.visitAnnotation(name, readUTF8(v, buf))); | |
1826 | break; | |
1827 | case'[': // array_value | |
1828 | int size = readUnsignedShort(v); | |
1829 | v += 2; | |
1830 | if(size == 0) | |
1831 | { | |
1832 | return readAnnotationValues(v - 2, | |
1833 | buf, | |
1834 | false, | |
1835 | av.visitArray(name)); | |
1836 | } | |
1837 | switch(this.b[v++] & 0xFF) | |
1838 | { | |
1839 | case'B': | |
1840 | byte[] bv = new byte[size]; | |
1841 | for(i = 0; i < size; i++) | |
1842 | { | |
1843 | bv[i] = (byte) readInt(items[readUnsignedShort(v)]); | |
1844 | v += 3; | |
1845 | } | |
1846 | av.visit(name, bv); | |
1847 | --v; | |
1848 | break; | |
1849 | case'Z': | |
1850 | boolean[] zv = new boolean[size]; | |
1851 | for(i = 0; i < size; i++) | |
1852 | { | |
1853 | zv[i] = readInt(items[readUnsignedShort(v)]) != 0; | |
1854 | v += 3; | |
1855 | } | |
1856 | av.visit(name, zv); | |
1857 | --v; | |
1858 | break; | |
1859 | case'S': | |
1860 | short[] sv = new short[size]; | |
1861 | for(i = 0; i < size; i++) | |
1862 | { | |
1863 | sv[i] = (short) readInt(items[readUnsignedShort(v)]); | |
1864 | v += 3; | |
1865 | } | |
1866 | av.visit(name, sv); | |
1867 | --v; | |
1868 | break; | |
1869 | case'C': | |
1870 | char[] cv = new char[size]; | |
1871 | for(i = 0; i < size; i++) | |
1872 | { | |
1873 | cv[i] = (char) readInt(items[readUnsignedShort(v)]); | |
1874 | v += 3; | |
1875 | } | |
1876 | av.visit(name, cv); | |
1877 | --v; | |
1878 | break; | |
1879 | case'I': | |
1880 | int[] iv = new int[size]; | |
1881 | for(i = 0; i < size; i++) | |
1882 | { | |
1883 | iv[i] = readInt(items[readUnsignedShort(v)]); | |
1884 | v += 3; | |
1885 | } | |
1886 | av.visit(name, iv); | |
1887 | --v; | |
1888 | break; | |
1889 | case'J': | |
1890 | long[] lv = new long[size]; | |
1891 | for(i = 0; i < size; i++) | |
1892 | { | |
1893 | lv[i] = readLong(items[readUnsignedShort(v)]); | |
1894 | v += 3; | |
1895 | } | |
1896 | av.visit(name, lv); | |
1897 | --v; | |
1898 | break; | |
1899 | case'F': | |
1900 | float[] fv = new float[size]; | |
1901 | for(i = 0; i < size; i++) | |
1902 | { | |
1903 | fv[i] = Float.intBitsToFloat(readInt(items[readUnsignedShort(v)])); | |
1904 | v += 3; | |
1905 | } | |
1906 | av.visit(name, fv); | |
1907 | --v; | |
1908 | break; | |
1909 | case'D': | |
1910 | double[] dv = new double[size]; | |
1911 | for(i = 0; i < size; i++) | |
1912 | { | |
1913 | dv[i] = Double.longBitsToDouble(readLong(items[readUnsignedShort(v)])); | |
1914 | v += 3; | |
1915 | } | |
1916 | av.visit(name, dv); | |
1917 | --v; | |
1918 | break; | |
1919 | default: | |
1920 | v = readAnnotationValues(v - 3, | |
1921 | buf, | |
1922 | false, | |
1923 | av.visitArray(name)); | |
1924 | } | |
1925 | } | |
1926 | return v; | |
1927 | } | |
1928 | ||
1929 | private int readFrameType( | |
1930 | final Object[] frame, | |
1931 | final int index, | |
1932 | int v, | |
1933 | final char[] buf, | |
1934 | final Label[] labels){ | |
1935 | int type = b[v++] & 0xFF; | |
1936 | switch(type) | |
1937 | { | |
1938 | case 0: | |
1939 | frame[index] = Opcodes.TOP; | |
1940 | break; | |
1941 | case 1: | |
1942 | frame[index] = Opcodes.INTEGER; | |
1943 | break; | |
1944 | case 2: | |
1945 | frame[index] = Opcodes.FLOAT; | |
1946 | break; | |
1947 | case 3: | |
1948 | frame[index] = Opcodes.DOUBLE; | |
1949 | break; | |
1950 | case 4: | |
1951 | frame[index] = Opcodes.LONG; | |
1952 | break; | |
1953 | case 5: | |
1954 | frame[index] = Opcodes.NULL; | |
1955 | break; | |
1956 | case 6: | |
1957 | frame[index] = Opcodes.UNINITIALIZED_THIS; | |
1958 | break; | |
1959 | case 7: // Object | |
1960 | frame[index] = readClass(v, buf); | |
1961 | v += 2; | |
1962 | break; | |
1963 | default: // Uninitialized | |
1964 | int offset = readUnsignedShort(v); | |
1965 | if(labels[offset] == null) | |
1966 | { | |
1967 | labels[offset] = new Label(); | |
1968 | } | |
1969 | frame[index] = labels[offset]; | |
1970 | v += 2; | |
1971 | } | |
1972 | return v; | |
1973 | } | |
1974 | ||
1975 | /** | |
1976 | * Reads an attribute in {@link #b b}. | |
1977 | * | |
1978 | * @param attrs prototypes of the attributes that must be parsed during the | |
1979 | * visit of the class. Any attribute whose type is not equal to the | |
1980 | * type of one the prototypes is ignored (i.e. an empty | |
1981 | * {@link Attribute} instance is returned). | |
1982 | * @param type the type of the attribute. | |
1983 | * @param off index of the first byte of the attribute's content in | |
1984 | * {@link #b b}. The 6 attribute header bytes, containing the type | |
1985 | * and the length of the attribute, are not taken into account here | |
1986 | * (they have already been read). | |
1987 | * @param len the length of the attribute's content. | |
1988 | * @param buf buffer to be used to call {@link #readUTF8 readUTF8}, | |
1989 | * {@link #readClass(int,char[]) readClass} or | |
1990 | * {@link #readConst readConst}. | |
1991 | * @param codeOff index of the first byte of code's attribute content in | |
1992 | * {@link #b b}, or -1 if the attribute to be read is not a code | |
1993 | * attribute. The 6 attribute header bytes, containing the type and | |
1994 | * the length of the attribute, are not taken into account here. | |
1995 | * @param labels the labels of the method's code, or <tt>null</tt> if the | |
1996 | * attribute to be read is not a code attribute. | |
1997 | * @return the attribute that has been read, or <tt>null</tt> to skip this | |
1998 | * attribute. | |
1999 | */ | |
2000 | private Attribute readAttribute( | |
2001 | final Attribute[] attrs, | |
2002 | final String type, | |
2003 | final int off, | |
2004 | final int len, | |
2005 | final char[] buf, | |
2006 | final int codeOff, | |
2007 | final Label[] labels){ | |
2008 | for(int i = 0; i < attrs.length; ++i) | |
2009 | { | |
2010 | if(attrs[i].type.equals(type)) | |
2011 | { | |
2012 | return attrs[i].read(this, off, len, buf, codeOff, labels); | |
2013 | } | |
2014 | } | |
2015 | return new Attribute(type).read(this, off, len, null, -1, null); | |
2016 | } | |
2017 | ||
2018 | // ------------------------------------------------------------------------ | |
2019 | // Utility methods: low level parsing | |
2020 | // ------------------------------------------------------------------------ | |
2021 | ||
2022 | /** | |
2023 | * Returns the start index of the constant pool item in {@link #b b}, plus | |
2024 | * one. <i>This method is intended for {@link Attribute} sub classes, and is | |
2025 | * normally not needed by class generators or adapters.</i> | |
2026 | * | |
2027 | * @param item the index a constant pool item. | |
2028 | * @return the start index of the constant pool item in {@link #b b}, plus | |
2029 | * one. | |
2030 | */ | |
2031 | public int getItem(final int item){ | |
2032 | return items[item]; | |
2033 | } | |
2034 | ||
2035 | /** | |
2036 | * Reads a byte value in {@link #b b}. <i>This method is intended for | |
2037 | * {@link Attribute} sub classes, and is normally not needed by class | |
2038 | * generators or adapters.</i> | |
2039 | * | |
2040 | * @param index the start index of the value to be read in {@link #b b}. | |
2041 | * @return the read value. | |
2042 | */ | |
2043 | public int readByte(final int index){ | |
2044 | return b[index] & 0xFF; | |
2045 | } | |
2046 | ||
2047 | /** | |
2048 | * Reads an unsigned short value in {@link #b b}. <i>This method is | |
2049 | * intended for {@link Attribute} sub classes, and is normally not needed by | |
2050 | * class generators or adapters.</i> | |
2051 | * | |
2052 | * @param index the start index of the value to be read in {@link #b b}. | |
2053 | * @return the read value. | |
2054 | */ | |
2055 | public int readUnsignedShort(final int index){ | |
2056 | byte[] b = this.b; | |
2057 | return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); | |
2058 | } | |
2059 | ||
2060 | /** | |
2061 | * Reads a signed short value in {@link #b b}. <i>This method is intended | |
2062 | * for {@link Attribute} sub classes, and is normally not needed by class | |
2063 | * generators or adapters.</i> | |
2064 | * | |
2065 | * @param index the start index of the value to be read in {@link #b b}. | |
2066 | * @return the read value. | |
2067 | */ | |
2068 | public short readShort(final int index){ | |
2069 | byte[] b = this.b; | |
2070 | return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); | |
2071 | } | |
2072 | ||
2073 | /** | |
2074 | * Reads a signed int value in {@link #b b}. <i>This method is intended for | |
2075 | * {@link Attribute} sub classes, and is normally not needed by class | |
2076 | * generators or adapters.</i> | |
2077 | * | |
2078 | * @param index the start index of the value to be read in {@link #b b}. | |
2079 | * @return the read value. | |
2080 | */ | |
2081 | public int readInt(final int index){ | |
2082 | byte[] b = this.b; | |
2083 | return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) | |
2084 | | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); | |
2085 | } | |
2086 | ||
2087 | /** | |
2088 | * Reads a signed long value in {@link #b b}. <i>This method is intended | |
2089 | * for {@link Attribute} sub classes, and is normally not needed by class | |
2090 | * generators or adapters.</i> | |
2091 | * | |
2092 | * @param index the start index of the value to be read in {@link #b b}. | |
2093 | * @return the read value. | |
2094 | */ | |
2095 | public long readLong(final int index){ | |
2096 | long l1 = readInt(index); | |
2097 | long l0 = readInt(index + 4) & 0xFFFFFFFFL; | |
2098 | return (l1 << 32) | l0; | |
2099 | } | |
2100 | ||
2101 | /** | |
2102 | * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method | |
2103 | * is intended for {@link Attribute} sub classes, and is normally not needed | |
2104 | * by class generators or adapters.</i> | |
2105 | * | |
2106 | * @param index the start index of an unsigned short value in {@link #b b}, | |
2107 | * whose value is the index of an UTF8 constant pool item. | |
2108 | * @param buf buffer to be used to read the item. This buffer must be | |
2109 | * sufficiently large. It is not automatically resized. | |
2110 | * @return the String corresponding to the specified UTF8 item. | |
2111 | */ | |
2112 | public String readUTF8(int index, final char[] buf){ | |
2113 | int item = readUnsignedShort(index); | |
2114 | String s = strings[item]; | |
2115 | if(s != null) | |
2116 | { | |
2117 | return s; | |
2118 | } | |
2119 | index = items[item]; | |
2120 | return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); | |
2121 | } | |
2122 | ||
2123 | /** | |
2124 | * Reads UTF8 string in {@link #b b}. | |
2125 | * | |
2126 | * @param index start offset of the UTF8 string to be read. | |
2127 | * @param utfLen length of the UTF8 string to be read. | |
2128 | * @param buf buffer to be used to read the string. This buffer must be | |
2129 | * sufficiently large. It is not automatically resized. | |
2130 | * @return the String corresponding to the specified UTF8 string. | |
2131 | */ | |
2132 | private String readUTF(int index, final int utfLen, final char[] buf){ | |
2133 | int endIndex = index + utfLen; | |
2134 | byte[] b = this.b; | |
2135 | int strLen = 0; | |
2136 | int c, d, e; | |
2137 | while(index < endIndex) | |
2138 | { | |
2139 | c = b[index++] & 0xFF; | |
2140 | switch(c >> 4) | |
2141 | { | |
2142 | case 0: | |
2143 | case 1: | |
2144 | case 2: | |
2145 | case 3: | |
2146 | case 4: | |
2147 | case 5: | |
2148 | case 6: | |
2149 | case 7: | |
2150 | // 0xxxxxxx | |
2151 | buf[strLen++] = (char) c; | |
2152 | break; | |
2153 | case 12: | |
2154 | case 13: | |
2155 | // 110x xxxx 10xx xxxx | |
2156 | d = b[index++]; | |
2157 | buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F)); | |
2158 | break; | |
2159 | default: | |
2160 | // 1110 xxxx 10xx xxxx 10xx xxxx | |
2161 | d = b[index++]; | |
2162 | e = b[index++]; | |
2163 | buf[strLen++] = (char) (((c & 0x0F) << 12) | |
2164 | | ((d & 0x3F) << 6) | (e & 0x3F)); | |
2165 | break; | |
2166 | } | |
2167 | } | |
2168 | return new String(buf, 0, strLen); | |
2169 | } | |
2170 | ||
2171 | /** | |
2172 | * Reads a class constant pool item in {@link #b b}. <i>This method is | |
2173 | * intended for {@link Attribute} sub classes, and is normally not needed by | |
2174 | * class generators or adapters.</i> | |
2175 | * | |
2176 | * @param index the start index of an unsigned short value in {@link #b b}, | |
2177 | * whose value is the index of a class constant pool item. | |
2178 | * @param buf buffer to be used to read the item. This buffer must be | |
2179 | * sufficiently large. It is not automatically resized. | |
2180 | * @return the String corresponding to the specified class item. | |
2181 | */ | |
2182 | public String readClass(final int index, final char[] buf){ | |
2183 | // computes the start index of the CONSTANT_Class item in b | |
2184 | // and reads the CONSTANT_Utf8 item designated by | |
2185 | // the first two bytes of this CONSTANT_Class item | |
2186 | return readUTF8(items[readUnsignedShort(index)], buf); | |
2187 | } | |
2188 | ||
2189 | /** | |
2190 | * Reads a numeric or string constant pool item in {@link #b b}. <i>This | |
2191 | * method is intended for {@link Attribute} sub classes, and is normally not | |
2192 | * needed by class generators or adapters.</i> | |
2193 | * | |
2194 | * @param item the index of a constant pool item. | |
2195 | * @param buf buffer to be used to read the item. This buffer must be | |
2196 | * sufficiently large. It is not automatically resized. | |
2197 | * @return the {@link Integer}, {@link Float}, {@link Long}, | |
2198 | * {@link Double}, {@link String} or {@link Type} corresponding to | |
2199 | * the given constant pool item. | |
2200 | */ | |
2201 | public Object readConst(final int item, final char[] buf){ | |
2202 | int index = items[item]; | |
2203 | switch(b[index - 1]) | |
2204 | { | |
2205 | case ClassWriter.INT: | |
2206 | return new Integer(readInt(index)); | |
2207 | case ClassWriter.FLOAT: | |
2208 | return new Float(Float.intBitsToFloat(readInt(index))); | |
2209 | case ClassWriter.LONG: | |
2210 | return new Long(readLong(index)); | |
2211 | case ClassWriter.DOUBLE: | |
2212 | return new Double(Double.longBitsToDouble(readLong(index))); | |
2213 | case ClassWriter.CLASS: | |
2214 | String s = readUTF8(index, buf); | |
2215 | return s.charAt(0) == '[' | |
2216 | ? Type.getType(s) | |
2217 | : Type.getObjectType(s); | |
2218 | // case ClassWriter.STR: | |
2219 | default: | |
2220 | return readUTF8(index, buf); | |
2221 | } | |
2222 | } | |
2223 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A visitor to visit a Java class. The methods of this interface must be called | |
33 | * in the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [ | |
34 | * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> | | |
35 | * <tt>visitAttribute</tt> )* (<tt>visitInnerClass</tt> | | |
36 | * <tt>visitField</tt> | <tt>visitMethod</tt> )* <tt>visitEnd</tt>. | |
37 | * | |
38 | * @author Eric Bruneton | |
39 | */ | |
40 | public interface ClassVisitor{ | |
41 | ||
42 | /** | |
43 | * Visits the header of the class. | |
44 | * | |
45 | * @param version the class version. | |
46 | * @param access the class's access flags (see {@link Opcodes}). This | |
47 | * parameter also indicates if the class is deprecated. | |
48 | * @param name the internal name of the class (see | |
49 | * {@link Type#getInternalName() getInternalName}). | |
50 | * @param signature the signature of this class. May be <tt>null</tt> if | |
51 | * the class is not a generic one, and does not extend or implement | |
52 | * generic classes or interfaces. | |
53 | * @param superName the internal of name of the super class (see | |
54 | * {@link Type#getInternalName() getInternalName}). For interfaces, | |
55 | * the super class is {@link Object}. May be <tt>null</tt>, but | |
56 | * only for the {@link Object} class. | |
57 | * @param interfaces the internal names of the class's interfaces (see | |
58 | * {@link Type#getInternalName() getInternalName}). May be | |
59 | * <tt>null</tt>. | |
60 | */ | |
61 | void visit( | |
62 | int version, | |
63 | int access, | |
64 | String name, | |
65 | String signature, | |
66 | String superName, | |
67 | String[] interfaces); | |
68 | ||
69 | /** | |
70 | * Visits the source of the class. | |
71 | * | |
72 | * @param source the name of the source file from which the class was | |
73 | * compiled. May be <tt>null</tt>. | |
74 | * @param debug additional debug information to compute the correspondance | |
75 | * between source and compiled elements of the class. May be | |
76 | * <tt>null</tt>. | |
77 | */ | |
78 | void visitSource(String source, String debug); | |
79 | ||
80 | /** | |
81 | * Visits the enclosing class of the class. This method must be called only | |
82 | * if the class has an enclosing class. | |
83 | * | |
84 | * @param owner internal name of the enclosing class of the class. | |
85 | * @param name the name of the method that contains the class, or | |
86 | * <tt>null</tt> if the class is not enclosed in a method of its | |
87 | * enclosing class. | |
88 | * @param desc the descriptor of the method that contains the class, or | |
89 | * <tt>null</tt> if the class is not enclosed in a method of its | |
90 | * enclosing class. | |
91 | */ | |
92 | void visitOuterClass(String owner, String name, String desc); | |
93 | ||
94 | /** | |
95 | * Visits an annotation of the class. | |
96 | * | |
97 | * @param desc the class descriptor of the annotation class. | |
98 | * @param visible <tt>true</tt> if the annotation is visible at runtime. | |
99 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
100 | * this visitor is not interested in visiting this annotation. | |
101 | */ | |
102 | AnnotationVisitor visitAnnotation(String desc, boolean visible); | |
103 | ||
104 | /** | |
105 | * Visits a non standard attribute of the class. | |
106 | * | |
107 | * @param attr an attribute. | |
108 | */ | |
109 | void visitAttribute(Attribute attr); | |
110 | ||
111 | /** | |
112 | * Visits information about an inner class. This inner class is not | |
113 | * necessarily a member of the class being visited. | |
114 | * | |
115 | * @param name the internal name of an inner class (see | |
116 | * {@link Type#getInternalName() getInternalName}). | |
117 | * @param outerName the internal name of the class to which the inner class | |
118 | * belongs (see {@link Type#getInternalName() getInternalName}). May | |
119 | * be <tt>null</tt> for not member classes. | |
120 | * @param innerName the (simple) name of the inner class inside its | |
121 | * enclosing class. May be <tt>null</tt> for anonymous inner | |
122 | * classes. | |
123 | * @param access the access flags of the inner class as originally declared | |
124 | * in the enclosing class. | |
125 | */ | |
126 | void visitInnerClass( | |
127 | String name, | |
128 | String outerName, | |
129 | String innerName, | |
130 | int access); | |
131 | ||
132 | /** | |
133 | * Visits a field of the class. | |
134 | * | |
135 | * @param access the field's access flags (see {@link Opcodes}). This | |
136 | * parameter also indicates if the field is synthetic and/or | |
137 | * deprecated. | |
138 | * @param name the field's name. | |
139 | * @param desc the field's descriptor (see {@link Type Type}). | |
140 | * @param signature the field's signature. May be <tt>null</tt> if the | |
141 | * field's type does not use generic types. | |
142 | * @param value the field's initial value. This parameter, which may be | |
143 | * <tt>null</tt> if the field does not have an initial value, must | |
144 | * be an {@link Integer}, a {@link Float}, a {@link Long}, a | |
145 | * {@link Double} or a {@link String} (for <tt>int</tt>, | |
146 | * <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields | |
147 | * respectively). <i>This parameter is only used for static fields</i>. | |
148 | * Its value is ignored for non static fields, which must be | |
149 | * initialized through bytecode instructions in constructors or | |
150 | * methods. | |
151 | * @return a visitor to visit field annotations and attributes, or | |
152 | * <tt>null</tt> if this class visitor is not interested in | |
153 | * visiting these annotations and attributes. | |
154 | */ | |
155 | FieldVisitor visitField( | |
156 | int access, | |
157 | String name, | |
158 | String desc, | |
159 | String signature, | |
160 | Object value); | |
161 | ||
162 | /** | |
163 | * Visits a method of the class. This method <i>must</i> return a new | |
164 | * {@link MethodVisitor} instance (or <tt>null</tt>) each time it is | |
165 | * called, i.e., it should not return a previously returned visitor. | |
166 | * | |
167 | * @param access the method's access flags (see {@link Opcodes}). This | |
168 | * parameter also indicates if the method is synthetic and/or | |
169 | * deprecated. | |
170 | * @param name the method's name. | |
171 | * @param desc the method's descriptor (see {@link Type Type}). | |
172 | * @param signature the method's signature. May be <tt>null</tt> if the | |
173 | * method parameters, return type and exceptions do not use generic | |
174 | * types. | |
175 | * @param exceptions the internal names of the method's exception classes | |
176 | * (see {@link Type#getInternalName() getInternalName}). May be | |
177 | * <tt>null</tt>. | |
178 | * @return an object to visit the byte code of the method, or <tt>null</tt> | |
179 | * if this class visitor is not interested in visiting the code of | |
180 | * this method. | |
181 | */ | |
182 | MethodVisitor visitMethod( | |
183 | int access, | |
184 | String name, | |
185 | String desc, | |
186 | String signature, | |
187 | String[] exceptions); | |
188 | ||
189 | /** | |
190 | * Visits the end of the class. This method, which is the last one to be | |
191 | * called, is used to inform the visitor that all the fields and methods of | |
192 | * the class have been visited. | |
193 | */ | |
194 | void visitEnd(); | |
195 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A {@link ClassVisitor} that generates classes in bytecode form. More | |
33 | * precisely this visitor generates a byte array conforming to the Java class | |
34 | * file format. It can be used alone, to generate a Java class "from scratch", | |
35 | * or with one or more {@link ClassReader ClassReader} and adapter class visitor | |
36 | * to generate a modified class from one or more existing Java classes. | |
37 | * | |
38 | * @author Eric Bruneton | |
39 | */ | |
40 | public class ClassWriter implements ClassVisitor{ | |
41 | ||
42 | /** | |
43 | * Flag to automatically compute the maximum stack size and the maximum | |
44 | * number of local variables of methods. If this flag is set, then the | |
45 | * arguments of the {@link MethodVisitor#visitMaxs visitMaxs} method of the | |
46 | * {@link MethodVisitor} returned by the {@link #visitMethod visitMethod} | |
47 | * method will be ignored, and computed automatically from the signature and | |
48 | * the bytecode of each method. | |
49 | * | |
50 | * @see #ClassWriter(int) | |
51 | */ | |
52 | public final static int COMPUTE_MAXS = 1; | |
53 | ||
54 | /** | |
55 | * Flag to automatically compute the stack map frames of methods from | |
56 | * scratch. If this flag is set, then the calls to the | |
57 | * {@link MethodVisitor#visitFrame} method are ignored, and the stack map | |
58 | * frames are recomputed from the methods bytecode. The arguments of the | |
59 | * {@link MethodVisitor#visitMaxs visitMaxs} method are also ignored and | |
60 | * recomputed from the bytecode. In other words, computeFrames implies | |
61 | * computeMaxs. | |
62 | * | |
63 | * @see #ClassWriter(int) | |
64 | */ | |
65 | public final static int COMPUTE_FRAMES = 2; | |
66 | ||
67 | /** | |
68 | * The type of instructions without any argument. | |
69 | */ | |
70 | final static int NOARG_INSN = 0; | |
71 | ||
72 | /** | |
73 | * The type of instructions with an signed byte argument. | |
74 | */ | |
75 | final static int SBYTE_INSN = 1; | |
76 | ||
77 | /** | |
78 | * The type of instructions with an signed short argument. | |
79 | */ | |
80 | final static int SHORT_INSN = 2; | |
81 | ||
82 | /** | |
83 | * The type of instructions with a local variable index argument. | |
84 | */ | |
85 | final static int VAR_INSN = 3; | |
86 | ||
87 | /** | |
88 | * The type of instructions with an implicit local variable index argument. | |
89 | */ | |
90 | final static int IMPLVAR_INSN = 4; | |
91 | ||
92 | /** | |
93 | * The type of instructions with a type descriptor argument. | |
94 | */ | |
95 | final static int TYPE_INSN = 5; | |
96 | ||
97 | /** | |
98 | * The type of field and method invocations instructions. | |
99 | */ | |
100 | final static int FIELDORMETH_INSN = 6; | |
101 | ||
102 | /** | |
103 | * The type of the INVOKEINTERFACE instruction. | |
104 | */ | |
105 | final static int ITFMETH_INSN = 7; | |
106 | ||
107 | /** | |
108 | * The type of instructions with a 2 bytes bytecode offset label. | |
109 | */ | |
110 | final static int LABEL_INSN = 8; | |
111 | ||
112 | /** | |
113 | * The type of instructions with a 4 bytes bytecode offset label. | |
114 | */ | |
115 | final static int LABELW_INSN = 9; | |
116 | ||
117 | /** | |
118 | * The type of the LDC instruction. | |
119 | */ | |
120 | final static int LDC_INSN = 10; | |
121 | ||
122 | /** | |
123 | * The type of the LDC_W and LDC2_W instructions. | |
124 | */ | |
125 | final static int LDCW_INSN = 11; | |
126 | ||
127 | /** | |
128 | * The type of the IINC instruction. | |
129 | */ | |
130 | final static int IINC_INSN = 12; | |
131 | ||
132 | /** | |
133 | * The type of the TABLESWITCH instruction. | |
134 | */ | |
135 | final static int TABL_INSN = 13; | |
136 | ||
137 | /** | |
138 | * The type of the LOOKUPSWITCH instruction. | |
139 | */ | |
140 | final static int LOOK_INSN = 14; | |
141 | ||
142 | /** | |
143 | * The type of the MULTIANEWARRAY instruction. | |
144 | */ | |
145 | final static int MANA_INSN = 15; | |
146 | ||
147 | /** | |
148 | * The type of the WIDE instruction. | |
149 | */ | |
150 | final static int WIDE_INSN = 16; | |
151 | ||
152 | /** | |
153 | * The instruction types of all JVM opcodes. | |
154 | */ | |
155 | static byte[] TYPE; | |
156 | ||
157 | /** | |
158 | * The type of CONSTANT_Class constant pool items. | |
159 | */ | |
160 | final static int CLASS = 7; | |
161 | ||
162 | /** | |
163 | * The type of CONSTANT_Fieldref constant pool items. | |
164 | */ | |
165 | final static int FIELD = 9; | |
166 | ||
167 | /** | |
168 | * The type of CONSTANT_Methodref constant pool items. | |
169 | */ | |
170 | final static int METH = 10; | |
171 | ||
172 | /** | |
173 | * The type of CONSTANT_InterfaceMethodref constant pool items. | |
174 | */ | |
175 | final static int IMETH = 11; | |
176 | ||
177 | /** | |
178 | * The type of CONSTANT_String constant pool items. | |
179 | */ | |
180 | final static int STR = 8; | |
181 | ||
182 | /** | |
183 | * The type of CONSTANT_Integer constant pool items. | |
184 | */ | |
185 | final static int INT = 3; | |
186 | ||
187 | /** | |
188 | * The type of CONSTANT_Float constant pool items. | |
189 | */ | |
190 | final static int FLOAT = 4; | |
191 | ||
192 | /** | |
193 | * The type of CONSTANT_Long constant pool items. | |
194 | */ | |
195 | final static int LONG = 5; | |
196 | ||
197 | /** | |
198 | * The type of CONSTANT_Double constant pool items. | |
199 | */ | |
200 | final static int DOUBLE = 6; | |
201 | ||
202 | /** | |
203 | * The type of CONSTANT_NameAndType constant pool items. | |
204 | */ | |
205 | final static int NAME_TYPE = 12; | |
206 | ||
207 | /** | |
208 | * The type of CONSTANT_Utf8 constant pool items. | |
209 | */ | |
210 | final static int UTF8 = 1; | |
211 | ||
212 | /** | |
213 | * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, | |
214 | * instead of the constant pool, in order to avoid clashes with normal | |
215 | * constant pool items in the ClassWriter constant pool's hash table. | |
216 | */ | |
217 | final static int TYPE_NORMAL = 13; | |
218 | ||
219 | /** | |
220 | * Uninitialized type Item stored in the ClassWriter | |
221 | * {@link ClassWriter#typeTable}, instead of the constant pool, in order to | |
222 | * avoid clashes with normal constant pool items in the ClassWriter constant | |
223 | * pool's hash table. | |
224 | */ | |
225 | final static int TYPE_UNINIT = 14; | |
226 | ||
227 | /** | |
228 | * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, | |
229 | * instead of the constant pool, in order to avoid clashes with normal | |
230 | * constant pool items in the ClassWriter constant pool's hash table. | |
231 | */ | |
232 | final static int TYPE_MERGED = 15; | |
233 | ||
234 | /** | |
235 | * The class reader from which this class writer was constructed, if any. | |
236 | */ | |
237 | ClassReader cr; | |
238 | ||
239 | /** | |
240 | * Minor and major version numbers of the class to be generated. | |
241 | */ | |
242 | int version; | |
243 | ||
244 | /** | |
245 | * Index of the next item to be added in the constant pool. | |
246 | */ | |
247 | int index; | |
248 | ||
249 | /** | |
250 | * The constant pool of this class. | |
251 | */ | |
252 | ByteVector pool; | |
253 | ||
254 | /** | |
255 | * The constant pool's hash table data. | |
256 | */ | |
257 | Item[] items; | |
258 | ||
259 | /** | |
260 | * The threshold of the constant pool's hash table. | |
261 | */ | |
262 | int threshold; | |
263 | ||
264 | /** | |
265 | * A reusable key used to look for items in the {@link #items} hash table. | |
266 | */ | |
267 | Item key; | |
268 | ||
269 | /** | |
270 | * A reusable key used to look for items in the {@link #items} hash table. | |
271 | */ | |
272 | Item key2; | |
273 | ||
274 | /** | |
275 | * A reusable key used to look for items in the {@link #items} hash table. | |
276 | */ | |
277 | Item key3; | |
278 | ||
279 | /** | |
280 | * A type table used to temporarily store internal names that will not | |
281 | * necessarily be stored in the constant pool. This type table is used by | |
282 | * the control flow and data flow analysis algorithm used to compute stack | |
283 | * map frames from scratch. This array associates to each index <tt>i</tt> | |
284 | * the Item whose index is <tt>i</tt>. All Item objects stored in this | |
285 | * array are also stored in the {@link #items} hash table. These two arrays | |
286 | * allow to retrieve an Item from its index or, conversly, to get the index | |
287 | * of an Item from its value. Each Item stores an internal name in its | |
288 | * {@link Item#strVal1} field. | |
289 | */ | |
290 | Item[] typeTable; | |
291 | ||
292 | /** | |
293 | * Number of elements in the {@link #typeTable} array. | |
294 | */ | |
295 | private short typeCount; // TODO int? | |
296 | ||
297 | /** | |
298 | * The access flags of this class. | |
299 | */ | |
300 | private int access; | |
301 | ||
302 | /** | |
303 | * The constant pool item that contains the internal name of this class. | |
304 | */ | |
305 | private int name; | |
306 | ||
307 | /** | |
308 | * The internal name of this class. | |
309 | */ | |
310 | String thisName; | |
311 | ||
312 | /** | |
313 | * The constant pool item that contains the signature of this class. | |
314 | */ | |
315 | private int signature; | |
316 | ||
317 | /** | |
318 | * The constant pool item that contains the internal name of the super class | |
319 | * of this class. | |
320 | */ | |
321 | private int superName; | |
322 | ||
323 | /** | |
324 | * Number of interfaces implemented or extended by this class or interface. | |
325 | */ | |
326 | private int interfaceCount; | |
327 | ||
328 | /** | |
329 | * The interfaces implemented or extended by this class or interface. More | |
330 | * precisely, this array contains the indexes of the constant pool items | |
331 | * that contain the internal names of these interfaces. | |
332 | */ | |
333 | private int[] interfaces; | |
334 | ||
335 | /** | |
336 | * The index of the constant pool item that contains the name of the source | |
337 | * file from which this class was compiled. | |
338 | */ | |
339 | private int sourceFile; | |
340 | ||
341 | /** | |
342 | * The SourceDebug attribute of this class. | |
343 | */ | |
344 | private ByteVector sourceDebug; | |
345 | ||
346 | /** | |
347 | * The constant pool item that contains the name of the enclosing class of | |
348 | * this class. | |
349 | */ | |
350 | private int enclosingMethodOwner; | |
351 | ||
352 | /** | |
353 | * The constant pool item that contains the name and descriptor of the | |
354 | * enclosing method of this class. | |
355 | */ | |
356 | private int enclosingMethod; | |
357 | ||
358 | /** | |
359 | * The runtime visible annotations of this class. | |
360 | */ | |
361 | private AnnotationWriter anns; | |
362 | ||
363 | /** | |
364 | * The runtime invisible annotations of this class. | |
365 | */ | |
366 | private AnnotationWriter ianns; | |
367 | ||
368 | /** | |
369 | * The non standard attributes of this class. | |
370 | */ | |
371 | private Attribute attrs; | |
372 | ||
373 | /** | |
374 | * The number of entries in the InnerClasses attribute. | |
375 | */ | |
376 | private int innerClassesCount; | |
377 | ||
378 | /** | |
379 | * The InnerClasses attribute. | |
380 | */ | |
381 | private ByteVector innerClasses; | |
382 | ||
383 | /** | |
384 | * The fields of this class. These fields are stored in a linked list of | |
385 | * {@link FieldWriter} objects, linked to each other by their | |
386 | * {@link FieldWriter#next} field. This field stores the first element of | |
387 | * this list. | |
388 | */ | |
389 | FieldWriter firstField; | |
390 | ||
391 | /** | |
392 | * The fields of this class. These fields are stored in a linked list of | |
393 | * {@link FieldWriter} objects, linked to each other by their | |
394 | * {@link FieldWriter#next} field. This field stores the last element of | |
395 | * this list. | |
396 | */ | |
397 | FieldWriter lastField; | |
398 | ||
399 | /** | |
400 | * The methods of this class. These methods are stored in a linked list of | |
401 | * {@link MethodWriter} objects, linked to each other by their | |
402 | * {@link MethodWriter#next} field. This field stores the first element of | |
403 | * this list. | |
404 | */ | |
405 | MethodWriter firstMethod; | |
406 | ||
407 | /** | |
408 | * The methods of this class. These methods are stored in a linked list of | |
409 | * {@link MethodWriter} objects, linked to each other by their | |
410 | * {@link MethodWriter#next} field. This field stores the last element of | |
411 | * this list. | |
412 | */ | |
413 | MethodWriter lastMethod; | |
414 | ||
415 | /** | |
416 | * <tt>true</tt> if the maximum stack size and number of local variables | |
417 | * must be automatically computed. | |
418 | */ | |
419 | private boolean computeMaxs; | |
420 | ||
421 | /** | |
422 | * <tt>true</tt> if the stack map frames must be recomputed from scratch. | |
423 | */ | |
424 | private boolean computeFrames; | |
425 | ||
426 | /** | |
427 | * <tt>true</tt> if the stack map tables of this class are invalid. The | |
428 | * {@link MethodWriter#resizeInstructions} method cannot transform existing | |
429 | * stack map tables, and so produces potentially invalid classes when it is | |
430 | * executed. In this case the class is reread and rewritten with the | |
431 | * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize | |
432 | * stack map tables when this option is used). | |
433 | */ | |
434 | boolean invalidFrames; | |
435 | ||
436 | // ------------------------------------------------------------------------ | |
437 | // Static initializer | |
438 | // ------------------------------------------------------------------------ | |
439 | ||
440 | /** | |
441 | * Computes the instruction types of JVM opcodes. | |
442 | */ | |
443 | static | |
444 | { | |
445 | int i; | |
446 | byte[] b = new byte[220]; | |
447 | String s = "AAAAAAAAAAAAAAAABCKLLDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" | |
448 | + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
449 | + "AAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAIIIIIIIIIIIIIIIIDNOAA" | |
450 | + "AAAAGGGGGGGHAFBFAAFFAAQPIIJJIIIIIIIIIIIIIIIIII"; | |
451 | for(i = 0; i < b.length; ++i) | |
452 | { | |
453 | b[i] = (byte) (s.charAt(i) - 'A'); | |
454 | } | |
455 | TYPE = b; | |
456 | ||
457 | // code to generate the above string | |
458 | // | |
459 | // // SBYTE_INSN instructions | |
460 | // b[Constants.NEWARRAY] = SBYTE_INSN; | |
461 | // b[Constants.BIPUSH] = SBYTE_INSN; | |
462 | // | |
463 | // // SHORT_INSN instructions | |
464 | // b[Constants.SIPUSH] = SHORT_INSN; | |
465 | // | |
466 | // // (IMPL)VAR_INSN instructions | |
467 | // b[Constants.RET] = VAR_INSN; | |
468 | // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { | |
469 | // b[i] = VAR_INSN; | |
470 | // } | |
471 | // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { | |
472 | // b[i] = VAR_INSN; | |
473 | // } | |
474 | // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 | |
475 | // b[i] = IMPLVAR_INSN; | |
476 | // } | |
477 | // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 | |
478 | // b[i] = IMPLVAR_INSN; | |
479 | // } | |
480 | // | |
481 | // // TYPE_INSN instructions | |
482 | // b[Constants.NEW] = TYPE_INSN; | |
483 | // b[Constants.ANEWARRAY] = TYPE_INSN; | |
484 | // b[Constants.CHECKCAST] = TYPE_INSN; | |
485 | // b[Constants.INSTANCEOF] = TYPE_INSN; | |
486 | // | |
487 | // // (Set)FIELDORMETH_INSN instructions | |
488 | // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { | |
489 | // b[i] = FIELDORMETH_INSN; | |
490 | // } | |
491 | // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; | |
492 | // | |
493 | // // LABEL(W)_INSN instructions | |
494 | // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { | |
495 | // b[i] = LABEL_INSN; | |
496 | // } | |
497 | // b[Constants.IFNULL] = LABEL_INSN; | |
498 | // b[Constants.IFNONNULL] = LABEL_INSN; | |
499 | // b[200] = LABELW_INSN; // GOTO_W | |
500 | // b[201] = LABELW_INSN; // JSR_W | |
501 | // // temporary opcodes used internally by ASM - see Label and | |
502 | // MethodWriter | |
503 | // for (i = 202; i < 220; ++i) { | |
504 | // b[i] = LABEL_INSN; | |
505 | // } | |
506 | // | |
507 | // // LDC(_W) instructions | |
508 | // b[Constants.LDC] = LDC_INSN; | |
509 | // b[19] = LDCW_INSN; // LDC_W | |
510 | // b[20] = LDCW_INSN; // LDC2_W | |
511 | // | |
512 | // // special instructions | |
513 | // b[Constants.IINC] = IINC_INSN; | |
514 | // b[Constants.TABLESWITCH] = TABL_INSN; | |
515 | // b[Constants.LOOKUPSWITCH] = LOOK_INSN; | |
516 | // b[Constants.MULTIANEWARRAY] = MANA_INSN; | |
517 | // b[196] = WIDE_INSN; // WIDE | |
518 | // | |
519 | // for (i = 0; i < b.length; ++i) { | |
520 | // System.err.print((char)('A' + b[i])); | |
521 | // } | |
522 | // System.err.println(); | |
523 | } | |
524 | ||
525 | // ------------------------------------------------------------------------ | |
526 | // Constructor | |
527 | // ------------------------------------------------------------------------ | |
528 | ||
529 | /** | |
530 | * Constructs a new {@link ClassWriter} object. | |
531 | * | |
532 | * @param flags option flags that can be used to modify the default behavior | |
533 | * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}. | |
534 | */ | |
535 | public ClassWriter(final int flags){ | |
536 | index = 1; | |
537 | pool = new ByteVector(); | |
538 | items = new Item[256]; | |
539 | threshold = (int) (0.75d * items.length); | |
540 | key = new Item(); | |
541 | key2 = new Item(); | |
542 | key3 = new Item(); | |
543 | this.computeMaxs = (flags & COMPUTE_MAXS) != 0; | |
544 | this.computeFrames = (flags & COMPUTE_FRAMES) != 0; | |
545 | } | |
546 | ||
547 | /** | |
548 | * Constructs a new {@link ClassWriter} object and enables optimizations for | |
549 | * "mostly add" bytecode transformations. These optimizations are the | |
550 | * following: | |
551 | * <p/> | |
552 | * <ul> <li>The constant pool from the original class is copied as is in | |
553 | * the new class, which saves time. New constant pool entries will be added | |
554 | * at the end if necessary, but unused constant pool entries <i>won't be | |
555 | * removed</i>.</li> <li>Methods that are not transformed are copied as | |
556 | * is in the new class, directly from the original class bytecode (i.e. | |
557 | * without emitting visit events for all the method instructions), which | |
558 | * saves a <i>lot</i> of time. Untransformed methods are detected by the | |
559 | * fact that the {@link ClassReader} receives {@link MethodVisitor} objects | |
560 | * that come from a {@link ClassWriter} (and not from a custom | |
561 | * {@link ClassAdapter} or any other {@link ClassVisitor} instance).</li> | |
562 | * </ul> | |
563 | * | |
564 | * @param classReader the {@link ClassReader} used to read the original | |
565 | * class. It will be used to copy the entire constant pool from the | |
566 | * original class and also to copy other fragments of original | |
567 | * bytecode where applicable. | |
568 | * @param flags option flags that can be used to modify the default behavior | |
569 | * of this class. See {@link #COMPUTE_MAXS}, {@link #COMPUTE_FRAMES}. | |
570 | */ | |
571 | public ClassWriter(final ClassReader classReader, final int flags){ | |
572 | this(flags); | |
573 | classReader.copyPool(this); | |
574 | this.cr = classReader; | |
575 | } | |
576 | ||
577 | // ------------------------------------------------------------------------ | |
578 | // Implementation of the ClassVisitor interface | |
579 | // ------------------------------------------------------------------------ | |
580 | ||
581 | public void visit( | |
582 | final int version, | |
583 | final int access, | |
584 | final String name, | |
585 | final String signature, | |
586 | final String superName, | |
587 | final String[] interfaces){ | |
588 | this.version = version; | |
589 | this.access = access; | |
590 | this.name = newClass(name); | |
591 | thisName = name; | |
592 | if(signature != null) | |
593 | { | |
594 | this.signature = newUTF8(signature); | |
595 | } | |
596 | this.superName = superName == null ? 0 : newClass(superName); | |
597 | if(interfaces != null && interfaces.length > 0) | |
598 | { | |
599 | interfaceCount = interfaces.length; | |
600 | this.interfaces = new int[interfaceCount]; | |
601 | for(int i = 0; i < interfaceCount; ++i) | |
602 | { | |
603 | this.interfaces[i] = newClass(interfaces[i]); | |
604 | } | |
605 | } | |
606 | } | |
607 | ||
608 | public void visitSource(final String file, final String debug){ | |
609 | if(file != null) | |
610 | { | |
611 | sourceFile = newUTF8(file); | |
612 | } | |
613 | if(debug != null) | |
614 | { | |
615 | sourceDebug = new ByteVector().putUTF8(debug); | |
616 | } | |
617 | } | |
618 | ||
619 | public void visitOuterClass( | |
620 | final String owner, | |
621 | final String name, | |
622 | final String desc){ | |
623 | enclosingMethodOwner = newClass(owner); | |
624 | if(name != null && desc != null) | |
625 | { | |
626 | enclosingMethod = newNameType(name, desc); | |
627 | } | |
628 | } | |
629 | ||
630 | public AnnotationVisitor visitAnnotation( | |
631 | final String desc, | |
632 | final boolean visible){ | |
633 | ByteVector bv = new ByteVector(); | |
634 | // write type, and reserve space for values count | |
635 | bv.putShort(newUTF8(desc)).putShort(0); | |
636 | AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); | |
637 | if(visible) | |
638 | { | |
639 | aw.next = anns; | |
640 | anns = aw; | |
641 | } | |
642 | else | |
643 | { | |
644 | aw.next = ianns; | |
645 | ianns = aw; | |
646 | } | |
647 | return aw; | |
648 | } | |
649 | ||
650 | public void visitAttribute(final Attribute attr){ | |
651 | attr.next = attrs; | |
652 | attrs = attr; | |
653 | } | |
654 | ||
655 | public void visitInnerClass( | |
656 | final String name, | |
657 | final String outerName, | |
658 | final String innerName, | |
659 | final int access){ | |
660 | if(innerClasses == null) | |
661 | { | |
662 | innerClasses = new ByteVector(); | |
663 | } | |
664 | ++innerClassesCount; | |
665 | innerClasses.putShort(name == null ? 0 : newClass(name)); | |
666 | innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); | |
667 | innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); | |
668 | innerClasses.putShort(access); | |
669 | } | |
670 | ||
671 | public FieldVisitor visitField( | |
672 | final int access, | |
673 | final String name, | |
674 | final String desc, | |
675 | final String signature, | |
676 | final Object value){ | |
677 | return new FieldWriter(this, access, name, desc, signature, value); | |
678 | } | |
679 | ||
680 | public MethodVisitor visitMethod( | |
681 | final int access, | |
682 | final String name, | |
683 | final String desc, | |
684 | final String signature, | |
685 | final String[] exceptions){ | |
686 | return new MethodWriter(this, | |
687 | access, | |
688 | name, | |
689 | desc, | |
690 | signature, | |
691 | exceptions, | |
692 | computeMaxs, | |
693 | computeFrames); | |
694 | } | |
695 | ||
696 | public void visitEnd(){ | |
697 | } | |
698 | ||
699 | // ------------------------------------------------------------------------ | |
700 | // Other public methods | |
701 | // ------------------------------------------------------------------------ | |
702 | ||
703 | /** | |
704 | * Returns the bytecode of the class that was build with this class writer. | |
705 | * | |
706 | * @return the bytecode of the class that was build with this class writer. | |
707 | */ | |
708 | public byte[] toByteArray(){ | |
709 | // computes the real size of the bytecode of this class | |
710 | int size = 24 + 2 * interfaceCount; | |
711 | int nbFields = 0; | |
712 | FieldWriter fb = firstField; | |
713 | while(fb != null) | |
714 | { | |
715 | ++nbFields; | |
716 | size += fb.getSize(); | |
717 | fb = fb.next; | |
718 | } | |
719 | int nbMethods = 0; | |
720 | MethodWriter mb = firstMethod; | |
721 | while(mb != null) | |
722 | { | |
723 | ++nbMethods; | |
724 | size += mb.getSize(); | |
725 | mb = mb.next; | |
726 | } | |
727 | int attributeCount = 0; | |
728 | if(signature != 0) | |
729 | { | |
730 | ++attributeCount; | |
731 | size += 8; | |
732 | newUTF8("Signature"); | |
733 | } | |
734 | if(sourceFile != 0) | |
735 | { | |
736 | ++attributeCount; | |
737 | size += 8; | |
738 | newUTF8("SourceFile"); | |
739 | } | |
740 | if(sourceDebug != null) | |
741 | { | |
742 | ++attributeCount; | |
743 | size += sourceDebug.length + 4; | |
744 | newUTF8("SourceDebugExtension"); | |
745 | } | |
746 | if(enclosingMethodOwner != 0) | |
747 | { | |
748 | ++attributeCount; | |
749 | size += 10; | |
750 | newUTF8("EnclosingMethod"); | |
751 | } | |
752 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
753 | { | |
754 | ++attributeCount; | |
755 | size += 6; | |
756 | newUTF8("Deprecated"); | |
757 | } | |
758 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
759 | && (version & 0xffff) < Opcodes.V1_5) | |
760 | { | |
761 | ++attributeCount; | |
762 | size += 6; | |
763 | newUTF8("Synthetic"); | |
764 | } | |
765 | if(innerClasses != null) | |
766 | { | |
767 | ++attributeCount; | |
768 | size += 8 + innerClasses.length; | |
769 | newUTF8("InnerClasses"); | |
770 | } | |
771 | if(anns != null) | |
772 | { | |
773 | ++attributeCount; | |
774 | size += 8 + anns.getSize(); | |
775 | newUTF8("RuntimeVisibleAnnotations"); | |
776 | } | |
777 | if(ianns != null) | |
778 | { | |
779 | ++attributeCount; | |
780 | size += 8 + ianns.getSize(); | |
781 | newUTF8("RuntimeInvisibleAnnotations"); | |
782 | } | |
783 | if(attrs != null) | |
784 | { | |
785 | attributeCount += attrs.getCount(); | |
786 | size += attrs.getSize(this, null, 0, -1, -1); | |
787 | } | |
788 | size += pool.length; | |
789 | // allocates a byte vector of this size, in order to avoid unnecessary | |
790 | // arraycopy operations in the ByteVector.enlarge() method | |
791 | ByteVector out = new ByteVector(size); | |
792 | out.putInt(0xCAFEBABE).putInt(version); | |
793 | out.putShort(index).putByteArray(pool.data, 0, pool.length); | |
794 | out.putShort(access).putShort(name).putShort(superName); | |
795 | out.putShort(interfaceCount); | |
796 | for(int i = 0; i < interfaceCount; ++i) | |
797 | { | |
798 | out.putShort(interfaces[i]); | |
799 | } | |
800 | out.putShort(nbFields); | |
801 | fb = firstField; | |
802 | while(fb != null) | |
803 | { | |
804 | fb.put(out); | |
805 | fb = fb.next; | |
806 | } | |
807 | out.putShort(nbMethods); | |
808 | mb = firstMethod; | |
809 | while(mb != null) | |
810 | { | |
811 | mb.put(out); | |
812 | mb = mb.next; | |
813 | } | |
814 | out.putShort(attributeCount); | |
815 | if(signature != 0) | |
816 | { | |
817 | out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); | |
818 | } | |
819 | if(sourceFile != 0) | |
820 | { | |
821 | out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); | |
822 | } | |
823 | if(sourceDebug != null) | |
824 | { | |
825 | int len = sourceDebug.length - 2; | |
826 | out.putShort(newUTF8("SourceDebugExtension")).putInt(len); | |
827 | out.putByteArray(sourceDebug.data, 2, len); | |
828 | } | |
829 | if(enclosingMethodOwner != 0) | |
830 | { | |
831 | out.putShort(newUTF8("EnclosingMethod")).putInt(4); | |
832 | out.putShort(enclosingMethodOwner).putShort(enclosingMethod); | |
833 | } | |
834 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
835 | { | |
836 | out.putShort(newUTF8("Deprecated")).putInt(0); | |
837 | } | |
838 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
839 | && (version & 0xffff) < Opcodes.V1_5) | |
840 | { | |
841 | out.putShort(newUTF8("Synthetic")).putInt(0); | |
842 | } | |
843 | if(innerClasses != null) | |
844 | { | |
845 | out.putShort(newUTF8("InnerClasses")); | |
846 | out.putInt(innerClasses.length + 2).putShort(innerClassesCount); | |
847 | out.putByteArray(innerClasses.data, 0, innerClasses.length); | |
848 | } | |
849 | if(anns != null) | |
850 | { | |
851 | out.putShort(newUTF8("RuntimeVisibleAnnotations")); | |
852 | anns.put(out); | |
853 | } | |
854 | if(ianns != null) | |
855 | { | |
856 | out.putShort(newUTF8("RuntimeInvisibleAnnotations")); | |
857 | ianns.put(out); | |
858 | } | |
859 | if(attrs != null) | |
860 | { | |
861 | attrs.put(this, null, 0, -1, -1, out); | |
862 | } | |
863 | if(invalidFrames) | |
864 | { | |
865 | ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); | |
866 | new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES); | |
867 | return cw.toByteArray(); | |
868 | } | |
869 | return out.data; | |
870 | } | |
871 | ||
872 | // ------------------------------------------------------------------------ | |
873 | // Utility methods: constant pool management | |
874 | // ------------------------------------------------------------------------ | |
875 | ||
876 | /** | |
877 | * Adds a number or string constant to the constant pool of the class being | |
878 | * build. Does nothing if the constant pool already contains a similar item. | |
879 | * | |
880 | * @param cst the value of the constant to be added to the constant pool. | |
881 | * This parameter must be an {@link Integer}, a {@link Float}, a | |
882 | * {@link Long}, a {@link Double}, a {@link String} or a | |
883 | * {@link Type}. | |
884 | * @return a new or already existing constant item with the given value. | |
885 | */ | |
886 | Item newConstItem(final Object cst){ | |
887 | if(cst instanceof Integer) | |
888 | { | |
889 | int val = ((Integer) cst).intValue(); | |
890 | return newInteger(val); | |
891 | } | |
892 | else if(cst instanceof Byte) | |
893 | { | |
894 | int val = ((Byte) cst).intValue(); | |
895 | return newInteger(val); | |
896 | } | |
897 | else if(cst instanceof Character) | |
898 | { | |
899 | int val = ((Character) cst).charValue(); | |
900 | return newInteger(val); | |
901 | } | |
902 | else if(cst instanceof Short) | |
903 | { | |
904 | int val = ((Short) cst).intValue(); | |
905 | return newInteger(val); | |
906 | } | |
907 | else if(cst instanceof Boolean) | |
908 | { | |
909 | int val = ((Boolean) cst).booleanValue() ? 1 : 0; | |
910 | return newInteger(val); | |
911 | } | |
912 | else if(cst instanceof Float) | |
913 | { | |
914 | float val = ((Float) cst).floatValue(); | |
915 | return newFloat(val); | |
916 | } | |
917 | else if(cst instanceof Long) | |
918 | { | |
919 | long val = ((Long) cst).longValue(); | |
920 | return newLong(val); | |
921 | } | |
922 | else if(cst instanceof Double) | |
923 | { | |
924 | double val = ((Double) cst).doubleValue(); | |
925 | return newDouble(val); | |
926 | } | |
927 | else if(cst instanceof String) | |
928 | { | |
929 | return newString((String) cst); | |
930 | } | |
931 | else if(cst instanceof Type) | |
932 | { | |
933 | Type t = (Type) cst; | |
934 | return newClassItem(t.getSort() == Type.OBJECT | |
935 | ? t.getInternalName() | |
936 | : t.getDescriptor()); | |
937 | } | |
938 | else | |
939 | { | |
940 | throw new IllegalArgumentException("value " + cst); | |
941 | } | |
942 | } | |
943 | ||
944 | /** | |
945 | * Adds a number or string constant to the constant pool of the class being | |
946 | * build. Does nothing if the constant pool already contains a similar item. | |
947 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
948 | * normally not needed by class generators or adapters.</i> | |
949 | * | |
950 | * @param cst the value of the constant to be added to the constant pool. | |
951 | * This parameter must be an {@link Integer}, a {@link Float}, a | |
952 | * {@link Long}, a {@link Double} or a {@link String}. | |
953 | * @return the index of a new or already existing constant item with the | |
954 | * given value. | |
955 | */ | |
956 | public int newConst(final Object cst){ | |
957 | return newConstItem(cst).index; | |
958 | } | |
959 | ||
960 | /** | |
961 | * Adds an UTF8 string to the constant pool of the class being build. Does | |
962 | * nothing if the constant pool already contains a similar item. <i>This | |
963 | * method is intended for {@link Attribute} sub classes, and is normally not | |
964 | * needed by class generators or adapters.</i> | |
965 | * | |
966 | * @param value the String value. | |
967 | * @return the index of a new or already existing UTF8 item. | |
968 | */ | |
969 | public int newUTF8(final String value){ | |
970 | key.set(UTF8, value, null, null); | |
971 | Item result = get(key); | |
972 | if(result == null) | |
973 | { | |
974 | pool.putByte(UTF8).putUTF8(value); | |
975 | result = new Item(index++, key); | |
976 | put(result); | |
977 | } | |
978 | return result.index; | |
979 | } | |
980 | ||
981 | /** | |
982 | * Adds a class reference to the constant pool of the class being build. | |
983 | * Does nothing if the constant pool already contains a similar item. | |
984 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
985 | * normally not needed by class generators or adapters.</i> | |
986 | * | |
987 | * @param value the internal name of the class. | |
988 | * @return a new or already existing class reference item. | |
989 | */ | |
990 | Item newClassItem(final String value){ | |
991 | key2.set(CLASS, value, null, null); | |
992 | Item result = get(key2); | |
993 | if(result == null) | |
994 | { | |
995 | pool.put12(CLASS, newUTF8(value)); | |
996 | result = new Item(index++, key2); | |
997 | put(result); | |
998 | } | |
999 | return result; | |
1000 | } | |
1001 | ||
1002 | /** | |
1003 | * Adds a class reference to the constant pool of the class being build. | |
1004 | * Does nothing if the constant pool already contains a similar item. | |
1005 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1006 | * normally not needed by class generators or adapters.</i> | |
1007 | * | |
1008 | * @param value the internal name of the class. | |
1009 | * @return the index of a new or already existing class reference item. | |
1010 | */ | |
1011 | public int newClass(final String value){ | |
1012 | return newClassItem(value).index; | |
1013 | } | |
1014 | ||
1015 | /** | |
1016 | * Adds a field reference to the constant pool of the class being build. | |
1017 | * Does nothing if the constant pool already contains a similar item. | |
1018 | * | |
1019 | * @param owner the internal name of the field's owner class. | |
1020 | * @param name the field's name. | |
1021 | * @param desc the field's descriptor. | |
1022 | * @return a new or already existing field reference item. | |
1023 | */ | |
1024 | Item newFieldItem(final String owner, final String name, final String desc){ | |
1025 | key3.set(FIELD, owner, name, desc); | |
1026 | Item result = get(key3); | |
1027 | if(result == null) | |
1028 | { | |
1029 | put122(FIELD, newClass(owner), newNameType(name, desc)); | |
1030 | result = new Item(index++, key3); | |
1031 | put(result); | |
1032 | } | |
1033 | return result; | |
1034 | } | |
1035 | ||
1036 | /** | |
1037 | * Adds a field reference to the constant pool of the class being build. | |
1038 | * Does nothing if the constant pool already contains a similar item. | |
1039 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1040 | * normally not needed by class generators or adapters.</i> | |
1041 | * | |
1042 | * @param owner the internal name of the field's owner class. | |
1043 | * @param name the field's name. | |
1044 | * @param desc the field's descriptor. | |
1045 | * @return the index of a new or already existing field reference item. | |
1046 | */ | |
1047 | public int newField(final String owner, final String name, final String desc){ | |
1048 | return newFieldItem(owner, name, desc).index; | |
1049 | } | |
1050 | ||
1051 | /** | |
1052 | * Adds a method reference to the constant pool of the class being build. | |
1053 | * Does nothing if the constant pool already contains a similar item. | |
1054 | * | |
1055 | * @param owner the internal name of the method's owner class. | |
1056 | * @param name the method's name. | |
1057 | * @param desc the method's descriptor. | |
1058 | * @param itf <tt>true</tt> if <tt>owner</tt> is an interface. | |
1059 | * @return a new or already existing method reference item. | |
1060 | */ | |
1061 | Item newMethodItem( | |
1062 | final String owner, | |
1063 | final String name, | |
1064 | final String desc, | |
1065 | final boolean itf){ | |
1066 | int type = itf ? IMETH : METH; | |
1067 | key3.set(type, owner, name, desc); | |
1068 | Item result = get(key3); | |
1069 | if(result == null) | |
1070 | { | |
1071 | put122(type, newClass(owner), newNameType(name, desc)); | |
1072 | result = new Item(index++, key3); | |
1073 | put(result); | |
1074 | } | |
1075 | return result; | |
1076 | } | |
1077 | ||
1078 | /** | |
1079 | * Adds a method reference to the constant pool of the class being build. | |
1080 | * Does nothing if the constant pool already contains a similar item. | |
1081 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1082 | * normally not needed by class generators or adapters.</i> | |
1083 | * | |
1084 | * @param owner the internal name of the method's owner class. | |
1085 | * @param name the method's name. | |
1086 | * @param desc the method's descriptor. | |
1087 | * @param itf <tt>true</tt> if <tt>owner</tt> is an interface. | |
1088 | * @return the index of a new or already existing method reference item. | |
1089 | */ | |
1090 | public int newMethod( | |
1091 | final String owner, | |
1092 | final String name, | |
1093 | final String desc, | |
1094 | final boolean itf){ | |
1095 | return newMethodItem(owner, name, desc, itf).index; | |
1096 | } | |
1097 | ||
1098 | /** | |
1099 | * Adds an integer to the constant pool of the class being build. Does | |
1100 | * nothing if the constant pool already contains a similar item. | |
1101 | * | |
1102 | * @param value the int value. | |
1103 | * @return a new or already existing int item. | |
1104 | */ | |
1105 | Item newInteger(final int value){ | |
1106 | key.set(value); | |
1107 | Item result = get(key); | |
1108 | if(result == null) | |
1109 | { | |
1110 | pool.putByte(INT).putInt(value); | |
1111 | result = new Item(index++, key); | |
1112 | put(result); | |
1113 | } | |
1114 | return result; | |
1115 | } | |
1116 | ||
1117 | /** | |
1118 | * Adds a float to the constant pool of the class being build. Does nothing | |
1119 | * if the constant pool already contains a similar item. | |
1120 | * | |
1121 | * @param value the float value. | |
1122 | * @return a new or already existing float item. | |
1123 | */ | |
1124 | Item newFloat(final float value){ | |
1125 | key.set(value); | |
1126 | Item result = get(key); | |
1127 | if(result == null) | |
1128 | { | |
1129 | pool.putByte(FLOAT).putInt(key.intVal); | |
1130 | result = new Item(index++, key); | |
1131 | put(result); | |
1132 | } | |
1133 | return result; | |
1134 | } | |
1135 | ||
1136 | /** | |
1137 | * Adds a long to the constant pool of the class being build. Does nothing | |
1138 | * if the constant pool already contains a similar item. | |
1139 | * | |
1140 | * @param value the long value. | |
1141 | * @return a new or already existing long item. | |
1142 | */ | |
1143 | Item newLong(final long value){ | |
1144 | key.set(value); | |
1145 | Item result = get(key); | |
1146 | if(result == null) | |
1147 | { | |
1148 | pool.putByte(LONG).putLong(value); | |
1149 | result = new Item(index, key); | |
1150 | put(result); | |
1151 | index += 2; | |
1152 | } | |
1153 | return result; | |
1154 | } | |
1155 | ||
1156 | /** | |
1157 | * Adds a double to the constant pool of the class being build. Does nothing | |
1158 | * if the constant pool already contains a similar item. | |
1159 | * | |
1160 | * @param value the double value. | |
1161 | * @return a new or already existing double item. | |
1162 | */ | |
1163 | Item newDouble(final double value){ | |
1164 | key.set(value); | |
1165 | Item result = get(key); | |
1166 | if(result == null) | |
1167 | { | |
1168 | pool.putByte(DOUBLE).putLong(key.longVal); | |
1169 | result = new Item(index, key); | |
1170 | put(result); | |
1171 | index += 2; | |
1172 | } | |
1173 | return result; | |
1174 | } | |
1175 | ||
1176 | /** | |
1177 | * Adds a string to the constant pool of the class being build. Does nothing | |
1178 | * if the constant pool already contains a similar item. | |
1179 | * | |
1180 | * @param value the String value. | |
1181 | * @return a new or already existing string item. | |
1182 | */ | |
1183 | private Item newString(final String value){ | |
1184 | key2.set(STR, value, null, null); | |
1185 | Item result = get(key2); | |
1186 | if(result == null) | |
1187 | { | |
1188 | pool.put12(STR, newUTF8(value)); | |
1189 | result = new Item(index++, key2); | |
1190 | put(result); | |
1191 | } | |
1192 | return result; | |
1193 | } | |
1194 | ||
1195 | /** | |
1196 | * Adds a name and type to the constant pool of the class being build. Does | |
1197 | * nothing if the constant pool already contains a similar item. <i>This | |
1198 | * method is intended for {@link Attribute} sub classes, and is normally not | |
1199 | * needed by class generators or adapters.</i> | |
1200 | * | |
1201 | * @param name a name. | |
1202 | * @param desc a type descriptor. | |
1203 | * @return the index of a new or already existing name and type item. | |
1204 | */ | |
1205 | public int newNameType(final String name, final String desc){ | |
1206 | key2.set(NAME_TYPE, name, desc, null); | |
1207 | Item result = get(key2); | |
1208 | if(result == null) | |
1209 | { | |
1210 | put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); | |
1211 | result = new Item(index++, key2); | |
1212 | put(result); | |
1213 | } | |
1214 | return result.index; | |
1215 | } | |
1216 | ||
1217 | /** | |
1218 | * Adds the given internal name to {@link #typeTable} and returns its index. | |
1219 | * Does nothing if the type table already contains this internal name. | |
1220 | * | |
1221 | * @param type the internal name to be added to the type table. | |
1222 | * @return the index of this internal name in the type table. | |
1223 | */ | |
1224 | int addType(final String type){ | |
1225 | key.set(TYPE_NORMAL, type, null, null); | |
1226 | Item result = get(key); | |
1227 | if(result == null) | |
1228 | { | |
1229 | result = addType(key); | |
1230 | } | |
1231 | return result.index; | |
1232 | } | |
1233 | ||
1234 | /** | |
1235 | * Adds the given "uninitialized" type to {@link #typeTable} and returns its | |
1236 | * index. This method is used for UNINITIALIZED types, made of an internal | |
1237 | * name and a bytecode offset. | |
1238 | * | |
1239 | * @param type the internal name to be added to the type table. | |
1240 | * @param offset the bytecode offset of the NEW instruction that created | |
1241 | * this UNINITIALIZED type value. | |
1242 | * @return the index of this internal name in the type table. | |
1243 | */ | |
1244 | int addUninitializedType(final String type, final int offset){ | |
1245 | key.type = TYPE_UNINIT; | |
1246 | key.intVal = offset; | |
1247 | key.strVal1 = type; | |
1248 | key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); | |
1249 | Item result = get(key); | |
1250 | if(result == null) | |
1251 | { | |
1252 | result = addType(key); | |
1253 | } | |
1254 | return result.index; | |
1255 | } | |
1256 | ||
1257 | /** | |
1258 | * Adds the given Item to {@link #typeTable}. | |
1259 | * | |
1260 | * @param item the value to be added to the type table. | |
1261 | * @return the added Item, which a new Item instance with the same value as | |
1262 | * the given Item. | |
1263 | */ | |
1264 | private Item addType(final Item item){ | |
1265 | ++typeCount; | |
1266 | Item result = new Item(typeCount, key); | |
1267 | put(result); | |
1268 | if(typeTable == null) | |
1269 | { | |
1270 | typeTable = new Item[16]; | |
1271 | } | |
1272 | if(typeCount == typeTable.length) | |
1273 | { | |
1274 | Item[] newTable = new Item[2 * typeTable.length]; | |
1275 | System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); | |
1276 | typeTable = newTable; | |
1277 | } | |
1278 | typeTable[typeCount] = result; | |
1279 | return result; | |
1280 | } | |
1281 | ||
1282 | /** | |
1283 | * Returns the index of the common super type of the two given types. This | |
1284 | * method calls {@link #getCommonSuperClass} and caches the result in the | |
1285 | * {@link #items} hash table to speedup future calls with the same | |
1286 | * parameters. | |
1287 | * | |
1288 | * @param type1 index of an internal name in {@link #typeTable}. | |
1289 | * @param type2 index of an internal name in {@link #typeTable}. | |
1290 | * @return the index of the common super type of the two given types. | |
1291 | */ | |
1292 | int getMergedType(final int type1, final int type2){ | |
1293 | key2.type = TYPE_MERGED; | |
1294 | key2.longVal = type1 | (((long) type2) << 32); | |
1295 | key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); | |
1296 | Item result = get(key2); | |
1297 | if(result == null) | |
1298 | { | |
1299 | String t = typeTable[type1].strVal1; | |
1300 | String u = typeTable[type2].strVal1; | |
1301 | key2.intVal = addType(getCommonSuperClass(t, u)); | |
1302 | result = new Item((short) 0, key2); | |
1303 | put(result); | |
1304 | } | |
1305 | return result.intVal; | |
1306 | } | |
1307 | ||
1308 | /** | |
1309 | * Returns the common super type of the two given types. The default | |
1310 | * implementation of this method <i>loads<i> the two given classes and uses | |
1311 | * the java.lang.Class methods to find the common super class. It can be | |
1312 | * overriden to compute this common super type in other ways, in particular | |
1313 | * without actually loading any class, or to take into account the class | |
1314 | * that is currently being generated by this ClassWriter, which can of | |
1315 | * course not be loaded since it is under construction. | |
1316 | * | |
1317 | * @param type1 the internal name of a class. | |
1318 | * @param type2 the internal name of another class. | |
1319 | * @return the internal name of the common super class of the two given | |
1320 | * classes. | |
1321 | */ | |
1322 | protected String getCommonSuperClass(final String type1, final String type2){ | |
1323 | Class c, d; | |
1324 | try | |
1325 | { | |
1326 | c = Class.forName(type1.replace('/', '.')); | |
1327 | d = Class.forName(type2.replace('/', '.')); | |
1328 | } | |
1329 | catch(ClassNotFoundException e) | |
1330 | { | |
1331 | throw new RuntimeException(e); | |
1332 | } | |
1333 | if(c.isAssignableFrom(d)) | |
1334 | { | |
1335 | return type1; | |
1336 | } | |
1337 | if(d.isAssignableFrom(c)) | |
1338 | { | |
1339 | return type2; | |
1340 | } | |
1341 | if(c.isInterface() || d.isInterface()) | |
1342 | { | |
1343 | return "java/lang/Object"; | |
1344 | } | |
1345 | else | |
1346 | { | |
1347 | do | |
1348 | { | |
1349 | c = c.getSuperclass(); | |
1350 | } while(!c.isAssignableFrom(d)); | |
1351 | return c.getName().replace('.', '/'); | |
1352 | } | |
1353 | } | |
1354 | ||
1355 | /** | |
1356 | * Returns the constant pool's hash table item which is equal to the given | |
1357 | * item. | |
1358 | * | |
1359 | * @param key a constant pool item. | |
1360 | * @return the constant pool's hash table item which is equal to the given | |
1361 | * item, or <tt>null</tt> if there is no such item. | |
1362 | */ | |
1363 | private Item get(final Item key){ | |
1364 | Item i = items[key.hashCode % items.length]; | |
1365 | while(i != null && !key.isEqualTo(i)) | |
1366 | { | |
1367 | i = i.next; | |
1368 | } | |
1369 | return i; | |
1370 | } | |
1371 | ||
1372 | /** | |
1373 | * Puts the given item in the constant pool's hash table. The hash table | |
1374 | * <i>must</i> not already contains this item. | |
1375 | * | |
1376 | * @param i the item to be added to the constant pool's hash table. | |
1377 | */ | |
1378 | private void put(final Item i){ | |
1379 | if(index > threshold) | |
1380 | { | |
1381 | int ll = items.length; | |
1382 | int nl = ll * 2 + 1; | |
1383 | Item[] newItems = new Item[nl]; | |
1384 | for(int l = ll - 1; l >= 0; --l) | |
1385 | { | |
1386 | Item j = items[l]; | |
1387 | while(j != null) | |
1388 | { | |
1389 | int index = j.hashCode % newItems.length; | |
1390 | Item k = j.next; | |
1391 | j.next = newItems[index]; | |
1392 | newItems[index] = j; | |
1393 | j = k; | |
1394 | } | |
1395 | } | |
1396 | items = newItems; | |
1397 | threshold = (int) (nl * 0.75); | |
1398 | } | |
1399 | int index = i.hashCode % items.length; | |
1400 | i.next = items[index]; | |
1401 | items[index] = i; | |
1402 | } | |
1403 | ||
1404 | /** | |
1405 | * Puts one byte and two shorts into the constant pool. | |
1406 | * | |
1407 | * @param b a byte. | |
1408 | * @param s1 a short. | |
1409 | * @param s2 another short. | |
1410 | */ | |
1411 | private void put122(final int b, final int s1, final int s2){ | |
1412 | pool.put12(b, s1).putShort(s2); | |
1413 | } | |
1414 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * An edge in the control flow graph of a method body. See {@link Label Label}. | |
33 | * | |
34 | * @author Eric Bruneton | |
35 | */ | |
36 | class Edge{ | |
37 | ||
38 | /** | |
39 | * Denotes a normal control flow graph edge. | |
40 | */ | |
41 | final static int NORMAL = 0; | |
42 | ||
43 | /** | |
44 | * Denotes a control flow graph edge corresponding to an exception handler. | |
45 | * More precisely any {@link Edge} whose {@link #info} is strictly positive | |
46 | * corresponds to an exception handler. The actual value of {@link #info} is | |
47 | * the index, in the {@link ClassWriter} type table, of the exception that | |
48 | * is catched. | |
49 | */ | |
50 | final static int EXCEPTION = 0x7FFFFFFF; | |
51 | ||
52 | /** | |
53 | * Information about this control flow graph edge. If | |
54 | * {@link ClassWriter#COMPUTE_MAXS} is used this field is the (relative) | |
55 | * stack size in the basic block from which this edge originates. This size | |
56 | * is equal to the stack size at the "jump" instruction to which this edge | |
57 | * corresponds, relatively to the stack size at the beginning of the | |
58 | * originating basic block. If {@link ClassWriter#COMPUTE_FRAMES} is used, | |
59 | * this field is the kind of this control flow graph edge (i.e. NORMAL or | |
60 | * EXCEPTION). | |
61 | */ | |
62 | int info; | |
63 | ||
64 | /** | |
65 | * The successor block of the basic block from which this edge originates. | |
66 | */ | |
67 | Label successor; | |
68 | ||
69 | /** | |
70 | * The next edge in the list of successors of the originating basic block. | |
71 | * See {@link Label#successors successors}. | |
72 | */ | |
73 | Edge next; | |
74 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A visitor to visit a Java field. The methods of this interface must be called | |
33 | * in the following order: ( <tt>visitAnnotation</tt> | | |
34 | * <tt>visitAttribute</tt> )* <tt>visitEnd</tt>. | |
35 | * | |
36 | * @author Eric Bruneton | |
37 | */ | |
38 | public interface FieldVisitor{ | |
39 | ||
40 | /** | |
41 | * Visits an annotation of the field. | |
42 | * | |
43 | * @param desc the class descriptor of the annotation class. | |
44 | * @param visible <tt>true</tt> if the annotation is visible at runtime. | |
45 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
46 | * this visitor is not interested in visiting this annotation. | |
47 | */ | |
48 | AnnotationVisitor visitAnnotation(String desc, boolean visible); | |
49 | ||
50 | /** | |
51 | * Visits a non standard attribute of the field. | |
52 | * | |
53 | * @param attr an attribute. | |
54 | */ | |
55 | void visitAttribute(Attribute attr); | |
56 | ||
57 | /** | |
58 | * Visits the end of the field. This method, which is the last one to be | |
59 | * called, is used to inform the visitor that all the annotations and | |
60 | * attributes of the field have been visited. | |
61 | */ | |
62 | void visitEnd(); | |
63 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * An {@link FieldVisitor} that generates Java fields in bytecode form. | |
33 | * | |
34 | * @author Eric Bruneton | |
35 | */ | |
36 | final class FieldWriter implements FieldVisitor{ | |
37 | ||
38 | /** | |
39 | * Next field writer (see {@link ClassWriter#firstField firstField}). | |
40 | */ | |
41 | FieldWriter next; | |
42 | ||
43 | /** | |
44 | * The class writer to which this field must be added. | |
45 | */ | |
46 | private ClassWriter cw; | |
47 | ||
48 | /** | |
49 | * Access flags of this field. | |
50 | */ | |
51 | private int access; | |
52 | ||
53 | /** | |
54 | * The index of the constant pool item that contains the name of this | |
55 | * method. | |
56 | */ | |
57 | private int name; | |
58 | ||
59 | /** | |
60 | * The index of the constant pool item that contains the descriptor of this | |
61 | * field. | |
62 | */ | |
63 | private int desc; | |
64 | ||
65 | /** | |
66 | * The index of the constant pool item that contains the signature of this | |
67 | * field. | |
68 | */ | |
69 | private int signature; | |
70 | ||
71 | /** | |
72 | * The index of the constant pool item that contains the constant value of | |
73 | * this field. | |
74 | */ | |
75 | private int value; | |
76 | ||
77 | /** | |
78 | * The runtime visible annotations of this field. May be <tt>null</tt>. | |
79 | */ | |
80 | private AnnotationWriter anns; | |
81 | ||
82 | /** | |
83 | * The runtime invisible annotations of this field. May be <tt>null</tt>. | |
84 | */ | |
85 | private AnnotationWriter ianns; | |
86 | ||
87 | /** | |
88 | * The non standard attributes of this field. May be <tt>null</tt>. | |
89 | */ | |
90 | private Attribute attrs; | |
91 | ||
92 | // ------------------------------------------------------------------------ | |
93 | // Constructor | |
94 | // ------------------------------------------------------------------------ | |
95 | ||
96 | /** | |
97 | * Constructs a new {@link FieldWriter}. | |
98 | * | |
99 | * @param cw the class writer to which this field must be added. | |
100 | * @param access the field's access flags (see {@link Opcodes}). | |
101 | * @param name the field's name. | |
102 | * @param desc the field's descriptor (see {@link Type}). | |
103 | * @param signature the field's signature. May be <tt>null</tt>. | |
104 | * @param value the field's constant value. May be <tt>null</tt>. | |
105 | */ | |
106 | protected FieldWriter( | |
107 | final ClassWriter cw, | |
108 | final int access, | |
109 | final String name, | |
110 | final String desc, | |
111 | final String signature, | |
112 | final Object value){ | |
113 | if(cw.firstField == null) | |
114 | { | |
115 | cw.firstField = this; | |
116 | } | |
117 | else | |
118 | { | |
119 | cw.lastField.next = this; | |
120 | } | |
121 | cw.lastField = this; | |
122 | this.cw = cw; | |
123 | this.access = access; | |
124 | this.name = cw.newUTF8(name); | |
125 | this.desc = cw.newUTF8(desc); | |
126 | if(signature != null) | |
127 | { | |
128 | this.signature = cw.newUTF8(signature); | |
129 | } | |
130 | if(value != null) | |
131 | { | |
132 | this.value = cw.newConstItem(value).index; | |
133 | } | |
134 | } | |
135 | ||
136 | // ------------------------------------------------------------------------ | |
137 | // Implementation of the FieldVisitor interface | |
138 | // ------------------------------------------------------------------------ | |
139 | ||
140 | public AnnotationVisitor visitAnnotation( | |
141 | final String desc, | |
142 | final boolean visible){ | |
143 | ByteVector bv = new ByteVector(); | |
144 | // write type, and reserve space for values count | |
145 | bv.putShort(cw.newUTF8(desc)).putShort(0); | |
146 | AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); | |
147 | if(visible) | |
148 | { | |
149 | aw.next = anns; | |
150 | anns = aw; | |
151 | } | |
152 | else | |
153 | { | |
154 | aw.next = ianns; | |
155 | ianns = aw; | |
156 | } | |
157 | return aw; | |
158 | } | |
159 | ||
160 | public void visitAttribute(final Attribute attr){ | |
161 | attr.next = attrs; | |
162 | attrs = attr; | |
163 | } | |
164 | ||
165 | public void visitEnd(){ | |
166 | } | |
167 | ||
168 | // ------------------------------------------------------------------------ | |
169 | // Utility methods | |
170 | // ------------------------------------------------------------------------ | |
171 | ||
172 | /** | |
173 | * Returns the size of this field. | |
174 | * | |
175 | * @return the size of this field. | |
176 | */ | |
177 | int getSize(){ | |
178 | int size = 8; | |
179 | if(value != 0) | |
180 | { | |
181 | cw.newUTF8("ConstantValue"); | |
182 | size += 8; | |
183 | } | |
184 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
185 | && (cw.version & 0xffff) < Opcodes.V1_5) | |
186 | { | |
187 | cw.newUTF8("Synthetic"); | |
188 | size += 6; | |
189 | } | |
190 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
191 | { | |
192 | cw.newUTF8("Deprecated"); | |
193 | size += 6; | |
194 | } | |
195 | if(signature != 0) | |
196 | { | |
197 | cw.newUTF8("Signature"); | |
198 | size += 8; | |
199 | } | |
200 | if(anns != null) | |
201 | { | |
202 | cw.newUTF8("RuntimeVisibleAnnotations"); | |
203 | size += 8 + anns.getSize(); | |
204 | } | |
205 | if(ianns != null) | |
206 | { | |
207 | cw.newUTF8("RuntimeInvisibleAnnotations"); | |
208 | size += 8 + ianns.getSize(); | |
209 | } | |
210 | if(attrs != null) | |
211 | { | |
212 | size += attrs.getSize(cw, null, 0, -1, -1); | |
213 | } | |
214 | return size; | |
215 | } | |
216 | ||
217 | /** | |
218 | * Puts the content of this field into the given byte vector. | |
219 | * | |
220 | * @param out where the content of this field must be put. | |
221 | */ | |
222 | void put(final ByteVector out){ | |
223 | out.putShort(access).putShort(name).putShort(desc); | |
224 | int attributeCount = 0; | |
225 | if(value != 0) | |
226 | { | |
227 | ++attributeCount; | |
228 | } | |
229 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
230 | && (cw.version & 0xffff) < Opcodes.V1_5) | |
231 | { | |
232 | ++attributeCount; | |
233 | } | |
234 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
235 | { | |
236 | ++attributeCount; | |
237 | } | |
238 | if(signature != 0) | |
239 | { | |
240 | ++attributeCount; | |
241 | } | |
242 | if(anns != null) | |
243 | { | |
244 | ++attributeCount; | |
245 | } | |
246 | if(ianns != null) | |
247 | { | |
248 | ++attributeCount; | |
249 | } | |
250 | if(attrs != null) | |
251 | { | |
252 | attributeCount += attrs.getCount(); | |
253 | } | |
254 | out.putShort(attributeCount); | |
255 | if(value != 0) | |
256 | { | |
257 | out.putShort(cw.newUTF8("ConstantValue")); | |
258 | out.putInt(2).putShort(value); | |
259 | } | |
260 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
261 | && (cw.version & 0xffff) < Opcodes.V1_5) | |
262 | { | |
263 | out.putShort(cw.newUTF8("Synthetic")).putInt(0); | |
264 | } | |
265 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
266 | { | |
267 | out.putShort(cw.newUTF8("Deprecated")).putInt(0); | |
268 | } | |
269 | if(signature != 0) | |
270 | { | |
271 | out.putShort(cw.newUTF8("Signature")); | |
272 | out.putInt(2).putShort(signature); | |
273 | } | |
274 | if(anns != null) | |
275 | { | |
276 | out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); | |
277 | anns.put(out); | |
278 | } | |
279 | if(ianns != null) | |
280 | { | |
281 | out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); | |
282 | ianns.put(out); | |
283 | } | |
284 | if(attrs != null) | |
285 | { | |
286 | attrs.put(cw, null, 0, -1, -1, out); | |
287 | } | |
288 | } | |
289 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * Information about the input and output stack map frames of a basic block. | |
33 | * | |
34 | * @author Eric Bruneton | |
35 | */ | |
36 | final class Frame{ | |
37 | ||
38 | /* | |
39 | * Frames are computed in a two steps process: during the visit of each | |
40 | * instruction, the state of the frame at the end of current basic block is | |
41 | * updated by simulating the action of the instruction on the previous state | |
42 | * of this so called "output frame". In visitMaxs, a fix point algorithm is | |
43 | * used to compute the "input frame" of each basic block, i.e. the stack map | |
44 | * frame at the begining of the basic block, starting from the input frame | |
45 | * of the first basic block (which is computed from the method descriptor), | |
46 | * and by using the previously computed output frames to compute the input | |
47 | * state of the other blocks. | |
48 | * | |
49 | * All output and input frames are stored as arrays of integers. Reference | |
50 | * and array types are represented by an index into a type table (which is | |
51 | * not the same as the constant pool of the class, in order to avoid adding | |
52 | * unnecessary constants in the pool - not all computed frames will end up | |
53 | * being stored in the stack map table). This allows very fast type | |
54 | * comparisons. | |
55 | * | |
56 | * Output stack map frames are computed relatively to the input frame of the | |
57 | * basic block, which is not yet known when output frames are computed. It | |
58 | * is therefore necessary to be able to represent abstract types such as | |
59 | * "the type at position x in the input frame locals" or "the type at | |
60 | * position x from the top of the input frame stack" or even "the type at | |
61 | * position x in the input frame, with y more (or less) array dimensions". | |
62 | * This explains the rather complicated type format used in output frames. | |
63 | * | |
64 | * This format is the following: DIM KIND VALUE (4, 4 and 24 bits). DIM is a | |
65 | * signed number of array dimensions (from -8 to 7). KIND is either BASE, | |
66 | * LOCAL or STACK. BASE is used for types that are not relative to the input | |
67 | * frame. LOCAL is used for types that are relative to the input local | |
68 | * variable types. STACK is used for types that are relative to the input | |
69 | * stack types. VALUE depends on KIND. For LOCAL types, it is an index in | |
70 | * the input local variable types. For STACK types, it is a position | |
71 | * relatively to the top of input frame stack. For BASE types, it is either | |
72 | * one of the constants defined in FrameVisitor, or for OBJECT and | |
73 | * UNINITIALIZED types, a tag and an index in the type table. | |
74 | * | |
75 | * Output frames can contain types of any kind and with a positive or | |
76 | * negative dimension (and even unassigned types, represented by 0 - which | |
77 | * does not correspond to any valid type value). Input frames can only | |
78 | * contain BASE types of positive or null dimension. In all cases the type | |
79 | * table contains only internal type names (array type descriptors are | |
80 | * forbidden - dimensions must be represented through the DIM field). | |
81 | * | |
82 | * The LONG and DOUBLE types are always represented by using two slots (LONG + | |
83 | * TOP or DOUBLE + TOP), for local variable types as well as in the operand | |
84 | * stack. This is necessary to be able to simulate DUPx_y instructions, | |
85 | * whose effect would be dependent on the actual type values if types were | |
86 | * always represented by a single slot in the stack (and this is not | |
87 | * possible, since actual type values are not always known - cf LOCAL and | |
88 | * STACK type kinds). | |
89 | */ | |
90 | ||
91 | /** | |
92 | * Mask to get the dimension of a frame type. This dimension is a signed | |
93 | * integer between -8 and 7. | |
94 | */ | |
95 | final static int DIM = 0xF0000000; | |
96 | ||
97 | /** | |
98 | * Constant to be added to a type to get a type with one more dimension. | |
99 | */ | |
100 | final static int ARRAY_OF = 0x10000000; | |
101 | ||
102 | /** | |
103 | * Constant to be added to a type to get a type with one less dimension. | |
104 | */ | |
105 | final static int ELEMENT_OF = 0xF0000000; | |
106 | ||
107 | /** | |
108 | * Mask to get the kind of a frame type. | |
109 | * | |
110 | * @see #BASE | |
111 | * @see #LOCAL | |
112 | * @see #STACK | |
113 | */ | |
114 | final static int KIND = 0xF000000; | |
115 | ||
116 | /** | |
117 | * Mask to get the value of a frame type. | |
118 | */ | |
119 | final static int VALUE = 0xFFFFFF; | |
120 | ||
121 | /** | |
122 | * Mask to get the kind of base types. | |
123 | */ | |
124 | final static int BASE_KIND = 0xFF00000; | |
125 | ||
126 | /** | |
127 | * Mask to get the value of base types. | |
128 | */ | |
129 | final static int BASE_VALUE = 0xFFFFF; | |
130 | ||
131 | /** | |
132 | * Kind of the types that are not relative to an input stack map frame. | |
133 | */ | |
134 | final static int BASE = 0x1000000; | |
135 | ||
136 | /** | |
137 | * Base kind of the base reference types. The BASE_VALUE of such types is an | |
138 | * index into the type table. | |
139 | */ | |
140 | final static int OBJECT = BASE | 0x700000; | |
141 | ||
142 | /** | |
143 | * Base kind of the uninitialized base types. The BASE_VALUE of such types | |
144 | * in an index into the type table (the Item at that index contains both an | |
145 | * instruction offset and an internal class name). | |
146 | */ | |
147 | final static int UNINITIALIZED = BASE | 0x800000; | |
148 | ||
149 | /** | |
150 | * Kind of the types that are relative to the local variable types of an | |
151 | * input stack map frame. The value of such types is a local variable index. | |
152 | */ | |
153 | private final static int LOCAL = 0x2000000; | |
154 | ||
155 | /** | |
156 | * Kind of the the types that are relative to the stack of an input stack | |
157 | * map frame. The value of such types is a position relatively to the top of | |
158 | * this stack. | |
159 | */ | |
160 | private final static int STACK = 0x3000000; | |
161 | ||
162 | /** | |
163 | * The TOP type. This is a BASE type. | |
164 | */ | |
165 | final static int TOP = BASE | 0; | |
166 | ||
167 | /** | |
168 | * The BOOLEAN type. This is a BASE type mainly used for array types. | |
169 | */ | |
170 | final static int BOOLEAN = BASE | 9; | |
171 | ||
172 | /** | |
173 | * The BYTE type. This is a BASE type mainly used for array types. | |
174 | */ | |
175 | final static int BYTE = BASE | 10; | |
176 | ||
177 | /** | |
178 | * The CHAR type. This is a BASE type mainly used for array types. | |
179 | */ | |
180 | final static int CHAR = BASE | 11; | |
181 | ||
182 | /** | |
183 | * The SHORT type. This is a BASE type mainly used for array types. | |
184 | */ | |
185 | final static int SHORT = BASE | 12; | |
186 | ||
187 | /** | |
188 | * The INTEGER type. This is a BASE type. | |
189 | */ | |
190 | final static int INTEGER = BASE | 1; | |
191 | ||
192 | /** | |
193 | * The FLOAT type. This is a BASE type. | |
194 | */ | |
195 | final static int FLOAT = BASE | 2; | |
196 | ||
197 | /** | |
198 | * The DOUBLE type. This is a BASE type. | |
199 | */ | |
200 | final static int DOUBLE = BASE | 3; | |
201 | ||
202 | /** | |
203 | * The LONG type. This is a BASE type. | |
204 | */ | |
205 | final static int LONG = BASE | 4; | |
206 | ||
207 | /** | |
208 | * The NULL type. This is a BASE type. | |
209 | */ | |
210 | final static int NULL = BASE | 5; | |
211 | ||
212 | /** | |
213 | * The UNINITIALIZED_THIS type. This is a BASE type. | |
214 | */ | |
215 | final static int UNINITIALIZED_THIS = BASE | 6; | |
216 | ||
217 | /** | |
218 | * The stack size variation corresponding to each JVM instruction. This | |
219 | * stack variation is equal to the size of the values produced by an | |
220 | * instruction, minus the size of the values consumed by this instruction. | |
221 | */ | |
222 | final static int[] SIZE; | |
223 | ||
224 | /** | |
225 | * Computes the stack size variation corresponding to each JVM instruction. | |
226 | */ | |
227 | static | |
228 | { | |
229 | int i; | |
230 | int[] b = new int[202]; | |
231 | String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" | |
232 | + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" | |
233 | + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" | |
234 | + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; | |
235 | for(i = 0; i < b.length; ++i) | |
236 | { | |
237 | b[i] = s.charAt(i) - 'E'; | |
238 | } | |
239 | SIZE = b; | |
240 | ||
241 | // code to generate the above string | |
242 | // | |
243 | // int NA = 0; // not applicable (unused opcode or variable size opcode) | |
244 | // | |
245 | // b = new int[] { | |
246 | // 0, //NOP, // visitInsn | |
247 | // 1, //ACONST_NULL, // - | |
248 | // 1, //ICONST_M1, // - | |
249 | // 1, //ICONST_0, // - | |
250 | // 1, //ICONST_1, // - | |
251 | // 1, //ICONST_2, // - | |
252 | // 1, //ICONST_3, // - | |
253 | // 1, //ICONST_4, // - | |
254 | // 1, //ICONST_5, // - | |
255 | // 2, //LCONST_0, // - | |
256 | // 2, //LCONST_1, // - | |
257 | // 1, //FCONST_0, // - | |
258 | // 1, //FCONST_1, // - | |
259 | // 1, //FCONST_2, // - | |
260 | // 2, //DCONST_0, // - | |
261 | // 2, //DCONST_1, // - | |
262 | // 1, //BIPUSH, // visitIntInsn | |
263 | // 1, //SIPUSH, // - | |
264 | // 1, //LDC, // visitLdcInsn | |
265 | // NA, //LDC_W, // - | |
266 | // NA, //LDC2_W, // - | |
267 | // 1, //ILOAD, // visitVarInsn | |
268 | // 2, //LLOAD, // - | |
269 | // 1, //FLOAD, // - | |
270 | // 2, //DLOAD, // - | |
271 | // 1, //ALOAD, // - | |
272 | // NA, //ILOAD_0, // - | |
273 | // NA, //ILOAD_1, // - | |
274 | // NA, //ILOAD_2, // - | |
275 | // NA, //ILOAD_3, // - | |
276 | // NA, //LLOAD_0, // - | |
277 | // NA, //LLOAD_1, // - | |
278 | // NA, //LLOAD_2, // - | |
279 | // NA, //LLOAD_3, // - | |
280 | // NA, //FLOAD_0, // - | |
281 | // NA, //FLOAD_1, // - | |
282 | // NA, //FLOAD_2, // - | |
283 | // NA, //FLOAD_3, // - | |
284 | // NA, //DLOAD_0, // - | |
285 | // NA, //DLOAD_1, // - | |
286 | // NA, //DLOAD_2, // - | |
287 | // NA, //DLOAD_3, // - | |
288 | // NA, //ALOAD_0, // - | |
289 | // NA, //ALOAD_1, // - | |
290 | // NA, //ALOAD_2, // - | |
291 | // NA, //ALOAD_3, // - | |
292 | // -1, //IALOAD, // visitInsn | |
293 | // 0, //LALOAD, // - | |
294 | // -1, //FALOAD, // - | |
295 | // 0, //DALOAD, // - | |
296 | // -1, //AALOAD, // - | |
297 | // -1, //BALOAD, // - | |
298 | // -1, //CALOAD, // - | |
299 | // -1, //SALOAD, // - | |
300 | // -1, //ISTORE, // visitVarInsn | |
301 | // -2, //LSTORE, // - | |
302 | // -1, //FSTORE, // - | |
303 | // -2, //DSTORE, // - | |
304 | // -1, //ASTORE, // - | |
305 | // NA, //ISTORE_0, // - | |
306 | // NA, //ISTORE_1, // - | |
307 | // NA, //ISTORE_2, // - | |
308 | // NA, //ISTORE_3, // - | |
309 | // NA, //LSTORE_0, // - | |
310 | // NA, //LSTORE_1, // - | |
311 | // NA, //LSTORE_2, // - | |
312 | // NA, //LSTORE_3, // - | |
313 | // NA, //FSTORE_0, // - | |
314 | // NA, //FSTORE_1, // - | |
315 | // NA, //FSTORE_2, // - | |
316 | // NA, //FSTORE_3, // - | |
317 | // NA, //DSTORE_0, // - | |
318 | // NA, //DSTORE_1, // - | |
319 | // NA, //DSTORE_2, // - | |
320 | // NA, //DSTORE_3, // - | |
321 | // NA, //ASTORE_0, // - | |
322 | // NA, //ASTORE_1, // - | |
323 | // NA, //ASTORE_2, // - | |
324 | // NA, //ASTORE_3, // - | |
325 | // -3, //IASTORE, // visitInsn | |
326 | // -4, //LASTORE, // - | |
327 | // -3, //FASTORE, // - | |
328 | // -4, //DASTORE, // - | |
329 | // -3, //AASTORE, // - | |
330 | // -3, //BASTORE, // - | |
331 | // -3, //CASTORE, // - | |
332 | // -3, //SASTORE, // - | |
333 | // -1, //POP, // - | |
334 | // -2, //POP2, // - | |
335 | // 1, //DUP, // - | |
336 | // 1, //DUP_X1, // - | |
337 | // 1, //DUP_X2, // - | |
338 | // 2, //DUP2, // - | |
339 | // 2, //DUP2_X1, // - | |
340 | // 2, //DUP2_X2, // - | |
341 | // 0, //SWAP, // - | |
342 | // -1, //IADD, // - | |
343 | // -2, //LADD, // - | |
344 | // -1, //FADD, // - | |
345 | // -2, //DADD, // - | |
346 | // -1, //ISUB, // - | |
347 | // -2, //LSUB, // - | |
348 | // -1, //FSUB, // - | |
349 | // -2, //DSUB, // - | |
350 | // -1, //IMUL, // - | |
351 | // -2, //LMUL, // - | |
352 | // -1, //FMUL, // - | |
353 | // -2, //DMUL, // - | |
354 | // -1, //IDIV, // - | |
355 | // -2, //LDIV, // - | |
356 | // -1, //FDIV, // - | |
357 | // -2, //DDIV, // - | |
358 | // -1, //IREM, // - | |
359 | // -2, //LREM, // - | |
360 | // -1, //FREM, // - | |
361 | // -2, //DREM, // - | |
362 | // 0, //INEG, // - | |
363 | // 0, //LNEG, // - | |
364 | // 0, //FNEG, // - | |
365 | // 0, //DNEG, // - | |
366 | // -1, //ISHL, // - | |
367 | // -1, //LSHL, // - | |
368 | // -1, //ISHR, // - | |
369 | // -1, //LSHR, // - | |
370 | // -1, //IUSHR, // - | |
371 | // -1, //LUSHR, // - | |
372 | // -1, //IAND, // - | |
373 | // -2, //LAND, // - | |
374 | // -1, //IOR, // - | |
375 | // -2, //LOR, // - | |
376 | // -1, //IXOR, // - | |
377 | // -2, //LXOR, // - | |
378 | // 0, //IINC, // visitIincInsn | |
379 | // 1, //I2L, // visitInsn | |
380 | // 0, //I2F, // - | |
381 | // 1, //I2D, // - | |
382 | // -1, //L2I, // - | |
383 | // -1, //L2F, // - | |
384 | // 0, //L2D, // - | |
385 | // 0, //F2I, // - | |
386 | // 1, //F2L, // - | |
387 | // 1, //F2D, // - | |
388 | // -1, //D2I, // - | |
389 | // 0, //D2L, // - | |
390 | // -1, //D2F, // - | |
391 | // 0, //I2B, // - | |
392 | // 0, //I2C, // - | |
393 | // 0, //I2S, // - | |
394 | // -3, //LCMP, // - | |
395 | // -1, //FCMPL, // - | |
396 | // -1, //FCMPG, // - | |
397 | // -3, //DCMPL, // - | |
398 | // -3, //DCMPG, // - | |
399 | // -1, //IFEQ, // visitJumpInsn | |
400 | // -1, //IFNE, // - | |
401 | // -1, //IFLT, // - | |
402 | // -1, //IFGE, // - | |
403 | // -1, //IFGT, // - | |
404 | // -1, //IFLE, // - | |
405 | // -2, //IF_ICMPEQ, // - | |
406 | // -2, //IF_ICMPNE, // - | |
407 | // -2, //IF_ICMPLT, // - | |
408 | // -2, //IF_ICMPGE, // - | |
409 | // -2, //IF_ICMPGT, // - | |
410 | // -2, //IF_ICMPLE, // - | |
411 | // -2, //IF_ACMPEQ, // - | |
412 | // -2, //IF_ACMPNE, // - | |
413 | // 0, //GOTO, // - | |
414 | // 1, //JSR, // - | |
415 | // 0, //RET, // visitVarInsn | |
416 | // -1, //TABLESWITCH, // visiTableSwitchInsn | |
417 | // -1, //LOOKUPSWITCH, // visitLookupSwitch | |
418 | // -1, //IRETURN, // visitInsn | |
419 | // -2, //LRETURN, // - | |
420 | // -1, //FRETURN, // - | |
421 | // -2, //DRETURN, // - | |
422 | // -1, //ARETURN, // - | |
423 | // 0, //RETURN, // - | |
424 | // NA, //GETSTATIC, // visitFieldInsn | |
425 | // NA, //PUTSTATIC, // - | |
426 | // NA, //GETFIELD, // - | |
427 | // NA, //PUTFIELD, // - | |
428 | // NA, //INVOKEVIRTUAL, // visitMethodInsn | |
429 | // NA, //INVOKESPECIAL, // - | |
430 | // NA, //INVOKESTATIC, // - | |
431 | // NA, //INVOKEINTERFACE, // - | |
432 | // NA, //UNUSED, // NOT VISITED | |
433 | // 1, //NEW, // visitTypeInsn | |
434 | // 0, //NEWARRAY, // visitIntInsn | |
435 | // 0, //ANEWARRAY, // visitTypeInsn | |
436 | // 0, //ARRAYLENGTH, // visitInsn | |
437 | // NA, //ATHROW, // - | |
438 | // 0, //CHECKCAST, // visitTypeInsn | |
439 | // 0, //INSTANCEOF, // - | |
440 | // -1, //MONITORENTER, // visitInsn | |
441 | // -1, //MONITOREXIT, // - | |
442 | // NA, //WIDE, // NOT VISITED | |
443 | // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn | |
444 | // -1, //IFNULL, // visitJumpInsn | |
445 | // -1, //IFNONNULL, // - | |
446 | // NA, //GOTO_W, // - | |
447 | // NA, //JSR_W, // - | |
448 | // }; | |
449 | // for (i = 0; i < b.length; ++i) { | |
450 | // System.err.print((char)('E' + b[i])); | |
451 | // } | |
452 | // System.err.println(); | |
453 | } | |
454 | ||
455 | /** | |
456 | * The label (i.e. basic block) to which these input and output stack map | |
457 | * frames correspond. | |
458 | */ | |
459 | Label owner; | |
460 | ||
461 | /** | |
462 | * The input stack map frame locals. | |
463 | */ | |
464 | int[] inputLocals; | |
465 | ||
466 | /** | |
467 | * The input stack map frame stack. | |
468 | */ | |
469 | int[] inputStack; | |
470 | ||
471 | /** | |
472 | * The output stack map frame locals. | |
473 | */ | |
474 | private int[] outputLocals; | |
475 | ||
476 | /** | |
477 | * The output stack map frame stack. | |
478 | */ | |
479 | private int[] outputStack; | |
480 | ||
481 | /** | |
482 | * Relative size of the output stack. The exact semantics of this field | |
483 | * depends on the algorithm that is used. | |
484 | * <p/> | |
485 | * When only the maximum stack size is computed, this field is the size of | |
486 | * the output stack relatively to the top of the input stack. | |
487 | * <p/> | |
488 | * When the stack map frames are completely computed, this field is the | |
489 | * actual number of types in {@link #outputStack}. | |
490 | */ | |
491 | private int outputStackTop; | |
492 | ||
493 | /** | |
494 | * Number of types that are initialized in the basic block. | |
495 | * | |
496 | * @see #initializations | |
497 | */ | |
498 | private int initializationCount; | |
499 | ||
500 | /** | |
501 | * The types that are initialized in the basic block. A constructor | |
502 | * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace | |
503 | * <i>every occurence</i> of this type in the local variables and in the | |
504 | * operand stack. This cannot be done during the first phase of the | |
505 | * algorithm since, during this phase, the local variables and the operand | |
506 | * stack are not completely computed. It is therefore necessary to store the | |
507 | * types on which constructors are invoked in the basic block, in order to | |
508 | * do this replacement during the second phase of the algorithm, where the | |
509 | * frames are fully computed. Note that this array can contain types that | |
510 | * are relative to input locals or to the input stack (see below for the | |
511 | * description of the algorithm). | |
512 | */ | |
513 | private int[] initializations; | |
514 | ||
515 | /** | |
516 | * Returns the output frame local variable type at the given index. | |
517 | * | |
518 | * @param local the index of the local that must be returned. | |
519 | * @return the output frame local variable type at the given index. | |
520 | */ | |
521 | private int get(final int local){ | |
522 | if(outputLocals == null || local >= outputLocals.length) | |
523 | { | |
524 | // this local has never been assigned in this basic block, | |
525 | // so it is still equal to its value in the input frame | |
526 | return LOCAL | local; | |
527 | } | |
528 | else | |
529 | { | |
530 | int type = outputLocals[local]; | |
531 | if(type == 0) | |
532 | { | |
533 | // this local has never been assigned in this basic block, | |
534 | // so it is still equal to its value in the input frame | |
535 | type = outputLocals[local] = LOCAL | local; | |
536 | } | |
537 | return type; | |
538 | } | |
539 | } | |
540 | ||
541 | /** | |
542 | * Sets the output frame local variable type at the given index. | |
543 | * | |
544 | * @param local the index of the local that must be set. | |
545 | * @param type the value of the local that must be set. | |
546 | */ | |
547 | private void set(final int local, final int type){ | |
548 | // creates and/or resizes the output local variables array if necessary | |
549 | if(outputLocals == null) | |
550 | { | |
551 | outputLocals = new int[10]; | |
552 | } | |
553 | int n = outputLocals.length; | |
554 | if(local >= n) | |
555 | { | |
556 | int[] t = new int[Math.max(local + 1, 2 * n)]; | |
557 | System.arraycopy(outputLocals, 0, t, 0, n); | |
558 | outputLocals = t; | |
559 | } | |
560 | // sets the local variable | |
561 | outputLocals[local] = type; | |
562 | } | |
563 | ||
564 | /** | |
565 | * Pushes a new type onto the output frame stack. | |
566 | * | |
567 | * @param type the type that must be pushed. | |
568 | */ | |
569 | private void push(final int type){ | |
570 | // creates and/or resizes the output stack array if necessary | |
571 | if(outputStack == null) | |
572 | { | |
573 | outputStack = new int[10]; | |
574 | } | |
575 | int n = outputStack.length; | |
576 | if(outputStackTop >= n) | |
577 | { | |
578 | int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; | |
579 | System.arraycopy(outputStack, 0, t, 0, n); | |
580 | outputStack = t; | |
581 | } | |
582 | // pushes the type on the output stack | |
583 | outputStack[outputStackTop++] = type; | |
584 | // updates the maximun height reached by the output stack, if needed | |
585 | int top = owner.inputStackTop + outputStackTop; | |
586 | if(top > owner.outputStackMax) | |
587 | { | |
588 | owner.outputStackMax = top; | |
589 | } | |
590 | } | |
591 | ||
592 | /** | |
593 | * Pushes a new type onto the output frame stack. | |
594 | * | |
595 | * @param cw the ClassWriter to which this label belongs. | |
596 | * @param desc the descriptor of the type to be pushed. Can also be a method | |
597 | * descriptor (in this case this method pushes its return type onto | |
598 | * the output frame stack). | |
599 | */ | |
600 | private void push(final ClassWriter cw, final String desc){ | |
601 | int type = type(cw, desc); | |
602 | if(type != 0) | |
603 | { | |
604 | push(type); | |
605 | if(type == LONG || type == DOUBLE) | |
606 | { | |
607 | push(TOP); | |
608 | } | |
609 | } | |
610 | } | |
611 | ||
612 | /** | |
613 | * Returns the int encoding of the given type. | |
614 | * | |
615 | * @param cw the ClassWriter to which this label belongs. | |
616 | * @param desc a type descriptor. | |
617 | * @return the int encoding of the given type. | |
618 | */ | |
619 | private int type(final ClassWriter cw, final String desc){ | |
620 | String t; | |
621 | int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; | |
622 | switch(desc.charAt(index)) | |
623 | { | |
624 | case'V': | |
625 | return 0; | |
626 | case'Z': | |
627 | case'C': | |
628 | case'B': | |
629 | case'S': | |
630 | case'I': | |
631 | return INTEGER; | |
632 | case'F': | |
633 | return FLOAT; | |
634 | case'J': | |
635 | return LONG; | |
636 | case'D': | |
637 | return DOUBLE; | |
638 | case'L': | |
639 | // stores the internal name, not the descriptor! | |
640 | t = desc.substring(index + 1, desc.length() - 1); | |
641 | return OBJECT | cw.addType(t); | |
642 | // case '[': | |
643 | default: | |
644 | // extracts the dimensions and the element type | |
645 | int data; | |
646 | int dims = index + 1; | |
647 | while(desc.charAt(dims) == '[') | |
648 | { | |
649 | ++dims; | |
650 | } | |
651 | switch(desc.charAt(dims)) | |
652 | { | |
653 | case'Z': | |
654 | data = BOOLEAN; | |
655 | break; | |
656 | case'C': | |
657 | data = CHAR; | |
658 | break; | |
659 | case'B': | |
660 | data = BYTE; | |
661 | break; | |
662 | case'S': | |
663 | data = SHORT; | |
664 | break; | |
665 | case'I': | |
666 | data = INTEGER; | |
667 | break; | |
668 | case'F': | |
669 | data = FLOAT; | |
670 | break; | |
671 | case'J': | |
672 | data = LONG; | |
673 | break; | |
674 | case'D': | |
675 | data = DOUBLE; | |
676 | break; | |
677 | // case 'L': | |
678 | default: | |
679 | // stores the internal name, not the descriptor | |
680 | t = desc.substring(dims + 1, desc.length() - 1); | |
681 | data = OBJECT | cw.addType(t); | |
682 | } | |
683 | return (dims - index) << 28 | data; | |
684 | } | |
685 | } | |
686 | ||
687 | /** | |
688 | * Pops a type from the output frame stack and returns its value. | |
689 | * | |
690 | * @return the type that has been popped from the output frame stack. | |
691 | */ | |
692 | private int pop(){ | |
693 | if(outputStackTop > 0) | |
694 | { | |
695 | return outputStack[--outputStackTop]; | |
696 | } | |
697 | else | |
698 | { | |
699 | // if the output frame stack is empty, pops from the input stack | |
700 | return STACK | -(--owner.inputStackTop); | |
701 | } | |
702 | } | |
703 | ||
704 | /** | |
705 | * Pops the given number of types from the output frame stack. | |
706 | * | |
707 | * @param elements the number of types that must be popped. | |
708 | */ | |
709 | private void pop(final int elements){ | |
710 | if(outputStackTop >= elements) | |
711 | { | |
712 | outputStackTop -= elements; | |
713 | } | |
714 | else | |
715 | { | |
716 | // if the number of elements to be popped is greater than the number | |
717 | // of elements in the output stack, clear it, and pops the remaining | |
718 | // elements from the input stack. | |
719 | owner.inputStackTop -= elements - outputStackTop; | |
720 | outputStackTop = 0; | |
721 | } | |
722 | } | |
723 | ||
724 | /** | |
725 | * Pops a type from the output frame stack. | |
726 | * | |
727 | * @param desc the descriptor of the type to be popped. Can also be a method | |
728 | * descriptor (in this case this method pops the types corresponding | |
729 | * to the method arguments). | |
730 | */ | |
731 | private void pop(final String desc){ | |
732 | char c = desc.charAt(0); | |
733 | if(c == '(') | |
734 | { | |
735 | pop((MethodWriter.getArgumentsAndReturnSizes(desc) >> 2) - 1); | |
736 | } | |
737 | else if(c == 'J' || c == 'D') | |
738 | { | |
739 | pop(2); | |
740 | } | |
741 | else | |
742 | { | |
743 | pop(1); | |
744 | } | |
745 | } | |
746 | ||
747 | /** | |
748 | * Adds a new type to the list of types on which a constructor is invoked in | |
749 | * the basic block. | |
750 | * | |
751 | * @param var a type on a which a constructor is invoked. | |
752 | */ | |
753 | private void init(final int var){ | |
754 | // creates and/or resizes the initializations array if necessary | |
755 | if(initializations == null) | |
756 | { | |
757 | initializations = new int[2]; | |
758 | } | |
759 | int n = initializations.length; | |
760 | if(initializationCount >= n) | |
761 | { | |
762 | int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; | |
763 | System.arraycopy(initializations, 0, t, 0, n); | |
764 | initializations = t; | |
765 | } | |
766 | // stores the type to be initialized | |
767 | initializations[initializationCount++] = var; | |
768 | } | |
769 | ||
770 | /** | |
771 | * Replaces the given type with the appropriate type if it is one of the | |
772 | * types on which a constructor is invoked in the basic block. | |
773 | * | |
774 | * @param cw the ClassWriter to which this label belongs. | |
775 | * @param t a type | |
776 | * @return t or, if t is one of the types on which a constructor is invoked | |
777 | * in the basic block, the type corresponding to this constructor. | |
778 | */ | |
779 | private int init(final ClassWriter cw, final int t){ | |
780 | int s; | |
781 | if(t == UNINITIALIZED_THIS) | |
782 | { | |
783 | s = OBJECT | cw.addType(cw.thisName); | |
784 | } | |
785 | else if((t & (DIM | BASE_KIND)) == UNINITIALIZED) | |
786 | { | |
787 | String type = cw.typeTable[t & BASE_VALUE].strVal1; | |
788 | s = OBJECT | cw.addType(type); | |
789 | } | |
790 | else | |
791 | { | |
792 | return t; | |
793 | } | |
794 | for(int j = 0; j < initializationCount; ++j) | |
795 | { | |
796 | int u = initializations[j]; | |
797 | int dim = u & DIM; | |
798 | int kind = u & KIND; | |
799 | if(kind == LOCAL) | |
800 | { | |
801 | u = dim + inputLocals[u & VALUE]; | |
802 | } | |
803 | else if(kind == STACK) | |
804 | { | |
805 | u = dim + inputStack[inputStack.length - (u & VALUE)]; | |
806 | } | |
807 | if(t == u) | |
808 | { | |
809 | return s; | |
810 | } | |
811 | } | |
812 | return t; | |
813 | } | |
814 | ||
815 | /** | |
816 | * Initializes the input frame of the first basic block from the method | |
817 | * descriptor. | |
818 | * | |
819 | * @param cw the ClassWriter to which this label belongs. | |
820 | * @param access the access flags of the method to which this label belongs. | |
821 | * @param args the formal parameter types of this method. | |
822 | * @param maxLocals the maximum number of local variables of this method. | |
823 | */ | |
824 | void initInputFrame( | |
825 | final ClassWriter cw, | |
826 | final int access, | |
827 | final Type[] args, | |
828 | final int maxLocals){ | |
829 | inputLocals = new int[maxLocals]; | |
830 | inputStack = new int[0]; | |
831 | int i = 0; | |
832 | if((access & Opcodes.ACC_STATIC) == 0) | |
833 | { | |
834 | if((access & MethodWriter.ACC_CONSTRUCTOR) == 0) | |
835 | { | |
836 | inputLocals[i++] = OBJECT | cw.addType(cw.thisName); | |
837 | } | |
838 | else | |
839 | { | |
840 | inputLocals[i++] = UNINITIALIZED_THIS; | |
841 | } | |
842 | } | |
843 | for(int j = 0; j < args.length; ++j) | |
844 | { | |
845 | int t = type(cw, args[j].getDescriptor()); | |
846 | inputLocals[i++] = t; | |
847 | if(t == LONG || t == DOUBLE) | |
848 | { | |
849 | inputLocals[i++] = TOP; | |
850 | } | |
851 | } | |
852 | while(i < maxLocals) | |
853 | { | |
854 | inputLocals[i++] = TOP; | |
855 | } | |
856 | } | |
857 | ||
858 | /** | |
859 | * Simulates the action of the given instruction on the output stack frame. | |
860 | * | |
861 | * @param opcode the opcode of the instruction. | |
862 | * @param arg the operand of the instruction, if any. | |
863 | * @param cw the class writer to which this label belongs. | |
864 | * @param item the operand of the instructions, if any. | |
865 | */ | |
866 | void execute( | |
867 | final int opcode, | |
868 | final int arg, | |
869 | final ClassWriter cw, | |
870 | final Item item){ | |
871 | int t1, t2, t3, t4; | |
872 | switch(opcode) | |
873 | { | |
874 | case Opcodes.NOP: | |
875 | case Opcodes.INEG: | |
876 | case Opcodes.LNEG: | |
877 | case Opcodes.FNEG: | |
878 | case Opcodes.DNEG: | |
879 | case Opcodes.I2B: | |
880 | case Opcodes.I2C: | |
881 | case Opcodes.I2S: | |
882 | case Opcodes.GOTO: | |
883 | case Opcodes.RETURN: | |
884 | break; | |
885 | case Opcodes.ACONST_NULL: | |
886 | push(NULL); | |
887 | break; | |
888 | case Opcodes.ICONST_M1: | |
889 | case Opcodes.ICONST_0: | |
890 | case Opcodes.ICONST_1: | |
891 | case Opcodes.ICONST_2: | |
892 | case Opcodes.ICONST_3: | |
893 | case Opcodes.ICONST_4: | |
894 | case Opcodes.ICONST_5: | |
895 | case Opcodes.BIPUSH: | |
896 | case Opcodes.SIPUSH: | |
897 | case Opcodes.ILOAD: | |
898 | push(INTEGER); | |
899 | break; | |
900 | case Opcodes.LCONST_0: | |
901 | case Opcodes.LCONST_1: | |
902 | case Opcodes.LLOAD: | |
903 | push(LONG); | |
904 | push(TOP); | |
905 | break; | |
906 | case Opcodes.FCONST_0: | |
907 | case Opcodes.FCONST_1: | |
908 | case Opcodes.FCONST_2: | |
909 | case Opcodes.FLOAD: | |
910 | push(FLOAT); | |
911 | break; | |
912 | case Opcodes.DCONST_0: | |
913 | case Opcodes.DCONST_1: | |
914 | case Opcodes.DLOAD: | |
915 | push(DOUBLE); | |
916 | push(TOP); | |
917 | break; | |
918 | case Opcodes.LDC: | |
919 | switch(item.type) | |
920 | { | |
921 | case ClassWriter.INT: | |
922 | push(INTEGER); | |
923 | break; | |
924 | case ClassWriter.LONG: | |
925 | push(LONG); | |
926 | push(TOP); | |
927 | break; | |
928 | case ClassWriter.FLOAT: | |
929 | push(FLOAT); | |
930 | break; | |
931 | case ClassWriter.DOUBLE: | |
932 | push(DOUBLE); | |
933 | push(TOP); | |
934 | break; | |
935 | case ClassWriter.CLASS: | |
936 | push(OBJECT | cw.addType("java/lang/Class")); | |
937 | break; | |
938 | // case ClassWriter.STR: | |
939 | default: | |
940 | push(OBJECT | cw.addType("java/lang/String")); | |
941 | } | |
942 | break; | |
943 | case Opcodes.ALOAD: | |
944 | push(get(arg)); | |
945 | break; | |
946 | case Opcodes.IALOAD: | |
947 | case Opcodes.BALOAD: | |
948 | case Opcodes.CALOAD: | |
949 | case Opcodes.SALOAD: | |
950 | pop(2); | |
951 | push(INTEGER); | |
952 | break; | |
953 | case Opcodes.LALOAD: | |
954 | case Opcodes.D2L: | |
955 | pop(2); | |
956 | push(LONG); | |
957 | push(TOP); | |
958 | break; | |
959 | case Opcodes.FALOAD: | |
960 | pop(2); | |
961 | push(FLOAT); | |
962 | break; | |
963 | case Opcodes.DALOAD: | |
964 | case Opcodes.L2D: | |
965 | pop(2); | |
966 | push(DOUBLE); | |
967 | push(TOP); | |
968 | break; | |
969 | case Opcodes.AALOAD: | |
970 | pop(1); | |
971 | t1 = pop(); | |
972 | push(ELEMENT_OF + t1); | |
973 | break; | |
974 | case Opcodes.ISTORE: | |
975 | case Opcodes.FSTORE: | |
976 | case Opcodes.ASTORE: | |
977 | t1 = pop(); | |
978 | set(arg, t1); | |
979 | if(arg > 0) | |
980 | { | |
981 | t2 = get(arg - 1); | |
982 | // if t2 is of kind STACK or LOCAL we cannot know its size! | |
983 | if(t2 == LONG || t2 == DOUBLE) | |
984 | { | |
985 | set(arg - 1, TOP); | |
986 | } | |
987 | } | |
988 | break; | |
989 | case Opcodes.LSTORE: | |
990 | case Opcodes.DSTORE: | |
991 | pop(1); | |
992 | t1 = pop(); | |
993 | set(arg, t1); | |
994 | set(arg + 1, TOP); | |
995 | if(arg > 0) | |
996 | { | |
997 | t2 = get(arg - 1); | |
998 | // if t2 is of kind STACK or LOCAL we cannot know its size! | |
999 | if(t2 == LONG || t2 == DOUBLE) | |
1000 | { | |
1001 | set(arg - 1, TOP); | |
1002 | } | |
1003 | } | |
1004 | break; | |
1005 | case Opcodes.IASTORE: | |
1006 | case Opcodes.BASTORE: | |
1007 | case Opcodes.CASTORE: | |
1008 | case Opcodes.SASTORE: | |
1009 | case Opcodes.FASTORE: | |
1010 | case Opcodes.AASTORE: | |
1011 | pop(3); | |
1012 | break; | |
1013 | case Opcodes.LASTORE: | |
1014 | case Opcodes.DASTORE: | |
1015 | pop(4); | |
1016 | break; | |
1017 | case Opcodes.POP: | |
1018 | case Opcodes.IFEQ: | |
1019 | case Opcodes.IFNE: | |
1020 | case Opcodes.IFLT: | |
1021 | case Opcodes.IFGE: | |
1022 | case Opcodes.IFGT: | |
1023 | case Opcodes.IFLE: | |
1024 | case Opcodes.IRETURN: | |
1025 | case Opcodes.FRETURN: | |
1026 | case Opcodes.ARETURN: | |
1027 | case Opcodes.TABLESWITCH: | |
1028 | case Opcodes.LOOKUPSWITCH: | |
1029 | case Opcodes.ATHROW: | |
1030 | case Opcodes.MONITORENTER: | |
1031 | case Opcodes.MONITOREXIT: | |
1032 | case Opcodes.IFNULL: | |
1033 | case Opcodes.IFNONNULL: | |
1034 | pop(1); | |
1035 | break; | |
1036 | case Opcodes.POP2: | |
1037 | case Opcodes.IF_ICMPEQ: | |
1038 | case Opcodes.IF_ICMPNE: | |
1039 | case Opcodes.IF_ICMPLT: | |
1040 | case Opcodes.IF_ICMPGE: | |
1041 | case Opcodes.IF_ICMPGT: | |
1042 | case Opcodes.IF_ICMPLE: | |
1043 | case Opcodes.IF_ACMPEQ: | |
1044 | case Opcodes.IF_ACMPNE: | |
1045 | case Opcodes.LRETURN: | |
1046 | case Opcodes.DRETURN: | |
1047 | pop(2); | |
1048 | break; | |
1049 | case Opcodes.DUP: | |
1050 | t1 = pop(); | |
1051 | push(t1); | |
1052 | push(t1); | |
1053 | break; | |
1054 | case Opcodes.DUP_X1: | |
1055 | t1 = pop(); | |
1056 | t2 = pop(); | |
1057 | push(t1); | |
1058 | push(t2); | |
1059 | push(t1); | |
1060 | break; | |
1061 | case Opcodes.DUP_X2: | |
1062 | t1 = pop(); | |
1063 | t2 = pop(); | |
1064 | t3 = pop(); | |
1065 | push(t1); | |
1066 | push(t3); | |
1067 | push(t2); | |
1068 | push(t1); | |
1069 | break; | |
1070 | case Opcodes.DUP2: | |
1071 | t1 = pop(); | |
1072 | t2 = pop(); | |
1073 | push(t2); | |
1074 | push(t1); | |
1075 | push(t2); | |
1076 | push(t1); | |
1077 | break; | |
1078 | case Opcodes.DUP2_X1: | |
1079 | t1 = pop(); | |
1080 | t2 = pop(); | |
1081 | t3 = pop(); | |
1082 | push(t2); | |
1083 | push(t1); | |
1084 | push(t3); | |
1085 | push(t2); | |
1086 | push(t1); | |
1087 | break; | |
1088 | case Opcodes.DUP2_X2: | |
1089 | t1 = pop(); | |
1090 | t2 = pop(); | |
1091 | t3 = pop(); | |
1092 | t4 = pop(); | |
1093 | push(t2); | |
1094 | push(t1); | |
1095 | push(t4); | |
1096 | push(t3); | |
1097 | push(t2); | |
1098 | push(t1); | |
1099 | break; | |
1100 | case Opcodes.SWAP: | |
1101 | t1 = pop(); | |
1102 | t2 = pop(); | |
1103 | push(t1); | |
1104 | push(t2); | |
1105 | break; | |
1106 | case Opcodes.IADD: | |
1107 | case Opcodes.ISUB: | |
1108 | case Opcodes.IMUL: | |
1109 | case Opcodes.IDIV: | |
1110 | case Opcodes.IREM: | |
1111 | case Opcodes.IAND: | |
1112 | case Opcodes.IOR: | |
1113 | case Opcodes.IXOR: | |
1114 | case Opcodes.ISHL: | |
1115 | case Opcodes.ISHR: | |
1116 | case Opcodes.IUSHR: | |
1117 | case Opcodes.L2I: | |
1118 | case Opcodes.D2I: | |
1119 | case Opcodes.FCMPL: | |
1120 | case Opcodes.FCMPG: | |
1121 | pop(2); | |
1122 | push(INTEGER); | |
1123 | break; | |
1124 | case Opcodes.LADD: | |
1125 | case Opcodes.LSUB: | |
1126 | case Opcodes.LMUL: | |
1127 | case Opcodes.LDIV: | |
1128 | case Opcodes.LREM: | |
1129 | case Opcodes.LAND: | |
1130 | case Opcodes.LOR: | |
1131 | case Opcodes.LXOR: | |
1132 | pop(4); | |
1133 | push(LONG); | |
1134 | push(TOP); | |
1135 | break; | |
1136 | case Opcodes.FADD: | |
1137 | case Opcodes.FSUB: | |
1138 | case Opcodes.FMUL: | |
1139 | case Opcodes.FDIV: | |
1140 | case Opcodes.FREM: | |
1141 | case Opcodes.L2F: | |
1142 | case Opcodes.D2F: | |
1143 | pop(2); | |
1144 | push(FLOAT); | |
1145 | break; | |
1146 | case Opcodes.DADD: | |
1147 | case Opcodes.DSUB: | |
1148 | case Opcodes.DMUL: | |
1149 | case Opcodes.DDIV: | |
1150 | case Opcodes.DREM: | |
1151 | pop(4); | |
1152 | push(DOUBLE); | |
1153 | push(TOP); | |
1154 | break; | |
1155 | case Opcodes.LSHL: | |
1156 | case Opcodes.LSHR: | |
1157 | case Opcodes.LUSHR: | |
1158 | pop(3); | |
1159 | push(LONG); | |
1160 | push(TOP); | |
1161 | break; | |
1162 | case Opcodes.IINC: | |
1163 | set(arg, INTEGER); | |
1164 | break; | |
1165 | case Opcodes.I2L: | |
1166 | case Opcodes.F2L: | |
1167 | pop(1); | |
1168 | push(LONG); | |
1169 | push(TOP); | |
1170 | break; | |
1171 | case Opcodes.I2F: | |
1172 | pop(1); | |
1173 | push(FLOAT); | |
1174 | break; | |
1175 | case Opcodes.I2D: | |
1176 | case Opcodes.F2D: | |
1177 | pop(1); | |
1178 | push(DOUBLE); | |
1179 | push(TOP); | |
1180 | break; | |
1181 | case Opcodes.F2I: | |
1182 | case Opcodes.ARRAYLENGTH: | |
1183 | case Opcodes.INSTANCEOF: | |
1184 | pop(1); | |
1185 | push(INTEGER); | |
1186 | break; | |
1187 | case Opcodes.LCMP: | |
1188 | case Opcodes.DCMPL: | |
1189 | case Opcodes.DCMPG: | |
1190 | pop(4); | |
1191 | push(INTEGER); | |
1192 | break; | |
1193 | case Opcodes.JSR: | |
1194 | case Opcodes.RET: | |
1195 | throw new RuntimeException("JSR/RET are not supported with computeFrames option"); | |
1196 | case Opcodes.GETSTATIC: | |
1197 | push(cw, item.strVal3); | |
1198 | break; | |
1199 | case Opcodes.PUTSTATIC: | |
1200 | pop(item.strVal3); | |
1201 | break; | |
1202 | case Opcodes.GETFIELD: | |
1203 | pop(1); | |
1204 | push(cw, item.strVal3); | |
1205 | break; | |
1206 | case Opcodes.PUTFIELD: | |
1207 | pop(item.strVal3); | |
1208 | pop(); | |
1209 | break; | |
1210 | case Opcodes.INVOKEVIRTUAL: | |
1211 | case Opcodes.INVOKESPECIAL: | |
1212 | case Opcodes.INVOKESTATIC: | |
1213 | case Opcodes.INVOKEINTERFACE: | |
1214 | pop(item.strVal3); | |
1215 | if(opcode != Opcodes.INVOKESTATIC) | |
1216 | { | |
1217 | t1 = pop(); | |
1218 | if(opcode == Opcodes.INVOKESPECIAL | |
1219 | && item.strVal2.charAt(0) == '<') | |
1220 | { | |
1221 | init(t1); | |
1222 | } | |
1223 | } | |
1224 | push(cw, item.strVal3); | |
1225 | break; | |
1226 | case Opcodes.NEW: | |
1227 | push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg)); | |
1228 | break; | |
1229 | case Opcodes.NEWARRAY: | |
1230 | pop(); | |
1231 | switch(arg) | |
1232 | { | |
1233 | case Opcodes.T_BOOLEAN: | |
1234 | push(ARRAY_OF | BOOLEAN); | |
1235 | break; | |
1236 | case Opcodes.T_CHAR: | |
1237 | push(ARRAY_OF | CHAR); | |
1238 | break; | |
1239 | case Opcodes.T_BYTE: | |
1240 | push(ARRAY_OF | BYTE); | |
1241 | break; | |
1242 | case Opcodes.T_SHORT: | |
1243 | push(ARRAY_OF | SHORT); | |
1244 | break; | |
1245 | case Opcodes.T_INT: | |
1246 | push(ARRAY_OF | INTEGER); | |
1247 | break; | |
1248 | case Opcodes.T_FLOAT: | |
1249 | push(ARRAY_OF | FLOAT); | |
1250 | break; | |
1251 | case Opcodes.T_DOUBLE: | |
1252 | push(ARRAY_OF | DOUBLE); | |
1253 | break; | |
1254 | // case Opcodes.T_LONG: | |
1255 | default: | |
1256 | push(ARRAY_OF | LONG); | |
1257 | break; | |
1258 | } | |
1259 | break; | |
1260 | case Opcodes.ANEWARRAY: | |
1261 | String s = item.strVal1; | |
1262 | pop(); | |
1263 | if(s.charAt(0) == '[') | |
1264 | { | |
1265 | push(cw, "[" + s); | |
1266 | } | |
1267 | else | |
1268 | { | |
1269 | push(ARRAY_OF | OBJECT | cw.addType(s)); | |
1270 | } | |
1271 | break; | |
1272 | case Opcodes.CHECKCAST: | |
1273 | s = item.strVal1; | |
1274 | pop(); | |
1275 | if(s.charAt(0) == '[') | |
1276 | { | |
1277 | push(cw, s); | |
1278 | } | |
1279 | else | |
1280 | { | |
1281 | push(OBJECT | cw.addType(s)); | |
1282 | } | |
1283 | break; | |
1284 | // case Opcodes.MULTIANEWARRAY: | |
1285 | default: | |
1286 | pop(arg); | |
1287 | push(cw, item.strVal1); | |
1288 | break; | |
1289 | } | |
1290 | } | |
1291 | ||
1292 | /** | |
1293 | * Merges the input frame of the given basic block with the input and output | |
1294 | * frames of this basic block. Returns <tt>true</tt> if the input frame of | |
1295 | * the given label has been changed by this operation. | |
1296 | * | |
1297 | * @param cw the ClassWriter to which this label belongs. | |
1298 | * @param frame the basic block whose input frame must be updated. | |
1299 | * @param edge the kind of the {@link Edge} between this label and 'label'. | |
1300 | * See {@link Edge#info}. | |
1301 | * @return <tt>true</tt> if the input frame of the given label has been | |
1302 | * changed by this operation. | |
1303 | */ | |
1304 | boolean merge(final ClassWriter cw, final Frame frame, final int edge){ | |
1305 | boolean changed = false; | |
1306 | int i, s, dim, kind, t; | |
1307 | ||
1308 | int nLocal = inputLocals.length; | |
1309 | int nStack = inputStack.length; | |
1310 | if(frame.inputLocals == null) | |
1311 | { | |
1312 | frame.inputLocals = new int[nLocal]; | |
1313 | changed = true; | |
1314 | } | |
1315 | ||
1316 | for(i = 0; i < nLocal; ++i) | |
1317 | { | |
1318 | if(outputLocals != null && i < outputLocals.length) | |
1319 | { | |
1320 | s = outputLocals[i]; | |
1321 | if(s == 0) | |
1322 | { | |
1323 | t = inputLocals[i]; | |
1324 | } | |
1325 | else | |
1326 | { | |
1327 | dim = s & DIM; | |
1328 | kind = s & KIND; | |
1329 | if(kind == LOCAL) | |
1330 | { | |
1331 | t = dim + inputLocals[s & VALUE]; | |
1332 | } | |
1333 | else if(kind == STACK) | |
1334 | { | |
1335 | t = dim + inputStack[nStack - (s & VALUE)]; | |
1336 | } | |
1337 | else | |
1338 | { | |
1339 | t = s; | |
1340 | } | |
1341 | } | |
1342 | } | |
1343 | else | |
1344 | { | |
1345 | t = inputLocals[i]; | |
1346 | } | |
1347 | if(initializations != null) | |
1348 | { | |
1349 | t = init(cw, t); | |
1350 | } | |
1351 | changed |= merge(cw, t, frame.inputLocals, i); | |
1352 | } | |
1353 | ||
1354 | if(edge > 0) | |
1355 | { | |
1356 | for(i = 0; i < nLocal; ++i) | |
1357 | { | |
1358 | t = inputLocals[i]; | |
1359 | changed |= merge(cw, t, frame.inputLocals, i); | |
1360 | } | |
1361 | if(frame.inputStack == null) | |
1362 | { | |
1363 | frame.inputStack = new int[1]; | |
1364 | changed = true; | |
1365 | } | |
1366 | changed |= merge(cw, edge, frame.inputStack, 0); | |
1367 | return changed; | |
1368 | } | |
1369 | ||
1370 | int nInputStack = inputStack.length + owner.inputStackTop; | |
1371 | if(frame.inputStack == null) | |
1372 | { | |
1373 | frame.inputStack = new int[nInputStack + outputStackTop]; | |
1374 | changed = true; | |
1375 | } | |
1376 | ||
1377 | for(i = 0; i < nInputStack; ++i) | |
1378 | { | |
1379 | t = inputStack[i]; | |
1380 | if(initializations != null) | |
1381 | { | |
1382 | t = init(cw, t); | |
1383 | } | |
1384 | changed |= merge(cw, t, frame.inputStack, i); | |
1385 | } | |
1386 | for(i = 0; i < outputStackTop; ++i) | |
1387 | { | |
1388 | s = outputStack[i]; | |
1389 | dim = s & DIM; | |
1390 | kind = s & KIND; | |
1391 | if(kind == LOCAL) | |
1392 | { | |
1393 | t = dim + inputLocals[s & VALUE]; | |
1394 | } | |
1395 | else if(kind == STACK) | |
1396 | { | |
1397 | t = dim + inputStack[nStack - (s & VALUE)]; | |
1398 | } | |
1399 | else | |
1400 | { | |
1401 | t = s; | |
1402 | } | |
1403 | if(initializations != null) | |
1404 | { | |
1405 | t = init(cw, t); | |
1406 | } | |
1407 | changed |= merge(cw, t, frame.inputStack, nInputStack + i); | |
1408 | } | |
1409 | return changed; | |
1410 | } | |
1411 | ||
1412 | /** | |
1413 | * Merges the type at the given index in the given type array with the given | |
1414 | * type. Returns <tt>true</tt> if the type array has been modified by this | |
1415 | * operation. | |
1416 | * | |
1417 | * @param cw the ClassWriter to which this label belongs. | |
1418 | * @param t the type with which the type array element must be merged. | |
1419 | * @param types an array of types. | |
1420 | * @param index the index of the type that must be merged in 'types'. | |
1421 | * @return <tt>true</tt> if the type array has been modified by this | |
1422 | * operation. | |
1423 | */ | |
1424 | private boolean merge( | |
1425 | final ClassWriter cw, | |
1426 | int t, | |
1427 | final int[] types, | |
1428 | final int index){ | |
1429 | int u = types[index]; | |
1430 | if(u == t) | |
1431 | { | |
1432 | // if the types are equal, merge(u,t)=u, so there is no change | |
1433 | return false; | |
1434 | } | |
1435 | if((t & ~DIM) == NULL) | |
1436 | { | |
1437 | if(u == NULL) | |
1438 | { | |
1439 | return false; | |
1440 | } | |
1441 | t = NULL; | |
1442 | } | |
1443 | if(u == 0) | |
1444 | { | |
1445 | // if types[index] has never been assigned, merge(u,t)=t | |
1446 | types[index] = t; | |
1447 | return true; | |
1448 | } | |
1449 | int v; | |
1450 | if((u & BASE_KIND) == OBJECT || (u & DIM) != 0) | |
1451 | { | |
1452 | // if u is a reference type of any dimension | |
1453 | if(t == NULL) | |
1454 | { | |
1455 | // if t is the NULL type, merge(u,t)=u, so there is no change | |
1456 | return false; | |
1457 | } | |
1458 | else if((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) | |
1459 | { | |
1460 | if((u & BASE_KIND) == OBJECT) | |
1461 | { | |
1462 | // if t is also a reference type, and if u and t have the | |
1463 | // same dimension merge(u,t) = dim(t) | common parent of the | |
1464 | // element types of u and t | |
1465 | v = (t & DIM) | OBJECT | |
1466 | | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); | |
1467 | } | |
1468 | else | |
1469 | { | |
1470 | // if u and t are array types, but not with the same element | |
1471 | // type, merge(u,t)=java/lang/Object | |
1472 | v = OBJECT | cw.addType("java/lang/Object"); | |
1473 | } | |
1474 | } | |
1475 | else if((t & BASE_KIND) == OBJECT || (t & DIM) != 0) | |
1476 | { | |
1477 | // if t is any other reference or array type, | |
1478 | // merge(u,t)=java/lang/Object | |
1479 | v = OBJECT | cw.addType("java/lang/Object"); | |
1480 | } | |
1481 | else | |
1482 | { | |
1483 | // if t is any other type, merge(u,t)=TOP | |
1484 | v = TOP; | |
1485 | } | |
1486 | } | |
1487 | else if(u == NULL) | |
1488 | { | |
1489 | // if u is the NULL type, merge(u,t)=t, | |
1490 | // or TOP if t is not a reference type | |
1491 | v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; | |
1492 | } | |
1493 | else | |
1494 | { | |
1495 | // if u is any other type, merge(u,t)=TOP whatever t | |
1496 | v = TOP; | |
1497 | } | |
1498 | if(u != v) | |
1499 | { | |
1500 | types[index] = v; | |
1501 | return true; | |
1502 | } | |
1503 | return false; | |
1504 | } | |
1505 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * Information about an exception handler block. | |
33 | * | |
34 | * @author Eric Bruneton | |
35 | */ | |
36 | class Handler{ | |
37 | ||
38 | /** | |
39 | * Beginning of the exception handler's scope (inclusive). | |
40 | */ | |
41 | Label start; | |
42 | ||
43 | /** | |
44 | * End of the exception handler's scope (exclusive). | |
45 | */ | |
46 | Label end; | |
47 | ||
48 | /** | |
49 | * Beginning of the exception handler's code. | |
50 | */ | |
51 | Label handler; | |
52 | ||
53 | /** | |
54 | * Internal name of the type of exceptions handled by this handler, or | |
55 | * <tt>null</tt> to catch any exceptions. | |
56 | */ | |
57 | String desc; | |
58 | ||
59 | /** | |
60 | * Constant pool index of the internal name of the type of exceptions | |
61 | * handled by this handler, or 0 to catch any exceptions. | |
62 | */ | |
63 | int type; | |
64 | ||
65 | /** | |
66 | * Next exception handler block info. | |
67 | */ | |
68 | Handler next; | |
69 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A constant pool item. Constant pool items can be created with the 'newXXX' | |
33 | * methods in the {@link ClassWriter} class. | |
34 | * | |
35 | * @author Eric Bruneton | |
36 | */ | |
37 | final class Item{ | |
38 | ||
39 | /** | |
40 | * Index of this item in the constant pool. | |
41 | */ | |
42 | int index; | |
43 | ||
44 | /** | |
45 | * Type of this constant pool item. A single class is used to represent all | |
46 | * constant pool item types, in order to minimize the bytecode size of this | |
47 | * package. The value of this field is one of {@link ClassWriter#INT}, | |
48 | * {@link ClassWriter#LONG}, {@link ClassWriter#FLOAT}, | |
49 | * {@link ClassWriter#DOUBLE}, {@link ClassWriter#UTF8}, | |
50 | * {@link ClassWriter#STR}, {@link ClassWriter#CLASS}, | |
51 | * {@link ClassWriter#NAME_TYPE}, {@link ClassWriter#FIELD}, | |
52 | * {@link ClassWriter#METH}, {@link ClassWriter#IMETH}. | |
53 | * <p/> | |
54 | * Special Item types are used for Items that are stored in the ClassWriter | |
55 | * {@link ClassWriter#typeTable}, instead of the constant pool, in order to | |
56 | * avoid clashes with normal constant pool items in the ClassWriter constant | |
57 | * pool's hash table. These special item types are | |
58 | * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and | |
59 | * {@link ClassWriter#TYPE_MERGED}. | |
60 | */ | |
61 | int type; | |
62 | ||
63 | /** | |
64 | * Value of this item, for an integer item. | |
65 | */ | |
66 | int intVal; | |
67 | ||
68 | /** | |
69 | * Value of this item, for a long item. | |
70 | */ | |
71 | long longVal; | |
72 | ||
73 | /** | |
74 | * First part of the value of this item, for items that do not hold a | |
75 | * primitive value. | |
76 | */ | |
77 | String strVal1; | |
78 | ||
79 | /** | |
80 | * Second part of the value of this item, for items that do not hold a | |
81 | * primitive value. | |
82 | */ | |
83 | String strVal2; | |
84 | ||
85 | /** | |
86 | * Third part of the value of this item, for items that do not hold a | |
87 | * primitive value. | |
88 | */ | |
89 | String strVal3; | |
90 | ||
91 | /** | |
92 | * The hash code value of this constant pool item. | |
93 | */ | |
94 | int hashCode; | |
95 | ||
96 | /** | |
97 | * Link to another constant pool item, used for collision lists in the | |
98 | * constant pool's hash table. | |
99 | */ | |
100 | Item next; | |
101 | ||
102 | /** | |
103 | * Constructs an uninitialized {@link Item}. | |
104 | */ | |
105 | Item(){ | |
106 | } | |
107 | ||
108 | /** | |
109 | * Constructs an uninitialized {@link Item} for constant pool element at | |
110 | * given position. | |
111 | * | |
112 | * @param index index of the item to be constructed. | |
113 | */ | |
114 | Item(final int index){ | |
115 | this.index = index; | |
116 | } | |
117 | ||
118 | /** | |
119 | * Constructs a copy of the given item. | |
120 | * | |
121 | * @param index index of the item to be constructed. | |
122 | * @param i the item that must be copied into the item to be constructed. | |
123 | */ | |
124 | Item(final int index, final Item i){ | |
125 | this.index = index; | |
126 | type = i.type; | |
127 | intVal = i.intVal; | |
128 | longVal = i.longVal; | |
129 | strVal1 = i.strVal1; | |
130 | strVal2 = i.strVal2; | |
131 | strVal3 = i.strVal3; | |
132 | hashCode = i.hashCode; | |
133 | } | |
134 | ||
135 | /** | |
136 | * Sets this item to an integer item. | |
137 | * | |
138 | * @param intVal the value of this item. | |
139 | */ | |
140 | void set(final int intVal){ | |
141 | this.type = ClassWriter.INT; | |
142 | this.intVal = intVal; | |
143 | this.hashCode = 0x7FFFFFFF & (type + intVal); | |
144 | } | |
145 | ||
146 | /** | |
147 | * Sets this item to a long item. | |
148 | * | |
149 | * @param longVal the value of this item. | |
150 | */ | |
151 | void set(final long longVal){ | |
152 | this.type = ClassWriter.LONG; | |
153 | this.longVal = longVal; | |
154 | this.hashCode = 0x7FFFFFFF & (type + (int) longVal); | |
155 | } | |
156 | ||
157 | /** | |
158 | * Sets this item to a float item. | |
159 | * | |
160 | * @param floatVal the value of this item. | |
161 | */ | |
162 | void set(final float floatVal){ | |
163 | this.type = ClassWriter.FLOAT; | |
164 | this.intVal = Float.floatToRawIntBits(floatVal); | |
165 | this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); | |
166 | } | |
167 | ||
168 | /** | |
169 | * Sets this item to a double item. | |
170 | * | |
171 | * @param doubleVal the value of this item. | |
172 | */ | |
173 | void set(final double doubleVal){ | |
174 | this.type = ClassWriter.DOUBLE; | |
175 | this.longVal = Double.doubleToRawLongBits(doubleVal); | |
176 | this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); | |
177 | } | |
178 | ||
179 | /** | |
180 | * Sets this item to an item that do not hold a primitive value. | |
181 | * | |
182 | * @param type the type of this item. | |
183 | * @param strVal1 first part of the value of this item. | |
184 | * @param strVal2 second part of the value of this item. | |
185 | * @param strVal3 third part of the value of this item. | |
186 | */ | |
187 | void set( | |
188 | final int type, | |
189 | final String strVal1, | |
190 | final String strVal2, | |
191 | final String strVal3){ | |
192 | this.type = type; | |
193 | this.strVal1 = strVal1; | |
194 | this.strVal2 = strVal2; | |
195 | this.strVal3 = strVal3; | |
196 | switch(type) | |
197 | { | |
198 | case ClassWriter.UTF8: | |
199 | case ClassWriter.STR: | |
200 | case ClassWriter.CLASS: | |
201 | case ClassWriter.TYPE_NORMAL: | |
202 | hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); | |
203 | return; | |
204 | case ClassWriter.NAME_TYPE: | |
205 | hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() | |
206 | * strVal2.hashCode()); | |
207 | return; | |
208 | // ClassWriter.FIELD: | |
209 | // ClassWriter.METH: | |
210 | // ClassWriter.IMETH: | |
211 | default: | |
212 | hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() | |
213 | * strVal2.hashCode() * strVal3.hashCode()); | |
214 | } | |
215 | } | |
216 | ||
217 | /** | |
218 | * Indicates if the given item is equal to this one. | |
219 | * | |
220 | * @param i the item to be compared to this one. | |
221 | * @return <tt>true</tt> if the given item if equal to this one, | |
222 | * <tt>false</tt> otherwise. | |
223 | */ | |
224 | boolean isEqualTo(final Item i){ | |
225 | if(i.type == type) | |
226 | { | |
227 | switch(type) | |
228 | { | |
229 | case ClassWriter.INT: | |
230 | case ClassWriter.FLOAT: | |
231 | return i.intVal == intVal; | |
232 | case ClassWriter.TYPE_MERGED: | |
233 | case ClassWriter.LONG: | |
234 | case ClassWriter.DOUBLE: | |
235 | return i.longVal == longVal; | |
236 | case ClassWriter.UTF8: | |
237 | case ClassWriter.STR: | |
238 | case ClassWriter.CLASS: | |
239 | case ClassWriter.TYPE_NORMAL: | |
240 | return i.strVal1.equals(strVal1); | |
241 | case ClassWriter.TYPE_UNINIT: | |
242 | return i.intVal == intVal && i.strVal1.equals(strVal1); | |
243 | case ClassWriter.NAME_TYPE: | |
244 | return i.strVal1.equals(strVal1) | |
245 | && i.strVal2.equals(strVal2); | |
246 | // ClassWriter.FIELD: | |
247 | // ClassWriter.METH: | |
248 | // ClassWriter.IMETH: | |
249 | default: | |
250 | return i.strVal1.equals(strVal1) | |
251 | && i.strVal2.equals(strVal2) | |
252 | && i.strVal3.equals(strVal3); | |
253 | } | |
254 | } | |
255 | return false; | |
256 | } | |
257 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A label represents a position in the bytecode of a method. Labels are used | |
33 | * for jump, goto, and switch instructions, and for try catch blocks. | |
34 | * | |
35 | * @author Eric Bruneton | |
36 | */ | |
37 | public class Label{ | |
38 | ||
39 | /** | |
40 | * Indicates if this label is only used for debug attributes. Such a label | |
41 | * is not the start of a basic block, the target of a jump instruction, or | |
42 | * an exception handler. It can be safely ignored in control flow graph | |
43 | * analysis algorithms (for optimization purposes). | |
44 | */ | |
45 | final static int DEBUG = 1; | |
46 | ||
47 | /** | |
48 | * Indicates if the position of this label is known. | |
49 | */ | |
50 | final static int RESOLVED = 2; | |
51 | ||
52 | /** | |
53 | * Indicates if this label has been updated, after instruction resizing. | |
54 | */ | |
55 | final static int RESIZED = 4; | |
56 | ||
57 | /** | |
58 | * Indicates if this basic block has been pushed in the basic block stack. | |
59 | * See {@link MethodWriter#visitMaxs visitMaxs}. | |
60 | */ | |
61 | final static int PUSHED = 8; | |
62 | ||
63 | /** | |
64 | * Indicates if this label is the target of a jump instruction, or the start | |
65 | * of an exception handler. | |
66 | */ | |
67 | final static int TARGET = 16; | |
68 | ||
69 | /** | |
70 | * Indicates if a stack map frame must be stored for this label. | |
71 | */ | |
72 | final static int STORE = 32; | |
73 | ||
74 | /** | |
75 | * Indicates if this label corresponds to a reachable basic block. | |
76 | */ | |
77 | final static int REACHABLE = 64; | |
78 | ||
79 | /** | |
80 | * Indicates if this basic block ends with a JSR instruction. | |
81 | */ | |
82 | final static int JSR = 128; | |
83 | ||
84 | /** | |
85 | * Indicates if this basic block ends with a RET instruction. | |
86 | */ | |
87 | final static int RET = 256; | |
88 | ||
89 | /** | |
90 | * Field used to associate user information to a label. | |
91 | */ | |
92 | public Object info; | |
93 | ||
94 | /** | |
95 | * Flags that indicate the status of this label. | |
96 | * | |
97 | * @see #DEBUG | |
98 | * @see #RESOLVED | |
99 | * @see #RESIZED | |
100 | * @see #PUSHED | |
101 | * @see #TARGET | |
102 | * @see #STORE | |
103 | * @see #REACHABLE | |
104 | * @see #JSR | |
105 | * @see #RET | |
106 | */ | |
107 | int status; | |
108 | ||
109 | /** | |
110 | * The line number corresponding to this label, if known. | |
111 | */ | |
112 | int line; | |
113 | ||
114 | /** | |
115 | * The position of this label in the code, if known. | |
116 | */ | |
117 | int position; | |
118 | ||
119 | /** | |
120 | * Number of forward references to this label, times two. | |
121 | */ | |
122 | private int referenceCount; | |
123 | ||
124 | /** | |
125 | * Informations about forward references. Each forward reference is | |
126 | * described by two consecutive integers in this array: the first one is the | |
127 | * position of the first byte of the bytecode instruction that contains the | |
128 | * forward reference, while the second is the position of the first byte of | |
129 | * the forward reference itself. In fact the sign of the first integer | |
130 | * indicates if this reference uses 2 or 4 bytes, and its absolute value | |
131 | * gives the position of the bytecode instruction. | |
132 | */ | |
133 | private int[] srcAndRefPositions; | |
134 | ||
135 | // ------------------------------------------------------------------------ | |
136 | ||
137 | /* | |
138 | * Fields for the control flow and data flow graph analysis algorithms (used | |
139 | * to compute the maximum stack size or the stack map frames). A control | |
140 | * flow graph contains one node per "basic block", and one edge per "jump" | |
141 | * from one basic block to another. Each node (i.e., each basic block) is | |
142 | * represented by the Label object that corresponds to the first instruction | |
143 | * of this basic block. Each node also stores the list of its successors in | |
144 | * the graph, as a linked list of Edge objects. | |
145 | * | |
146 | * The control flow analysis algorithms used to compute the maximum stack | |
147 | * size or the stack map frames are similar and use two steps. The first | |
148 | * step, during the visit of each instruction, builds information about the | |
149 | * state of the local variables and the operand stack at the end of each | |
150 | * basic block, called the "output frame", <i>relatively</i> to the frame | |
151 | * state at the beginning of the basic block, which is called the "input | |
152 | * frame", and which is <i>unknown</i> during this step. The second step, | |
153 | * in {@link MethodWriter#visitMaxs}, is a fix point algorithm that | |
154 | * computes information about the input frame of each basic block, from the | |
155 | * input state of the first basic block (known from the method signature), | |
156 | * and by the using the previously computed relative output frames. | |
157 | * | |
158 | * The algorithm used to compute the maximum stack size only computes the | |
159 | * relative output and absolute input stack heights, while the algorithm | |
160 | * used to compute stack map frames computes relative output frames and | |
161 | * absolute input frames. | |
162 | */ | |
163 | ||
164 | /** | |
165 | * Start of the output stack relatively to the input stack. The exact | |
166 | * semantics of this field depends on the algorithm that is used. | |
167 | * <p/> | |
168 | * When only the maximum stack size is computed, this field is the number of | |
169 | * elements in the input stack. | |
170 | * <p/> | |
171 | * When the stack map frames are completely computed, this field is the | |
172 | * offset of the first output stack element relatively to the top of the | |
173 | * input stack. This offset is always negative or null. A null offset means | |
174 | * that the output stack must be appended to the input stack. A -n offset | |
175 | * means that the first n output stack elements must replace the top n input | |
176 | * stack elements, and that the other elements must be appended to the input | |
177 | * stack. | |
178 | */ | |
179 | int inputStackTop; | |
180 | ||
181 | /** | |
182 | * Maximum height reached by the output stack, relatively to the top of the | |
183 | * input stack. This maximum is always positive or null. | |
184 | */ | |
185 | int outputStackMax; | |
186 | ||
187 | /** | |
188 | * Information about the input and output stack map frames of this basic | |
189 | * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} | |
190 | * option is used. | |
191 | */ | |
192 | Frame frame; | |
193 | ||
194 | /** | |
195 | * The successor of this label, in the order they are visited. This linked | |
196 | * list does not include labels used for debug info only. If | |
197 | * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it | |
198 | * does not contain successive labels that denote the same bytecode position | |
199 | * (in this case only the first label appears in this list). | |
200 | */ | |
201 | Label successor; | |
202 | ||
203 | /** | |
204 | * The successors of this node in the control flow graph. These successors | |
205 | * are stored in a linked list of {@link Edge Edge} objects, linked to each | |
206 | * other by their {@link Edge#next} field. | |
207 | */ | |
208 | Edge successors; | |
209 | ||
210 | /** | |
211 | * The next basic block in the basic block stack. This stack is used in the | |
212 | * main loop of the fix point algorithm used in the second step of the | |
213 | * control flow analysis algorithms. | |
214 | * | |
215 | * @see MethodWriter#visitMaxs | |
216 | */ | |
217 | Label next; | |
218 | ||
219 | // ------------------------------------------------------------------------ | |
220 | // Constructor | |
221 | // ------------------------------------------------------------------------ | |
222 | ||
223 | /** | |
224 | * Constructs a new label. | |
225 | */ | |
226 | public Label(){ | |
227 | } | |
228 | ||
229 | /** | |
230 | * Constructs a new label. | |
231 | * | |
232 | * @param debug if this label is only used for debug attributes. | |
233 | */ | |
234 | Label(final boolean debug){ | |
235 | this.status = debug ? DEBUG : 0; | |
236 | } | |
237 | ||
238 | // ------------------------------------------------------------------------ | |
239 | // Methods to compute offsets and to manage forward references | |
240 | // ------------------------------------------------------------------------ | |
241 | ||
242 | /** | |
243 | * Returns the offset corresponding to this label. This offset is computed | |
244 | * from the start of the method's bytecode. <i>This method is intended for | |
245 | * {@link Attribute} sub classes, and is normally not needed by class | |
246 | * generators or adapters.</i> | |
247 | * | |
248 | * @return the offset corresponding to this label. | |
249 | * @throws IllegalStateException if this label is not resolved yet. | |
250 | */ | |
251 | public int getOffset(){ | |
252 | if((status & RESOLVED) == 0) | |
253 | { | |
254 | throw new IllegalStateException("Label offset position has not been resolved yet"); | |
255 | } | |
256 | return position; | |
257 | } | |
258 | ||
259 | /** | |
260 | * Puts a reference to this label in the bytecode of a method. If the | |
261 | * position of the label is known, the offset is computed and written | |
262 | * directly. Otherwise, a null offset is written and a new forward reference | |
263 | * is declared for this label. | |
264 | * | |
265 | * @param owner the code writer that calls this method. | |
266 | * @param out the bytecode of the method. | |
267 | * @param source the position of first byte of the bytecode instruction that | |
268 | * contains this label. | |
269 | * @param wideOffset <tt>true</tt> if the reference must be stored in 4 | |
270 | * bytes, or <tt>false</tt> if it must be stored with 2 bytes. | |
271 | * @throws IllegalArgumentException if this label has not been created by | |
272 | * the given code writer. | |
273 | */ | |
274 | void put( | |
275 | final MethodWriter owner, | |
276 | final ByteVector out, | |
277 | final int source, | |
278 | final boolean wideOffset){ | |
279 | if((status & RESOLVED) != 0) | |
280 | { | |
281 | if(wideOffset) | |
282 | { | |
283 | out.putInt(position - source); | |
284 | } | |
285 | else | |
286 | { | |
287 | out.putShort(position - source); | |
288 | } | |
289 | } | |
290 | else | |
291 | { | |
292 | if(wideOffset) | |
293 | { | |
294 | addReference(-1 - source, out.length); | |
295 | out.putInt(-1); | |
296 | } | |
297 | else | |
298 | { | |
299 | addReference(source, out.length); | |
300 | out.putShort(-1); | |
301 | } | |
302 | } | |
303 | } | |
304 | ||
305 | /** | |
306 | * Adds a forward reference to this label. This method must be called only | |
307 | * for a true forward reference, i.e. only if this label is not resolved | |
308 | * yet. For backward references, the offset of the reference can be, and | |
309 | * must be, computed and stored directly. | |
310 | * | |
311 | * @param sourcePosition the position of the referencing instruction. This | |
312 | * position will be used to compute the offset of this forward | |
313 | * reference. | |
314 | * @param referencePosition the position where the offset for this forward | |
315 | * reference must be stored. | |
316 | */ | |
317 | private void addReference( | |
318 | final int sourcePosition, | |
319 | final int referencePosition){ | |
320 | if(srcAndRefPositions == null) | |
321 | { | |
322 | srcAndRefPositions = new int[6]; | |
323 | } | |
324 | if(referenceCount >= srcAndRefPositions.length) | |
325 | { | |
326 | int[] a = new int[srcAndRefPositions.length + 6]; | |
327 | System.arraycopy(srcAndRefPositions, | |
328 | 0, | |
329 | a, | |
330 | 0, | |
331 | srcAndRefPositions.length); | |
332 | srcAndRefPositions = a; | |
333 | } | |
334 | srcAndRefPositions[referenceCount++] = sourcePosition; | |
335 | srcAndRefPositions[referenceCount++] = referencePosition; | |
336 | } | |
337 | ||
338 | /** | |
339 | * Resolves all forward references to this label. This method must be called | |
340 | * when this label is added to the bytecode of the method, i.e. when its | |
341 | * position becomes known. This method fills in the blanks that where left | |
342 | * in the bytecode by each forward reference previously added to this label. | |
343 | * | |
344 | * @param owner the code writer that calls this method. | |
345 | * @param position the position of this label in the bytecode. | |
346 | * @param data the bytecode of the method. | |
347 | * @return <tt>true</tt> if a blank that was left for this label was to | |
348 | * small to store the offset. In such a case the corresponding jump | |
349 | * instruction is replaced with a pseudo instruction (using unused | |
350 | * opcodes) using an unsigned two bytes offset. These pseudo | |
351 | * instructions will need to be replaced with true instructions with | |
352 | * wider offsets (4 bytes instead of 2). This is done in | |
353 | * {@link MethodWriter#resizeInstructions}. | |
354 | * @throws IllegalArgumentException if this label has already been resolved, | |
355 | * or if it has not been created by the given code writer. | |
356 | */ | |
357 | boolean resolve( | |
358 | final MethodWriter owner, | |
359 | final int position, | |
360 | final byte[] data){ | |
361 | boolean needUpdate = false; | |
362 | this.status |= RESOLVED; | |
363 | this.position = position; | |
364 | int i = 0; | |
365 | while(i < referenceCount) | |
366 | { | |
367 | int source = srcAndRefPositions[i++]; | |
368 | int reference = srcAndRefPositions[i++]; | |
369 | int offset; | |
370 | if(source >= 0) | |
371 | { | |
372 | offset = position - source; | |
373 | if(offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) | |
374 | { | |
375 | /* | |
376 | * changes the opcode of the jump instruction, in order to | |
377 | * be able to find it later (see resizeInstructions in | |
378 | * MethodWriter). These temporary opcodes are similar to | |
379 | * jump instruction opcodes, except that the 2 bytes offset | |
380 | * is unsigned (and can therefore represent values from 0 to | |
381 | * 65535, which is sufficient since the size of a method is | |
382 | * limited to 65535 bytes). | |
383 | */ | |
384 | int opcode = data[reference - 1] & 0xFF; | |
385 | if(opcode <= Opcodes.JSR) | |
386 | { | |
387 | // changes IFEQ ... JSR to opcodes 202 to 217 | |
388 | data[reference - 1] = (byte) (opcode + 49); | |
389 | } | |
390 | else | |
391 | { | |
392 | // changes IFNULL and IFNONNULL to opcodes 218 and 219 | |
393 | data[reference - 1] = (byte) (opcode + 20); | |
394 | } | |
395 | needUpdate = true; | |
396 | } | |
397 | data[reference++] = (byte) (offset >>> 8); | |
398 | data[reference] = (byte) offset; | |
399 | } | |
400 | else | |
401 | { | |
402 | offset = position + source + 1; | |
403 | data[reference++] = (byte) (offset >>> 24); | |
404 | data[reference++] = (byte) (offset >>> 16); | |
405 | data[reference++] = (byte) (offset >>> 8); | |
406 | data[reference] = (byte) offset; | |
407 | } | |
408 | } | |
409 | return needUpdate; | |
410 | } | |
411 | ||
412 | /** | |
413 | * Returns the first label of the series to which this label belongs. For an | |
414 | * isolated label or for the first label in a series of successive labels, | |
415 | * this method returns the label itself. For other labels it returns the | |
416 | * first label of the series. | |
417 | * | |
418 | * @return the first label of the series to which this label belongs. | |
419 | */ | |
420 | Label getFirst(){ | |
421 | return frame == null ? this : frame.owner; | |
422 | } | |
423 | ||
424 | // ------------------------------------------------------------------------ | |
425 | // Overriden Object methods | |
426 | // ------------------------------------------------------------------------ | |
427 | ||
428 | /** | |
429 | * Returns a string representation of this label. | |
430 | * | |
431 | * @return a string representation of this label. | |
432 | */ | |
433 | public String toString(){ | |
434 | return "L" + System.identityHashCode(this); | |
435 | } | |
436 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * An empty {@link MethodVisitor} that delegates to another | |
33 | * {@link MethodVisitor}. This class can be used as a super class to quickly | |
34 | * implement usefull method adapter classes, just by overriding the necessary | |
35 | * methods. | |
36 | * | |
37 | * @author Eric Bruneton | |
38 | */ | |
39 | public class MethodAdapter implements MethodVisitor{ | |
40 | ||
41 | /** | |
42 | * The {@link MethodVisitor} to which this adapter delegates calls. | |
43 | */ | |
44 | protected MethodVisitor mv; | |
45 | ||
46 | /** | |
47 | * Constructs a new {@link MethodAdapter} object. | |
48 | * | |
49 | * @param mv the code visitor to which this adapter must delegate calls. | |
50 | */ | |
51 | public MethodAdapter(final MethodVisitor mv){ | |
52 | this.mv = mv; | |
53 | } | |
54 | ||
55 | public AnnotationVisitor visitAnnotationDefault(){ | |
56 | return mv.visitAnnotationDefault(); | |
57 | } | |
58 | ||
59 | public AnnotationVisitor visitAnnotation( | |
60 | final String desc, | |
61 | final boolean visible){ | |
62 | return mv.visitAnnotation(desc, visible); | |
63 | } | |
64 | ||
65 | public AnnotationVisitor visitParameterAnnotation( | |
66 | final int parameter, | |
67 | final String desc, | |
68 | final boolean visible){ | |
69 | return mv.visitParameterAnnotation(parameter, desc, visible); | |
70 | } | |
71 | ||
72 | public void visitAttribute(final Attribute attr){ | |
73 | mv.visitAttribute(attr); | |
74 | } | |
75 | ||
76 | public void visitCode(){ | |
77 | mv.visitCode(); | |
78 | } | |
79 | ||
80 | public void visitFrame( | |
81 | final int type, | |
82 | final int nLocal, | |
83 | final Object[] local, | |
84 | final int nStack, | |
85 | final Object[] stack){ | |
86 | mv.visitFrame(type, nLocal, local, nStack, stack); | |
87 | } | |
88 | ||
89 | public void visitInsn(final int opcode){ | |
90 | mv.visitInsn(opcode); | |
91 | } | |
92 | ||
93 | public void visitIntInsn(final int opcode, final int operand){ | |
94 | mv.visitIntInsn(opcode, operand); | |
95 | } | |
96 | ||
97 | public void visitVarInsn(final int opcode, final int var){ | |
98 | mv.visitVarInsn(opcode, var); | |
99 | } | |
100 | ||
101 | public void visitTypeInsn(final int opcode, final String desc){ | |
102 | mv.visitTypeInsn(opcode, desc); | |
103 | } | |
104 | ||
105 | public void visitFieldInsn( | |
106 | final int opcode, | |
107 | final String owner, | |
108 | final String name, | |
109 | final String desc){ | |
110 | mv.visitFieldInsn(opcode, owner, name, desc); | |
111 | } | |
112 | ||
113 | public void visitMethodInsn( | |
114 | final int opcode, | |
115 | final String owner, | |
116 | final String name, | |
117 | final String desc){ | |
118 | mv.visitMethodInsn(opcode, owner, name, desc); | |
119 | } | |
120 | ||
121 | public void visitJumpInsn(final int opcode, final Label label){ | |
122 | mv.visitJumpInsn(opcode, label); | |
123 | } | |
124 | ||
125 | public void visitLabel(final Label label){ | |
126 | mv.visitLabel(label); | |
127 | } | |
128 | ||
129 | public void visitLdcInsn(final Object cst){ | |
130 | mv.visitLdcInsn(cst); | |
131 | } | |
132 | ||
133 | public void visitIincInsn(final int var, final int increment){ | |
134 | mv.visitIincInsn(var, increment); | |
135 | } | |
136 | ||
137 | public void visitTableSwitchInsn( | |
138 | final int min, | |
139 | final int max, | |
140 | final Label dflt, | |
141 | final Label labels[]){ | |
142 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
143 | } | |
144 | ||
145 | public void visitLookupSwitchInsn( | |
146 | final Label dflt, | |
147 | final int keys[], | |
148 | final Label labels[]){ | |
149 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
150 | } | |
151 | ||
152 | public void visitMultiANewArrayInsn(final String desc, final int dims){ | |
153 | mv.visitMultiANewArrayInsn(desc, dims); | |
154 | } | |
155 | ||
156 | public void visitTryCatchBlock( | |
157 | final Label start, | |
158 | final Label end, | |
159 | final Label handler, | |
160 | final String type){ | |
161 | mv.visitTryCatchBlock(start, end, handler, type); | |
162 | } | |
163 | ||
164 | public void visitLocalVariable( | |
165 | final String name, | |
166 | final String desc, | |
167 | final String signature, | |
168 | final Label start, | |
169 | final Label end, | |
170 | final int index){ | |
171 | mv.visitLocalVariable(name, desc, signature, start, end, index); | |
172 | } | |
173 | ||
174 | public void visitLineNumber(final int line, final Label start){ | |
175 | mv.visitLineNumber(line, start); | |
176 | } | |
177 | ||
178 | public void visitMaxs(final int maxStack, final int maxLocals){ | |
179 | mv.visitMaxs(maxStack, maxLocals); | |
180 | } | |
181 | ||
182 | public void visitEnd(){ | |
183 | mv.visitEnd(); | |
184 | } | |
185 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A visitor to visit a Java method. The methods of this interface must be | |
33 | * called in the following order: [ <tt>visitAnnotationDefault</tt> ] ( | |
34 | * <tt>visitAnnotation</tt> | <tt>visitParameterAnnotation</tt> | | |
35 | * <tt>visitAttribute</tt> )* [ <tt>visitCode</tt> ( <tt>visitFrame</tt> | | |
36 | * <tt>visit</tt><i>X</i>Insn</tt> | <tt>visitLabel</tt> | <tt>visitTryCatchBlock</tt> | | |
37 | * <tt>visitLocalVariable</tt> | <tt>visitLineNumber</tt>)* <tt>visitMaxs</tt> ] | |
38 | * <tt>visitEnd</tt>. In addition, the <tt>visit</tt><i>X</i>Insn</tt> | |
39 | * and <tt>visitLabel</tt> methods must be called in the sequential order of | |
40 | * the bytecode instructions of the visited code, <tt>visitTryCatchBlock</tt> | |
41 | * must be called <i>before</i> the labels passed as arguments have been | |
42 | * visited, and the <tt>visitLocalVariable</tt> and <tt>visitLineNumber</tt> | |
43 | * methods must be called <i>after</i> the labels passed as arguments have been | |
44 | * visited. | |
45 | * | |
46 | * @author Eric Bruneton | |
47 | */ | |
48 | public interface MethodVisitor{ | |
49 | ||
50 | // ------------------------------------------------------------------------- | |
51 | // Annotations and non standard attributes | |
52 | // ------------------------------------------------------------------------- | |
53 | ||
54 | /** | |
55 | * Visits the default value of this annotation interface method. | |
56 | * | |
57 | * @return a visitor to the visit the actual default value of this | |
58 | * annotation interface method, or <tt>null</tt> if this visitor | |
59 | * is not interested in visiting this default value. The 'name' | |
60 | * parameters passed to the methods of this annotation visitor are | |
61 | * ignored. Moreover, exacly one visit method must be called on this | |
62 | * annotation visitor, followed by visitEnd. | |
63 | */ | |
64 | AnnotationVisitor visitAnnotationDefault(); | |
65 | ||
66 | /** | |
67 | * Visits an annotation of this method. | |
68 | * | |
69 | * @param desc the class descriptor of the annotation class. | |
70 | * @param visible <tt>true</tt> if the annotation is visible at runtime. | |
71 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
72 | * this visitor is not interested in visiting this annotation. | |
73 | */ | |
74 | AnnotationVisitor visitAnnotation(String desc, boolean visible); | |
75 | ||
76 | /** | |
77 | * Visits an annotation of a parameter this method. | |
78 | * | |
79 | * @param parameter the parameter index. | |
80 | * @param desc the class descriptor of the annotation class. | |
81 | * @param visible <tt>true</tt> if the annotation is visible at runtime. | |
82 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
83 | * this visitor is not interested in visiting this annotation. | |
84 | */ | |
85 | AnnotationVisitor visitParameterAnnotation( | |
86 | int parameter, | |
87 | String desc, | |
88 | boolean visible); | |
89 | ||
90 | /** | |
91 | * Visits a non standard attribute of this method. | |
92 | * | |
93 | * @param attr an attribute. | |
94 | */ | |
95 | void visitAttribute(Attribute attr); | |
96 | ||
97 | /** | |
98 | * Starts the visit of the method's code, if any (i.e. non abstract method). | |
99 | */ | |
100 | void visitCode(); | |
101 | ||
102 | /** | |
103 | * Visits the current state of the local variables and operand stack | |
104 | * elements. This method must(*) be called <i>just before</i> any | |
105 | * instruction <b>i</b> that follows an unconditionnal branch instruction | |
106 | * such as GOTO or THROW, that is the target of a jump instruction, or that | |
107 | * starts an exception handler block. The visited types must describe the | |
108 | * values of the local variables and of the operand stack elements <i>just | |
109 | * before</i> <b>i</b> is executed. <br> <br> (*) this is mandatory only | |
110 | * for classes whose version is greater than or equal to | |
111 | * {@link Opcodes#V1_6 V1_6}. <br> <br> Packed frames are basically | |
112 | * "deltas" from the state of the previous frame (very first frame is | |
113 | * implicitly defined by the method's parameters and access flags): <ul> | |
114 | * <li>{@link Opcodes#F_SAME} representing frame with exactly the same | |
115 | * locals as the previous frame and with the empty stack.</li> <li>{@link Opcodes#F_SAME1} | |
116 | * representing frame with exactly the same locals as the previous frame and | |
117 | * with single value on the stack (<code>nStack</code> is 1 and | |
118 | * <code>stack[0]</code> contains value for the type of the stack item).</li> | |
119 | * <li>{@link Opcodes#F_APPEND} representing frame with current locals are | |
120 | * the same as the locals in the previous frame, except that additional | |
121 | * locals are defined (<code>nLocal</code> is 1, 2 or 3 and | |
122 | * <code>local</code> elements contains values representing added types).</li> | |
123 | * <li>{@link Opcodes#F_CHOP} representing frame with current locals are | |
124 | * the same as the locals in the previous frame, except that the last 1-3 | |
125 | * locals are absent and with the empty stack (<code>nLocals</code> is 1, | |
126 | * 2 or 3). </li> <li>{@link Opcodes#F_FULL} representing complete frame | |
127 | * data.</li> </li> </ul> | |
128 | * | |
129 | * @param type the type of this stack map frame. Must be | |
130 | * {@link Opcodes#F_NEW} for expanded frames, or | |
131 | * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, | |
132 | * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or | |
133 | * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for compressed | |
134 | * frames. | |
135 | * @param nLocal the number of local variables in the visited frame. | |
136 | * @param local the local variable types in this frame. This array must not | |
137 | * be modified. Primitive types are represented by | |
138 | * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, | |
139 | * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, | |
140 | * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or | |
141 | * {@link Opcodes#UNINITIALIZED_THIS} (long and double are | |
142 | * represented by a single element). Reference types are represented | |
143 | * by String objects (representing internal names, or type | |
144 | * descriptors for array types), and uninitialized types by Label | |
145 | * objects (this label designates the NEW instruction that created | |
146 | * this uninitialized value). | |
147 | * @param nStack the number of operand stack elements in the visited frame. | |
148 | * @param stack the operand stack types in this frame. This array must not | |
149 | * be modified. Its content has the same format as the "local" array. | |
150 | */ | |
151 | void visitFrame( | |
152 | int type, | |
153 | int nLocal, | |
154 | Object[] local, | |
155 | int nStack, | |
156 | Object[] stack); | |
157 | ||
158 | // ------------------------------------------------------------------------- | |
159 | // Normal instructions | |
160 | // ------------------------------------------------------------------------- | |
161 | ||
162 | /** | |
163 | * Visits a zero operand instruction. | |
164 | * | |
165 | * @param opcode the opcode of the instruction to be visited. This opcode is | |
166 | * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, | |
167 | * ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, FCONST_0, | |
168 | * FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, LALOAD, FALOAD, | |
169 | * DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IASTORE, LASTORE, FASTORE, | |
170 | * DASTORE, AASTORE, BASTORE, CASTORE, SASTORE, POP, POP2, DUP, | |
171 | * DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP, IADD, LADD, FADD, | |
172 | * DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, | |
173 | * FDIV, DDIV, IREM, LREM, FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, | |
174 | * LSHL, ISHR, LSHR, IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, | |
175 | * I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, | |
176 | * I2C, I2S, LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, | |
177 | * FRETURN, DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, | |
178 | * MONITORENTER, or MONITOREXIT. | |
179 | */ | |
180 | void visitInsn(int opcode); | |
181 | ||
182 | /** | |
183 | * Visits an instruction with a single int operand. | |
184 | * | |
185 | * @param opcode the opcode of the instruction to be visited. This opcode is | |
186 | * either BIPUSH, SIPUSH or NEWARRAY. | |
187 | * @param operand the operand of the instruction to be visited.<br> When | |
188 | * opcode is BIPUSH, operand value should be between Byte.MIN_VALUE | |
189 | * and Byte.MAX_VALUE.<br> When opcode is SIPUSH, operand value | |
190 | * should be between Short.MIN_VALUE and Short.MAX_VALUE.<br> When | |
191 | * opcode is NEWARRAY, operand value should be one of | |
192 | * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, | |
193 | * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, | |
194 | * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, | |
195 | * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. | |
196 | */ | |
197 | void visitIntInsn(int opcode, int operand); | |
198 | ||
199 | /** | |
200 | * Visits a local variable instruction. A local variable instruction is an | |
201 | * instruction that loads or stores the value of a local variable. | |
202 | * | |
203 | * @param opcode the opcode of the local variable instruction to be visited. | |
204 | * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, | |
205 | * LSTORE, FSTORE, DSTORE, ASTORE or RET. | |
206 | * @param var the operand of the instruction to be visited. This operand is | |
207 | * the index of a local variable. | |
208 | */ | |
209 | void visitVarInsn(int opcode, int var); | |
210 | ||
211 | /** | |
212 | * Visits a type instruction. A type instruction is an instruction that | |
213 | * takes a type descriptor as parameter. | |
214 | * | |
215 | * @param opcode the opcode of the type instruction to be visited. This | |
216 | * opcode is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. | |
217 | * @param desc the operand of the instruction to be visited. This operand is | |
218 | * must be a fully qualified class name in internal form, or the type | |
219 | * descriptor of an array type (see {@link Type Type}). | |
220 | */ | |
221 | void visitTypeInsn(int opcode, String desc); | |
222 | ||
223 | /** | |
224 | * Visits a field instruction. A field instruction is an instruction that | |
225 | * loads or stores the value of a field of an object. | |
226 | * | |
227 | * @param opcode the opcode of the type instruction to be visited. This | |
228 | * opcode is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. | |
229 | * @param owner the internal name of the field's owner class (see {@link | |
230 | * Type#getInternalName() getInternalName}). | |
231 | * @param name the field's name. | |
232 | * @param desc the field's descriptor (see {@link Type Type}). | |
233 | */ | |
234 | void visitFieldInsn(int opcode, String owner, String name, String desc); | |
235 | ||
236 | /** | |
237 | * Visits a method instruction. A method instruction is an instruction that | |
238 | * invokes a method. | |
239 | * | |
240 | * @param opcode the opcode of the type instruction to be visited. This | |
241 | * opcode is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or | |
242 | * INVOKEINTERFACE. | |
243 | * @param owner the internal name of the method's owner class (see {@link | |
244 | * Type#getInternalName() getInternalName}). | |
245 | * @param name the method's name. | |
246 | * @param desc the method's descriptor (see {@link Type Type}). | |
247 | */ | |
248 | void visitMethodInsn(int opcode, String owner, String name, String desc); | |
249 | ||
250 | /** | |
251 | * Visits a jump instruction. A jump instruction is an instruction that may | |
252 | * jump to another instruction. | |
253 | * | |
254 | * @param opcode the opcode of the type instruction to be visited. This | |
255 | * opcode is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, | |
256 | * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, | |
257 | * IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. | |
258 | * @param label the operand of the instruction to be visited. This operand | |
259 | * is a label that designates the instruction to which the jump | |
260 | * instruction may jump. | |
261 | */ | |
262 | void visitJumpInsn(int opcode, Label label); | |
263 | ||
264 | /** | |
265 | * Visits a label. A label designates the instruction that will be visited | |
266 | * just after it. | |
267 | * | |
268 | * @param label a {@link Label Label} object. | |
269 | */ | |
270 | void visitLabel(Label label); | |
271 | ||
272 | // ------------------------------------------------------------------------- | |
273 | // Special instructions | |
274 | // ------------------------------------------------------------------------- | |
275 | ||
276 | /** | |
277 | * Visits a LDC instruction. | |
278 | * | |
279 | * @param cst the constant to be loaded on the stack. This parameter must be | |
280 | * a non null {@link Integer}, a {@link Float}, a {@link Long}, a | |
281 | * {@link Double} a {@link String} (or a {@link Type} for | |
282 | * <tt>.class</tt> constants, for classes whose version is 49.0 or | |
283 | * more). | |
284 | */ | |
285 | void visitLdcInsn(Object cst); | |
286 | ||
287 | /** | |
288 | * Visits an IINC instruction. | |
289 | * | |
290 | * @param var index of the local variable to be incremented. | |
291 | * @param increment amount to increment the local variable by. | |
292 | */ | |
293 | void visitIincInsn(int var, int increment); | |
294 | ||
295 | /** | |
296 | * Visits a TABLESWITCH instruction. | |
297 | * | |
298 | * @param min the minimum key value. | |
299 | * @param max the maximum key value. | |
300 | * @param dflt beginning of the default handler block. | |
301 | * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is | |
302 | * the beginning of the handler block for the <tt>min + i</tt> key. | |
303 | */ | |
304 | void visitTableSwitchInsn(int min, int max, Label dflt, Label labels[]); | |
305 | ||
306 | /** | |
307 | * Visits a LOOKUPSWITCH instruction. | |
308 | * | |
309 | * @param dflt beginning of the default handler block. | |
310 | * @param keys the values of the keys. | |
311 | * @param labels beginnings of the handler blocks. <tt>labels[i]</tt> is | |
312 | * the beginning of the handler block for the <tt>keys[i]</tt> key. | |
313 | */ | |
314 | void visitLookupSwitchInsn(Label dflt, int keys[], Label labels[]); | |
315 | ||
316 | /** | |
317 | * Visits a MULTIANEWARRAY instruction. | |
318 | * | |
319 | * @param desc an array type descriptor (see {@link Type Type}). | |
320 | * @param dims number of dimensions of the array to allocate. | |
321 | */ | |
322 | void visitMultiANewArrayInsn(String desc, int dims); | |
323 | ||
324 | // ------------------------------------------------------------------------- | |
325 | // Exceptions table entries, debug information, max stack and max locals | |
326 | // ------------------------------------------------------------------------- | |
327 | ||
328 | /** | |
329 | * Visits a try catch block. | |
330 | * | |
331 | * @param start beginning of the exception handler's scope (inclusive). | |
332 | * @param end end of the exception handler's scope (exclusive). | |
333 | * @param handler beginning of the exception handler's code. | |
334 | * @param type internal name of the type of exceptions handled by the | |
335 | * handler, or <tt>null</tt> to catch any exceptions (for "finally" | |
336 | * blocks). | |
337 | * @throws IllegalArgumentException if one of the labels has already been | |
338 | * visited by this visitor (by the {@link #visitLabel visitLabel} | |
339 | * method). | |
340 | */ | |
341 | void visitTryCatchBlock(Label start, Label end, Label handler, String type); | |
342 | ||
343 | /** | |
344 | * Visits a local variable declaration. | |
345 | * | |
346 | * @param name the name of a local variable. | |
347 | * @param desc the type descriptor of this local variable. | |
348 | * @param signature the type signature of this local variable. May be | |
349 | * <tt>null</tt> if the local variable type does not use generic | |
350 | * types. | |
351 | * @param start the first instruction corresponding to the scope of this | |
352 | * local variable (inclusive). | |
353 | * @param end the last instruction corresponding to the scope of this local | |
354 | * variable (exclusive). | |
355 | * @param index the local variable's index. | |
356 | * @throws IllegalArgumentException if one of the labels has not already | |
357 | * been visited by this visitor (by the | |
358 | * {@link #visitLabel visitLabel} method). | |
359 | */ | |
360 | void visitLocalVariable( | |
361 | String name, | |
362 | String desc, | |
363 | String signature, | |
364 | Label start, | |
365 | Label end, | |
366 | int index); | |
367 | ||
368 | /** | |
369 | * Visits a line number declaration. | |
370 | * | |
371 | * @param line a line number. This number refers to the source file from | |
372 | * which the class was compiled. | |
373 | * @param start the first instruction corresponding to this line number. | |
374 | * @throws IllegalArgumentException if <tt>start</tt> has not already been | |
375 | * visited by this visitor (by the {@link #visitLabel visitLabel} | |
376 | * method). | |
377 | */ | |
378 | void visitLineNumber(int line, Label start); | |
379 | ||
380 | /** | |
381 | * Visits the maximum stack size and the maximum number of local variables | |
382 | * of the method. | |
383 | * | |
384 | * @param maxStack maximum stack size of the method. | |
385 | * @param maxLocals maximum number of local variables for the method. | |
386 | */ | |
387 | void visitMaxs(int maxStack, int maxLocals); | |
388 | ||
389 | /** | |
390 | * Visits the end of the method. This method, which is the last one to be | |
391 | * called, is used to inform the visitor that all the annotations and | |
392 | * attributes of the method have been visited. | |
393 | */ | |
394 | void visitEnd(); | |
395 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * A {@link MethodVisitor} that generates methods in bytecode form. Each visit | |
33 | * method of this class appends the bytecode corresponding to the visited | |
34 | * instruction to a byte vector, in the order these methods are called. | |
35 | * | |
36 | * @author Eric Bruneton | |
37 | * @author Eugene Kuleshov | |
38 | */ | |
39 | class MethodWriter implements MethodVisitor{ | |
40 | ||
41 | /** | |
42 | * Pseudo access flag used to denote constructors. | |
43 | */ | |
44 | final static int ACC_CONSTRUCTOR = 262144; | |
45 | ||
46 | /** | |
47 | * Frame has exactly the same locals as the previous stack map frame and | |
48 | * number of stack items is zero. | |
49 | */ | |
50 | final static int SAME_FRAME = 0; // to 63 (0-3f) | |
51 | ||
52 | /** | |
53 | * Frame has exactly the same locals as the previous stack map frame and | |
54 | * number of stack items is 1 | |
55 | */ | |
56 | final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) | |
57 | ||
58 | /** | |
59 | * Reserved for future use | |
60 | */ | |
61 | final static int RESERVED = 128; | |
62 | ||
63 | /** | |
64 | * Frame has exactly the same locals as the previous stack map frame and | |
65 | * number of stack items is 1. Offset is bigger then 63; | |
66 | */ | |
67 | final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7 | |
68 | ||
69 | /** | |
70 | * Frame where current locals are the same as the locals in the previous | |
71 | * frame, except that the k last locals are absent. The value of k is given | |
72 | * by the formula 251-frame_type. | |
73 | */ | |
74 | final static int CHOP_FRAME = 248; // to 250 (f8-fA) | |
75 | ||
76 | /** | |
77 | * Frame has exactly the same locals as the previous stack map frame and | |
78 | * number of stack items is zero. Offset is bigger then 63; | |
79 | */ | |
80 | final static int SAME_FRAME_EXTENDED = 251; // fb | |
81 | ||
82 | /** | |
83 | * Frame where current locals are the same as the locals in the previous | |
84 | * frame, except that k additional locals are defined. The value of k is | |
85 | * given by the formula frame_type-251. | |
86 | */ | |
87 | final static int APPEND_FRAME = 252; // to 254 // fc-fe | |
88 | ||
89 | /** | |
90 | * Full frame | |
91 | */ | |
92 | final static int FULL_FRAME = 255; // ff | |
93 | ||
94 | /** | |
95 | * Indicates that the stack map frames must be recomputed from scratch. In | |
96 | * this case the maximum stack size and number of local variables is also | |
97 | * recomputed from scratch. | |
98 | * | |
99 | * @see #compute | |
100 | */ | |
101 | private final static int FRAMES = 0; | |
102 | ||
103 | /** | |
104 | * Indicates that the maximum stack size and number of local variables must | |
105 | * be automatically computed. | |
106 | * | |
107 | * @see #compute | |
108 | */ | |
109 | private final static int MAXS = 1; | |
110 | ||
111 | /** | |
112 | * Indicates that nothing must be automatically computed. | |
113 | * | |
114 | * @see #compute | |
115 | */ | |
116 | private final static int NOTHING = 2; | |
117 | ||
118 | /** | |
119 | * Next method writer (see {@link ClassWriter#firstMethod firstMethod}). | |
120 | */ | |
121 | MethodWriter next; | |
122 | ||
123 | /** | |
124 | * The class writer to which this method must be added. | |
125 | */ | |
126 | ClassWriter cw; | |
127 | ||
128 | /** | |
129 | * Access flags of this method. | |
130 | */ | |
131 | private int access; | |
132 | ||
133 | /** | |
134 | * The index of the constant pool item that contains the name of this | |
135 | * method. | |
136 | */ | |
137 | private int name; | |
138 | ||
139 | /** | |
140 | * The index of the constant pool item that contains the descriptor of this | |
141 | * method. | |
142 | */ | |
143 | private int desc; | |
144 | ||
145 | /** | |
146 | * The descriptor of this method. | |
147 | */ | |
148 | private String descriptor; | |
149 | ||
150 | /** | |
151 | * The signature of this method. | |
152 | */ | |
153 | String signature; | |
154 | ||
155 | /** | |
156 | * If not zero, indicates that the code of this method must be copied from | |
157 | * the ClassReader associated to this writer in <code>cw.cr</code>. More | |
158 | * precisely, this field gives the index of the first byte to copied from | |
159 | * <code>cw.cr.b</code>. | |
160 | */ | |
161 | int classReaderOffset; | |
162 | ||
163 | /** | |
164 | * If not zero, indicates that the code of this method must be copied from | |
165 | * the ClassReader associated to this writer in <code>cw.cr</code>. More | |
166 | * precisely, this field gives the number of bytes to copied from | |
167 | * <code>cw.cr.b</code>. | |
168 | */ | |
169 | int classReaderLength; | |
170 | ||
171 | /** | |
172 | * Number of exceptions that can be thrown by this method. | |
173 | */ | |
174 | int exceptionCount; | |
175 | ||
176 | /** | |
177 | * The exceptions that can be thrown by this method. More precisely, this | |
178 | * array contains the indexes of the constant pool items that contain the | |
179 | * internal names of these exception classes. | |
180 | */ | |
181 | int[] exceptions; | |
182 | ||
183 | /** | |
184 | * The annotation default attribute of this method. May be <tt>null</tt>. | |
185 | */ | |
186 | private ByteVector annd; | |
187 | ||
188 | /** | |
189 | * The runtime visible annotations of this method. May be <tt>null</tt>. | |
190 | */ | |
191 | private AnnotationWriter anns; | |
192 | ||
193 | /** | |
194 | * The runtime invisible annotations of this method. May be <tt>null</tt>. | |
195 | */ | |
196 | private AnnotationWriter ianns; | |
197 | ||
198 | /** | |
199 | * The runtime visible parameter annotations of this method. May be | |
200 | * <tt>null</tt>. | |
201 | */ | |
202 | private AnnotationWriter[] panns; | |
203 | ||
204 | /** | |
205 | * The runtime invisible parameter annotations of this method. May be | |
206 | * <tt>null</tt>. | |
207 | */ | |
208 | private AnnotationWriter[] ipanns; | |
209 | ||
210 | /** | |
211 | * The non standard attributes of the method. | |
212 | */ | |
213 | private Attribute attrs; | |
214 | ||
215 | /** | |
216 | * The bytecode of this method. | |
217 | */ | |
218 | private ByteVector code = new ByteVector(); | |
219 | ||
220 | /** | |
221 | * Maximum stack size of this method. | |
222 | */ | |
223 | private int maxStack; | |
224 | ||
225 | /** | |
226 | * Maximum number of local variables for this method. | |
227 | */ | |
228 | private int maxLocals; | |
229 | ||
230 | /** | |
231 | * Number of stack map frames in the StackMapTable attribute. | |
232 | */ | |
233 | private int frameCount; | |
234 | ||
235 | /** | |
236 | * The StackMapTable attribute. | |
237 | */ | |
238 | private ByteVector stackMap; | |
239 | ||
240 | /** | |
241 | * The offset of the last frame that was written in the StackMapTable | |
242 | * attribute. | |
243 | */ | |
244 | private int previousFrameOffset; | |
245 | ||
246 | /** | |
247 | * The last frame that was written in the StackMapTable attribute. | |
248 | * | |
249 | * @see #frame | |
250 | */ | |
251 | private int[] previousFrame; | |
252 | ||
253 | /** | |
254 | * Index of the next element to be added in {@link #frame}. | |
255 | */ | |
256 | private int frameIndex; | |
257 | ||
258 | /** | |
259 | * The current stack map frame. The first element contains the offset of the | |
260 | * instruction to which the frame corresponds, the second element is the | |
261 | * number of locals and the third one is the number of stack elements. The | |
262 | * local variables start at index 3 and are followed by the operand stack | |
263 | * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] = | |
264 | * nStack, frame[3] = nLocal. All types are encoded as integers, with the | |
265 | * same format as the one used in {@link Label}, but limited to BASE types. | |
266 | */ | |
267 | private int[] frame; | |
268 | ||
269 | /** | |
270 | * Number of elements in the exception handler list. | |
271 | */ | |
272 | private int handlerCount; | |
273 | ||
274 | /** | |
275 | * The first element in the exception handler list. | |
276 | */ | |
277 | private Handler firstHandler; | |
278 | ||
279 | /** | |
280 | * The last element in the exception handler list. | |
281 | */ | |
282 | private Handler lastHandler; | |
283 | ||
284 | /** | |
285 | * Number of entries in the LocalVariableTable attribute. | |
286 | */ | |
287 | private int localVarCount; | |
288 | ||
289 | /** | |
290 | * The LocalVariableTable attribute. | |
291 | */ | |
292 | private ByteVector localVar; | |
293 | ||
294 | /** | |
295 | * Number of entries in the LocalVariableTypeTable attribute. | |
296 | */ | |
297 | private int localVarTypeCount; | |
298 | ||
299 | /** | |
300 | * The LocalVariableTypeTable attribute. | |
301 | */ | |
302 | private ByteVector localVarType; | |
303 | ||
304 | /** | |
305 | * Number of entries in the LineNumberTable attribute. | |
306 | */ | |
307 | private int lineNumberCount; | |
308 | ||
309 | /** | |
310 | * The LineNumberTable attribute. | |
311 | */ | |
312 | private ByteVector lineNumber; | |
313 | ||
314 | /** | |
315 | * The non standard attributes of the method's code. | |
316 | */ | |
317 | private Attribute cattrs; | |
318 | ||
319 | /** | |
320 | * Indicates if some jump instructions are too small and need to be resized. | |
321 | */ | |
322 | private boolean resize; | |
323 | ||
324 | /** | |
325 | * Indicates if the instructions contain at least one JSR instruction. | |
326 | */ | |
327 | private boolean jsr; | |
328 | ||
329 | // ------------------------------------------------------------------------ | |
330 | ||
331 | /* | |
332 | * Fields for the control flow graph analysis algorithm (used to compute the | |
333 | * maximum stack size). A control flow graph contains one node per "basic | |
334 | * block", and one edge per "jump" from one basic block to another. Each | |
335 | * node (i.e., each basic block) is represented by the Label object that | |
336 | * corresponds to the first instruction of this basic block. Each node also | |
337 | * stores the list of its successors in the graph, as a linked list of Edge | |
338 | * objects. | |
339 | */ | |
340 | ||
341 | /** | |
342 | * Indicates what must be automatically computed. | |
343 | * | |
344 | * @see FRAMES | |
345 | * @see MAXS | |
346 | * @see NOTHING | |
347 | */ | |
348 | private int compute; | |
349 | ||
350 | /** | |
351 | * A list of labels. This list is the list of basic blocks in the method, | |
352 | * i.e. a list of Label objects linked to each other by their | |
353 | * {@link Label#successor} field, in the order they are visited by | |
354 | * {@link visitLabel}, and starting with the first basic block. | |
355 | */ | |
356 | private Label labels; | |
357 | ||
358 | /** | |
359 | * The previous basic block. | |
360 | */ | |
361 | private Label previousBlock; | |
362 | ||
363 | /** | |
364 | * The current basic block. | |
365 | */ | |
366 | private Label currentBlock; | |
367 | ||
368 | /** | |
369 | * The (relative) stack size after the last visited instruction. This size | |
370 | * is relative to the beginning of the current basic block, i.e., the true | |
371 | * stack size after the last visited instruction is equal to the | |
372 | * {@link Label#inputStackTop beginStackSize} of the current basic block | |
373 | * plus <tt>stackSize</tt>. | |
374 | */ | |
375 | private int stackSize; | |
376 | ||
377 | /** | |
378 | * The (relative) maximum stack size after the last visited instruction. | |
379 | * This size is relative to the beginning of the current basic block, i.e., | |
380 | * the true maximum stack size after the last visited instruction is equal | |
381 | * to the {@link Label#inputStackTop beginStackSize} of the current basic | |
382 | * block plus <tt>stackSize</tt>. | |
383 | */ | |
384 | private int maxStackSize; | |
385 | ||
386 | // ------------------------------------------------------------------------ | |
387 | // Constructor | |
388 | // ------------------------------------------------------------------------ | |
389 | ||
390 | /** | |
391 | * Constructs a new {@link MethodWriter}. | |
392 | * | |
393 | * @param cw the class writer in which the method must be added. | |
394 | * @param access the method's access flags (see {@link Opcodes}). | |
395 | * @param name the method's name. | |
396 | * @param desc the method's descriptor (see {@link Type}). | |
397 | * @param signature the method's signature. May be <tt>null</tt>. | |
398 | * @param exceptions the internal names of the method's exceptions. May be | |
399 | * <tt>null</tt>. | |
400 | * @param computeMaxs <tt>true</tt> if the maximum stack size and number | |
401 | * of local variables must be automatically computed. | |
402 | * @param computeFrames <tt>true</tt> if the stack map tables must be | |
403 | * recomputed from scratch. | |
404 | */ | |
405 | MethodWriter( | |
406 | final ClassWriter cw, | |
407 | final int access, | |
408 | final String name, | |
409 | final String desc, | |
410 | final String signature, | |
411 | final String[] exceptions, | |
412 | final boolean computeMaxs, | |
413 | final boolean computeFrames){ | |
414 | if(cw.firstMethod == null) | |
415 | { | |
416 | cw.firstMethod = this; | |
417 | } | |
418 | else | |
419 | { | |
420 | cw.lastMethod.next = this; | |
421 | } | |
422 | cw.lastMethod = this; | |
423 | this.cw = cw; | |
424 | this.access = access; | |
425 | this.name = cw.newUTF8(name); | |
426 | this.desc = cw.newUTF8(desc); | |
427 | this.descriptor = desc; | |
428 | this.signature = signature; | |
429 | if(exceptions != null && exceptions.length > 0) | |
430 | { | |
431 | exceptionCount = exceptions.length; | |
432 | this.exceptions = new int[exceptionCount]; | |
433 | for(int i = 0; i < exceptionCount; ++i) | |
434 | { | |
435 | this.exceptions[i] = cw.newClass(exceptions[i]); | |
436 | } | |
437 | } | |
438 | this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); | |
439 | if(computeMaxs || computeFrames) | |
440 | { | |
441 | if(computeFrames && name.equals("<init>")) | |
442 | { | |
443 | this.access |= ACC_CONSTRUCTOR; | |
444 | } | |
445 | // updates maxLocals | |
446 | int size = getArgumentsAndReturnSizes(descriptor) >> 2; | |
447 | if((access & Opcodes.ACC_STATIC) != 0) | |
448 | { | |
449 | --size; | |
450 | } | |
451 | maxLocals = size; | |
452 | // creates and visits the label for the first basic block | |
453 | labels = new Label(); | |
454 | labels.status |= Label.PUSHED; | |
455 | visitLabel(labels); | |
456 | } | |
457 | } | |
458 | ||
459 | // ------------------------------------------------------------------------ | |
460 | // Implementation of the MethodVisitor interface | |
461 | // ------------------------------------------------------------------------ | |
462 | ||
463 | public AnnotationVisitor visitAnnotationDefault(){ | |
464 | annd = new ByteVector(); | |
465 | return new AnnotationWriter(cw, false, annd, null, 0); | |
466 | } | |
467 | ||
468 | public AnnotationVisitor visitAnnotation( | |
469 | final String desc, | |
470 | final boolean visible){ | |
471 | ByteVector bv = new ByteVector(); | |
472 | // write type, and reserve space for values count | |
473 | bv.putShort(cw.newUTF8(desc)).putShort(0); | |
474 | AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); | |
475 | if(visible) | |
476 | { | |
477 | aw.next = anns; | |
478 | anns = aw; | |
479 | } | |
480 | else | |
481 | { | |
482 | aw.next = ianns; | |
483 | ianns = aw; | |
484 | } | |
485 | return aw; | |
486 | } | |
487 | ||
488 | public AnnotationVisitor visitParameterAnnotation( | |
489 | final int parameter, | |
490 | final String desc, | |
491 | final boolean visible){ | |
492 | ByteVector bv = new ByteVector(); | |
493 | // write type, and reserve space for values count | |
494 | bv.putShort(cw.newUTF8(desc)).putShort(0); | |
495 | AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); | |
496 | if(visible) | |
497 | { | |
498 | if(panns == null) | |
499 | { | |
500 | panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; | |
501 | } | |
502 | aw.next = panns[parameter]; | |
503 | panns[parameter] = aw; | |
504 | } | |
505 | else | |
506 | { | |
507 | if(ipanns == null) | |
508 | { | |
509 | ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; | |
510 | } | |
511 | aw.next = ipanns[parameter]; | |
512 | ipanns[parameter] = aw; | |
513 | } | |
514 | return aw; | |
515 | } | |
516 | ||
517 | public void visitAttribute(final Attribute attr){ | |
518 | if(attr.isCodeAttribute()) | |
519 | { | |
520 | attr.next = cattrs; | |
521 | cattrs = attr; | |
522 | } | |
523 | else | |
524 | { | |
525 | attr.next = attrs; | |
526 | attrs = attr; | |
527 | } | |
528 | } | |
529 | ||
530 | public void visitCode(){ | |
531 | } | |
532 | ||
533 | public void visitFrame( | |
534 | final int type, | |
535 | final int nLocal, | |
536 | final Object[] local, | |
537 | final int nStack, | |
538 | final Object[] stack){ | |
539 | if(compute == FRAMES) | |
540 | { | |
541 | return; | |
542 | } | |
543 | ||
544 | if(type == Opcodes.F_NEW) | |
545 | { | |
546 | startFrame(code.length, nLocal, nStack); | |
547 | for(int i = 0; i < nLocal; ++i) | |
548 | { | |
549 | if(local[i] instanceof String) | |
550 | { | |
551 | frame[frameIndex++] = Frame.OBJECT | |
552 | | cw.addType((String) local[i]); | |
553 | } | |
554 | else if(local[i] instanceof Integer) | |
555 | { | |
556 | frame[frameIndex++] = ((Integer) local[i]).intValue(); | |
557 | } | |
558 | else | |
559 | { | |
560 | frame[frameIndex++] = Frame.UNINITIALIZED | |
561 | | cw.addUninitializedType("", | |
562 | ((Label) local[i]).position); | |
563 | } | |
564 | } | |
565 | for(int i = 0; i < nStack; ++i) | |
566 | { | |
567 | if(stack[i] instanceof String) | |
568 | { | |
569 | frame[frameIndex++] = Frame.OBJECT | |
570 | | cw.addType((String) stack[i]); | |
571 | } | |
572 | else if(stack[i] instanceof Integer) | |
573 | { | |
574 | frame[frameIndex++] = ((Integer) stack[i]).intValue(); | |
575 | } | |
576 | else | |
577 | { | |
578 | frame[frameIndex++] = Frame.UNINITIALIZED | |
579 | | cw.addUninitializedType("", | |
580 | ((Label) stack[i]).position); | |
581 | } | |
582 | } | |
583 | endFrame(); | |
584 | } | |
585 | else | |
586 | { | |
587 | int delta; | |
588 | if(stackMap == null) | |
589 | { | |
590 | stackMap = new ByteVector(); | |
591 | delta = code.length; | |
592 | } | |
593 | else | |
594 | { | |
595 | delta = code.length - previousFrameOffset - 1; | |
596 | } | |
597 | ||
598 | switch(type) | |
599 | { | |
600 | case Opcodes.F_FULL: | |
601 | stackMap.putByte(FULL_FRAME) | |
602 | .putShort(delta) | |
603 | .putShort(nLocal); | |
604 | for(int i = 0; i < nLocal; ++i) | |
605 | { | |
606 | writeFrameType(local[i]); | |
607 | } | |
608 | stackMap.putShort(nStack); | |
609 | for(int i = 0; i < nStack; ++i) | |
610 | { | |
611 | writeFrameType(stack[i]); | |
612 | } | |
613 | break; | |
614 | case Opcodes.F_APPEND: | |
615 | stackMap.putByte(SAME_FRAME_EXTENDED + nLocal) | |
616 | .putShort(delta); | |
617 | for(int i = 0; i < nLocal; ++i) | |
618 | { | |
619 | writeFrameType(local[i]); | |
620 | } | |
621 | break; | |
622 | case Opcodes.F_CHOP: | |
623 | stackMap.putByte(SAME_FRAME_EXTENDED - nLocal) | |
624 | .putShort(delta); | |
625 | break; | |
626 | case Opcodes.F_SAME: | |
627 | if(delta < 64) | |
628 | { | |
629 | stackMap.putByte(delta); | |
630 | } | |
631 | else | |
632 | { | |
633 | stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); | |
634 | } | |
635 | break; | |
636 | case Opcodes.F_SAME1: | |
637 | if(delta < 64) | |
638 | { | |
639 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); | |
640 | } | |
641 | else | |
642 | { | |
643 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) | |
644 | .putShort(delta); | |
645 | } | |
646 | writeFrameType(stack[0]); | |
647 | break; | |
648 | } | |
649 | ||
650 | previousFrameOffset = code.length; | |
651 | ++frameCount; | |
652 | } | |
653 | } | |
654 | ||
655 | public void visitInsn(final int opcode){ | |
656 | // adds the instruction to the bytecode of the method | |
657 | code.putByte(opcode); | |
658 | // update currentBlock | |
659 | // Label currentBlock = this.currentBlock; | |
660 | if(currentBlock != null) | |
661 | { | |
662 | if(compute == FRAMES) | |
663 | { | |
664 | currentBlock.frame.execute(opcode, 0, null, null); | |
665 | } | |
666 | else | |
667 | { | |
668 | // updates current and max stack sizes | |
669 | int size = stackSize + Frame.SIZE[opcode]; | |
670 | if(size > maxStackSize) | |
671 | { | |
672 | maxStackSize = size; | |
673 | } | |
674 | stackSize = size; | |
675 | } | |
676 | // if opcode == ATHROW or xRETURN, ends current block (no successor) | |
677 | if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) | |
678 | || opcode == Opcodes.ATHROW) | |
679 | { | |
680 | noSuccessor(); | |
681 | } | |
682 | } | |
683 | } | |
684 | ||
685 | public void visitIntInsn(final int opcode, final int operand){ | |
686 | // Label currentBlock = this.currentBlock; | |
687 | if(currentBlock != null) | |
688 | { | |
689 | if(compute == FRAMES) | |
690 | { | |
691 | currentBlock.frame.execute(opcode, operand, null, null); | |
692 | } | |
693 | else if(opcode != Opcodes.NEWARRAY) | |
694 | { | |
695 | // updates current and max stack sizes only for NEWARRAY | |
696 | // (stack size variation = 0 for BIPUSH or SIPUSH) | |
697 | int size = stackSize + 1; | |
698 | if(size > maxStackSize) | |
699 | { | |
700 | maxStackSize = size; | |
701 | } | |
702 | stackSize = size; | |
703 | } | |
704 | } | |
705 | // adds the instruction to the bytecode of the method | |
706 | if(opcode == Opcodes.SIPUSH) | |
707 | { | |
708 | code.put12(opcode, operand); | |
709 | } | |
710 | else | |
711 | { // BIPUSH or NEWARRAY | |
712 | code.put11(opcode, operand); | |
713 | } | |
714 | } | |
715 | ||
716 | public void visitVarInsn(final int opcode, final int var){ | |
717 | // Label currentBlock = this.currentBlock; | |
718 | if(currentBlock != null) | |
719 | { | |
720 | if(compute == FRAMES) | |
721 | { | |
722 | currentBlock.frame.execute(opcode, var, null, null); | |
723 | } | |
724 | else | |
725 | { | |
726 | // updates current and max stack sizes | |
727 | if(opcode == Opcodes.RET) | |
728 | { | |
729 | // no stack change, but end of current block (no successor) | |
730 | currentBlock.status |= Label.RET; | |
731 | // save 'stackSize' here for future use | |
732 | // (see {@link #findSubroutineSuccessors}) | |
733 | currentBlock.inputStackTop = stackSize; | |
734 | noSuccessor(); | |
735 | } | |
736 | else | |
737 | { // xLOAD or xSTORE | |
738 | int size = stackSize + Frame.SIZE[opcode]; | |
739 | if(size > maxStackSize) | |
740 | { | |
741 | maxStackSize = size; | |
742 | } | |
743 | stackSize = size; | |
744 | } | |
745 | } | |
746 | } | |
747 | if(compute != NOTHING) | |
748 | { | |
749 | // updates max locals | |
750 | int n; | |
751 | if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD | |
752 | || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) | |
753 | { | |
754 | n = var + 2; | |
755 | } | |
756 | else | |
757 | { | |
758 | n = var + 1; | |
759 | } | |
760 | if(n > maxLocals) | |
761 | { | |
762 | maxLocals = n; | |
763 | } | |
764 | } | |
765 | // adds the instruction to the bytecode of the method | |
766 | if(var < 4 && opcode != Opcodes.RET) | |
767 | { | |
768 | int opt; | |
769 | if(opcode < Opcodes.ISTORE) | |
770 | { | |
771 | /* ILOAD_0 */ | |
772 | opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; | |
773 | } | |
774 | else | |
775 | { | |
776 | /* ISTORE_0 */ | |
777 | opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; | |
778 | } | |
779 | code.putByte(opt); | |
780 | } | |
781 | else if(var >= 256) | |
782 | { | |
783 | code.putByte(196 /* WIDE */).put12(opcode, var); | |
784 | } | |
785 | else | |
786 | { | |
787 | code.put11(opcode, var); | |
788 | } | |
789 | if(opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) | |
790 | { | |
791 | visitLabel(new Label()); | |
792 | } | |
793 | } | |
794 | ||
795 | public void visitTypeInsn(final int opcode, final String desc){ | |
796 | Item i = cw.newClassItem(desc); | |
797 | // Label currentBlock = this.currentBlock; | |
798 | if(currentBlock != null) | |
799 | { | |
800 | if(compute == FRAMES) | |
801 | { | |
802 | currentBlock.frame.execute(opcode, code.length, cw, i); | |
803 | } | |
804 | else if(opcode == Opcodes.NEW) | |
805 | { | |
806 | // updates current and max stack sizes only if opcode == NEW | |
807 | // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) | |
808 | int size = stackSize + 1; | |
809 | if(size > maxStackSize) | |
810 | { | |
811 | maxStackSize = size; | |
812 | } | |
813 | stackSize = size; | |
814 | } | |
815 | } | |
816 | // adds the instruction to the bytecode of the method | |
817 | code.put12(opcode, i.index); | |
818 | } | |
819 | ||
820 | public void visitFieldInsn( | |
821 | final int opcode, | |
822 | final String owner, | |
823 | final String name, | |
824 | final String desc){ | |
825 | Item i = cw.newFieldItem(owner, name, desc); | |
826 | // Label currentBlock = this.currentBlock; | |
827 | if(currentBlock != null) | |
828 | { | |
829 | if(compute == FRAMES) | |
830 | { | |
831 | currentBlock.frame.execute(opcode, 0, cw, i); | |
832 | } | |
833 | else | |
834 | { | |
835 | int size; | |
836 | // computes the stack size variation | |
837 | char c = desc.charAt(0); | |
838 | switch(opcode) | |
839 | { | |
840 | case Opcodes.GETSTATIC: | |
841 | size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); | |
842 | break; | |
843 | case Opcodes.PUTSTATIC: | |
844 | size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); | |
845 | break; | |
846 | case Opcodes.GETFIELD: | |
847 | size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); | |
848 | break; | |
849 | // case Constants.PUTFIELD: | |
850 | default: | |
851 | size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); | |
852 | break; | |
853 | } | |
854 | // updates current and max stack sizes | |
855 | if(size > maxStackSize) | |
856 | { | |
857 | maxStackSize = size; | |
858 | } | |
859 | stackSize = size; | |
860 | } | |
861 | } | |
862 | // adds the instruction to the bytecode of the method | |
863 | code.put12(opcode, i.index); | |
864 | } | |
865 | ||
866 | public void visitMethodInsn( | |
867 | final int opcode, | |
868 | final String owner, | |
869 | final String name, | |
870 | final String desc){ | |
871 | boolean itf = opcode == Opcodes.INVOKEINTERFACE; | |
872 | Item i = cw.newMethodItem(owner, name, desc, itf); | |
873 | int argSize = i.intVal; | |
874 | // Label currentBlock = this.currentBlock; | |
875 | if(currentBlock != null) | |
876 | { | |
877 | if(compute == FRAMES) | |
878 | { | |
879 | currentBlock.frame.execute(opcode, 0, cw, i); | |
880 | } | |
881 | else | |
882 | { | |
883 | /* | |
884 | * computes the stack size variation. In order not to recompute | |
885 | * several times this variation for the same Item, we use the | |
886 | * intVal field of this item to store this variation, once it | |
887 | * has been computed. More precisely this intVal field stores | |
888 | * the sizes of the arguments and of the return value | |
889 | * corresponding to desc. | |
890 | */ | |
891 | if(argSize == 0) | |
892 | { | |
893 | // the above sizes have not been computed yet, | |
894 | // so we compute them... | |
895 | argSize = getArgumentsAndReturnSizes(desc); | |
896 | // ... and we save them in order | |
897 | // not to recompute them in the future | |
898 | i.intVal = argSize; | |
899 | } | |
900 | int size; | |
901 | if(opcode == Opcodes.INVOKESTATIC) | |
902 | { | |
903 | size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; | |
904 | } | |
905 | else | |
906 | { | |
907 | size = stackSize - (argSize >> 2) + (argSize & 0x03); | |
908 | } | |
909 | // updates current and max stack sizes | |
910 | if(size > maxStackSize) | |
911 | { | |
912 | maxStackSize = size; | |
913 | } | |
914 | stackSize = size; | |
915 | } | |
916 | } | |
917 | // adds the instruction to the bytecode of the method | |
918 | if(itf) | |
919 | { | |
920 | if(argSize == 0) | |
921 | { | |
922 | argSize = getArgumentsAndReturnSizes(desc); | |
923 | i.intVal = argSize; | |
924 | } | |
925 | code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); | |
926 | } | |
927 | else | |
928 | { | |
929 | code.put12(opcode, i.index); | |
930 | } | |
931 | } | |
932 | ||
933 | public void visitJumpInsn(final int opcode, final Label label){ | |
934 | Label nextInsn = null; | |
935 | // Label currentBlock = this.currentBlock; | |
936 | if(currentBlock != null) | |
937 | { | |
938 | if(compute == FRAMES) | |
939 | { | |
940 | currentBlock.frame.execute(opcode, 0, null, null); | |
941 | // 'label' is the target of a jump instruction | |
942 | label.getFirst().status |= Label.TARGET; | |
943 | // adds 'label' as a successor of this basic block | |
944 | addSuccessor(Edge.NORMAL, label); | |
945 | if(opcode != Opcodes.GOTO) | |
946 | { | |
947 | // creates a Label for the next basic block | |
948 | nextInsn = new Label(); | |
949 | } | |
950 | } | |
951 | else | |
952 | { | |
953 | if(opcode == Opcodes.JSR) | |
954 | { | |
955 | jsr = true; | |
956 | currentBlock.status |= Label.JSR; | |
957 | addSuccessor(stackSize + 1, label); | |
958 | // creates a Label for the next basic block | |
959 | nextInsn = new Label(); | |
960 | /* | |
961 | * note that, by construction in this method, a JSR block | |
962 | * has at least two successors in the control flow graph: | |
963 | * the first one leads the next instruction after the JSR, | |
964 | * while the second one leads to the JSR target. | |
965 | */ | |
966 | } | |
967 | else | |
968 | { | |
969 | // updates current stack size (max stack size unchanged | |
970 | // because stack size variation always negative in this | |
971 | // case) | |
972 | stackSize += Frame.SIZE[opcode]; | |
973 | addSuccessor(stackSize, label); | |
974 | } | |
975 | } | |
976 | } | |
977 | // adds the instruction to the bytecode of the method | |
978 | if((label.status & Label.RESOLVED) != 0 | |
979 | && label.position - code.length < Short.MIN_VALUE) | |
980 | { | |
981 | /* | |
982 | * case of a backward jump with an offset < -32768. In this case we | |
983 | * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx | |
984 | * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the | |
985 | * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'> | |
986 | * designates the instruction just after the GOTO_W. | |
987 | */ | |
988 | if(opcode == Opcodes.GOTO) | |
989 | { | |
990 | code.putByte(200); // GOTO_W | |
991 | } | |
992 | else if(opcode == Opcodes.JSR) | |
993 | { | |
994 | code.putByte(201); // JSR_W | |
995 | } | |
996 | else | |
997 | { | |
998 | // if the IF instruction is transformed into IFNOT GOTO_W the | |
999 | // next instruction becomes the target of the IFNOT instruction | |
1000 | if(nextInsn != null) | |
1001 | { | |
1002 | nextInsn.status |= Label.TARGET; | |
1003 | } | |
1004 | code.putByte(opcode <= 166 | |
1005 | ? ((opcode + 1) ^ 1) - 1 | |
1006 | : opcode ^ 1); | |
1007 | code.putShort(8); // jump offset | |
1008 | code.putByte(200); // GOTO_W | |
1009 | } | |
1010 | label.put(this, code, code.length - 1, true); | |
1011 | } | |
1012 | else | |
1013 | { | |
1014 | /* | |
1015 | * case of a backward jump with an offset >= -32768, or of a forward | |
1016 | * jump with, of course, an unknown offset. In these cases we store | |
1017 | * the offset in 2 bytes (which will be increased in | |
1018 | * resizeInstructions, if needed). | |
1019 | */ | |
1020 | code.putByte(opcode); | |
1021 | label.put(this, code, code.length - 1, false); | |
1022 | } | |
1023 | if(currentBlock != null) | |
1024 | { | |
1025 | if(nextInsn != null) | |
1026 | { | |
1027 | // if the jump instruction is not a GOTO, the next instruction | |
1028 | // is also a successor of this instruction. Calling visitLabel | |
1029 | // adds the label of this next instruction as a successor of the | |
1030 | // current block, and starts a new basic block | |
1031 | visitLabel(nextInsn); | |
1032 | } | |
1033 | if(opcode == Opcodes.GOTO) | |
1034 | { | |
1035 | noSuccessor(); | |
1036 | } | |
1037 | } | |
1038 | } | |
1039 | ||
1040 | public void visitLabel(final Label label){ | |
1041 | // resolves previous forward references to label, if any | |
1042 | resize |= label.resolve(this, code.length, code.data); | |
1043 | // updates currentBlock | |
1044 | if((label.status & Label.DEBUG) != 0) | |
1045 | { | |
1046 | return; | |
1047 | } | |
1048 | if(compute == FRAMES) | |
1049 | { | |
1050 | if(currentBlock != null) | |
1051 | { | |
1052 | if(label.position == currentBlock.position) | |
1053 | { | |
1054 | // successive labels, do not start a new basic block | |
1055 | currentBlock.status |= (label.status & Label.TARGET); | |
1056 | label.frame = currentBlock.frame; | |
1057 | return; | |
1058 | } | |
1059 | // ends current block (with one new successor) | |
1060 | addSuccessor(Edge.NORMAL, label); | |
1061 | } | |
1062 | // begins a new current block | |
1063 | currentBlock = label; | |
1064 | if(label.frame == null) | |
1065 | { | |
1066 | label.frame = new Frame(); | |
1067 | label.frame.owner = label; | |
1068 | } | |
1069 | // updates the basic block list | |
1070 | if(previousBlock != null) | |
1071 | { | |
1072 | if(label.position == previousBlock.position) | |
1073 | { | |
1074 | previousBlock.status |= (label.status & Label.TARGET); | |
1075 | label.frame = previousBlock.frame; | |
1076 | currentBlock = previousBlock; | |
1077 | return; | |
1078 | } | |
1079 | previousBlock.successor = label; | |
1080 | } | |
1081 | previousBlock = label; | |
1082 | } | |
1083 | else if(compute == MAXS) | |
1084 | { | |
1085 | if(currentBlock != null) | |
1086 | { | |
1087 | // ends current block (with one new successor) | |
1088 | currentBlock.outputStackMax = maxStackSize; | |
1089 | addSuccessor(stackSize, label); | |
1090 | } | |
1091 | // begins a new current block | |
1092 | currentBlock = label; | |
1093 | // resets the relative current and max stack sizes | |
1094 | stackSize = 0; | |
1095 | maxStackSize = 0; | |
1096 | // updates the basic block list | |
1097 | if(previousBlock != null) | |
1098 | { | |
1099 | previousBlock.successor = label; | |
1100 | } | |
1101 | previousBlock = label; | |
1102 | } | |
1103 | } | |
1104 | ||
1105 | public void visitLdcInsn(final Object cst){ | |
1106 | Item i = cw.newConstItem(cst); | |
1107 | // Label currentBlock = this.currentBlock; | |
1108 | if(currentBlock != null) | |
1109 | { | |
1110 | if(compute == FRAMES) | |
1111 | { | |
1112 | currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); | |
1113 | } | |
1114 | else | |
1115 | { | |
1116 | int size; | |
1117 | // computes the stack size variation | |
1118 | if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) | |
1119 | { | |
1120 | size = stackSize + 2; | |
1121 | } | |
1122 | else | |
1123 | { | |
1124 | size = stackSize + 1; | |
1125 | } | |
1126 | // updates current and max stack sizes | |
1127 | if(size > maxStackSize) | |
1128 | { | |
1129 | maxStackSize = size; | |
1130 | } | |
1131 | stackSize = size; | |
1132 | } | |
1133 | } | |
1134 | // adds the instruction to the bytecode of the method | |
1135 | int index = i.index; | |
1136 | if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) | |
1137 | { | |
1138 | code.put12(20 /* LDC2_W */, index); | |
1139 | } | |
1140 | else if(index >= 256) | |
1141 | { | |
1142 | code.put12(19 /* LDC_W */, index); | |
1143 | } | |
1144 | else | |
1145 | { | |
1146 | code.put11(Opcodes.LDC, index); | |
1147 | } | |
1148 | } | |
1149 | ||
1150 | public void visitIincInsn(final int var, final int increment){ | |
1151 | if(currentBlock != null) | |
1152 | { | |
1153 | if(compute == FRAMES) | |
1154 | { | |
1155 | currentBlock.frame.execute(Opcodes.IINC, var, null, null); | |
1156 | } | |
1157 | } | |
1158 | if(compute != NOTHING) | |
1159 | { | |
1160 | // updates max locals | |
1161 | int n = var + 1; | |
1162 | if(n > maxLocals) | |
1163 | { | |
1164 | maxLocals = n; | |
1165 | } | |
1166 | } | |
1167 | // adds the instruction to the bytecode of the method | |
1168 | if((var > 255) || (increment > 127) || (increment < -128)) | |
1169 | { | |
1170 | code.putByte(196 /* WIDE */) | |
1171 | .put12(Opcodes.IINC, var) | |
1172 | .putShort(increment); | |
1173 | } | |
1174 | else | |
1175 | { | |
1176 | code.putByte(Opcodes.IINC).put11(var, increment); | |
1177 | } | |
1178 | } | |
1179 | ||
1180 | public void visitTableSwitchInsn( | |
1181 | final int min, | |
1182 | final int max, | |
1183 | final Label dflt, | |
1184 | final Label labels[]){ | |
1185 | // adds the instruction to the bytecode of the method | |
1186 | int source = code.length; | |
1187 | code.putByte(Opcodes.TABLESWITCH); | |
1188 | code.length += (4 - code.length % 4) % 4; | |
1189 | dflt.put(this, code, source, true); | |
1190 | code.putInt(min).putInt(max); | |
1191 | for(int i = 0; i < labels.length; ++i) | |
1192 | { | |
1193 | labels[i].put(this, code, source, true); | |
1194 | } | |
1195 | // updates currentBlock | |
1196 | visitSwitchInsn(dflt, labels); | |
1197 | } | |
1198 | ||
1199 | public void visitLookupSwitchInsn( | |
1200 | final Label dflt, | |
1201 | final int keys[], | |
1202 | final Label labels[]){ | |
1203 | // adds the instruction to the bytecode of the method | |
1204 | int source = code.length; | |
1205 | code.putByte(Opcodes.LOOKUPSWITCH); | |
1206 | code.length += (4 - code.length % 4) % 4; | |
1207 | dflt.put(this, code, source, true); | |
1208 | code.putInt(labels.length); | |
1209 | for(int i = 0; i < labels.length; ++i) | |
1210 | { | |
1211 | code.putInt(keys[i]); | |
1212 | labels[i].put(this, code, source, true); | |
1213 | } | |
1214 | // updates currentBlock | |
1215 | visitSwitchInsn(dflt, labels); | |
1216 | } | |
1217 | ||
1218 | private void visitSwitchInsn(final Label dflt, final Label[] labels){ | |
1219 | // Label currentBlock = this.currentBlock; | |
1220 | if(currentBlock != null) | |
1221 | { | |
1222 | if(compute == FRAMES) | |
1223 | { | |
1224 | currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); | |
1225 | // adds current block successors | |
1226 | addSuccessor(Edge.NORMAL, dflt); | |
1227 | dflt.getFirst().status |= Label.TARGET; | |
1228 | for(int i = 0; i < labels.length; ++i) | |
1229 | { | |
1230 | addSuccessor(Edge.NORMAL, labels[i]); | |
1231 | labels[i].getFirst().status |= Label.TARGET; | |
1232 | } | |
1233 | } | |
1234 | else | |
1235 | { | |
1236 | // updates current stack size (max stack size unchanged) | |
1237 | --stackSize; | |
1238 | // adds current block successors | |
1239 | addSuccessor(stackSize, dflt); | |
1240 | for(int i = 0; i < labels.length; ++i) | |
1241 | { | |
1242 | addSuccessor(stackSize, labels[i]); | |
1243 | } | |
1244 | } | |
1245 | // ends current block | |
1246 | noSuccessor(); | |
1247 | } | |
1248 | } | |
1249 | ||
1250 | public void visitMultiANewArrayInsn(final String desc, final int dims){ | |
1251 | Item i = cw.newClassItem(desc); | |
1252 | // Label currentBlock = this.currentBlock; | |
1253 | if(currentBlock != null) | |
1254 | { | |
1255 | if(compute == FRAMES) | |
1256 | { | |
1257 | currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); | |
1258 | } | |
1259 | else | |
1260 | { | |
1261 | // updates current stack size (max stack size unchanged because | |
1262 | // stack size variation always negative or null) | |
1263 | stackSize += 1 - dims; | |
1264 | } | |
1265 | } | |
1266 | // adds the instruction to the bytecode of the method | |
1267 | code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); | |
1268 | } | |
1269 | ||
1270 | public void visitTryCatchBlock( | |
1271 | final Label start, | |
1272 | final Label end, | |
1273 | final Label handler, | |
1274 | final String type){ | |
1275 | ++handlerCount; | |
1276 | Handler h = new Handler(); | |
1277 | h.start = start; | |
1278 | h.end = end; | |
1279 | h.handler = handler; | |
1280 | h.desc = type; | |
1281 | h.type = type != null ? cw.newClass(type) : 0; | |
1282 | if(lastHandler == null) | |
1283 | { | |
1284 | firstHandler = h; | |
1285 | } | |
1286 | else | |
1287 | { | |
1288 | lastHandler.next = h; | |
1289 | } | |
1290 | lastHandler = h; | |
1291 | } | |
1292 | ||
1293 | public void visitLocalVariable( | |
1294 | final String name, | |
1295 | final String desc, | |
1296 | final String signature, | |
1297 | final Label start, | |
1298 | final Label end, | |
1299 | final int index){ | |
1300 | if(signature != null) | |
1301 | { | |
1302 | if(localVarType == null) | |
1303 | { | |
1304 | localVarType = new ByteVector(); | |
1305 | } | |
1306 | ++localVarTypeCount; | |
1307 | localVarType.putShort(start.position) | |
1308 | .putShort(end.position - start.position) | |
1309 | .putShort(cw.newUTF8(name)) | |
1310 | .putShort(cw.newUTF8(signature)) | |
1311 | .putShort(index); | |
1312 | } | |
1313 | if(localVar == null) | |
1314 | { | |
1315 | localVar = new ByteVector(); | |
1316 | } | |
1317 | ++localVarCount; | |
1318 | localVar.putShort(start.position) | |
1319 | .putShort(end.position - start.position) | |
1320 | .putShort(cw.newUTF8(name)) | |
1321 | .putShort(cw.newUTF8(desc)) | |
1322 | .putShort(index); | |
1323 | if(compute != NOTHING) | |
1324 | { | |
1325 | // updates max locals | |
1326 | char c = desc.charAt(0); | |
1327 | int n = index + (c == 'J' || c == 'D' ? 2 : 1); | |
1328 | if(n > maxLocals) | |
1329 | { | |
1330 | maxLocals = n; | |
1331 | } | |
1332 | } | |
1333 | } | |
1334 | ||
1335 | public void visitLineNumber(final int line, final Label start){ | |
1336 | if(lineNumber == null) | |
1337 | { | |
1338 | lineNumber = new ByteVector(); | |
1339 | } | |
1340 | ++lineNumberCount; | |
1341 | lineNumber.putShort(start.position); | |
1342 | lineNumber.putShort(line); | |
1343 | } | |
1344 | ||
1345 | public void visitMaxs(final int maxStack, final int maxLocals){ | |
1346 | if(compute == FRAMES) | |
1347 | { | |
1348 | // completes the control flow graph with exception handler blocks | |
1349 | Handler handler = firstHandler; | |
1350 | while(handler != null) | |
1351 | { | |
1352 | Label l = handler.start.getFirst(); | |
1353 | Label h = handler.handler.getFirst(); | |
1354 | Label e = handler.end.getFirst(); | |
1355 | // computes the kind of the edges to 'h' | |
1356 | String t = handler.desc == null | |
1357 | ? "java/lang/Throwable" | |
1358 | : handler.desc; | |
1359 | int kind = Frame.OBJECT | cw.addType(t); | |
1360 | // h is an exception handler | |
1361 | h.status |= Label.TARGET; | |
1362 | // adds 'h' as a successor of labels between 'start' and 'end' | |
1363 | while(l != e) | |
1364 | { | |
1365 | // creates an edge to 'h' | |
1366 | Edge b = new Edge(); | |
1367 | b.info = kind; | |
1368 | b.successor = h; | |
1369 | // adds it to the successors of 'l' | |
1370 | b.next = l.successors; | |
1371 | l.successors = b; | |
1372 | // goes to the next label | |
1373 | l = l.successor; | |
1374 | } | |
1375 | handler = handler.next; | |
1376 | } | |
1377 | ||
1378 | // creates and visits the first (implicit) frame | |
1379 | Frame f = labels.frame; | |
1380 | Type[] args = Type.getArgumentTypes(descriptor); | |
1381 | f.initInputFrame(cw, access, args, this.maxLocals); | |
1382 | visitFrame(f); | |
1383 | ||
1384 | /* | |
1385 | * fix point algorithm: mark the first basic block as 'changed' | |
1386 | * (i.e. put it in the 'changed' list) and, while there are changed | |
1387 | * basic blocks, choose one, mark it as unchanged, and update its | |
1388 | * successors (which can be changed in the process). | |
1389 | */ | |
1390 | int max = 0; | |
1391 | Label changed = labels; | |
1392 | while(changed != null) | |
1393 | { | |
1394 | // removes a basic block from the list of changed basic blocks | |
1395 | Label l = changed; | |
1396 | changed = changed.next; | |
1397 | l.next = null; | |
1398 | f = l.frame; | |
1399 | // a reacheable jump target must be stored in the stack map | |
1400 | if((l.status & Label.TARGET) != 0) | |
1401 | { | |
1402 | l.status |= Label.STORE; | |
1403 | } | |
1404 | // all visited labels are reacheable, by definition | |
1405 | l.status |= Label.REACHABLE; | |
1406 | // updates the (absolute) maximum stack size | |
1407 | int blockMax = f.inputStack.length + l.outputStackMax; | |
1408 | if(blockMax > max) | |
1409 | { | |
1410 | max = blockMax; | |
1411 | } | |
1412 | // updates the successors of the current basic block | |
1413 | Edge e = l.successors; | |
1414 | while(e != null) | |
1415 | { | |
1416 | Label n = e.successor.getFirst(); | |
1417 | boolean change = f.merge(cw, n.frame, e.info); | |
1418 | if(change && n.next == null) | |
1419 | { | |
1420 | // if n has changed and is not already in the 'changed' | |
1421 | // list, adds it to this list | |
1422 | n.next = changed; | |
1423 | changed = n; | |
1424 | } | |
1425 | e = e.next; | |
1426 | } | |
1427 | } | |
1428 | this.maxStack = max; | |
1429 | ||
1430 | // visits all the frames that must be stored in the stack map | |
1431 | Label l = labels; | |
1432 | while(l != null) | |
1433 | { | |
1434 | f = l.frame; | |
1435 | if((l.status & Label.STORE) != 0) | |
1436 | { | |
1437 | visitFrame(f); | |
1438 | } | |
1439 | if((l.status & Label.REACHABLE) == 0) | |
1440 | { | |
1441 | // finds start and end of dead basic block | |
1442 | Label k = l.successor; | |
1443 | int start = l.position; | |
1444 | int end = (k == null ? code.length : k.position) - 1; | |
1445 | // if non empty basic block | |
1446 | if(end >= start) | |
1447 | { | |
1448 | // replaces instructions with NOP ... NOP ATHROW | |
1449 | for(int i = start; i < end; ++i) | |
1450 | { | |
1451 | code.data[i] = Opcodes.NOP; | |
1452 | } | |
1453 | code.data[end] = (byte) Opcodes.ATHROW; | |
1454 | // emits a frame for this unreachable block | |
1455 | startFrame(start, 0, 1); | |
1456 | frame[frameIndex++] = Frame.OBJECT | |
1457 | | cw.addType("java/lang/Throwable"); | |
1458 | endFrame(); | |
1459 | } | |
1460 | } | |
1461 | l = l.successor; | |
1462 | } | |
1463 | } | |
1464 | else if(compute == MAXS) | |
1465 | { | |
1466 | // completes the control flow graph with exception handler blocks | |
1467 | Handler handler = firstHandler; | |
1468 | while(handler != null) | |
1469 | { | |
1470 | Label l = handler.start; | |
1471 | Label h = handler.handler; | |
1472 | Label e = handler.end; | |
1473 | // adds 'h' as a successor of labels between 'start' and 'end' | |
1474 | while(l != e) | |
1475 | { | |
1476 | // creates an edge to 'h' | |
1477 | Edge b = new Edge(); | |
1478 | b.info = Edge.EXCEPTION; | |
1479 | b.successor = h; | |
1480 | // adds it to the successors of 'l' | |
1481 | if((l.status & Label.JSR) != 0) | |
1482 | { | |
1483 | // if l is a JSR block, adds b after the first two edges | |
1484 | // to preserve the hypothesis about JSR block successors | |
1485 | // order (see {@link #visitJumpInsn}) | |
1486 | b.next = l.successors.next.next; | |
1487 | l.successors.next.next = b; | |
1488 | } | |
1489 | else | |
1490 | { | |
1491 | b.next = l.successors; | |
1492 | l.successors = b; | |
1493 | } | |
1494 | // goes to the next label | |
1495 | l = l.successor; | |
1496 | } | |
1497 | handler = handler.next; | |
1498 | } | |
1499 | ||
1500 | if(jsr) | |
1501 | { | |
1502 | // completes the control flow graph with the RET successors | |
1503 | /* | |
1504 | * first step: finds the subroutines. This step determines, for | |
1505 | * each basic block, to which subroutine(s) it belongs, and | |
1506 | * stores this set as a bit set in the {@link Label#status} | |
1507 | * field. Subroutines are numbered with powers of two, from | |
1508 | * 0x1000 to 0x80000000 (so there must be at most 20 subroutines | |
1509 | * in a method). | |
1510 | */ | |
1511 | // finds the basic blocks that belong to the "main" subroutine | |
1512 | int id = 0x1000; | |
1513 | findSubroutine(labels, id); | |
1514 | // finds the basic blocks that belong to the real subroutines | |
1515 | Label l = labels; | |
1516 | while(l != null) | |
1517 | { | |
1518 | if((l.status & Label.JSR) != 0) | |
1519 | { | |
1520 | // the subroutine is defined by l's TARGET, not by l | |
1521 | Label subroutine = l.successors.next.successor; | |
1522 | // if this subroutine does not have an id yet... | |
1523 | if((subroutine.status & ~0xFFF) == 0) | |
1524 | { | |
1525 | // ...assigns it a new id and finds its basic blocks | |
1526 | id = id << 1; | |
1527 | findSubroutine(subroutine, id); | |
1528 | } | |
1529 | } | |
1530 | l = l.successor; | |
1531 | } | |
1532 | // second step: finds the successors of RET blocks | |
1533 | findSubroutineSuccessors(0x1000, new Label[10], 0); | |
1534 | } | |
1535 | ||
1536 | /* | |
1537 | * control flow analysis algorithm: while the block stack is not | |
1538 | * empty, pop a block from this stack, update the max stack size, | |
1539 | * compute the true (non relative) begin stack size of the | |
1540 | * successors of this block, and push these successors onto the | |
1541 | * stack (unless they have already been pushed onto the stack). | |
1542 | * Note: by hypothesis, the {@link Label#inputStackTop} of the | |
1543 | * blocks in the block stack are the true (non relative) beginning | |
1544 | * stack sizes of these blocks. | |
1545 | */ | |
1546 | int max = 0; | |
1547 | Label stack = labels; | |
1548 | while(stack != null) | |
1549 | { | |
1550 | // pops a block from the stack | |
1551 | Label l = stack; | |
1552 | stack = stack.next; | |
1553 | // computes the true (non relative) max stack size of this block | |
1554 | int start = l.inputStackTop; | |
1555 | int blockMax = start + l.outputStackMax; | |
1556 | // updates the global max stack size | |
1557 | if(blockMax > max) | |
1558 | { | |
1559 | max = blockMax; | |
1560 | } | |
1561 | // analyses the successors of the block | |
1562 | Edge b = l.successors; | |
1563 | if((l.status & Label.JSR) != 0) | |
1564 | { | |
1565 | // ignores the first edge of JSR blocks (virtual successor) | |
1566 | b = b.next; | |
1567 | } | |
1568 | while(b != null) | |
1569 | { | |
1570 | l = b.successor; | |
1571 | // if this successor has not already been pushed... | |
1572 | if((l.status & Label.PUSHED) == 0) | |
1573 | { | |
1574 | // computes its true beginning stack size... | |
1575 | l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start | |
1576 | + b.info; | |
1577 | // ...and pushes it onto the stack | |
1578 | l.status |= Label.PUSHED; | |
1579 | l.next = stack; | |
1580 | stack = l; | |
1581 | } | |
1582 | b = b.next; | |
1583 | } | |
1584 | } | |
1585 | this.maxStack = max; | |
1586 | } | |
1587 | else | |
1588 | { | |
1589 | this.maxStack = maxStack; | |
1590 | this.maxLocals = maxLocals; | |
1591 | } | |
1592 | } | |
1593 | ||
1594 | public void visitEnd(){ | |
1595 | } | |
1596 | ||
1597 | // ------------------------------------------------------------------------ | |
1598 | // Utility methods: control flow analysis algorithm | |
1599 | // ------------------------------------------------------------------------ | |
1600 | ||
1601 | /** | |
1602 | * Computes the size of the arguments and of the return value of a method. | |
1603 | * | |
1604 | * @param desc the descriptor of a method. | |
1605 | * @return the size of the arguments of the method (plus one for the | |
1606 | * implicit this argument), argSize, and the size of its return | |
1607 | * value, retSize, packed into a single int i = | |
1608 | * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal | |
1609 | * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). | |
1610 | */ | |
1611 | static int getArgumentsAndReturnSizes(final String desc){ | |
1612 | int n = 1; | |
1613 | int c = 1; | |
1614 | while(true) | |
1615 | { | |
1616 | char car = desc.charAt(c++); | |
1617 | if(car == ')') | |
1618 | { | |
1619 | car = desc.charAt(c); | |
1620 | return n << 2 | |
1621 | | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); | |
1622 | } | |
1623 | else if(car == 'L') | |
1624 | { | |
1625 | while(desc.charAt(c++) != ';') | |
1626 | { | |
1627 | } | |
1628 | n += 1; | |
1629 | } | |
1630 | else if(car == '[') | |
1631 | { | |
1632 | while((car = desc.charAt(c)) == '[') | |
1633 | { | |
1634 | ++c; | |
1635 | } | |
1636 | if(car == 'D' || car == 'J') | |
1637 | { | |
1638 | n -= 1; | |
1639 | } | |
1640 | } | |
1641 | else if(car == 'D' || car == 'J') | |
1642 | { | |
1643 | n += 2; | |
1644 | } | |
1645 | else | |
1646 | { | |
1647 | n += 1; | |
1648 | } | |
1649 | } | |
1650 | } | |
1651 | ||
1652 | /** | |
1653 | * Adds a successor to the {@link #currentBlock currentBlock} block. | |
1654 | * | |
1655 | * @param info information about the control flow edge to be added. | |
1656 | * @param successor the successor block to be added to the current block. | |
1657 | */ | |
1658 | private void addSuccessor(final int info, final Label successor){ | |
1659 | // creates and initializes an Edge object... | |
1660 | Edge b = new Edge(); | |
1661 | b.info = info; | |
1662 | b.successor = successor; | |
1663 | // ...and adds it to the successor list of the currentBlock block | |
1664 | b.next = currentBlock.successors; | |
1665 | currentBlock.successors = b; | |
1666 | } | |
1667 | ||
1668 | /** | |
1669 | * Ends the current basic block. This method must be used in the case where | |
1670 | * the current basic block does not have any successor. | |
1671 | */ | |
1672 | private void noSuccessor(){ | |
1673 | if(compute == FRAMES) | |
1674 | { | |
1675 | Label l = new Label(); | |
1676 | l.frame = new Frame(); | |
1677 | l.frame.owner = l; | |
1678 | l.resolve(this, code.length, code.data); | |
1679 | previousBlock.successor = l; | |
1680 | previousBlock = l; | |
1681 | } | |
1682 | else | |
1683 | { | |
1684 | currentBlock.outputStackMax = maxStackSize; | |
1685 | } | |
1686 | currentBlock = null; | |
1687 | } | |
1688 | ||
1689 | /** | |
1690 | * Finds the basic blocks that belong to a given subroutine, and marks these | |
1691 | * blocks as belonging to this subroutine (by using {@link Label#status} as | |
1692 | * a bit set (see {@link #visitMaxs}). This recursive method follows the | |
1693 | * control flow graph to find all the blocks that are reachable from the | |
1694 | * given block WITHOUT following any JSR target. | |
1695 | * | |
1696 | * @param block a block that belongs to the subroutine | |
1697 | * @param id the id of this subroutine | |
1698 | */ | |
1699 | private void findSubroutine(final Label block, final int id){ | |
1700 | // if 'block' is already marked as belonging to subroutine 'id', returns | |
1701 | if((block.status & id) != 0) | |
1702 | { | |
1703 | return; | |
1704 | } | |
1705 | // marks 'block' as belonging to subroutine 'id' | |
1706 | block.status |= id; | |
1707 | // calls this method recursively on each successor, except JSR targets | |
1708 | Edge e = block.successors; | |
1709 | while(e != null) | |
1710 | { | |
1711 | // if 'block' is a JSR block, then 'block.successors.next' leads | |
1712 | // to the JSR target (see {@link #visitJumpInsn}) and must therefore | |
1713 | // not be followed | |
1714 | if((block.status & Label.JSR) == 0 || e != block.successors.next) | |
1715 | { | |
1716 | findSubroutine(e.successor, id); | |
1717 | } | |
1718 | e = e.next; | |
1719 | } | |
1720 | } | |
1721 | ||
1722 | /** | |
1723 | * Finds the successors of the RET blocks of the specified subroutine, and | |
1724 | * of any nested subroutine it calls. | |
1725 | * | |
1726 | * @param id id of the subroutine whose RET block successors must be found. | |
1727 | * @param JSRs the JSR blocks that were followed to reach this subroutine. | |
1728 | * @param nJSRs number of JSR blocks in the JSRs array. | |
1729 | */ | |
1730 | private void findSubroutineSuccessors( | |
1731 | final int id, | |
1732 | final Label[] JSRs, | |
1733 | final int nJSRs){ | |
1734 | // iterates over all the basic blocks... | |
1735 | Label l = labels; | |
1736 | while(l != null) | |
1737 | { | |
1738 | // for those that belong to subroutine 'id'... | |
1739 | if((l.status & id) != 0) | |
1740 | { | |
1741 | if((l.status & Label.JSR) != 0) | |
1742 | { | |
1743 | // finds the subroutine to which 'l' leads by following the | |
1744 | // second edge of l.successors (see {@link #visitJumpInsn}) | |
1745 | int nId = l.successors.next.successor.status & ~0xFFF; | |
1746 | if(nId != id) | |
1747 | { | |
1748 | // calls this method recursively with l pushed onto the | |
1749 | // JSRs stack to find the successors of the RET blocks | |
1750 | // of this nested subroutine 'nId' | |
1751 | JSRs[nJSRs] = l; | |
1752 | findSubroutineSuccessors(nId, JSRs, nJSRs + 1); | |
1753 | } | |
1754 | } | |
1755 | else if((l.status & Label.RET) != 0) | |
1756 | { | |
1757 | /* | |
1758 | * finds the JSR block in the JSRs stack that corresponds to | |
1759 | * this RET block, and updates the successors of this RET | |
1760 | * block accordingly. This corresponding JSR is the one that | |
1761 | * leads to the subroutine to which the RET block belongs. | |
1762 | * But the RET block can belong to several subroutines (if a | |
1763 | * nested subroutine returns to its parent subroutine | |
1764 | * implicitely, without a RET). So, in fact, the JSR that | |
1765 | * corresponds to this RET is the first block in the JSRs | |
1766 | * stack, starting from the bottom of the stack, that leads | |
1767 | * to a subroutine to which the RET block belongs. | |
1768 | */ | |
1769 | for(int i = 0; i < nJSRs; ++i) | |
1770 | { | |
1771 | int JSRstatus = JSRs[i].successors.next.successor.status; | |
1772 | if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0) | |
1773 | { | |
1774 | Edge e = new Edge(); | |
1775 | e.info = l.inputStackTop; | |
1776 | e.successor = JSRs[i].successors.successor; | |
1777 | e.next = l.successors; | |
1778 | l.successors = e; | |
1779 | break; | |
1780 | } | |
1781 | } | |
1782 | } | |
1783 | } | |
1784 | l = l.successor; | |
1785 | } | |
1786 | } | |
1787 | ||
1788 | // ------------------------------------------------------------------------ | |
1789 | // Utility methods: stack map frames | |
1790 | // ------------------------------------------------------------------------ | |
1791 | ||
1792 | /** | |
1793 | * Visits a frame that has been computed from scratch. | |
1794 | * | |
1795 | * @param f the frame that must be visited. | |
1796 | */ | |
1797 | private void visitFrame(final Frame f){ | |
1798 | int i, t; | |
1799 | int nTop = 0; | |
1800 | int nLocal = 0; | |
1801 | int nStack = 0; | |
1802 | int[] locals = f.inputLocals; | |
1803 | int[] stacks = f.inputStack; | |
1804 | // computes the number of locals (ignores TOP types that are just after | |
1805 | // a LONG or a DOUBLE, and all trailing TOP types) | |
1806 | for(i = 0; i < locals.length; ++i) | |
1807 | { | |
1808 | t = locals[i]; | |
1809 | if(t == Frame.TOP) | |
1810 | { | |
1811 | ++nTop; | |
1812 | } | |
1813 | else | |
1814 | { | |
1815 | nLocal += nTop + 1; | |
1816 | nTop = 0; | |
1817 | } | |
1818 | if(t == Frame.LONG || t == Frame.DOUBLE) | |
1819 | { | |
1820 | ++i; | |
1821 | } | |
1822 | } | |
1823 | // computes the stack size (ignores TOP types that are just after | |
1824 | // a LONG or a DOUBLE) | |
1825 | for(i = 0; i < stacks.length; ++i) | |
1826 | { | |
1827 | t = stacks[i]; | |
1828 | ++nStack; | |
1829 | if(t == Frame.LONG || t == Frame.DOUBLE) | |
1830 | { | |
1831 | ++i; | |
1832 | } | |
1833 | } | |
1834 | // visits the frame and its content | |
1835 | startFrame(f.owner.position, nLocal, nStack); | |
1836 | for(i = 0; nLocal > 0; ++i, --nLocal) | |
1837 | { | |
1838 | t = locals[i]; | |
1839 | frame[frameIndex++] = t; | |
1840 | if(t == Frame.LONG || t == Frame.DOUBLE) | |
1841 | { | |
1842 | ++i; | |
1843 | } | |
1844 | } | |
1845 | for(i = 0; i < stacks.length; ++i) | |
1846 | { | |
1847 | t = stacks[i]; | |
1848 | frame[frameIndex++] = t; | |
1849 | if(t == Frame.LONG || t == Frame.DOUBLE) | |
1850 | { | |
1851 | ++i; | |
1852 | } | |
1853 | } | |
1854 | endFrame(); | |
1855 | } | |
1856 | ||
1857 | /** | |
1858 | * Starts the visit of a stack map frame. | |
1859 | * | |
1860 | * @param offset the offset of the instruction to which the frame | |
1861 | * corresponds. | |
1862 | * @param nLocal the number of local variables in the frame. | |
1863 | * @param nStack the number of stack elements in the frame. | |
1864 | */ | |
1865 | private void startFrame(final int offset, final int nLocal, final int nStack){ | |
1866 | int n = 3 + nLocal + nStack; | |
1867 | if(frame == null || frame.length < n) | |
1868 | { | |
1869 | frame = new int[n]; | |
1870 | } | |
1871 | frame[0] = offset; | |
1872 | frame[1] = nLocal; | |
1873 | frame[2] = nStack; | |
1874 | frameIndex = 3; | |
1875 | } | |
1876 | ||
1877 | /** | |
1878 | * Checks if the visit of the current frame {@link #frame} is finished, and | |
1879 | * if yes, write it in the StackMapTable attribute. | |
1880 | */ | |
1881 | private void endFrame(){ | |
1882 | if(previousFrame != null) | |
1883 | { // do not write the first frame | |
1884 | if(stackMap == null) | |
1885 | { | |
1886 | stackMap = new ByteVector(); | |
1887 | } | |
1888 | writeFrame(); | |
1889 | ++frameCount; | |
1890 | } | |
1891 | previousFrame = frame; | |
1892 | frame = null; | |
1893 | } | |
1894 | ||
1895 | /** | |
1896 | * Compress and writes the current frame {@link #frame} in the StackMapTable | |
1897 | * attribute. | |
1898 | */ | |
1899 | private void writeFrame(){ | |
1900 | int clocalsSize = frame[1]; | |
1901 | int cstackSize = frame[2]; | |
1902 | if((cw.version & 0xFFFF) < Opcodes.V1_6) | |
1903 | { | |
1904 | stackMap.putShort(frame[0]).putShort(clocalsSize); | |
1905 | writeFrameTypes(3, 3 + clocalsSize); | |
1906 | stackMap.putShort(cstackSize); | |
1907 | writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); | |
1908 | return; | |
1909 | } | |
1910 | int localsSize = previousFrame[1]; | |
1911 | int type = FULL_FRAME; | |
1912 | int k = 0; | |
1913 | int delta; | |
1914 | if(frameCount == 0) | |
1915 | { | |
1916 | delta = frame[0]; | |
1917 | } | |
1918 | else | |
1919 | { | |
1920 | delta = frame[0] - previousFrame[0] - 1; | |
1921 | } | |
1922 | if(cstackSize == 0) | |
1923 | { | |
1924 | k = clocalsSize - localsSize; | |
1925 | switch(k) | |
1926 | { | |
1927 | case-3: | |
1928 | case-2: | |
1929 | case-1: | |
1930 | type = CHOP_FRAME; | |
1931 | localsSize = clocalsSize; | |
1932 | break; | |
1933 | case 0: | |
1934 | type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; | |
1935 | break; | |
1936 | case 1: | |
1937 | case 2: | |
1938 | case 3: | |
1939 | type = APPEND_FRAME; | |
1940 | break; | |
1941 | } | |
1942 | } | |
1943 | else if(clocalsSize == localsSize && cstackSize == 1) | |
1944 | { | |
1945 | type = delta < 63 | |
1946 | ? SAME_LOCALS_1_STACK_ITEM_FRAME | |
1947 | : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; | |
1948 | } | |
1949 | if(type != FULL_FRAME) | |
1950 | { | |
1951 | // verify if locals are the same | |
1952 | int l = 3; | |
1953 | for(int j = 0; j < localsSize; j++) | |
1954 | { | |
1955 | if(frame[l] != previousFrame[l]) | |
1956 | { | |
1957 | type = FULL_FRAME; | |
1958 | break; | |
1959 | } | |
1960 | l++; | |
1961 | } | |
1962 | } | |
1963 | switch(type) | |
1964 | { | |
1965 | case SAME_FRAME: | |
1966 | stackMap.putByte(delta); | |
1967 | break; | |
1968 | case SAME_LOCALS_1_STACK_ITEM_FRAME: | |
1969 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); | |
1970 | writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); | |
1971 | break; | |
1972 | case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: | |
1973 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) | |
1974 | .putShort(delta); | |
1975 | writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); | |
1976 | break; | |
1977 | case SAME_FRAME_EXTENDED: | |
1978 | stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); | |
1979 | break; | |
1980 | case CHOP_FRAME: | |
1981 | stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); | |
1982 | break; | |
1983 | case APPEND_FRAME: | |
1984 | stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); | |
1985 | writeFrameTypes(3 + localsSize, 3 + clocalsSize); | |
1986 | break; | |
1987 | // case FULL_FRAME: | |
1988 | default: | |
1989 | stackMap.putByte(FULL_FRAME) | |
1990 | .putShort(delta) | |
1991 | .putShort(clocalsSize); | |
1992 | writeFrameTypes(3, 3 + clocalsSize); | |
1993 | stackMap.putShort(cstackSize); | |
1994 | writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); | |
1995 | } | |
1996 | } | |
1997 | ||
1998 | /** | |
1999 | * Writes some types of the current frame {@link #frame} into the | |
2000 | * StackMapTableAttribute. This method converts types from the format used | |
2001 | * in {@link Label} to the format used in StackMapTable attributes. In | |
2002 | * particular, it converts type table indexes to constant pool indexes. | |
2003 | * | |
2004 | * @param start index of the first type in {@link #frame} to write. | |
2005 | * @param end index of last type in {@link #frame} to write (exclusive). | |
2006 | */ | |
2007 | private void writeFrameTypes(final int start, final int end){ | |
2008 | for(int i = start; i < end; ++i) | |
2009 | { | |
2010 | int t = frame[i]; | |
2011 | int d = t & Frame.DIM; | |
2012 | if(d == 0) | |
2013 | { | |
2014 | int v = t & Frame.BASE_VALUE; | |
2015 | switch(t & Frame.BASE_KIND) | |
2016 | { | |
2017 | case Frame.OBJECT: | |
2018 | stackMap.putByte(7) | |
2019 | .putShort(cw.newClass(cw.typeTable[v].strVal1)); | |
2020 | break; | |
2021 | case Frame.UNINITIALIZED: | |
2022 | stackMap.putByte(8).putShort(cw.typeTable[v].intVal); | |
2023 | break; | |
2024 | default: | |
2025 | stackMap.putByte(v); | |
2026 | } | |
2027 | } | |
2028 | else | |
2029 | { | |
2030 | StringBuffer buf = new StringBuffer(); | |
2031 | d >>= 28; | |
2032 | while(d-- > 0) | |
2033 | { | |
2034 | buf.append('['); | |
2035 | } | |
2036 | if((t & Frame.BASE_KIND) == Frame.OBJECT) | |
2037 | { | |
2038 | buf.append('L'); | |
2039 | buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); | |
2040 | buf.append(';'); | |
2041 | } | |
2042 | else | |
2043 | { | |
2044 | switch(t & 0xF) | |
2045 | { | |
2046 | case 1: | |
2047 | buf.append('I'); | |
2048 | break; | |
2049 | case 2: | |
2050 | buf.append('F'); | |
2051 | break; | |
2052 | case 3: | |
2053 | buf.append('D'); | |
2054 | break; | |
2055 | case 9: | |
2056 | buf.append('Z'); | |
2057 | break; | |
2058 | case 10: | |
2059 | buf.append('B'); | |
2060 | break; | |
2061 | case 11: | |
2062 | buf.append('C'); | |
2063 | break; | |
2064 | case 12: | |
2065 | buf.append('S'); | |
2066 | break; | |
2067 | default: | |
2068 | buf.append('J'); | |
2069 | } | |
2070 | } | |
2071 | stackMap.putByte(7).putShort(cw.newClass(buf.toString())); | |
2072 | } | |
2073 | } | |
2074 | } | |
2075 | ||
2076 | private void writeFrameType(final Object type){ | |
2077 | if(type instanceof String) | |
2078 | { | |
2079 | stackMap.putByte(7).putShort(cw.newClass((String) type)); | |
2080 | } | |
2081 | else if(type instanceof Integer) | |
2082 | { | |
2083 | stackMap.putByte(((Integer) type).intValue()); | |
2084 | } | |
2085 | else | |
2086 | { | |
2087 | stackMap.putByte(8).putShort(((Label) type).position); | |
2088 | } | |
2089 | } | |
2090 | ||
2091 | // ------------------------------------------------------------------------ | |
2092 | // Utility methods: dump bytecode array | |
2093 | // ------------------------------------------------------------------------ | |
2094 | ||
2095 | /** | |
2096 | * Returns the size of the bytecode of this method. | |
2097 | * | |
2098 | * @return the size of the bytecode of this method. | |
2099 | */ | |
2100 | final int getSize(){ | |
2101 | if(classReaderOffset != 0) | |
2102 | { | |
2103 | return 6 + classReaderLength; | |
2104 | } | |
2105 | if(resize) | |
2106 | { | |
2107 | // replaces the temporary jump opcodes introduced by Label.resolve. | |
2108 | resizeInstructions(); | |
2109 | } | |
2110 | int size = 8; | |
2111 | if(code.length > 0) | |
2112 | { | |
2113 | cw.newUTF8("Code"); | |
2114 | size += 18 + code.length + 8 * handlerCount; | |
2115 | if(localVar != null) | |
2116 | { | |
2117 | cw.newUTF8("LocalVariableTable"); | |
2118 | size += 8 + localVar.length; | |
2119 | } | |
2120 | if(localVarType != null) | |
2121 | { | |
2122 | cw.newUTF8("LocalVariableTypeTable"); | |
2123 | size += 8 + localVarType.length; | |
2124 | } | |
2125 | if(lineNumber != null) | |
2126 | { | |
2127 | cw.newUTF8("LineNumberTable"); | |
2128 | size += 8 + lineNumber.length; | |
2129 | } | |
2130 | if(stackMap != null) | |
2131 | { | |
2132 | boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; | |
2133 | cw.newUTF8(zip ? "StackMapTable" : "StackMap"); | |
2134 | size += 8 + stackMap.length; | |
2135 | } | |
2136 | if(cattrs != null) | |
2137 | { | |
2138 | size += cattrs.getSize(cw, | |
2139 | code.data, | |
2140 | code.length, | |
2141 | maxStack, | |
2142 | maxLocals); | |
2143 | } | |
2144 | } | |
2145 | if(exceptionCount > 0) | |
2146 | { | |
2147 | cw.newUTF8("Exceptions"); | |
2148 | size += 8 + 2 * exceptionCount; | |
2149 | } | |
2150 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
2151 | && (cw.version & 0xffff) < Opcodes.V1_5) | |
2152 | { | |
2153 | cw.newUTF8("Synthetic"); | |
2154 | size += 6; | |
2155 | } | |
2156 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
2157 | { | |
2158 | cw.newUTF8("Deprecated"); | |
2159 | size += 6; | |
2160 | } | |
2161 | if(signature != null) | |
2162 | { | |
2163 | cw.newUTF8("Signature"); | |
2164 | cw.newUTF8(signature); | |
2165 | size += 8; | |
2166 | } | |
2167 | if(annd != null) | |
2168 | { | |
2169 | cw.newUTF8("AnnotationDefault"); | |
2170 | size += 6 + annd.length; | |
2171 | } | |
2172 | if(anns != null) | |
2173 | { | |
2174 | cw.newUTF8("RuntimeVisibleAnnotations"); | |
2175 | size += 8 + anns.getSize(); | |
2176 | } | |
2177 | if(ianns != null) | |
2178 | { | |
2179 | cw.newUTF8("RuntimeInvisibleAnnotations"); | |
2180 | size += 8 + ianns.getSize(); | |
2181 | } | |
2182 | if(panns != null) | |
2183 | { | |
2184 | cw.newUTF8("RuntimeVisibleParameterAnnotations"); | |
2185 | size += 7 + 2 * panns.length; | |
2186 | for(int i = panns.length - 1; i >= 0; --i) | |
2187 | { | |
2188 | size += panns[i] == null ? 0 : panns[i].getSize(); | |
2189 | } | |
2190 | } | |
2191 | if(ipanns != null) | |
2192 | { | |
2193 | cw.newUTF8("RuntimeInvisibleParameterAnnotations"); | |
2194 | size += 7 + 2 * ipanns.length; | |
2195 | for(int i = ipanns.length - 1; i >= 0; --i) | |
2196 | { | |
2197 | size += ipanns[i] == null ? 0 : ipanns[i].getSize(); | |
2198 | } | |
2199 | } | |
2200 | if(attrs != null) | |
2201 | { | |
2202 | size += attrs.getSize(cw, null, 0, -1, -1); | |
2203 | } | |
2204 | return size; | |
2205 | } | |
2206 | ||
2207 | /** | |
2208 | * Puts the bytecode of this method in the given byte vector. | |
2209 | * | |
2210 | * @param out the byte vector into which the bytecode of this method must be | |
2211 | * copied. | |
2212 | */ | |
2213 | final void put(final ByteVector out){ | |
2214 | out.putShort(access).putShort(name).putShort(desc); | |
2215 | if(classReaderOffset != 0) | |
2216 | { | |
2217 | out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); | |
2218 | return; | |
2219 | } | |
2220 | int attributeCount = 0; | |
2221 | if(code.length > 0) | |
2222 | { | |
2223 | ++attributeCount; | |
2224 | } | |
2225 | if(exceptionCount > 0) | |
2226 | { | |
2227 | ++attributeCount; | |
2228 | } | |
2229 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
2230 | && (cw.version & 0xffff) < Opcodes.V1_5) | |
2231 | { | |
2232 | ++attributeCount; | |
2233 | } | |
2234 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
2235 | { | |
2236 | ++attributeCount; | |
2237 | } | |
2238 | if(signature != null) | |
2239 | { | |
2240 | ++attributeCount; | |
2241 | } | |
2242 | if(annd != null) | |
2243 | { | |
2244 | ++attributeCount; | |
2245 | } | |
2246 | if(anns != null) | |
2247 | { | |
2248 | ++attributeCount; | |
2249 | } | |
2250 | if(ianns != null) | |
2251 | { | |
2252 | ++attributeCount; | |
2253 | } | |
2254 | if(panns != null) | |
2255 | { | |
2256 | ++attributeCount; | |
2257 | } | |
2258 | if(ipanns != null) | |
2259 | { | |
2260 | ++attributeCount; | |
2261 | } | |
2262 | if(attrs != null) | |
2263 | { | |
2264 | attributeCount += attrs.getCount(); | |
2265 | } | |
2266 | out.putShort(attributeCount); | |
2267 | if(code.length > 0) | |
2268 | { | |
2269 | int size = 12 + code.length + 8 * handlerCount; | |
2270 | if(localVar != null) | |
2271 | { | |
2272 | size += 8 + localVar.length; | |
2273 | } | |
2274 | if(localVarType != null) | |
2275 | { | |
2276 | size += 8 + localVarType.length; | |
2277 | } | |
2278 | if(lineNumber != null) | |
2279 | { | |
2280 | size += 8 + lineNumber.length; | |
2281 | } | |
2282 | if(stackMap != null) | |
2283 | { | |
2284 | size += 8 + stackMap.length; | |
2285 | } | |
2286 | if(cattrs != null) | |
2287 | { | |
2288 | size += cattrs.getSize(cw, | |
2289 | code.data, | |
2290 | code.length, | |
2291 | maxStack, | |
2292 | maxLocals); | |
2293 | } | |
2294 | out.putShort(cw.newUTF8("Code")).putInt(size); | |
2295 | out.putShort(maxStack).putShort(maxLocals); | |
2296 | out.putInt(code.length).putByteArray(code.data, 0, code.length); | |
2297 | out.putShort(handlerCount); | |
2298 | if(handlerCount > 0) | |
2299 | { | |
2300 | Handler h = firstHandler; | |
2301 | while(h != null) | |
2302 | { | |
2303 | out.putShort(h.start.position) | |
2304 | .putShort(h.end.position) | |
2305 | .putShort(h.handler.position) | |
2306 | .putShort(h.type); | |
2307 | h = h.next; | |
2308 | } | |
2309 | } | |
2310 | attributeCount = 0; | |
2311 | if(localVar != null) | |
2312 | { | |
2313 | ++attributeCount; | |
2314 | } | |
2315 | if(localVarType != null) | |
2316 | { | |
2317 | ++attributeCount; | |
2318 | } | |
2319 | if(lineNumber != null) | |
2320 | { | |
2321 | ++attributeCount; | |
2322 | } | |
2323 | if(stackMap != null) | |
2324 | { | |
2325 | ++attributeCount; | |
2326 | } | |
2327 | if(cattrs != null) | |
2328 | { | |
2329 | attributeCount += cattrs.getCount(); | |
2330 | } | |
2331 | out.putShort(attributeCount); | |
2332 | if(localVar != null) | |
2333 | { | |
2334 | out.putShort(cw.newUTF8("LocalVariableTable")); | |
2335 | out.putInt(localVar.length + 2).putShort(localVarCount); | |
2336 | out.putByteArray(localVar.data, 0, localVar.length); | |
2337 | } | |
2338 | if(localVarType != null) | |
2339 | { | |
2340 | out.putShort(cw.newUTF8("LocalVariableTypeTable")); | |
2341 | out.putInt(localVarType.length + 2).putShort(localVarTypeCount); | |
2342 | out.putByteArray(localVarType.data, 0, localVarType.length); | |
2343 | } | |
2344 | if(lineNumber != null) | |
2345 | { | |
2346 | out.putShort(cw.newUTF8("LineNumberTable")); | |
2347 | out.putInt(lineNumber.length + 2).putShort(lineNumberCount); | |
2348 | out.putByteArray(lineNumber.data, 0, lineNumber.length); | |
2349 | } | |
2350 | if(stackMap != null) | |
2351 | { | |
2352 | boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; | |
2353 | out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); | |
2354 | out.putInt(stackMap.length + 2).putShort(frameCount); | |
2355 | out.putByteArray(stackMap.data, 0, stackMap.length); | |
2356 | } | |
2357 | if(cattrs != null) | |
2358 | { | |
2359 | cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); | |
2360 | } | |
2361 | } | |
2362 | if(exceptionCount > 0) | |
2363 | { | |
2364 | out.putShort(cw.newUTF8("Exceptions")) | |
2365 | .putInt(2 * exceptionCount + 2); | |
2366 | out.putShort(exceptionCount); | |
2367 | for(int i = 0; i < exceptionCount; ++i) | |
2368 | { | |
2369 | out.putShort(exceptions[i]); | |
2370 | } | |
2371 | } | |
2372 | if((access & Opcodes.ACC_SYNTHETIC) != 0 | |
2373 | && (cw.version & 0xffff) < Opcodes.V1_5) | |
2374 | { | |
2375 | out.putShort(cw.newUTF8("Synthetic")).putInt(0); | |
2376 | } | |
2377 | if((access & Opcodes.ACC_DEPRECATED) != 0) | |
2378 | { | |
2379 | out.putShort(cw.newUTF8("Deprecated")).putInt(0); | |
2380 | } | |
2381 | if(signature != null) | |
2382 | { | |
2383 | out.putShort(cw.newUTF8("Signature")) | |
2384 | .putInt(2) | |
2385 | .putShort(cw.newUTF8(signature)); | |
2386 | } | |
2387 | if(annd != null) | |
2388 | { | |
2389 | out.putShort(cw.newUTF8("AnnotationDefault")); | |
2390 | out.putInt(annd.length); | |
2391 | out.putByteArray(annd.data, 0, annd.length); | |
2392 | } | |
2393 | if(anns != null) | |
2394 | { | |
2395 | out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); | |
2396 | anns.put(out); | |
2397 | } | |
2398 | if(ianns != null) | |
2399 | { | |
2400 | out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); | |
2401 | ianns.put(out); | |
2402 | } | |
2403 | if(panns != null) | |
2404 | { | |
2405 | out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); | |
2406 | AnnotationWriter.put(panns, out); | |
2407 | } | |
2408 | if(ipanns != null) | |
2409 | { | |
2410 | out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); | |
2411 | AnnotationWriter.put(ipanns, out); | |
2412 | } | |
2413 | if(attrs != null) | |
2414 | { | |
2415 | attrs.put(cw, null, 0, -1, -1, out); | |
2416 | } | |
2417 | } | |
2418 | ||
2419 | // ------------------------------------------------------------------------ | |
2420 | // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) | |
2421 | // ------------------------------------------------------------------------ | |
2422 | ||
2423 | /** | |
2424 | * Resizes and replaces the temporary instructions inserted by | |
2425 | * {@link Label#resolve} for wide forward jumps, while keeping jump offsets | |
2426 | * and instruction addresses consistent. This may require to resize other | |
2427 | * existing instructions, or even to introduce new instructions: for | |
2428 | * example, increasing the size of an instruction by 2 at the middle of a | |
2429 | * method can increases the offset of an IFEQ instruction from 32766 to | |
2430 | * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W | |
2431 | * 32765. This, in turn, may require to increase the size of another jump | |
2432 | * instruction, and so on... All these operations are handled automatically | |
2433 | * by this method. <p> <i>This method must be called after all the method | |
2434 | * that is being built has been visited</i>. In particular, the | |
2435 | * {@link Label Label} objects used to construct the method are no longer | |
2436 | * valid after this method has been called. | |
2437 | */ | |
2438 | private void resizeInstructions(){ | |
2439 | byte[] b = code.data; // bytecode of the method | |
2440 | int u, v, label; // indexes in b | |
2441 | int i, j; // loop indexes | |
2442 | /* | |
2443 | * 1st step: As explained above, resizing an instruction may require to | |
2444 | * resize another one, which may require to resize yet another one, and | |
2445 | * so on. The first step of the algorithm consists in finding all the | |
2446 | * instructions that need to be resized, without modifying the code. | |
2447 | * This is done by the following "fix point" algorithm: | |
2448 | * | |
2449 | * Parse the code to find the jump instructions whose offset will need | |
2450 | * more than 2 bytes to be stored (the future offset is computed from | |
2451 | * the current offset and from the number of bytes that will be inserted | |
2452 | * or removed between the source and target instructions). For each such | |
2453 | * instruction, adds an entry in (a copy of) the indexes and sizes | |
2454 | * arrays (if this has not already been done in a previous iteration!). | |
2455 | * | |
2456 | * If at least one entry has been added during the previous step, go | |
2457 | * back to the beginning, otherwise stop. | |
2458 | * | |
2459 | * In fact the real algorithm is complicated by the fact that the size | |
2460 | * of TABLESWITCH and LOOKUPSWITCH instructions depends on their | |
2461 | * position in the bytecode (because of padding). In order to ensure the | |
2462 | * convergence of the algorithm, the number of bytes to be added or | |
2463 | * removed from these instructions is over estimated during the previous | |
2464 | * loop, and computed exactly only after the loop is finished (this | |
2465 | * requires another pass to parse the bytecode of the method). | |
2466 | */ | |
2467 | int[] allIndexes = new int[0]; // copy of indexes | |
2468 | int[] allSizes = new int[0]; // copy of sizes | |
2469 | boolean[] resize; // instructions to be resized | |
2470 | int newOffset; // future offset of a jump instruction | |
2471 | ||
2472 | resize = new boolean[code.length]; | |
2473 | ||
2474 | // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done | |
2475 | int state = 3; | |
2476 | do | |
2477 | { | |
2478 | if(state == 3) | |
2479 | { | |
2480 | state = 2; | |
2481 | } | |
2482 | u = 0; | |
2483 | while(u < b.length) | |
2484 | { | |
2485 | int opcode = b[u] & 0xFF; // opcode of current instruction | |
2486 | int insert = 0; // bytes to be added after this instruction | |
2487 | ||
2488 | switch(ClassWriter.TYPE[opcode]) | |
2489 | { | |
2490 | case ClassWriter.NOARG_INSN: | |
2491 | case ClassWriter.IMPLVAR_INSN: | |
2492 | u += 1; | |
2493 | break; | |
2494 | case ClassWriter.LABEL_INSN: | |
2495 | if(opcode > 201) | |
2496 | { | |
2497 | // converts temporary opcodes 202 to 217, 218 and | |
2498 | // 219 to IFEQ ... JSR (inclusive), IFNULL and | |
2499 | // IFNONNULL | |
2500 | opcode = opcode < 218 ? opcode - 49 : opcode - 20; | |
2501 | label = u + readUnsignedShort(b, u + 1); | |
2502 | } | |
2503 | else | |
2504 | { | |
2505 | label = u + readShort(b, u + 1); | |
2506 | } | |
2507 | newOffset = getNewOffset(allIndexes, allSizes, u, label); | |
2508 | if(newOffset < Short.MIN_VALUE | |
2509 | || newOffset > Short.MAX_VALUE) | |
2510 | { | |
2511 | if(!resize[u]) | |
2512 | { | |
2513 | if(opcode == Opcodes.GOTO | |
2514 | || opcode == Opcodes.JSR) | |
2515 | { | |
2516 | // two additional bytes will be required to | |
2517 | // replace this GOTO or JSR instruction with | |
2518 | // a GOTO_W or a JSR_W | |
2519 | insert = 2; | |
2520 | } | |
2521 | else | |
2522 | { | |
2523 | // five additional bytes will be required to | |
2524 | // replace this IFxxx <l> instruction with | |
2525 | // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx | |
2526 | // is the "opposite" opcode of IFxxx (i.e., | |
2527 | // IFNE for IFEQ) and where <l'> designates | |
2528 | // the instruction just after the GOTO_W. | |
2529 | insert = 5; | |
2530 | } | |
2531 | resize[u] = true; | |
2532 | } | |
2533 | } | |
2534 | u += 3; | |
2535 | break; | |
2536 | case ClassWriter.LABELW_INSN: | |
2537 | u += 5; | |
2538 | break; | |
2539 | case ClassWriter.TABL_INSN: | |
2540 | if(state == 1) | |
2541 | { | |
2542 | // true number of bytes to be added (or removed) | |
2543 | // from this instruction = (future number of padding | |
2544 | // bytes - current number of padding byte) - | |
2545 | // previously over estimated variation = | |
2546 | // = ((3 - newOffset%4) - (3 - u%4)) - u%4 | |
2547 | // = (-newOffset%4 + u%4) - u%4 | |
2548 | // = -(newOffset & 3) | |
2549 | newOffset = getNewOffset(allIndexes, allSizes, 0, u); | |
2550 | insert = -(newOffset & 3); | |
2551 | } | |
2552 | else if(!resize[u]) | |
2553 | { | |
2554 | // over estimation of the number of bytes to be | |
2555 | // added to this instruction = 3 - current number | |
2556 | // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 | |
2557 | insert = u & 3; | |
2558 | resize[u] = true; | |
2559 | } | |
2560 | // skips instruction | |
2561 | u = u + 4 - (u & 3); | |
2562 | u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; | |
2563 | break; | |
2564 | case ClassWriter.LOOK_INSN: | |
2565 | if(state == 1) | |
2566 | { | |
2567 | // like TABL_INSN | |
2568 | newOffset = getNewOffset(allIndexes, allSizes, 0, u); | |
2569 | insert = -(newOffset & 3); | |
2570 | } | |
2571 | else if(!resize[u]) | |
2572 | { | |
2573 | // like TABL_INSN | |
2574 | insert = u & 3; | |
2575 | resize[u] = true; | |
2576 | } | |
2577 | // skips instruction | |
2578 | u = u + 4 - (u & 3); | |
2579 | u += 8 * readInt(b, u + 4) + 8; | |
2580 | break; | |
2581 | case ClassWriter.WIDE_INSN: | |
2582 | opcode = b[u + 1] & 0xFF; | |
2583 | if(opcode == Opcodes.IINC) | |
2584 | { | |
2585 | u += 6; | |
2586 | } | |
2587 | else | |
2588 | { | |
2589 | u += 4; | |
2590 | } | |
2591 | break; | |
2592 | case ClassWriter.VAR_INSN: | |
2593 | case ClassWriter.SBYTE_INSN: | |
2594 | case ClassWriter.LDC_INSN: | |
2595 | u += 2; | |
2596 | break; | |
2597 | case ClassWriter.SHORT_INSN: | |
2598 | case ClassWriter.LDCW_INSN: | |
2599 | case ClassWriter.FIELDORMETH_INSN: | |
2600 | case ClassWriter.TYPE_INSN: | |
2601 | case ClassWriter.IINC_INSN: | |
2602 | u += 3; | |
2603 | break; | |
2604 | case ClassWriter.ITFMETH_INSN: | |
2605 | u += 5; | |
2606 | break; | |
2607 | // case ClassWriter.MANA_INSN: | |
2608 | default: | |
2609 | u += 4; | |
2610 | break; | |
2611 | } | |
2612 | if(insert != 0) | |
2613 | { | |
2614 | // adds a new (u, insert) entry in the allIndexes and | |
2615 | // allSizes arrays | |
2616 | int[] newIndexes = new int[allIndexes.length + 1]; | |
2617 | int[] newSizes = new int[allSizes.length + 1]; | |
2618 | System.arraycopy(allIndexes, | |
2619 | 0, | |
2620 | newIndexes, | |
2621 | 0, | |
2622 | allIndexes.length); | |
2623 | System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); | |
2624 | newIndexes[allIndexes.length] = u; | |
2625 | newSizes[allSizes.length] = insert; | |
2626 | allIndexes = newIndexes; | |
2627 | allSizes = newSizes; | |
2628 | if(insert > 0) | |
2629 | { | |
2630 | state = 3; | |
2631 | } | |
2632 | } | |
2633 | } | |
2634 | if(state < 3) | |
2635 | { | |
2636 | --state; | |
2637 | } | |
2638 | } while(state != 0); | |
2639 | ||
2640 | // 2nd step: | |
2641 | // copies the bytecode of the method into a new bytevector, updates the | |
2642 | // offsets, and inserts (or removes) bytes as requested. | |
2643 | ||
2644 | ByteVector newCode = new ByteVector(code.length); | |
2645 | ||
2646 | u = 0; | |
2647 | while(u < code.length) | |
2648 | { | |
2649 | int opcode = b[u] & 0xFF; | |
2650 | switch(ClassWriter.TYPE[opcode]) | |
2651 | { | |
2652 | case ClassWriter.NOARG_INSN: | |
2653 | case ClassWriter.IMPLVAR_INSN: | |
2654 | newCode.putByte(opcode); | |
2655 | u += 1; | |
2656 | break; | |
2657 | case ClassWriter.LABEL_INSN: | |
2658 | if(opcode > 201) | |
2659 | { | |
2660 | // changes temporary opcodes 202 to 217 (inclusive), 218 | |
2661 | // and 219 to IFEQ ... JSR (inclusive), IFNULL and | |
2662 | // IFNONNULL | |
2663 | opcode = opcode < 218 ? opcode - 49 : opcode - 20; | |
2664 | label = u + readUnsignedShort(b, u + 1); | |
2665 | } | |
2666 | else | |
2667 | { | |
2668 | label = u + readShort(b, u + 1); | |
2669 | } | |
2670 | newOffset = getNewOffset(allIndexes, allSizes, u, label); | |
2671 | if(resize[u]) | |
2672 | { | |
2673 | // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx | |
2674 | // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is | |
2675 | // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) | |
2676 | // and where <l'> designates the instruction just after | |
2677 | // the GOTO_W. | |
2678 | if(opcode == Opcodes.GOTO) | |
2679 | { | |
2680 | newCode.putByte(200); // GOTO_W | |
2681 | } | |
2682 | else if(opcode == Opcodes.JSR) | |
2683 | { | |
2684 | newCode.putByte(201); // JSR_W | |
2685 | } | |
2686 | else | |
2687 | { | |
2688 | newCode.putByte(opcode <= 166 | |
2689 | ? ((opcode + 1) ^ 1) - 1 | |
2690 | : opcode ^ 1); | |
2691 | newCode.putShort(8); // jump offset | |
2692 | newCode.putByte(200); // GOTO_W | |
2693 | // newOffset now computed from start of GOTO_W | |
2694 | newOffset -= 3; | |
2695 | } | |
2696 | newCode.putInt(newOffset); | |
2697 | } | |
2698 | else | |
2699 | { | |
2700 | newCode.putByte(opcode); | |
2701 | newCode.putShort(newOffset); | |
2702 | } | |
2703 | u += 3; | |
2704 | break; | |
2705 | case ClassWriter.LABELW_INSN: | |
2706 | label = u + readInt(b, u + 1); | |
2707 | newOffset = getNewOffset(allIndexes, allSizes, u, label); | |
2708 | newCode.putByte(opcode); | |
2709 | newCode.putInt(newOffset); | |
2710 | u += 5; | |
2711 | break; | |
2712 | case ClassWriter.TABL_INSN: | |
2713 | // skips 0 to 3 padding bytes | |
2714 | v = u; | |
2715 | u = u + 4 - (v & 3); | |
2716 | // reads and copies instruction | |
2717 | newCode.putByte(Opcodes.TABLESWITCH); | |
2718 | newCode.length += (4 - newCode.length % 4) % 4; | |
2719 | label = v + readInt(b, u); | |
2720 | u += 4; | |
2721 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2722 | newCode.putInt(newOffset); | |
2723 | j = readInt(b, u); | |
2724 | u += 4; | |
2725 | newCode.putInt(j); | |
2726 | j = readInt(b, u) - j + 1; | |
2727 | u += 4; | |
2728 | newCode.putInt(readInt(b, u - 4)); | |
2729 | for(; j > 0; --j) | |
2730 | { | |
2731 | label = v + readInt(b, u); | |
2732 | u += 4; | |
2733 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2734 | newCode.putInt(newOffset); | |
2735 | } | |
2736 | break; | |
2737 | case ClassWriter.LOOK_INSN: | |
2738 | // skips 0 to 3 padding bytes | |
2739 | v = u; | |
2740 | u = u + 4 - (v & 3); | |
2741 | // reads and copies instruction | |
2742 | newCode.putByte(Opcodes.LOOKUPSWITCH); | |
2743 | newCode.length += (4 - newCode.length % 4) % 4; | |
2744 | label = v + readInt(b, u); | |
2745 | u += 4; | |
2746 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2747 | newCode.putInt(newOffset); | |
2748 | j = readInt(b, u); | |
2749 | u += 4; | |
2750 | newCode.putInt(j); | |
2751 | for(; j > 0; --j) | |
2752 | { | |
2753 | newCode.putInt(readInt(b, u)); | |
2754 | u += 4; | |
2755 | label = v + readInt(b, u); | |
2756 | u += 4; | |
2757 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2758 | newCode.putInt(newOffset); | |
2759 | } | |
2760 | break; | |
2761 | case ClassWriter.WIDE_INSN: | |
2762 | opcode = b[u + 1] & 0xFF; | |
2763 | if(opcode == Opcodes.IINC) | |
2764 | { | |
2765 | newCode.putByteArray(b, u, 6); | |
2766 | u += 6; | |
2767 | } | |
2768 | else | |
2769 | { | |
2770 | newCode.putByteArray(b, u, 4); | |
2771 | u += 4; | |
2772 | } | |
2773 | break; | |
2774 | case ClassWriter.VAR_INSN: | |
2775 | case ClassWriter.SBYTE_INSN: | |
2776 | case ClassWriter.LDC_INSN: | |
2777 | newCode.putByteArray(b, u, 2); | |
2778 | u += 2; | |
2779 | break; | |
2780 | case ClassWriter.SHORT_INSN: | |
2781 | case ClassWriter.LDCW_INSN: | |
2782 | case ClassWriter.FIELDORMETH_INSN: | |
2783 | case ClassWriter.TYPE_INSN: | |
2784 | case ClassWriter.IINC_INSN: | |
2785 | newCode.putByteArray(b, u, 3); | |
2786 | u += 3; | |
2787 | break; | |
2788 | case ClassWriter.ITFMETH_INSN: | |
2789 | newCode.putByteArray(b, u, 5); | |
2790 | u += 5; | |
2791 | break; | |
2792 | // case MANA_INSN: | |
2793 | default: | |
2794 | newCode.putByteArray(b, u, 4); | |
2795 | u += 4; | |
2796 | break; | |
2797 | } | |
2798 | } | |
2799 | ||
2800 | // recomputes the stack map frames | |
2801 | if(frameCount > 0) | |
2802 | { | |
2803 | if(compute == FRAMES) | |
2804 | { | |
2805 | frameCount = 0; | |
2806 | stackMap = null; | |
2807 | previousFrame = null; | |
2808 | frame = null; | |
2809 | Frame f = new Frame(); | |
2810 | f.owner = labels; | |
2811 | Type[] args = Type.getArgumentTypes(descriptor); | |
2812 | f.initInputFrame(cw, access, args, maxLocals); | |
2813 | visitFrame(f); | |
2814 | Label l = labels; | |
2815 | while(l != null) | |
2816 | { | |
2817 | /* | |
2818 | * here we need the original label position. getNewOffset | |
2819 | * must therefore never have been called for this label. | |
2820 | */ | |
2821 | u = l.position - 3; | |
2822 | if((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) | |
2823 | { | |
2824 | getNewOffset(allIndexes, allSizes, l); | |
2825 | // TODO update offsets in UNINITIALIZED values | |
2826 | visitFrame(l.frame); | |
2827 | } | |
2828 | l = l.successor; | |
2829 | } | |
2830 | } | |
2831 | else | |
2832 | { | |
2833 | /* | |
2834 | * Resizing an existing stack map frame table is really hard. | |
2835 | * Not only the table must be parsed to update the offets, but | |
2836 | * new frames may be needed for jump instructions that were | |
2837 | * inserted by this method. And updating the offsets or | |
2838 | * inserting frames can change the format of the following | |
2839 | * frames, in case of packed frames. In practice the whole table | |
2840 | * must be recomputed. For this the frames are marked as | |
2841 | * potentially invalid. This will cause the whole class to be | |
2842 | * reread and rewritten with the COMPUTE_FRAMES option (see the | |
2843 | * ClassWriter.toByteArray method). This is not very efficient | |
2844 | * but is much easier and requires much less code than any other | |
2845 | * method I can think of. | |
2846 | */ | |
2847 | cw.invalidFrames = true; | |
2848 | } | |
2849 | } | |
2850 | // updates the exception handler block labels | |
2851 | Handler h = firstHandler; | |
2852 | while(h != null) | |
2853 | { | |
2854 | getNewOffset(allIndexes, allSizes, h.start); | |
2855 | getNewOffset(allIndexes, allSizes, h.end); | |
2856 | getNewOffset(allIndexes, allSizes, h.handler); | |
2857 | h = h.next; | |
2858 | } | |
2859 | // updates the instructions addresses in the | |
2860 | // local var and line number tables | |
2861 | for(i = 0; i < 2; ++i) | |
2862 | { | |
2863 | ByteVector bv = i == 0 ? localVar : localVarType; | |
2864 | if(bv != null) | |
2865 | { | |
2866 | b = bv.data; | |
2867 | u = 0; | |
2868 | while(u < bv.length) | |
2869 | { | |
2870 | label = readUnsignedShort(b, u); | |
2871 | newOffset = getNewOffset(allIndexes, allSizes, 0, label); | |
2872 | writeShort(b, u, newOffset); | |
2873 | label += readUnsignedShort(b, u + 2); | |
2874 | newOffset = getNewOffset(allIndexes, allSizes, 0, label) | |
2875 | - newOffset; | |
2876 | writeShort(b, u + 2, newOffset); | |
2877 | u += 10; | |
2878 | } | |
2879 | } | |
2880 | } | |
2881 | if(lineNumber != null) | |
2882 | { | |
2883 | b = lineNumber.data; | |
2884 | u = 0; | |
2885 | while(u < lineNumber.length) | |
2886 | { | |
2887 | writeShort(b, u, getNewOffset(allIndexes, | |
2888 | allSizes, | |
2889 | 0, | |
2890 | readUnsignedShort(b, u))); | |
2891 | u += 4; | |
2892 | } | |
2893 | } | |
2894 | // updates the labels of the other attributes | |
2895 | Attribute attr = cattrs; | |
2896 | while(attr != null) | |
2897 | { | |
2898 | Label[] labels = attr.getLabels(); | |
2899 | if(labels != null) | |
2900 | { | |
2901 | for(i = labels.length - 1; i >= 0; --i) | |
2902 | { | |
2903 | getNewOffset(allIndexes, allSizes, labels[i]); | |
2904 | } | |
2905 | } | |
2906 | attr = attr.next; | |
2907 | } | |
2908 | ||
2909 | // replaces old bytecodes with new ones | |
2910 | code = newCode; | |
2911 | } | |
2912 | ||
2913 | /** | |
2914 | * Reads an unsigned short value in the given byte array. | |
2915 | * | |
2916 | * @param b a byte array. | |
2917 | * @param index the start index of the value to be read. | |
2918 | * @return the read value. | |
2919 | */ | |
2920 | static int readUnsignedShort(final byte[] b, final int index){ | |
2921 | return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); | |
2922 | } | |
2923 | ||
2924 | /** | |
2925 | * Reads a signed short value in the given byte array. | |
2926 | * | |
2927 | * @param b a byte array. | |
2928 | * @param index the start index of the value to be read. | |
2929 | * @return the read value. | |
2930 | */ | |
2931 | static short readShort(final byte[] b, final int index){ | |
2932 | return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); | |
2933 | } | |
2934 | ||
2935 | /** | |
2936 | * Reads a signed int value in the given byte array. | |
2937 | * | |
2938 | * @param b a byte array. | |
2939 | * @param index the start index of the value to be read. | |
2940 | * @return the read value. | |
2941 | */ | |
2942 | static int readInt(final byte[] b, final int index){ | |
2943 | return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) | |
2944 | | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); | |
2945 | } | |
2946 | ||
2947 | /** | |
2948 | * Writes a short value in the given byte array. | |
2949 | * | |
2950 | * @param b a byte array. | |
2951 | * @param index where the first byte of the short value must be written. | |
2952 | * @param s the value to be written in the given byte array. | |
2953 | */ | |
2954 | static void writeShort(final byte[] b, final int index, final int s){ | |
2955 | b[index] = (byte) (s >>> 8); | |
2956 | b[index + 1] = (byte) s; | |
2957 | } | |
2958 | ||
2959 | /** | |
2960 | * Computes the future value of a bytecode offset. <p> Note: it is possible | |
2961 | * to have several entries for the same instruction in the <tt>indexes</tt> | |
2962 | * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b') | |
2963 | * are equivalent to a single entry (index=a,size=b+b'). | |
2964 | * | |
2965 | * @param indexes current positions of the instructions to be resized. Each | |
2966 | * instruction must be designated by the index of its <i>last</i> | |
2967 | * byte, plus one (or, in other words, by the index of the <i>first</i> | |
2968 | * byte of the <i>next</i> instruction). | |
2969 | * @param sizes the number of bytes to be <i>added</i> to the above | |
2970 | * instructions. More precisely, for each i < <tt>len</tt>, | |
2971 | * <tt>sizes</tt>[i] bytes will be added at the end of the | |
2972 | * instruction designated by <tt>indexes</tt>[i] or, if | |
2973 | * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| | |
2974 | * bytes of the instruction will be removed (the instruction size | |
2975 | * <i>must not</i> become negative or null). | |
2976 | * @param begin index of the first byte of the source instruction. | |
2977 | * @param end index of the first byte of the target instruction. | |
2978 | * @return the future value of the given bytecode offset. | |
2979 | */ | |
2980 | static int getNewOffset( | |
2981 | final int[] indexes, | |
2982 | final int[] sizes, | |
2983 | final int begin, | |
2984 | final int end){ | |
2985 | int offset = end - begin; | |
2986 | for(int i = 0; i < indexes.length; ++i) | |
2987 | { | |
2988 | if(begin < indexes[i] && indexes[i] <= end) | |
2989 | { | |
2990 | // forward jump | |
2991 | offset += sizes[i]; | |
2992 | } | |
2993 | else if(end < indexes[i] && indexes[i] <= begin) | |
2994 | { | |
2995 | // backward jump | |
2996 | offset -= sizes[i]; | |
2997 | } | |
2998 | } | |
2999 | return offset; | |
3000 | } | |
3001 | ||
3002 | /** | |
3003 | * Updates the offset of the given label. | |
3004 | * | |
3005 | * @param indexes current positions of the instructions to be resized. Each | |
3006 | * instruction must be designated by the index of its <i>last</i> | |
3007 | * byte, plus one (or, in other words, by the index of the <i>first</i> | |
3008 | * byte of the <i>next</i> instruction). | |
3009 | * @param sizes the number of bytes to be <i>added</i> to the above | |
3010 | * instructions. More precisely, for each i < <tt>len</tt>, | |
3011 | * <tt>sizes</tt>[i] bytes will be added at the end of the | |
3012 | * instruction designated by <tt>indexes</tt>[i] or, if | |
3013 | * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>| | |
3014 | * bytes of the instruction will be removed (the instruction size | |
3015 | * <i>must not</i> become negative or null). | |
3016 | * @param label the label whose offset must be updated. | |
3017 | */ | |
3018 | static void getNewOffset( | |
3019 | final int[] indexes, | |
3020 | final int[] sizes, | |
3021 | final Label label){ | |
3022 | if((label.status & Label.RESIZED) == 0) | |
3023 | { | |
3024 | label.position = getNewOffset(indexes, sizes, 0, label.position); | |
3025 | label.status |= Label.RESIZED; | |
3026 | } | |
3027 | } | |
3028 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | /** | |
32 | * Defines the JVM opcodes, access flags and array type codes. This interface | |
33 | * does not define all the JVM opcodes because some opcodes are automatically | |
34 | * handled. For example, the xLOAD and xSTORE opcodes are automatically replaced | |
35 | * by xLOAD_n and xSTORE_n opcodes when possible. The xLOAD_n and xSTORE_n | |
36 | * opcodes are therefore not defined in this interface. Likewise for LDC, | |
37 | * automatically replaced by LDC_W or LDC2_W when necessary, WIDE, GOTO_W and | |
38 | * JSR_W. | |
39 | * | |
40 | * @author Eric Bruneton | |
41 | * @author Eugene Kuleshov | |
42 | */ | |
43 | public interface Opcodes{ | |
44 | ||
45 | // versions | |
46 | ||
47 | int V1_1 = 3 << 16 | 45; | |
48 | int V1_2 = 0 << 16 | 46; | |
49 | int V1_3 = 0 << 16 | 47; | |
50 | int V1_4 = 0 << 16 | 48; | |
51 | int V1_5 = 0 << 16 | 49; | |
52 | int V1_6 = 0 << 16 | 50; | |
53 | ||
54 | // access flags | |
55 | ||
56 | int ACC_PUBLIC = 0x0001; // class, field, method | |
57 | int ACC_PRIVATE = 0x0002; // class, field, method | |
58 | int ACC_PROTECTED = 0x0004; // class, field, method | |
59 | int ACC_STATIC = 0x0008; // field, method | |
60 | int ACC_FINAL = 0x0010; // class, field, method | |
61 | int ACC_SUPER = 0x0020; // class | |
62 | int ACC_SYNCHRONIZED = 0x0020; // method | |
63 | int ACC_VOLATILE = 0x0040; // field | |
64 | int ACC_BRIDGE = 0x0040; // method | |
65 | int ACC_VARARGS = 0x0080; // method | |
66 | int ACC_TRANSIENT = 0x0080; // field | |
67 | int ACC_NATIVE = 0x0100; // method | |
68 | int ACC_INTERFACE = 0x0200; // class | |
69 | int ACC_ABSTRACT = 0x0400; // class, method | |
70 | int ACC_STRICT = 0x0800; // method | |
71 | int ACC_SYNTHETIC = 0x1000; // class, field, method | |
72 | int ACC_ANNOTATION = 0x2000; // class | |
73 | int ACC_ENUM = 0x4000; // class(?) field inner | |
74 | ||
75 | // ASM specific pseudo access flags | |
76 | ||
77 | int ACC_DEPRECATED = 131072; // class, field, method | |
78 | ||
79 | // types for NEWARRAY | |
80 | ||
81 | int T_BOOLEAN = 4; | |
82 | int T_CHAR = 5; | |
83 | int T_FLOAT = 6; | |
84 | int T_DOUBLE = 7; | |
85 | int T_BYTE = 8; | |
86 | int T_SHORT = 9; | |
87 | int T_INT = 10; | |
88 | int T_LONG = 11; | |
89 | ||
90 | // stack map frame types | |
91 | ||
92 | /** | |
93 | * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. | |
94 | */ | |
95 | int F_NEW = -1; | |
96 | ||
97 | /** | |
98 | * Represents a compressed frame with complete frame data. | |
99 | */ | |
100 | int F_FULL = 0; | |
101 | ||
102 | /** | |
103 | * Represents a compressed frame where locals are the same as the locals in | |
104 | * the previous frame, except that additional 1-3 locals are defined, and | |
105 | * with an empty stack. | |
106 | */ | |
107 | int F_APPEND = 1; | |
108 | ||
109 | /** | |
110 | * Represents a compressed frame where locals are the same as the locals in | |
111 | * the previous frame, except that the last 1-3 locals are absent and with | |
112 | * an empty stack. | |
113 | */ | |
114 | int F_CHOP = 2; | |
115 | ||
116 | /** | |
117 | * Represents a compressed frame with exactly the same locals as the | |
118 | * previous frame and with an empty stack. | |
119 | */ | |
120 | int F_SAME = 3; | |
121 | ||
122 | /** | |
123 | * Represents a compressed frame with exactly the same locals as the | |
124 | * previous frame and with a single value on the stack. | |
125 | */ | |
126 | int F_SAME1 = 4; | |
127 | ||
128 | Integer TOP = new Integer(0); | |
129 | Integer INTEGER = new Integer(1); | |
130 | Integer FLOAT = new Integer(2); | |
131 | Integer DOUBLE = new Integer(3); | |
132 | Integer LONG = new Integer(4); | |
133 | Integer NULL = new Integer(5); | |
134 | Integer UNINITIALIZED_THIS = new Integer(6); | |
135 | ||
136 | // opcodes // visit method (- = idem) | |
137 | ||
138 | int NOP = 0; // visitInsn | |
139 | int ACONST_NULL = 1; // - | |
140 | int ICONST_M1 = 2; // - | |
141 | int ICONST_0 = 3; // - | |
142 | int ICONST_1 = 4; // - | |
143 | int ICONST_2 = 5; // - | |
144 | int ICONST_3 = 6; // - | |
145 | int ICONST_4 = 7; // - | |
146 | int ICONST_5 = 8; // - | |
147 | int LCONST_0 = 9; // - | |
148 | int LCONST_1 = 10; // - | |
149 | int FCONST_0 = 11; // - | |
150 | int FCONST_1 = 12; // - | |
151 | int FCONST_2 = 13; // - | |
152 | int DCONST_0 = 14; // - | |
153 | int DCONST_1 = 15; // - | |
154 | int BIPUSH = 16; // visitIntInsn | |
155 | int SIPUSH = 17; // - | |
156 | int LDC = 18; // visitLdcInsn | |
157 | // int LDC_W = 19; // - | |
158 | // int LDC2_W = 20; // - | |
159 | int ILOAD = 21; // visitVarInsn | |
160 | int LLOAD = 22; // - | |
161 | int FLOAD = 23; // - | |
162 | int DLOAD = 24; // - | |
163 | int ALOAD = 25; // - | |
164 | // int ILOAD_0 = 26; // - | |
165 | // int ILOAD_1 = 27; // - | |
166 | // int ILOAD_2 = 28; // - | |
167 | // int ILOAD_3 = 29; // - | |
168 | // int LLOAD_0 = 30; // - | |
169 | // int LLOAD_1 = 31; // - | |
170 | // int LLOAD_2 = 32; // - | |
171 | // int LLOAD_3 = 33; // - | |
172 | // int FLOAD_0 = 34; // - | |
173 | // int FLOAD_1 = 35; // - | |
174 | // int FLOAD_2 = 36; // - | |
175 | // int FLOAD_3 = 37; // - | |
176 | // int DLOAD_0 = 38; // - | |
177 | // int DLOAD_1 = 39; // - | |
178 | // int DLOAD_2 = 40; // - | |
179 | // int DLOAD_3 = 41; // - | |
180 | // int ALOAD_0 = 42; // - | |
181 | // int ALOAD_1 = 43; // - | |
182 | // int ALOAD_2 = 44; // - | |
183 | // int ALOAD_3 = 45; // - | |
184 | int IALOAD = 46; // visitInsn | |
185 | int LALOAD = 47; // - | |
186 | int FALOAD = 48; // - | |
187 | int DALOAD = 49; // - | |
188 | int AALOAD = 50; // - | |
189 | int BALOAD = 51; // - | |
190 | int CALOAD = 52; // - | |
191 | int SALOAD = 53; // - | |
192 | int ISTORE = 54; // visitVarInsn | |
193 | int LSTORE = 55; // - | |
194 | int FSTORE = 56; // - | |
195 | int DSTORE = 57; // - | |
196 | int ASTORE = 58; // - | |
197 | // int ISTORE_0 = 59; // - | |
198 | // int ISTORE_1 = 60; // - | |
199 | // int ISTORE_2 = 61; // - | |
200 | // int ISTORE_3 = 62; // - | |
201 | // int LSTORE_0 = 63; // - | |
202 | // int LSTORE_1 = 64; // - | |
203 | // int LSTORE_2 = 65; // - | |
204 | // int LSTORE_3 = 66; // - | |
205 | // int FSTORE_0 = 67; // - | |
206 | // int FSTORE_1 = 68; // - | |
207 | // int FSTORE_2 = 69; // - | |
208 | // int FSTORE_3 = 70; // - | |
209 | // int DSTORE_0 = 71; // - | |
210 | // int DSTORE_1 = 72; // - | |
211 | // int DSTORE_2 = 73; // - | |
212 | // int DSTORE_3 = 74; // - | |
213 | // int ASTORE_0 = 75; // - | |
214 | // int ASTORE_1 = 76; // - | |
215 | // int ASTORE_2 = 77; // - | |
216 | // int ASTORE_3 = 78; // - | |
217 | int IASTORE = 79; // visitInsn | |
218 | int LASTORE = 80; // - | |
219 | int FASTORE = 81; // - | |
220 | int DASTORE = 82; // - | |
221 | int AASTORE = 83; // - | |
222 | int BASTORE = 84; // - | |
223 | int CASTORE = 85; // - | |
224 | int SASTORE = 86; // - | |
225 | int POP = 87; // - | |
226 | int POP2 = 88; // - | |
227 | int DUP = 89; // - | |
228 | int DUP_X1 = 90; // - | |
229 | int DUP_X2 = 91; // - | |
230 | int DUP2 = 92; // - | |
231 | int DUP2_X1 = 93; // - | |
232 | int DUP2_X2 = 94; // - | |
233 | int SWAP = 95; // - | |
234 | int IADD = 96; // - | |
235 | int LADD = 97; // - | |
236 | int FADD = 98; // - | |
237 | int DADD = 99; // - | |
238 | int ISUB = 100; // - | |
239 | int LSUB = 101; // - | |
240 | int FSUB = 102; // - | |
241 | int DSUB = 103; // - | |
242 | int IMUL = 104; // - | |
243 | int LMUL = 105; // - | |
244 | int FMUL = 106; // - | |
245 | int DMUL = 107; // - | |
246 | int IDIV = 108; // - | |
247 | int LDIV = 109; // - | |
248 | int FDIV = 110; // - | |
249 | int DDIV = 111; // - | |
250 | int IREM = 112; // - | |
251 | int LREM = 113; // - | |
252 | int FREM = 114; // - | |
253 | int DREM = 115; // - | |
254 | int INEG = 116; // - | |
255 | int LNEG = 117; // - | |
256 | int FNEG = 118; // - | |
257 | int DNEG = 119; // - | |
258 | int ISHL = 120; // - | |
259 | int LSHL = 121; // - | |
260 | int ISHR = 122; // - | |
261 | int LSHR = 123; // - | |
262 | int IUSHR = 124; // - | |
263 | int LUSHR = 125; // - | |
264 | int IAND = 126; // - | |
265 | int LAND = 127; // - | |
266 | int IOR = 128; // - | |
267 | int LOR = 129; // - | |
268 | int IXOR = 130; // - | |
269 | int LXOR = 131; // - | |
270 | int IINC = 132; // visitIincInsn | |
271 | int I2L = 133; // visitInsn | |
272 | int I2F = 134; // - | |
273 | int I2D = 135; // - | |
274 | int L2I = 136; // - | |
275 | int L2F = 137; // - | |
276 | int L2D = 138; // - | |
277 | int F2I = 139; // - | |
278 | int F2L = 140; // - | |
279 | int F2D = 141; // - | |
280 | int D2I = 142; // - | |
281 | int D2L = 143; // - | |
282 | int D2F = 144; // - | |
283 | int I2B = 145; // - | |
284 | int I2C = 146; // - | |
285 | int I2S = 147; // - | |
286 | int LCMP = 148; // - | |
287 | int FCMPL = 149; // - | |
288 | int FCMPG = 150; // - | |
289 | int DCMPL = 151; // - | |
290 | int DCMPG = 152; // - | |
291 | int IFEQ = 153; // visitJumpInsn | |
292 | int IFNE = 154; // - | |
293 | int IFLT = 155; // - | |
294 | int IFGE = 156; // - | |
295 | int IFGT = 157; // - | |
296 | int IFLE = 158; // - | |
297 | int IF_ICMPEQ = 159; // - | |
298 | int IF_ICMPNE = 160; // - | |
299 | int IF_ICMPLT = 161; // - | |
300 | int IF_ICMPGE = 162; // - | |
301 | int IF_ICMPGT = 163; // - | |
302 | int IF_ICMPLE = 164; // - | |
303 | int IF_ACMPEQ = 165; // - | |
304 | int IF_ACMPNE = 166; // - | |
305 | int GOTO = 167; // - | |
306 | int JSR = 168; // - | |
307 | int RET = 169; // visitVarInsn | |
308 | int TABLESWITCH = 170; // visiTableSwitchInsn | |
309 | int LOOKUPSWITCH = 171; // visitLookupSwitch | |
310 | int IRETURN = 172; // visitInsn | |
311 | int LRETURN = 173; // - | |
312 | int FRETURN = 174; // - | |
313 | int DRETURN = 175; // - | |
314 | int ARETURN = 176; // - | |
315 | int RETURN = 177; // - | |
316 | int GETSTATIC = 178; // visitFieldInsn | |
317 | int PUTSTATIC = 179; // - | |
318 | int GETFIELD = 180; // - | |
319 | int PUTFIELD = 181; // - | |
320 | int INVOKEVIRTUAL = 182; // visitMethodInsn | |
321 | int INVOKESPECIAL = 183; // - | |
322 | int INVOKESTATIC = 184; // - | |
323 | int INVOKEINTERFACE = 185; // - | |
324 | // int UNUSED = 186; // NOT VISITED | |
325 | int NEW = 187; // visitTypeInsn | |
326 | int NEWARRAY = 188; // visitIntInsn | |
327 | int ANEWARRAY = 189; // visitTypeInsn | |
328 | int ARRAYLENGTH = 190; // visitInsn | |
329 | int ATHROW = 191; // - | |
330 | int CHECKCAST = 192; // visitTypeInsn | |
331 | int INSTANCEOF = 193; // - | |
332 | int MONITORENTER = 194; // visitInsn | |
333 | int MONITOREXIT = 195; // - | |
334 | // int WIDE = 196; // NOT VISITED | |
335 | int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn | |
336 | int IFNULL = 198; // visitJumpInsn | |
337 | int IFNONNULL = 199; // - | |
338 | // int GOTO_W = 200; // - | |
339 | // int JSR_W = 201; // - | |
340 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm; | |
30 | ||
31 | import java.lang.reflect.Constructor; | |
32 | import java.lang.reflect.Method; | |
33 | ||
34 | /** | |
35 | * A Java type. This class can be used to make it easier to manipulate type and | |
36 | * method descriptors. | |
37 | * | |
38 | * @author Eric Bruneton | |
39 | * @author Chris Nokleberg | |
40 | */ | |
41 | public class Type{ | |
42 | ||
43 | /** | |
44 | * The sort of the <tt>void</tt> type. See {@link #getSort getSort}. | |
45 | */ | |
46 | public final static int VOID = 0; | |
47 | ||
48 | /** | |
49 | * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}. | |
50 | */ | |
51 | public final static int BOOLEAN = 1; | |
52 | ||
53 | /** | |
54 | * The sort of the <tt>char</tt> type. See {@link #getSort getSort}. | |
55 | */ | |
56 | public final static int CHAR = 2; | |
57 | ||
58 | /** | |
59 | * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}. | |
60 | */ | |
61 | public final static int BYTE = 3; | |
62 | ||
63 | /** | |
64 | * The sort of the <tt>short</tt> type. See {@link #getSort getSort}. | |
65 | */ | |
66 | public final static int SHORT = 4; | |
67 | ||
68 | /** | |
69 | * The sort of the <tt>int</tt> type. See {@link #getSort getSort}. | |
70 | */ | |
71 | public final static int INT = 5; | |
72 | ||
73 | /** | |
74 | * The sort of the <tt>float</tt> type. See {@link #getSort getSort}. | |
75 | */ | |
76 | public final static int FLOAT = 6; | |
77 | ||
78 | /** | |
79 | * The sort of the <tt>long</tt> type. See {@link #getSort getSort}. | |
80 | */ | |
81 | public final static int LONG = 7; | |
82 | ||
83 | /** | |
84 | * The sort of the <tt>double</tt> type. See {@link #getSort getSort}. | |
85 | */ | |
86 | public final static int DOUBLE = 8; | |
87 | ||
88 | /** | |
89 | * The sort of array reference types. See {@link #getSort getSort}. | |
90 | */ | |
91 | public final static int ARRAY = 9; | |
92 | ||
93 | /** | |
94 | * The sort of object reference type. See {@link #getSort getSort}. | |
95 | */ | |
96 | public final static int OBJECT = 10; | |
97 | ||
98 | /** | |
99 | * The <tt>void</tt> type. | |
100 | */ | |
101 | public final static Type VOID_TYPE = new Type(VOID); | |
102 | ||
103 | /** | |
104 | * The <tt>boolean</tt> type. | |
105 | */ | |
106 | public final static Type BOOLEAN_TYPE = new Type(BOOLEAN); | |
107 | ||
108 | /** | |
109 | * The <tt>char</tt> type. | |
110 | */ | |
111 | public final static Type CHAR_TYPE = new Type(CHAR); | |
112 | ||
113 | /** | |
114 | * The <tt>byte</tt> type. | |
115 | */ | |
116 | public final static Type BYTE_TYPE = new Type(BYTE); | |
117 | ||
118 | /** | |
119 | * The <tt>short</tt> type. | |
120 | */ | |
121 | public final static Type SHORT_TYPE = new Type(SHORT); | |
122 | ||
123 | /** | |
124 | * The <tt>int</tt> type. | |
125 | */ | |
126 | public final static Type INT_TYPE = new Type(INT); | |
127 | ||
128 | /** | |
129 | * The <tt>float</tt> type. | |
130 | */ | |
131 | public final static Type FLOAT_TYPE = new Type(FLOAT); | |
132 | ||
133 | /** | |
134 | * The <tt>long</tt> type. | |
135 | */ | |
136 | public final static Type LONG_TYPE = new Type(LONG); | |
137 | ||
138 | /** | |
139 | * The <tt>double</tt> type. | |
140 | */ | |
141 | public final static Type DOUBLE_TYPE = new Type(DOUBLE); | |
142 | ||
143 | // ------------------------------------------------------------------------ | |
144 | // Fields | |
145 | // ------------------------------------------------------------------------ | |
146 | ||
147 | /** | |
148 | * The sort of this Java type. | |
149 | */ | |
150 | private final int sort; | |
151 | ||
152 | /** | |
153 | * A buffer containing the descriptor of this Java type. This field is only | |
154 | * used for reference types. | |
155 | */ | |
156 | private char[] buf; | |
157 | ||
158 | /** | |
159 | * The offset of the descriptor of this Java type in {@link #buf buf}. This | |
160 | * field is only used for reference types. | |
161 | */ | |
162 | private int off; | |
163 | ||
164 | /** | |
165 | * The length of the descriptor of this Java type. | |
166 | */ | |
167 | private int len; | |
168 | ||
169 | // ------------------------------------------------------------------------ | |
170 | // Constructors | |
171 | // ------------------------------------------------------------------------ | |
172 | ||
173 | /** | |
174 | * Constructs a primitive type. | |
175 | * | |
176 | * @param sort the sort of the primitive type to be constructed. | |
177 | */ | |
178 | private Type(final int sort){ | |
179 | this.sort = sort; | |
180 | this.len = 1; | |
181 | } | |
182 | ||
183 | /** | |
184 | * Constructs a reference type. | |
185 | * | |
186 | * @param sort the sort of the reference type to be constructed. | |
187 | * @param buf a buffer containing the descriptor of the previous type. | |
188 | * @param off the offset of this descriptor in the previous buffer. | |
189 | * @param len the length of this descriptor. | |
190 | */ | |
191 | private Type(final int sort, final char[] buf, final int off, final int len){ | |
192 | this.sort = sort; | |
193 | this.buf = buf; | |
194 | this.off = off; | |
195 | this.len = len; | |
196 | } | |
197 | ||
198 | /** | |
199 | * Returns the Java type corresponding to the given type descriptor. | |
200 | * | |
201 | * @param typeDescriptor a type descriptor. | |
202 | * @return the Java type corresponding to the given type descriptor. | |
203 | */ | |
204 | public static Type getType(final String typeDescriptor){ | |
205 | return getType(typeDescriptor.toCharArray(), 0); | |
206 | } | |
207 | ||
208 | /** | |
209 | * Returns the Java type corresponding to the given class. | |
210 | * | |
211 | * @param c a class. | |
212 | * @return the Java type corresponding to the given class. | |
213 | */ | |
214 | public static Type getType(final Class c){ | |
215 | if(c.isPrimitive()) | |
216 | { | |
217 | if(c == Integer.TYPE) | |
218 | { | |
219 | return INT_TYPE; | |
220 | } | |
221 | else if(c == Void.TYPE) | |
222 | { | |
223 | return VOID_TYPE; | |
224 | } | |
225 | else if(c == Boolean.TYPE) | |
226 | { | |
227 | return BOOLEAN_TYPE; | |
228 | } | |
229 | else if(c == Byte.TYPE) | |
230 | { | |
231 | return BYTE_TYPE; | |
232 | } | |
233 | else if(c == Character.TYPE) | |
234 | { | |
235 | return CHAR_TYPE; | |
236 | } | |
237 | else if(c == Short.TYPE) | |
238 | { | |
239 | return SHORT_TYPE; | |
240 | } | |
241 | else if(c == Double.TYPE) | |
242 | { | |
243 | return DOUBLE_TYPE; | |
244 | } | |
245 | else if(c == Float.TYPE) | |
246 | { | |
247 | return FLOAT_TYPE; | |
248 | } | |
249 | else /* if (c == Long.TYPE) */ | |
250 | { | |
251 | return LONG_TYPE; | |
252 | } | |
253 | } | |
254 | else | |
255 | { | |
256 | return getType(getDescriptor(c)); | |
257 | } | |
258 | } | |
259 | ||
260 | /** | |
261 | * Returns the {@link Type#OBJECT} type for the given internal class name. | |
262 | * This is a shortcut method for <code>Type.getType("L"+name+";")</code>. | |
263 | * <i>Note that opposed to {@link Type#getType(String)}, this method takes | |
264 | * internal class names and not class descriptor.</i> | |
265 | * | |
266 | * @param name an internal class name. | |
267 | * @return the the {@link Type#OBJECT} type for the given class name. | |
268 | */ | |
269 | public static Type getObjectType(String name){ | |
270 | int l = name.length(); | |
271 | char[] buf = new char[l + 2]; | |
272 | buf[0] = 'L'; | |
273 | buf[l + 1] = ';'; | |
274 | name.getChars(0, l, buf, 1); | |
275 | return new Type(OBJECT, buf, 0, l + 2); | |
276 | } | |
277 | ||
278 | /** | |
279 | * Returns the Java types corresponding to the argument types of the given | |
280 | * method descriptor. | |
281 | * | |
282 | * @param methodDescriptor a method descriptor. | |
283 | * @return the Java types corresponding to the argument types of the given | |
284 | * method descriptor. | |
285 | */ | |
286 | public static Type[] getArgumentTypes(final String methodDescriptor){ | |
287 | char[] buf = methodDescriptor.toCharArray(); | |
288 | int off = 1; | |
289 | int size = 0; | |
290 | while(true) | |
291 | { | |
292 | char car = buf[off++]; | |
293 | if(car == ')') | |
294 | { | |
295 | break; | |
296 | } | |
297 | else if(car == 'L') | |
298 | { | |
299 | while(buf[off++] != ';') | |
300 | { | |
301 | } | |
302 | ++size; | |
303 | } | |
304 | else if(car != '[') | |
305 | { | |
306 | ++size; | |
307 | } | |
308 | } | |
309 | Type[] args = new Type[size]; | |
310 | off = 1; | |
311 | size = 0; | |
312 | while(buf[off] != ')') | |
313 | { | |
314 | args[size] = getType(buf, off); | |
315 | off += args[size].len; | |
316 | size += 1; | |
317 | } | |
318 | return args; | |
319 | } | |
320 | ||
321 | /** | |
322 | * Returns the Java types corresponding to the argument types of the given | |
323 | * method. | |
324 | * | |
325 | * @param method a method. | |
326 | * @return the Java types corresponding to the argument types of the given | |
327 | * method. | |
328 | */ | |
329 | public static Type[] getArgumentTypes(final Method method){ | |
330 | Class[] classes = method.getParameterTypes(); | |
331 | Type[] types = new Type[classes.length]; | |
332 | for(int i = classes.length - 1; i >= 0; --i) | |
333 | { | |
334 | types[i] = getType(classes[i]); | |
335 | } | |
336 | return types; | |
337 | } | |
338 | ||
339 | /** | |
340 | * Returns the Java type corresponding to the return type of the given | |
341 | * method descriptor. | |
342 | * | |
343 | * @param methodDescriptor a method descriptor. | |
344 | * @return the Java type corresponding to the return type of the given | |
345 | * method descriptor. | |
346 | */ | |
347 | public static Type getReturnType(final String methodDescriptor){ | |
348 | char[] buf = methodDescriptor.toCharArray(); | |
349 | return getType(buf, methodDescriptor.indexOf(')') + 1); | |
350 | } | |
351 | ||
352 | /** | |
353 | * Returns the Java type corresponding to the return type of the given | |
354 | * method. | |
355 | * | |
356 | * @param method a method. | |
357 | * @return the Java type corresponding to the return type of the given | |
358 | * method. | |
359 | */ | |
360 | public static Type getReturnType(final Method method){ | |
361 | return getType(method.getReturnType()); | |
362 | } | |
363 | ||
364 | /** | |
365 | * Returns the Java type corresponding to the given type descriptor. | |
366 | * | |
367 | * @param buf a buffer containing a type descriptor. | |
368 | * @param off the offset of this descriptor in the previous buffer. | |
369 | * @return the Java type corresponding to the given type descriptor. | |
370 | */ | |
371 | private static Type getType(final char[] buf, final int off){ | |
372 | int len; | |
373 | switch(buf[off]) | |
374 | { | |
375 | case'V': | |
376 | return VOID_TYPE; | |
377 | case'Z': | |
378 | return BOOLEAN_TYPE; | |
379 | case'C': | |
380 | return CHAR_TYPE; | |
381 | case'B': | |
382 | return BYTE_TYPE; | |
383 | case'S': | |
384 | return SHORT_TYPE; | |
385 | case'I': | |
386 | return INT_TYPE; | |
387 | case'F': | |
388 | return FLOAT_TYPE; | |
389 | case'J': | |
390 | return LONG_TYPE; | |
391 | case'D': | |
392 | return DOUBLE_TYPE; | |
393 | case'[': | |
394 | len = 1; | |
395 | while(buf[off + len] == '[') | |
396 | { | |
397 | ++len; | |
398 | } | |
399 | if(buf[off + len] == 'L') | |
400 | { | |
401 | ++len; | |
402 | while(buf[off + len] != ';') | |
403 | { | |
404 | ++len; | |
405 | } | |
406 | } | |
407 | return new Type(ARRAY, buf, off, len + 1); | |
408 | // case 'L': | |
409 | default: | |
410 | len = 1; | |
411 | while(buf[off + len] != ';') | |
412 | { | |
413 | ++len; | |
414 | } | |
415 | return new Type(OBJECT, buf, off, len + 1); | |
416 | } | |
417 | } | |
418 | ||
419 | // ------------------------------------------------------------------------ | |
420 | // Accessors | |
421 | // ------------------------------------------------------------------------ | |
422 | ||
423 | /** | |
424 | * Returns the sort of this Java type. | |
425 | * | |
426 | * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, | |
427 | * {@link #CHAR CHAR}, {@link #BYTE BYTE}, {@link #SHORT SHORT}, | |
428 | * {@link #INT INT}, {@link #FLOAT FLOAT}, {@link #LONG LONG}, | |
429 | * {@link #DOUBLE DOUBLE}, {@link #ARRAY ARRAY} or | |
430 | * {@link #OBJECT OBJECT}. | |
431 | */ | |
432 | public int getSort(){ | |
433 | return sort; | |
434 | } | |
435 | ||
436 | /** | |
437 | * Returns the number of dimensions of this array type. This method should | |
438 | * only be used for an array type. | |
439 | * | |
440 | * @return the number of dimensions of this array type. | |
441 | */ | |
442 | public int getDimensions(){ | |
443 | int i = 1; | |
444 | while(buf[off + i] == '[') | |
445 | { | |
446 | ++i; | |
447 | } | |
448 | return i; | |
449 | } | |
450 | ||
451 | /** | |
452 | * Returns the type of the elements of this array type. This method should | |
453 | * only be used for an array type. | |
454 | * | |
455 | * @return Returns the type of the elements of this array type. | |
456 | */ | |
457 | public Type getElementType(){ | |
458 | return getType(buf, off + getDimensions()); | |
459 | } | |
460 | ||
461 | /** | |
462 | * Returns the name of the class corresponding to this type. | |
463 | * | |
464 | * @return the fully qualified name of the class corresponding to this type. | |
465 | */ | |
466 | public String getClassName(){ | |
467 | switch(sort) | |
468 | { | |
469 | case VOID: | |
470 | return "void"; | |
471 | case BOOLEAN: | |
472 | return "boolean"; | |
473 | case CHAR: | |
474 | return "char"; | |
475 | case BYTE: | |
476 | return "byte"; | |
477 | case SHORT: | |
478 | return "short"; | |
479 | case INT: | |
480 | return "int"; | |
481 | case FLOAT: | |
482 | return "float"; | |
483 | case LONG: | |
484 | return "long"; | |
485 | case DOUBLE: | |
486 | return "double"; | |
487 | case ARRAY: | |
488 | StringBuffer b = new StringBuffer(getElementType().getClassName()); | |
489 | for(int i = getDimensions(); i > 0; --i) | |
490 | { | |
491 | b.append("[]"); | |
492 | } | |
493 | return b.toString(); | |
494 | // case OBJECT: | |
495 | default: | |
496 | return new String(buf, off + 1, len - 2).replace('/', '.'); | |
497 | } | |
498 | } | |
499 | ||
500 | /** | |
501 | * Returns the internal name of the class corresponding to this object type. | |
502 | * The internal name of a class is its fully qualified name, where '.' are | |
503 | * replaced by '/'. This method should only be used for an object type. | |
504 | * | |
505 | * @return the internal name of the class corresponding to this object type. | |
506 | */ | |
507 | public String getInternalName(){ | |
508 | return new String(buf, off + 1, len - 2); | |
509 | } | |
510 | ||
511 | // ------------------------------------------------------------------------ | |
512 | // Conversion to type descriptors | |
513 | // ------------------------------------------------------------------------ | |
514 | ||
515 | /** | |
516 | * Returns the descriptor corresponding to this Java type. | |
517 | * | |
518 | * @return the descriptor corresponding to this Java type. | |
519 | */ | |
520 | public String getDescriptor(){ | |
521 | StringBuffer buf = new StringBuffer(); | |
522 | getDescriptor(buf); | |
523 | return buf.toString(); | |
524 | } | |
525 | ||
526 | /** | |
527 | * Returns the descriptor corresponding to the given argument and return | |
528 | * types. | |
529 | * | |
530 | * @param returnType the return type of the method. | |
531 | * @param argumentTypes the argument types of the method. | |
532 | * @return the descriptor corresponding to the given argument and return | |
533 | * types. | |
534 | */ | |
535 | public static String getMethodDescriptor( | |
536 | final Type returnType, | |
537 | final Type[] argumentTypes){ | |
538 | StringBuffer buf = new StringBuffer(); | |
539 | buf.append('('); | |
540 | for(int i = 0; i < argumentTypes.length; ++i) | |
541 | { | |
542 | argumentTypes[i].getDescriptor(buf); | |
543 | } | |
544 | buf.append(')'); | |
545 | returnType.getDescriptor(buf); | |
546 | return buf.toString(); | |
547 | } | |
548 | ||
549 | /** | |
550 | * Appends the descriptor corresponding to this Java type to the given | |
551 | * string buffer. | |
552 | * | |
553 | * @param buf the string buffer to which the descriptor must be appended. | |
554 | */ | |
555 | private void getDescriptor(final StringBuffer buf){ | |
556 | switch(sort) | |
557 | { | |
558 | case VOID: | |
559 | buf.append('V'); | |
560 | return; | |
561 | case BOOLEAN: | |
562 | buf.append('Z'); | |
563 | return; | |
564 | case CHAR: | |
565 | buf.append('C'); | |
566 | return; | |
567 | case BYTE: | |
568 | buf.append('B'); | |
569 | return; | |
570 | case SHORT: | |
571 | buf.append('S'); | |
572 | return; | |
573 | case INT: | |
574 | buf.append('I'); | |
575 | return; | |
576 | case FLOAT: | |
577 | buf.append('F'); | |
578 | return; | |
579 | case LONG: | |
580 | buf.append('J'); | |
581 | return; | |
582 | case DOUBLE: | |
583 | buf.append('D'); | |
584 | return; | |
585 | // case ARRAY: | |
586 | // case OBJECT: | |
587 | default: | |
588 | buf.append(this.buf, off, len); | |
589 | } | |
590 | } | |
591 | ||
592 | // ------------------------------------------------------------------------ | |
593 | // Direct conversion from classes to type descriptors, | |
594 | // without intermediate Type objects | |
595 | // ------------------------------------------------------------------------ | |
596 | ||
597 | /** | |
598 | * Returns the internal name of the given class. The internal name of a | |
599 | * class is its fully qualified name, where '.' are replaced by '/'. | |
600 | * | |
601 | * @param c an object class. | |
602 | * @return the internal name of the given class. | |
603 | */ | |
604 | public static String getInternalName(final Class c){ | |
605 | return c.getName().replace('.', '/'); | |
606 | } | |
607 | ||
608 | /** | |
609 | * Returns the descriptor corresponding to the given Java type. | |
610 | * | |
611 | * @param c an object class, a primitive class or an array class. | |
612 | * @return the descriptor corresponding to the given class. | |
613 | */ | |
614 | public static String getDescriptor(final Class c){ | |
615 | StringBuffer buf = new StringBuffer(); | |
616 | getDescriptor(buf, c); | |
617 | return buf.toString(); | |
618 | } | |
619 | ||
620 | /** | |
621 | * Returns the descriptor corresponding to the given constructor. | |
622 | * | |
623 | * @param c a {@link Constructor Constructor} object. | |
624 | * @return the descriptor of the given constructor. | |
625 | */ | |
626 | public static String getConstructorDescriptor(final Constructor c){ | |
627 | Class[] parameters = c.getParameterTypes(); | |
628 | StringBuffer buf = new StringBuffer(); | |
629 | buf.append('('); | |
630 | for(int i = 0; i < parameters.length; ++i) | |
631 | { | |
632 | getDescriptor(buf, parameters[i]); | |
633 | } | |
634 | return buf.append(")V").toString(); | |
635 | } | |
636 | ||
637 | /** | |
638 | * Returns the descriptor corresponding to the given method. | |
639 | * | |
640 | * @param m a {@link Method Method} object. | |
641 | * @return the descriptor of the given method. | |
642 | */ | |
643 | public static String getMethodDescriptor(final Method m){ | |
644 | Class[] parameters = m.getParameterTypes(); | |
645 | StringBuffer buf = new StringBuffer(); | |
646 | buf.append('('); | |
647 | for(int i = 0; i < parameters.length; ++i) | |
648 | { | |
649 | getDescriptor(buf, parameters[i]); | |
650 | } | |
651 | buf.append(')'); | |
652 | getDescriptor(buf, m.getReturnType()); | |
653 | return buf.toString(); | |
654 | } | |
655 | ||
656 | /** | |
657 | * Appends the descriptor of the given class to the given string buffer. | |
658 | * | |
659 | * @param buf the string buffer to which the descriptor must be appended. | |
660 | * @param c the class whose descriptor must be computed. | |
661 | */ | |
662 | private static void getDescriptor(final StringBuffer buf, final Class c){ | |
663 | Class d = c; | |
664 | while(true) | |
665 | { | |
666 | if(d.isPrimitive()) | |
667 | { | |
668 | char car; | |
669 | if(d == Integer.TYPE) | |
670 | { | |
671 | car = 'I'; | |
672 | } | |
673 | else if(d == Void.TYPE) | |
674 | { | |
675 | car = 'V'; | |
676 | } | |
677 | else if(d == Boolean.TYPE) | |
678 | { | |
679 | car = 'Z'; | |
680 | } | |
681 | else if(d == Byte.TYPE) | |
682 | { | |
683 | car = 'B'; | |
684 | } | |
685 | else if(d == Character.TYPE) | |
686 | { | |
687 | car = 'C'; | |
688 | } | |
689 | else if(d == Short.TYPE) | |
690 | { | |
691 | car = 'S'; | |
692 | } | |
693 | else if(d == Double.TYPE) | |
694 | { | |
695 | car = 'D'; | |
696 | } | |
697 | else if(d == Float.TYPE) | |
698 | { | |
699 | car = 'F'; | |
700 | } | |
701 | else /* if (d == Long.TYPE) */ | |
702 | { | |
703 | car = 'J'; | |
704 | } | |
705 | buf.append(car); | |
706 | return; | |
707 | } | |
708 | else if(d.isArray()) | |
709 | { | |
710 | buf.append('['); | |
711 | d = d.getComponentType(); | |
712 | } | |
713 | else | |
714 | { | |
715 | buf.append('L'); | |
716 | String name = d.getName(); | |
717 | int len = name.length(); | |
718 | for(int i = 0; i < len; ++i) | |
719 | { | |
720 | char car = name.charAt(i); | |
721 | buf.append(car == '.' ? '/' : car); | |
722 | } | |
723 | buf.append(';'); | |
724 | return; | |
725 | } | |
726 | } | |
727 | } | |
728 | ||
729 | // ------------------------------------------------------------------------ | |
730 | // Corresponding size and opcodes | |
731 | // ------------------------------------------------------------------------ | |
732 | ||
733 | /** | |
734 | * Returns the size of values of this type. | |
735 | * | |
736 | * @return the size of values of this type, i.e., 2 for <tt>long</tt> and | |
737 | * <tt>double</tt>, and 1 otherwise. | |
738 | */ | |
739 | public int getSize(){ | |
740 | return sort == LONG || sort == DOUBLE ? 2 : 1; | |
741 | } | |
742 | ||
743 | /** | |
744 | * Returns a JVM instruction opcode adapted to this Java type. | |
745 | * | |
746 | * @param opcode a JVM instruction opcode. This opcode must be one of ILOAD, | |
747 | * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, ISHL, | |
748 | * ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. | |
749 | * @return an opcode that is similar to the given opcode, but adapted to | |
750 | * this Java type. For example, if this type is <tt>float</tt> and | |
751 | * <tt>opcode</tt> is IRETURN, this method returns FRETURN. | |
752 | */ | |
753 | public int getOpcode(final int opcode){ | |
754 | if(opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) | |
755 | { | |
756 | switch(sort) | |
757 | { | |
758 | case BOOLEAN: | |
759 | case BYTE: | |
760 | return opcode + 5; | |
761 | case CHAR: | |
762 | return opcode + 6; | |
763 | case SHORT: | |
764 | return opcode + 7; | |
765 | case INT: | |
766 | return opcode; | |
767 | case FLOAT: | |
768 | return opcode + 2; | |
769 | case LONG: | |
770 | return opcode + 1; | |
771 | case DOUBLE: | |
772 | return opcode + 3; | |
773 | // case ARRAY: | |
774 | // case OBJECT: | |
775 | default: | |
776 | return opcode + 4; | |
777 | } | |
778 | } | |
779 | else | |
780 | { | |
781 | switch(sort) | |
782 | { | |
783 | case VOID: | |
784 | return opcode + 5; | |
785 | case BOOLEAN: | |
786 | case CHAR: | |
787 | case BYTE: | |
788 | case SHORT: | |
789 | case INT: | |
790 | return opcode; | |
791 | case FLOAT: | |
792 | return opcode + 2; | |
793 | case LONG: | |
794 | return opcode + 1; | |
795 | case DOUBLE: | |
796 | return opcode + 3; | |
797 | // case ARRAY: | |
798 | // case OBJECT: | |
799 | default: | |
800 | return opcode + 4; | |
801 | } | |
802 | } | |
803 | } | |
804 | ||
805 | // ------------------------------------------------------------------------ | |
806 | // Equals, hashCode and toString | |
807 | // ------------------------------------------------------------------------ | |
808 | ||
809 | /** | |
810 | * Tests if the given object is equal to this type. | |
811 | * | |
812 | * @param o the object to be compared to this type. | |
813 | * @return <tt>true</tt> if the given object is equal to this type. | |
814 | */ | |
815 | public boolean equals(final Object o){ | |
816 | if(this == o) | |
817 | { | |
818 | return true; | |
819 | } | |
820 | if(!(o instanceof Type)) | |
821 | { | |
822 | return false; | |
823 | } | |
824 | Type t = (Type) o; | |
825 | if(sort != t.sort) | |
826 | { | |
827 | return false; | |
828 | } | |
829 | if(sort == Type.OBJECT || sort == Type.ARRAY) | |
830 | { | |
831 | if(len != t.len) | |
832 | { | |
833 | return false; | |
834 | } | |
835 | for(int i = off, j = t.off, end = i + len; i < end; i++, j++) | |
836 | { | |
837 | if(buf[i] != t.buf[j]) | |
838 | { | |
839 | return false; | |
840 | } | |
841 | } | |
842 | } | |
843 | return true; | |
844 | } | |
845 | ||
846 | /** | |
847 | * Returns a hash code value for this type. | |
848 | * | |
849 | * @return a hash code value for this type. | |
850 | */ | |
851 | public int hashCode(){ | |
852 | int hc = 13 * sort; | |
853 | if(sort == Type.OBJECT || sort == Type.ARRAY) | |
854 | { | |
855 | for(int i = off, end = i + len; i < end; i++) | |
856 | { | |
857 | hc = 17 * (hc + buf[i]); | |
858 | } | |
859 | } | |
860 | return hc; | |
861 | } | |
862 | ||
863 | /** | |
864 | * Returns a string representation of this type. | |
865 | * | |
866 | * @return the descriptor of this type. | |
867 | */ | |
868 | public String toString(){ | |
869 | return getDescriptor(); | |
870 | } | |
871 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import java.util.ArrayList; | |
32 | import java.util.HashMap; | |
33 | ||
34 | import clojure.asm.Label; | |
35 | import clojure.asm.MethodVisitor; | |
36 | import clojure.asm.Opcodes; | |
37 | import clojure.asm.Type; | |
38 | ||
39 | /** | |
40 | * A {@link clojure.asm.MethodAdapter} to insert before, after and around | |
41 | * advices in methods and constructors. <p> The behavior for constructors is | |
42 | * like this: <ol> | |
43 | * <p/> | |
44 | * <li>as long as the INVOKESPECIAL for the object initialization has not been | |
45 | * reached, every bytecode instruction is dispatched in the ctor code visitor</li> | |
46 | * <p/> | |
47 | * <li>when this one is reached, it is only added in the ctor code visitor and | |
48 | * a JP invoke is added</li> | |
49 | * <p/> | |
50 | * <li>after that, only the other code visitor receives the instructions</li> | |
51 | * <p/> | |
52 | * </ol> | |
53 | * | |
54 | * @author Eugene Kuleshov | |
55 | * @author Eric Bruneton | |
56 | */ | |
57 | public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes{ | |
58 | private static final Object THIS = new Object(); | |
59 | private static final Object OTHER = new Object(); | |
60 | ||
61 | protected int methodAccess; | |
62 | protected String methodDesc; | |
63 | ||
64 | private boolean constructor; | |
65 | private boolean superInitialized; | |
66 | private ArrayList stackFrame; | |
67 | private HashMap branches; | |
68 | ||
69 | /** | |
70 | * Creates a new {@link AdviceAdapter}. | |
71 | * | |
72 | * @param mv the method visitor to which this adapter delegates calls. | |
73 | * @param access the method's access flags (see {@link Opcodes}). | |
74 | * @param name the method's name. | |
75 | * @param desc the method's descriptor (see {@link Type Type}). | |
76 | */ | |
77 | public AdviceAdapter( | |
78 | final MethodVisitor mv, | |
79 | final int access, | |
80 | final String name, | |
81 | final String desc){ | |
82 | super(mv, access, name, desc); | |
83 | methodAccess = access; | |
84 | methodDesc = desc; | |
85 | ||
86 | constructor = "<init>".equals(name); | |
87 | } | |
88 | ||
89 | public void visitCode(){ | |
90 | mv.visitCode(); | |
91 | if(!constructor) | |
92 | { | |
93 | superInitialized = true; | |
94 | onMethodEnter(); | |
95 | } | |
96 | else | |
97 | { | |
98 | stackFrame = new ArrayList(); | |
99 | branches = new HashMap(); | |
100 | } | |
101 | } | |
102 | ||
103 | public void visitLabel(final Label label){ | |
104 | mv.visitLabel(label); | |
105 | ||
106 | if(constructor && branches != null) | |
107 | { | |
108 | ArrayList frame = (ArrayList) branches.get(label); | |
109 | if(frame != null) | |
110 | { | |
111 | stackFrame = frame; | |
112 | branches.remove(label); | |
113 | } | |
114 | } | |
115 | } | |
116 | ||
117 | public void visitInsn(final int opcode){ | |
118 | if(constructor) | |
119 | { | |
120 | switch(opcode) | |
121 | { | |
122 | case RETURN: // empty stack | |
123 | onMethodExit(opcode); | |
124 | break; | |
125 | ||
126 | case IRETURN: // 1 before n/a after | |
127 | case FRETURN: // 1 before n/a after | |
128 | case ARETURN: // 1 before n/a after | |
129 | case ATHROW: // 1 before n/a after | |
130 | popValue(); | |
131 | popValue(); | |
132 | onMethodExit(opcode); | |
133 | break; | |
134 | ||
135 | case LRETURN: // 2 before n/a after | |
136 | case DRETURN: // 2 before n/a after | |
137 | popValue(); | |
138 | popValue(); | |
139 | onMethodExit(opcode); | |
140 | break; | |
141 | ||
142 | case NOP: | |
143 | case LALOAD: // remove 2 add 2 | |
144 | case DALOAD: // remove 2 add 2 | |
145 | case LNEG: | |
146 | case DNEG: | |
147 | case FNEG: | |
148 | case INEG: | |
149 | case L2D: | |
150 | case D2L: | |
151 | case F2I: | |
152 | case I2B: | |
153 | case I2C: | |
154 | case I2S: | |
155 | case I2F: | |
156 | case Opcodes.ARRAYLENGTH: | |
157 | break; | |
158 | ||
159 | case ACONST_NULL: | |
160 | case ICONST_M1: | |
161 | case ICONST_0: | |
162 | case ICONST_1: | |
163 | case ICONST_2: | |
164 | case ICONST_3: | |
165 | case ICONST_4: | |
166 | case ICONST_5: | |
167 | case FCONST_0: | |
168 | case FCONST_1: | |
169 | case FCONST_2: | |
170 | case F2L: // 1 before 2 after | |
171 | case F2D: | |
172 | case I2L: | |
173 | case I2D: | |
174 | pushValue(OTHER); | |
175 | break; | |
176 | ||
177 | case LCONST_0: | |
178 | case LCONST_1: | |
179 | case DCONST_0: | |
180 | case DCONST_1: | |
181 | pushValue(OTHER); | |
182 | pushValue(OTHER); | |
183 | break; | |
184 | ||
185 | case IALOAD: // remove 2 add 1 | |
186 | case FALOAD: // remove 2 add 1 | |
187 | case AALOAD: // remove 2 add 1 | |
188 | case BALOAD: // remove 2 add 1 | |
189 | case CALOAD: // remove 2 add 1 | |
190 | case SALOAD: // remove 2 add 1 | |
191 | case POP: | |
192 | case IADD: | |
193 | case FADD: | |
194 | case ISUB: | |
195 | case LSHL: // 3 before 2 after | |
196 | case LSHR: // 3 before 2 after | |
197 | case LUSHR: // 3 before 2 after | |
198 | case L2I: // 2 before 1 after | |
199 | case L2F: // 2 before 1 after | |
200 | case D2I: // 2 before 1 after | |
201 | case D2F: // 2 before 1 after | |
202 | case FSUB: | |
203 | case FMUL: | |
204 | case FDIV: | |
205 | case FREM: | |
206 | case FCMPL: // 2 before 1 after | |
207 | case FCMPG: // 2 before 1 after | |
208 | case IMUL: | |
209 | case IDIV: | |
210 | case IREM: | |
211 | case ISHL: | |
212 | case ISHR: | |
213 | case IUSHR: | |
214 | case IAND: | |
215 | case IOR: | |
216 | case IXOR: | |
217 | case MONITORENTER: | |
218 | case MONITOREXIT: | |
219 | popValue(); | |
220 | break; | |
221 | ||
222 | case POP2: | |
223 | case LSUB: | |
224 | case LMUL: | |
225 | case LDIV: | |
226 | case LREM: | |
227 | case LADD: | |
228 | case LAND: | |
229 | case LOR: | |
230 | case LXOR: | |
231 | case DADD: | |
232 | case DMUL: | |
233 | case DSUB: | |
234 | case DDIV: | |
235 | case DREM: | |
236 | popValue(); | |
237 | popValue(); | |
238 | break; | |
239 | ||
240 | case IASTORE: | |
241 | case FASTORE: | |
242 | case AASTORE: | |
243 | case BASTORE: | |
244 | case CASTORE: | |
245 | case SASTORE: | |
246 | case LCMP: // 4 before 1 after | |
247 | case DCMPL: | |
248 | case DCMPG: | |
249 | popValue(); | |
250 | popValue(); | |
251 | popValue(); | |
252 | break; | |
253 | ||
254 | case LASTORE: | |
255 | case DASTORE: | |
256 | popValue(); | |
257 | popValue(); | |
258 | popValue(); | |
259 | popValue(); | |
260 | break; | |
261 | ||
262 | case DUP: | |
263 | pushValue(peekValue()); | |
264 | break; | |
265 | ||
266 | case DUP_X1: | |
267 | // TODO optimize this | |
268 | { | |
269 | Object o1 = popValue(); | |
270 | Object o2 = popValue(); | |
271 | pushValue(o1); | |
272 | pushValue(o2); | |
273 | pushValue(o1); | |
274 | } | |
275 | break; | |
276 | ||
277 | case DUP_X2: | |
278 | // TODO optimize this | |
279 | { | |
280 | Object o1 = popValue(); | |
281 | Object o2 = popValue(); | |
282 | Object o3 = popValue(); | |
283 | pushValue(o1); | |
284 | pushValue(o3); | |
285 | pushValue(o2); | |
286 | pushValue(o1); | |
287 | } | |
288 | break; | |
289 | ||
290 | case DUP2: | |
291 | // TODO optimize this | |
292 | { | |
293 | Object o1 = popValue(); | |
294 | Object o2 = popValue(); | |
295 | pushValue(o2); | |
296 | pushValue(o1); | |
297 | pushValue(o2); | |
298 | pushValue(o1); | |
299 | } | |
300 | break; | |
301 | ||
302 | case DUP2_X1: | |
303 | // TODO optimize this | |
304 | { | |
305 | Object o1 = popValue(); | |
306 | Object o2 = popValue(); | |
307 | Object o3 = popValue(); | |
308 | pushValue(o2); | |
309 | pushValue(o1); | |
310 | pushValue(o3); | |
311 | pushValue(o2); | |
312 | pushValue(o1); | |
313 | } | |
314 | break; | |
315 | ||
316 | case DUP2_X2: | |
317 | // TODO optimize this | |
318 | { | |
319 | Object o1 = popValue(); | |
320 | Object o2 = popValue(); | |
321 | Object o3 = popValue(); | |
322 | Object o4 = popValue(); | |
323 | pushValue(o2); | |
324 | pushValue(o1); | |
325 | pushValue(o4); | |
326 | pushValue(o3); | |
327 | pushValue(o2); | |
328 | pushValue(o1); | |
329 | } | |
330 | break; | |
331 | ||
332 | case SWAP: | |
333 | { | |
334 | Object o1 = popValue(); | |
335 | Object o2 = popValue(); | |
336 | pushValue(o1); | |
337 | pushValue(o2); | |
338 | } | |
339 | break; | |
340 | } | |
341 | } | |
342 | else | |
343 | { | |
344 | switch(opcode) | |
345 | { | |
346 | case RETURN: | |
347 | case IRETURN: | |
348 | case FRETURN: | |
349 | case ARETURN: | |
350 | case LRETURN: | |
351 | case DRETURN: | |
352 | case ATHROW: | |
353 | onMethodExit(opcode); | |
354 | break; | |
355 | } | |
356 | } | |
357 | mv.visitInsn(opcode); | |
358 | } | |
359 | ||
360 | public void visitVarInsn(final int opcode, final int var){ | |
361 | super.visitVarInsn(opcode, var); | |
362 | ||
363 | if(constructor) | |
364 | { | |
365 | switch(opcode) | |
366 | { | |
367 | case ILOAD: | |
368 | case FLOAD: | |
369 | pushValue(OTHER); | |
370 | break; | |
371 | case LLOAD: | |
372 | case DLOAD: | |
373 | pushValue(OTHER); | |
374 | pushValue(OTHER); | |
375 | break; | |
376 | case ALOAD: | |
377 | pushValue(var == 0 ? THIS : OTHER); | |
378 | break; | |
379 | case ASTORE: | |
380 | case ISTORE: | |
381 | case FSTORE: | |
382 | popValue(); | |
383 | break; | |
384 | case LSTORE: | |
385 | case DSTORE: | |
386 | popValue(); | |
387 | popValue(); | |
388 | break; | |
389 | } | |
390 | } | |
391 | } | |
392 | ||
393 | public void visitFieldInsn( | |
394 | final int opcode, | |
395 | final String owner, | |
396 | final String name, | |
397 | final String desc){ | |
398 | mv.visitFieldInsn(opcode, owner, name, desc); | |
399 | ||
400 | if(constructor) | |
401 | { | |
402 | char c = desc.charAt(0); | |
403 | boolean longOrDouble = c == 'J' || c == 'D'; | |
404 | switch(opcode) | |
405 | { | |
406 | case GETSTATIC: | |
407 | pushValue(OTHER); | |
408 | if(longOrDouble) | |
409 | { | |
410 | pushValue(OTHER); | |
411 | } | |
412 | break; | |
413 | case PUTSTATIC: | |
414 | popValue(); | |
415 | if(longOrDouble) | |
416 | { | |
417 | popValue(); | |
418 | } | |
419 | break; | |
420 | case PUTFIELD: | |
421 | popValue(); | |
422 | if(longOrDouble) | |
423 | { | |
424 | popValue(); | |
425 | popValue(); | |
426 | } | |
427 | break; | |
428 | // case GETFIELD: | |
429 | default: | |
430 | if(longOrDouble) | |
431 | { | |
432 | pushValue(OTHER); | |
433 | } | |
434 | } | |
435 | } | |
436 | } | |
437 | ||
438 | public void visitIntInsn(final int opcode, final int operand){ | |
439 | mv.visitIntInsn(opcode, operand); | |
440 | ||
441 | if(constructor && opcode != NEWARRAY) | |
442 | { | |
443 | pushValue(OTHER); | |
444 | } | |
445 | } | |
446 | ||
447 | public void visitLdcInsn(final Object cst){ | |
448 | mv.visitLdcInsn(cst); | |
449 | ||
450 | if(constructor) | |
451 | { | |
452 | pushValue(OTHER); | |
453 | if(cst instanceof Double || cst instanceof Long) | |
454 | { | |
455 | pushValue(OTHER); | |
456 | } | |
457 | } | |
458 | } | |
459 | ||
460 | public void visitMultiANewArrayInsn(final String desc, final int dims){ | |
461 | mv.visitMultiANewArrayInsn(desc, dims); | |
462 | ||
463 | if(constructor) | |
464 | { | |
465 | for(int i = 0; i < dims; i++) | |
466 | { | |
467 | popValue(); | |
468 | } | |
469 | pushValue(OTHER); | |
470 | } | |
471 | } | |
472 | ||
473 | public void visitTypeInsn(final int opcode, final String name){ | |
474 | mv.visitTypeInsn(opcode, name); | |
475 | ||
476 | // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack | |
477 | if(constructor && opcode == NEW) | |
478 | { | |
479 | pushValue(OTHER); | |
480 | } | |
481 | } | |
482 | ||
483 | public void visitMethodInsn( | |
484 | final int opcode, | |
485 | final String owner, | |
486 | final String name, | |
487 | final String desc){ | |
488 | mv.visitMethodInsn(opcode, owner, name, desc); | |
489 | ||
490 | if(constructor) | |
491 | { | |
492 | Type[] types = Type.getArgumentTypes(desc); | |
493 | for(int i = 0; i < types.length; i++) | |
494 | { | |
495 | popValue(); | |
496 | if(types[i].getSize() == 2) | |
497 | { | |
498 | popValue(); | |
499 | } | |
500 | } | |
501 | switch(opcode) | |
502 | { | |
503 | // case INVOKESTATIC: | |
504 | // break; | |
505 | ||
506 | case INVOKEINTERFACE: | |
507 | case INVOKEVIRTUAL: | |
508 | popValue(); // objectref | |
509 | break; | |
510 | ||
511 | case INVOKESPECIAL: | |
512 | Object type = popValue(); // objectref | |
513 | if(type == THIS && !superInitialized) | |
514 | { | |
515 | onMethodEnter(); | |
516 | superInitialized = true; | |
517 | // once super has been initialized it is no longer | |
518 | // necessary to keep track of stack state | |
519 | constructor = false; | |
520 | } | |
521 | break; | |
522 | } | |
523 | ||
524 | Type returnType = Type.getReturnType(desc); | |
525 | if(returnType != Type.VOID_TYPE) | |
526 | { | |
527 | pushValue(OTHER); | |
528 | if(returnType.getSize() == 2) | |
529 | { | |
530 | pushValue(OTHER); | |
531 | } | |
532 | } | |
533 | } | |
534 | } | |
535 | ||
536 | public void visitJumpInsn(final int opcode, final Label label){ | |
537 | mv.visitJumpInsn(opcode, label); | |
538 | ||
539 | if(constructor) | |
540 | { | |
541 | switch(opcode) | |
542 | { | |
543 | case IFEQ: | |
544 | case IFNE: | |
545 | case IFLT: | |
546 | case IFGE: | |
547 | case IFGT: | |
548 | case IFLE: | |
549 | case IFNULL: | |
550 | case IFNONNULL: | |
551 | popValue(); | |
552 | break; | |
553 | ||
554 | case IF_ICMPEQ: | |
555 | case IF_ICMPNE: | |
556 | case IF_ICMPLT: | |
557 | case IF_ICMPGE: | |
558 | case IF_ICMPGT: | |
559 | case IF_ICMPLE: | |
560 | case IF_ACMPEQ: | |
561 | case IF_ACMPNE: | |
562 | popValue(); | |
563 | popValue(); | |
564 | break; | |
565 | ||
566 | case JSR: | |
567 | pushValue(OTHER); | |
568 | break; | |
569 | } | |
570 | addBranch(label); | |
571 | } | |
572 | } | |
573 | ||
574 | public void visitLookupSwitchInsn( | |
575 | final Label dflt, | |
576 | final int[] keys, | |
577 | final Label[] labels){ | |
578 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
579 | ||
580 | if(constructor) | |
581 | { | |
582 | popValue(); | |
583 | addBranches(dflt, labels); | |
584 | } | |
585 | } | |
586 | ||
587 | public void visitTableSwitchInsn( | |
588 | final int min, | |
589 | final int max, | |
590 | final Label dflt, | |
591 | final Label[] labels){ | |
592 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
593 | ||
594 | if(constructor) | |
595 | { | |
596 | popValue(); | |
597 | addBranches(dflt, labels); | |
598 | } | |
599 | } | |
600 | ||
601 | private void addBranches(final Label dflt, final Label[] labels){ | |
602 | addBranch(dflt); | |
603 | for(int i = 0; i < labels.length; i++) | |
604 | { | |
605 | addBranch(labels[i]); | |
606 | } | |
607 | } | |
608 | ||
609 | private void addBranch(final Label label){ | |
610 | if(branches.containsKey(label)) | |
611 | { | |
612 | return; | |
613 | } | |
614 | ArrayList frame = new ArrayList(); | |
615 | frame.addAll(stackFrame); | |
616 | branches.put(label, frame); | |
617 | } | |
618 | ||
619 | private Object popValue(){ | |
620 | return stackFrame.remove(stackFrame.size() - 1); | |
621 | } | |
622 | ||
623 | private Object peekValue(){ | |
624 | return stackFrame.get(stackFrame.size() - 1); | |
625 | } | |
626 | ||
627 | private void pushValue(final Object o){ | |
628 | stackFrame.add(o); | |
629 | } | |
630 | ||
631 | /** | |
632 | * Called at the beginning of the method or after super class class call in | |
633 | * the constructor. <br><br> | |
634 | * <p/> | |
635 | * <i>Custom code can use or change all the local variables, but should not | |
636 | * change state of the stack.</i> | |
637 | */ | |
638 | protected abstract void onMethodEnter(); | |
639 | ||
640 | /** | |
641 | * Called before explicit exit from the method using either return or throw. | |
642 | * Top element on the stack contains the return value or exception instance. | |
643 | * For example: | |
644 | * <p/> | |
645 | * <pre> | |
646 | * public void onMethodExit(int opcode) { | |
647 | * if(opcode==RETURN) { | |
648 | * visitInsn(ACONST_NULL); | |
649 | * } else if(opcode==ARETURN || opcode==ATHROW) { | |
650 | * dup(); | |
651 | * } else { | |
652 | * if(opcode==LRETURN || opcode==DRETURN) { | |
653 | * dup2(); | |
654 | * } else { | |
655 | * dup(); | |
656 | * } | |
657 | * box(Type.getReturnType(this.methodDesc)); | |
658 | * } | |
659 | * visitIntInsn(SIPUSH, opcode); | |
660 | * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V"); | |
661 | * } | |
662 | * <p/> | |
663 | * // an actual call back method | |
664 | * public static void onExit(int opcode, Object param) { | |
665 | * ... | |
666 | * </pre> | |
667 | * <p/> | |
668 | * <br><br> | |
669 | * <p/> | |
670 | * <i>Custom code can use or change all the local variables, but should not | |
671 | * change state of the stack.</i> | |
672 | * | |
673 | * @param opcode one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, | |
674 | * DRETURN or ATHROW | |
675 | */ | |
676 | protected abstract void onMethodExit(int opcode); | |
677 | ||
678 | // TODO onException, onMethodCall | |
679 | ||
680 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import java.util.ArrayList; | |
32 | import java.util.HashMap; | |
33 | import java.util.List; | |
34 | import java.util.Map; | |
35 | ||
36 | import clojure.asm.Label; | |
37 | import clojure.asm.MethodAdapter; | |
38 | import clojure.asm.MethodVisitor; | |
39 | import clojure.asm.Opcodes; | |
40 | import clojure.asm.Type; | |
41 | ||
42 | /** | |
43 | * A {@link MethodAdapter} that keeps track of stack map frame changes between | |
44 | * {@link #visitFrame(int,int,Object[],int,Object[]) visitFrame} calls. This | |
45 | * adapter must be used with the | |
46 | * {@link clojure.asm.ClassReader#EXPAND_FRAMES} option. Each visit<i>XXX</i> | |
47 | * instruction delegates to the next visitor in the chain, if any, and then | |
48 | * simulates the effect of this instruction on the stack map frame, represented | |
49 | * by {@link #locals} and {@link #stack}. The next visitor in the chain can get | |
50 | * the state of the stack map frame <i>before</i> each instruction by reading | |
51 | * the value of these fields in its visit<i>XXX</i> methods (this requires a | |
52 | * reference to the AnalyzerAdapter that is before it in the chain). | |
53 | * | |
54 | * @author Eric Bruneton | |
55 | */ | |
56 | public class AnalyzerAdapter extends MethodAdapter{ | |
57 | ||
58 | /** | |
59 | * <code>List</code> of the local variable slots for current execution | |
60 | * frame. Primitive types are represented by {@link Opcodes#TOP}, | |
61 | * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, | |
62 | * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or | |
63 | * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a | |
64 | * two elements, the second one being TOP). Reference types are represented | |
65 | * by String objects (representing internal names, or type descriptors for | |
66 | * array types), and uninitialized types by Label objects (this label | |
67 | * designates the NEW instruction that created this uninitialized value). | |
68 | * This field is <tt>null</tt> for unreacheable instructions. | |
69 | */ | |
70 | public List locals; | |
71 | ||
72 | /** | |
73 | * <code>List</code> of the operand stack slots for current execution | |
74 | * frame. Primitive types are represented by {@link Opcodes#TOP}, | |
75 | * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, | |
76 | * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or | |
77 | * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by a | |
78 | * two elements, the second one being TOP). Reference types are represented | |
79 | * by String objects (representing internal names, or type descriptors for | |
80 | * array types), and uninitialized types by Label objects (this label | |
81 | * designates the NEW instruction that created this uninitialized value). | |
82 | * This field is <tt>null</tt> for unreacheable instructions. | |
83 | */ | |
84 | public List stack; | |
85 | ||
86 | /** | |
87 | * The labels that designate the next instruction to be visited. May be | |
88 | * <tt>null</tt>. | |
89 | */ | |
90 | private List labels; | |
91 | ||
92 | /** | |
93 | * Information about uninitialized types in the current execution frame. | |
94 | * This map associates internal names to Label objects. Each label | |
95 | * designates a NEW instruction that created the currently uninitialized | |
96 | * types, and the associated internal name represents the NEW operand, i.e. | |
97 | * the final, initialized type value. | |
98 | */ | |
99 | private Map uninitializedTypes; | |
100 | ||
101 | /** | |
102 | * The maximum stack size of this method. | |
103 | */ | |
104 | private int maxStack; | |
105 | ||
106 | /** | |
107 | * The maximum number of local variables of this method. | |
108 | */ | |
109 | private int maxLocals; | |
110 | ||
111 | /** | |
112 | * Creates a new {@link AnalyzerAdapter}. | |
113 | * | |
114 | * @param owner the owner's class name. | |
115 | * @param access the method's access flags (see {@link Opcodes}). | |
116 | * @param name the method's name. | |
117 | * @param desc the method's descriptor (see {@link Type Type}). | |
118 | * @param mv the method visitor to which this adapter delegates calls. May | |
119 | * be <tt>null</tt>. | |
120 | */ | |
121 | public AnalyzerAdapter( | |
122 | final String owner, | |
123 | final int access, | |
124 | final String name, | |
125 | final String desc, | |
126 | final MethodVisitor mv){ | |
127 | super(mv); | |
128 | locals = new ArrayList(); | |
129 | stack = new ArrayList(); | |
130 | uninitializedTypes = new HashMap(); | |
131 | ||
132 | if((access & Opcodes.ACC_STATIC) == 0) | |
133 | { | |
134 | if(name.equals("<init>")) | |
135 | { | |
136 | locals.add(Opcodes.UNINITIALIZED_THIS); | |
137 | } | |
138 | else | |
139 | { | |
140 | locals.add(owner); | |
141 | } | |
142 | } | |
143 | Type[] types = Type.getArgumentTypes(desc); | |
144 | for(int i = 0; i < types.length; ++i) | |
145 | { | |
146 | Type type = types[i]; | |
147 | switch(type.getSort()) | |
148 | { | |
149 | case Type.BOOLEAN: | |
150 | case Type.CHAR: | |
151 | case Type.BYTE: | |
152 | case Type.SHORT: | |
153 | case Type.INT: | |
154 | locals.add(Opcodes.INTEGER); | |
155 | break; | |
156 | case Type.FLOAT: | |
157 | locals.add(Opcodes.FLOAT); | |
158 | break; | |
159 | case Type.LONG: | |
160 | locals.add(Opcodes.LONG); | |
161 | locals.add(Opcodes.TOP); | |
162 | break; | |
163 | case Type.DOUBLE: | |
164 | locals.add(Opcodes.DOUBLE); | |
165 | locals.add(Opcodes.TOP); | |
166 | break; | |
167 | case Type.ARRAY: | |
168 | locals.add(types[i].getDescriptor()); | |
169 | break; | |
170 | // case Type.OBJECT: | |
171 | default: | |
172 | locals.add(types[i].getInternalName()); | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | public void visitFrame( | |
178 | final int type, | |
179 | final int nLocal, | |
180 | final Object[] local, | |
181 | final int nStack, | |
182 | final Object[] stack){ | |
183 | if(type != Opcodes.F_NEW) | |
184 | { // uncompressed frame | |
185 | throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); | |
186 | } | |
187 | ||
188 | if(mv != null) | |
189 | { | |
190 | mv.visitFrame(type, nLocal, local, nStack, stack); | |
191 | } | |
192 | ||
193 | if(this.locals != null) | |
194 | { | |
195 | this.locals.clear(); | |
196 | this.stack.clear(); | |
197 | } | |
198 | else | |
199 | { | |
200 | this.locals = new ArrayList(); | |
201 | this.stack = new ArrayList(); | |
202 | } | |
203 | visitFrameTypes(nLocal, local, this.locals); | |
204 | visitFrameTypes(nStack, stack, this.stack); | |
205 | maxStack = Math.max(maxStack, this.stack.size()); | |
206 | } | |
207 | ||
208 | private void visitFrameTypes( | |
209 | final int n, | |
210 | final Object[] types, | |
211 | final List result){ | |
212 | for(int i = 0; i < n; ++i) | |
213 | { | |
214 | Object type = types[i]; | |
215 | result.add(type); | |
216 | if(type == Opcodes.LONG || type == Opcodes.DOUBLE) | |
217 | { | |
218 | result.add(Opcodes.TOP); | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
223 | public void visitInsn(final int opcode){ | |
224 | if(mv != null) | |
225 | { | |
226 | mv.visitInsn(opcode); | |
227 | } | |
228 | execute(opcode, 0, null); | |
229 | if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) | |
230 | || opcode == Opcodes.ATHROW) | |
231 | { | |
232 | this.locals = null; | |
233 | this.stack = null; | |
234 | } | |
235 | } | |
236 | ||
237 | public void visitIntInsn(final int opcode, final int operand){ | |
238 | if(mv != null) | |
239 | { | |
240 | mv.visitIntInsn(opcode, operand); | |
241 | } | |
242 | execute(opcode, operand, null); | |
243 | } | |
244 | ||
245 | public void visitVarInsn(final int opcode, final int var){ | |
246 | if(mv != null) | |
247 | { | |
248 | mv.visitVarInsn(opcode, var); | |
249 | } | |
250 | execute(opcode, var, null); | |
251 | } | |
252 | ||
253 | public void visitTypeInsn(final int opcode, final String desc){ | |
254 | if(opcode == Opcodes.NEW) | |
255 | { | |
256 | if(labels == null) | |
257 | { | |
258 | Label l = new Label(); | |
259 | labels = new ArrayList(3); | |
260 | labels.add(l); | |
261 | if(mv != null) | |
262 | { | |
263 | mv.visitLabel(l); | |
264 | } | |
265 | } | |
266 | for(int i = 0; i < labels.size(); ++i) | |
267 | { | |
268 | uninitializedTypes.put(labels.get(i), desc); | |
269 | } | |
270 | } | |
271 | if(mv != null) | |
272 | { | |
273 | mv.visitTypeInsn(opcode, desc); | |
274 | } | |
275 | execute(opcode, 0, desc); | |
276 | } | |
277 | ||
278 | public void visitFieldInsn( | |
279 | final int opcode, | |
280 | final String owner, | |
281 | final String name, | |
282 | final String desc){ | |
283 | if(mv != null) | |
284 | { | |
285 | mv.visitFieldInsn(opcode, owner, name, desc); | |
286 | } | |
287 | execute(opcode, 0, desc); | |
288 | } | |
289 | ||
290 | public void visitMethodInsn( | |
291 | final int opcode, | |
292 | final String owner, | |
293 | final String name, | |
294 | final String desc){ | |
295 | if(mv != null) | |
296 | { | |
297 | mv.visitMethodInsn(opcode, owner, name, desc); | |
298 | } | |
299 | pop(desc); | |
300 | if(opcode != Opcodes.INVOKESTATIC) | |
301 | { | |
302 | Object t = pop(); | |
303 | if(opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<') | |
304 | { | |
305 | Object u; | |
306 | if(t == Opcodes.UNINITIALIZED_THIS) | |
307 | { | |
308 | u = owner; | |
309 | } | |
310 | else | |
311 | { | |
312 | u = uninitializedTypes.get(t); | |
313 | } | |
314 | for(int i = 0; i < locals.size(); ++i) | |
315 | { | |
316 | if(locals.get(i) == t) | |
317 | { | |
318 | locals.set(i, u); | |
319 | } | |
320 | } | |
321 | for(int i = 0; i < stack.size(); ++i) | |
322 | { | |
323 | if(stack.get(i) == t) | |
324 | { | |
325 | stack.set(i, u); | |
326 | } | |
327 | } | |
328 | } | |
329 | } | |
330 | pushDesc(desc); | |
331 | labels = null; | |
332 | } | |
333 | ||
334 | public void visitJumpInsn(final int opcode, final Label label){ | |
335 | if(mv != null) | |
336 | { | |
337 | mv.visitJumpInsn(opcode, label); | |
338 | } | |
339 | execute(opcode, 0, null); | |
340 | if(opcode == Opcodes.GOTO) | |
341 | { | |
342 | this.locals = null; | |
343 | this.stack = null; | |
344 | } | |
345 | } | |
346 | ||
347 | public void visitLabel(final Label label){ | |
348 | if(mv != null) | |
349 | { | |
350 | mv.visitLabel(label); | |
351 | } | |
352 | if(labels == null) | |
353 | { | |
354 | labels = new ArrayList(3); | |
355 | } | |
356 | labels.add(label); | |
357 | } | |
358 | ||
359 | public void visitLdcInsn(final Object cst){ | |
360 | if(mv != null) | |
361 | { | |
362 | mv.visitLdcInsn(cst); | |
363 | } | |
364 | if(cst instanceof Integer) | |
365 | { | |
366 | push(Opcodes.INTEGER); | |
367 | } | |
368 | else if(cst instanceof Long) | |
369 | { | |
370 | push(Opcodes.LONG); | |
371 | push(Opcodes.TOP); | |
372 | } | |
373 | else if(cst instanceof Float) | |
374 | { | |
375 | push(Opcodes.FLOAT); | |
376 | } | |
377 | else if(cst instanceof Double) | |
378 | { | |
379 | push(Opcodes.DOUBLE); | |
380 | push(Opcodes.TOP); | |
381 | } | |
382 | else if(cst instanceof String) | |
383 | { | |
384 | push("java/lang/String"); | |
385 | } | |
386 | else if(cst instanceof Type) | |
387 | { | |
388 | push("java/lang/Class"); | |
389 | } | |
390 | else | |
391 | { | |
392 | throw new IllegalArgumentException(); | |
393 | } | |
394 | labels = null; | |
395 | } | |
396 | ||
397 | public void visitIincInsn(final int var, final int increment){ | |
398 | if(mv != null) | |
399 | { | |
400 | mv.visitIincInsn(var, increment); | |
401 | } | |
402 | execute(Opcodes.IINC, var, null); | |
403 | } | |
404 | ||
405 | public void visitTableSwitchInsn( | |
406 | final int min, | |
407 | final int max, | |
408 | final Label dflt, | |
409 | final Label labels[]){ | |
410 | if(mv != null) | |
411 | { | |
412 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
413 | } | |
414 | execute(Opcodes.TABLESWITCH, 0, null); | |
415 | this.locals = null; | |
416 | this.stack = null; | |
417 | } | |
418 | ||
419 | public void visitLookupSwitchInsn( | |
420 | final Label dflt, | |
421 | final int keys[], | |
422 | final Label labels[]){ | |
423 | if(mv != null) | |
424 | { | |
425 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
426 | } | |
427 | execute(Opcodes.LOOKUPSWITCH, 0, null); | |
428 | this.locals = null; | |
429 | this.stack = null; | |
430 | } | |
431 | ||
432 | public void visitMultiANewArrayInsn(final String desc, final int dims){ | |
433 | if(mv != null) | |
434 | { | |
435 | mv.visitMultiANewArrayInsn(desc, dims); | |
436 | } | |
437 | execute(Opcodes.MULTIANEWARRAY, dims, desc); | |
438 | } | |
439 | ||
440 | public void visitMaxs(final int maxStack, final int maxLocals){ | |
441 | if(mv != null) | |
442 | { | |
443 | this.maxStack = Math.max(this.maxStack, maxStack); | |
444 | this.maxLocals = Math.max(this.maxLocals, maxLocals); | |
445 | mv.visitMaxs(this.maxStack, this.maxLocals); | |
446 | } | |
447 | } | |
448 | ||
449 | // ------------------------------------------------------------------------ | |
450 | ||
451 | private Object get(final int local){ | |
452 | maxLocals = Math.max(maxLocals, local); | |
453 | return local < locals.size() ? locals.get(local) : Opcodes.TOP; | |
454 | } | |
455 | ||
456 | private void set(final int local, final Object type){ | |
457 | maxLocals = Math.max(maxLocals, local); | |
458 | while(local >= locals.size()) | |
459 | { | |
460 | locals.add(Opcodes.TOP); | |
461 | } | |
462 | locals.set(local, type); | |
463 | } | |
464 | ||
465 | private void push(final Object type){ | |
466 | stack.add(type); | |
467 | maxStack = Math.max(maxStack, stack.size()); | |
468 | } | |
469 | ||
470 | private void pushDesc(final String desc){ | |
471 | int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; | |
472 | switch(desc.charAt(index)) | |
473 | { | |
474 | case'V': | |
475 | return; | |
476 | case'Z': | |
477 | case'C': | |
478 | case'B': | |
479 | case'S': | |
480 | case'I': | |
481 | push(Opcodes.INTEGER); | |
482 | return; | |
483 | case'F': | |
484 | push(Opcodes.FLOAT); | |
485 | return; | |
486 | case'J': | |
487 | push(Opcodes.LONG); | |
488 | push(Opcodes.TOP); | |
489 | return; | |
490 | case'D': | |
491 | push(Opcodes.DOUBLE); | |
492 | push(Opcodes.TOP); | |
493 | return; | |
494 | case'[': | |
495 | if(index == 0) | |
496 | { | |
497 | push(desc); | |
498 | } | |
499 | else | |
500 | { | |
501 | push(desc.substring(index, desc.length())); | |
502 | } | |
503 | break; | |
504 | // case 'L': | |
505 | default: | |
506 | if(index == 0) | |
507 | { | |
508 | push(desc.substring(1, desc.length() - 1)); | |
509 | } | |
510 | else | |
511 | { | |
512 | push(desc.substring(index + 1, desc.length() - 1)); | |
513 | } | |
514 | return; | |
515 | } | |
516 | } | |
517 | ||
518 | private Object pop(){ | |
519 | return stack.remove(stack.size() - 1); | |
520 | } | |
521 | ||
522 | private void pop(final int n){ | |
523 | int size = stack.size(); | |
524 | int end = size - n; | |
525 | for(int i = size - 1; i >= end; --i) | |
526 | { | |
527 | stack.remove(i); | |
528 | } | |
529 | } | |
530 | ||
531 | private void pop(final String desc){ | |
532 | char c = desc.charAt(0); | |
533 | if(c == '(') | |
534 | { | |
535 | int n = 0; | |
536 | Type[] types = Type.getArgumentTypes(desc); | |
537 | for(int i = 0; i < types.length; ++i) | |
538 | { | |
539 | n += types[i].getSize(); | |
540 | } | |
541 | pop(n); | |
542 | } | |
543 | else if(c == 'J' || c == 'D') | |
544 | { | |
545 | pop(2); | |
546 | } | |
547 | else | |
548 | { | |
549 | pop(1); | |
550 | } | |
551 | } | |
552 | ||
553 | private void execute(final int opcode, final int iarg, final String sarg){ | |
554 | if(this.locals == null) | |
555 | { | |
556 | return; | |
557 | } | |
558 | Object t1, t2, t3, t4; | |
559 | switch(opcode) | |
560 | { | |
561 | case Opcodes.NOP: | |
562 | case Opcodes.INEG: | |
563 | case Opcodes.LNEG: | |
564 | case Opcodes.FNEG: | |
565 | case Opcodes.DNEG: | |
566 | case Opcodes.I2B: | |
567 | case Opcodes.I2C: | |
568 | case Opcodes.I2S: | |
569 | case Opcodes.GOTO: | |
570 | case Opcodes.RETURN: | |
571 | break; | |
572 | case Opcodes.ACONST_NULL: | |
573 | push(Opcodes.NULL); | |
574 | break; | |
575 | case Opcodes.ICONST_M1: | |
576 | case Opcodes.ICONST_0: | |
577 | case Opcodes.ICONST_1: | |
578 | case Opcodes.ICONST_2: | |
579 | case Opcodes.ICONST_3: | |
580 | case Opcodes.ICONST_4: | |
581 | case Opcodes.ICONST_5: | |
582 | case Opcodes.BIPUSH: | |
583 | case Opcodes.SIPUSH: | |
584 | push(Opcodes.INTEGER); | |
585 | break; | |
586 | case Opcodes.LCONST_0: | |
587 | case Opcodes.LCONST_1: | |
588 | push(Opcodes.LONG); | |
589 | push(Opcodes.TOP); | |
590 | break; | |
591 | case Opcodes.FCONST_0: | |
592 | case Opcodes.FCONST_1: | |
593 | case Opcodes.FCONST_2: | |
594 | push(Opcodes.FLOAT); | |
595 | break; | |
596 | case Opcodes.DCONST_0: | |
597 | case Opcodes.DCONST_1: | |
598 | push(Opcodes.DOUBLE); | |
599 | push(Opcodes.TOP); | |
600 | break; | |
601 | case Opcodes.ILOAD: | |
602 | case Opcodes.FLOAD: | |
603 | case Opcodes.ALOAD: | |
604 | push(get(iarg)); | |
605 | break; | |
606 | case Opcodes.LLOAD: | |
607 | case Opcodes.DLOAD: | |
608 | push(get(iarg)); | |
609 | push(Opcodes.TOP); | |
610 | break; | |
611 | case Opcodes.IALOAD: | |
612 | case Opcodes.BALOAD: | |
613 | case Opcodes.CALOAD: | |
614 | case Opcodes.SALOAD: | |
615 | pop(2); | |
616 | push(Opcodes.INTEGER); | |
617 | break; | |
618 | case Opcodes.LALOAD: | |
619 | case Opcodes.D2L: | |
620 | pop(2); | |
621 | push(Opcodes.LONG); | |
622 | push(Opcodes.TOP); | |
623 | break; | |
624 | case Opcodes.FALOAD: | |
625 | pop(2); | |
626 | push(Opcodes.FLOAT); | |
627 | break; | |
628 | case Opcodes.DALOAD: | |
629 | case Opcodes.L2D: | |
630 | pop(2); | |
631 | push(Opcodes.DOUBLE); | |
632 | push(Opcodes.TOP); | |
633 | break; | |
634 | case Opcodes.AALOAD: | |
635 | pop(1); | |
636 | t1 = pop(); | |
637 | pushDesc(((String) t1).substring(1)); | |
638 | break; | |
639 | case Opcodes.ISTORE: | |
640 | case Opcodes.FSTORE: | |
641 | case Opcodes.ASTORE: | |
642 | t1 = pop(); | |
643 | set(iarg, t1); | |
644 | if(iarg > 0) | |
645 | { | |
646 | t2 = get(iarg - 1); | |
647 | if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) | |
648 | { | |
649 | set(iarg - 1, Opcodes.TOP); | |
650 | } | |
651 | } | |
652 | break; | |
653 | case Opcodes.LSTORE: | |
654 | case Opcodes.DSTORE: | |
655 | pop(1); | |
656 | t1 = pop(); | |
657 | set(iarg, t1); | |
658 | set(iarg + 1, Opcodes.TOP); | |
659 | if(iarg > 0) | |
660 | { | |
661 | t2 = get(iarg - 1); | |
662 | if(t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) | |
663 | { | |
664 | set(iarg - 1, Opcodes.TOP); | |
665 | } | |
666 | } | |
667 | break; | |
668 | case Opcodes.IASTORE: | |
669 | case Opcodes.BASTORE: | |
670 | case Opcodes.CASTORE: | |
671 | case Opcodes.SASTORE: | |
672 | case Opcodes.FASTORE: | |
673 | case Opcodes.AASTORE: | |
674 | pop(3); | |
675 | break; | |
676 | case Opcodes.LASTORE: | |
677 | case Opcodes.DASTORE: | |
678 | pop(4); | |
679 | break; | |
680 | case Opcodes.POP: | |
681 | case Opcodes.IFEQ: | |
682 | case Opcodes.IFNE: | |
683 | case Opcodes.IFLT: | |
684 | case Opcodes.IFGE: | |
685 | case Opcodes.IFGT: | |
686 | case Opcodes.IFLE: | |
687 | case Opcodes.IRETURN: | |
688 | case Opcodes.FRETURN: | |
689 | case Opcodes.ARETURN: | |
690 | case Opcodes.TABLESWITCH: | |
691 | case Opcodes.LOOKUPSWITCH: | |
692 | case Opcodes.ATHROW: | |
693 | case Opcodes.MONITORENTER: | |
694 | case Opcodes.MONITOREXIT: | |
695 | case Opcodes.IFNULL: | |
696 | case Opcodes.IFNONNULL: | |
697 | pop(1); | |
698 | break; | |
699 | case Opcodes.POP2: | |
700 | case Opcodes.IF_ICMPEQ: | |
701 | case Opcodes.IF_ICMPNE: | |
702 | case Opcodes.IF_ICMPLT: | |
703 | case Opcodes.IF_ICMPGE: | |
704 | case Opcodes.IF_ICMPGT: | |
705 | case Opcodes.IF_ICMPLE: | |
706 | case Opcodes.IF_ACMPEQ: | |
707 | case Opcodes.IF_ACMPNE: | |
708 | case Opcodes.LRETURN: | |
709 | case Opcodes.DRETURN: | |
710 | pop(2); | |
711 | break; | |
712 | case Opcodes.DUP: | |
713 | t1 = pop(); | |
714 | push(t1); | |
715 | push(t1); | |
716 | break; | |
717 | case Opcodes.DUP_X1: | |
718 | t1 = pop(); | |
719 | t2 = pop(); | |
720 | push(t1); | |
721 | push(t2); | |
722 | push(t1); | |
723 | break; | |
724 | case Opcodes.DUP_X2: | |
725 | t1 = pop(); | |
726 | t2 = pop(); | |
727 | t3 = pop(); | |
728 | push(t1); | |
729 | push(t3); | |
730 | push(t2); | |
731 | push(t1); | |
732 | break; | |
733 | case Opcodes.DUP2: | |
734 | t1 = pop(); | |
735 | t2 = pop(); | |
736 | push(t2); | |
737 | push(t1); | |
738 | push(t2); | |
739 | push(t1); | |
740 | break; | |
741 | case Opcodes.DUP2_X1: | |
742 | t1 = pop(); | |
743 | t2 = pop(); | |
744 | t3 = pop(); | |
745 | push(t2); | |
746 | push(t1); | |
747 | push(t3); | |
748 | push(t2); | |
749 | push(t1); | |
750 | break; | |
751 | case Opcodes.DUP2_X2: | |
752 | t1 = pop(); | |
753 | t2 = pop(); | |
754 | t3 = pop(); | |
755 | t4 = pop(); | |
756 | push(t2); | |
757 | push(t1); | |
758 | push(t4); | |
759 | push(t3); | |
760 | push(t2); | |
761 | push(t1); | |
762 | break; | |
763 | case Opcodes.SWAP: | |
764 | t1 = pop(); | |
765 | t2 = pop(); | |
766 | push(t1); | |
767 | push(t2); | |
768 | break; | |
769 | case Opcodes.IADD: | |
770 | case Opcodes.ISUB: | |
771 | case Opcodes.IMUL: | |
772 | case Opcodes.IDIV: | |
773 | case Opcodes.IREM: | |
774 | case Opcodes.IAND: | |
775 | case Opcodes.IOR: | |
776 | case Opcodes.IXOR: | |
777 | case Opcodes.ISHL: | |
778 | case Opcodes.ISHR: | |
779 | case Opcodes.IUSHR: | |
780 | case Opcodes.L2I: | |
781 | case Opcodes.D2I: | |
782 | case Opcodes.FCMPL: | |
783 | case Opcodes.FCMPG: | |
784 | pop(2); | |
785 | push(Opcodes.INTEGER); | |
786 | break; | |
787 | case Opcodes.LADD: | |
788 | case Opcodes.LSUB: | |
789 | case Opcodes.LMUL: | |
790 | case Opcodes.LDIV: | |
791 | case Opcodes.LREM: | |
792 | case Opcodes.LAND: | |
793 | case Opcodes.LOR: | |
794 | case Opcodes.LXOR: | |
795 | pop(4); | |
796 | push(Opcodes.LONG); | |
797 | push(Opcodes.TOP); | |
798 | break; | |
799 | case Opcodes.FADD: | |
800 | case Opcodes.FSUB: | |
801 | case Opcodes.FMUL: | |
802 | case Opcodes.FDIV: | |
803 | case Opcodes.FREM: | |
804 | case Opcodes.L2F: | |
805 | case Opcodes.D2F: | |
806 | pop(2); | |
807 | push(Opcodes.FLOAT); | |
808 | break; | |
809 | case Opcodes.DADD: | |
810 | case Opcodes.DSUB: | |
811 | case Opcodes.DMUL: | |
812 | case Opcodes.DDIV: | |
813 | case Opcodes.DREM: | |
814 | pop(4); | |
815 | push(Opcodes.DOUBLE); | |
816 | push(Opcodes.TOP); | |
817 | break; | |
818 | case Opcodes.LSHL: | |
819 | case Opcodes.LSHR: | |
820 | case Opcodes.LUSHR: | |
821 | pop(3); | |
822 | push(Opcodes.LONG); | |
823 | push(Opcodes.TOP); | |
824 | break; | |
825 | case Opcodes.IINC: | |
826 | set(iarg, Opcodes.INTEGER); | |
827 | break; | |
828 | case Opcodes.I2L: | |
829 | case Opcodes.F2L: | |
830 | pop(1); | |
831 | push(Opcodes.LONG); | |
832 | push(Opcodes.TOP); | |
833 | break; | |
834 | case Opcodes.I2F: | |
835 | pop(1); | |
836 | push(Opcodes.FLOAT); | |
837 | break; | |
838 | case Opcodes.I2D: | |
839 | case Opcodes.F2D: | |
840 | pop(1); | |
841 | push(Opcodes.DOUBLE); | |
842 | push(Opcodes.TOP); | |
843 | break; | |
844 | case Opcodes.F2I: | |
845 | case Opcodes.ARRAYLENGTH: | |
846 | case Opcodes.INSTANCEOF: | |
847 | pop(1); | |
848 | push(Opcodes.INTEGER); | |
849 | break; | |
850 | case Opcodes.LCMP: | |
851 | case Opcodes.DCMPL: | |
852 | case Opcodes.DCMPG: | |
853 | pop(4); | |
854 | push(Opcodes.INTEGER); | |
855 | break; | |
856 | case Opcodes.JSR: | |
857 | case Opcodes.RET: | |
858 | throw new RuntimeException("JSR/RET are not supported"); | |
859 | case Opcodes.GETSTATIC: | |
860 | pushDesc(sarg); | |
861 | break; | |
862 | case Opcodes.PUTSTATIC: | |
863 | pop(sarg); | |
864 | break; | |
865 | case Opcodes.GETFIELD: | |
866 | pop(1); | |
867 | pushDesc(sarg); | |
868 | break; | |
869 | case Opcodes.PUTFIELD: | |
870 | pop(sarg); | |
871 | pop(); | |
872 | break; | |
873 | case Opcodes.NEW: | |
874 | push(labels.get(0)); | |
875 | break; | |
876 | case Opcodes.NEWARRAY: | |
877 | pop(); | |
878 | switch(iarg) | |
879 | { | |
880 | case Opcodes.T_BOOLEAN: | |
881 | pushDesc("[Z"); | |
882 | break; | |
883 | case Opcodes.T_CHAR: | |
884 | pushDesc("[C"); | |
885 | break; | |
886 | case Opcodes.T_BYTE: | |
887 | pushDesc("[B"); | |
888 | break; | |
889 | case Opcodes.T_SHORT: | |
890 | pushDesc("[S"); | |
891 | break; | |
892 | case Opcodes.T_INT: | |
893 | pushDesc("[I"); | |
894 | break; | |
895 | case Opcodes.T_FLOAT: | |
896 | pushDesc("[F"); | |
897 | break; | |
898 | case Opcodes.T_DOUBLE: | |
899 | pushDesc("[D"); | |
900 | break; | |
901 | // case Opcodes.T_LONG: | |
902 | default: | |
903 | pushDesc("[J"); | |
904 | break; | |
905 | } | |
906 | break; | |
907 | case Opcodes.ANEWARRAY: | |
908 | pop(); | |
909 | if(sarg.charAt(0) == '[') | |
910 | { | |
911 | pushDesc("[" + sarg); | |
912 | } | |
913 | else | |
914 | { | |
915 | pushDesc("[L" + sarg + ";"); | |
916 | } | |
917 | break; | |
918 | case Opcodes.CHECKCAST: | |
919 | pop(); | |
920 | if(sarg.charAt(0) == '[') | |
921 | { | |
922 | pushDesc(sarg); | |
923 | } | |
924 | else | |
925 | { | |
926 | push(sarg); | |
927 | } | |
928 | break; | |
929 | // case Opcodes.MULTIANEWARRAY: | |
930 | default: | |
931 | pop(iarg); | |
932 | pushDesc(sarg); | |
933 | break; | |
934 | } | |
935 | labels = null; | |
936 | } | |
937 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import clojure.asm.Label; | |
32 | import clojure.asm.MethodAdapter; | |
33 | import clojure.asm.MethodVisitor; | |
34 | import clojure.asm.Opcodes; | |
35 | ||
36 | /** | |
37 | * A {@link MethodAdapter} that can be used to approximate method size. | |
38 | * | |
39 | * @author Eugene Kuleshov | |
40 | */ | |
41 | public class CodeSizeEvaluator extends MethodAdapter implements Opcodes{ | |
42 | ||
43 | private int minSize; | |
44 | ||
45 | private int maxSize; | |
46 | ||
47 | public CodeSizeEvaluator(final MethodVisitor mv){ | |
48 | super(mv); | |
49 | } | |
50 | ||
51 | public int getMinSize(){ | |
52 | return this.minSize; | |
53 | } | |
54 | ||
55 | public int getMaxSize(){ | |
56 | return this.maxSize; | |
57 | } | |
58 | ||
59 | public void visitInsn(final int opcode){ | |
60 | minSize += 1; | |
61 | maxSize += 1; | |
62 | if(mv != null) | |
63 | { | |
64 | mv.visitInsn(opcode); | |
65 | } | |
66 | } | |
67 | ||
68 | public void visitIntInsn(final int opcode, final int operand){ | |
69 | if(opcode == SIPUSH) | |
70 | { | |
71 | minSize += 3; | |
72 | maxSize += 3; | |
73 | } | |
74 | else | |
75 | { | |
76 | minSize += 2; | |
77 | maxSize += 2; | |
78 | } | |
79 | if(mv != null) | |
80 | { | |
81 | mv.visitIntInsn(opcode, operand); | |
82 | } | |
83 | } | |
84 | ||
85 | public void visitVarInsn(final int opcode, final int var){ | |
86 | if(var < 4 && opcode != Opcodes.RET) | |
87 | { | |
88 | minSize += 1; | |
89 | maxSize += 1; | |
90 | } | |
91 | else if(var >= 256) | |
92 | { | |
93 | minSize += 4; | |
94 | maxSize += 4; | |
95 | } | |
96 | else | |
97 | { | |
98 | minSize += 2; | |
99 | maxSize += 2; | |
100 | } | |
101 | if(mv != null) | |
102 | { | |
103 | mv.visitVarInsn(opcode, var); | |
104 | } | |
105 | } | |
106 | ||
107 | public void visitTypeInsn(final int opcode, final String desc){ | |
108 | minSize += 3; | |
109 | maxSize += 3; | |
110 | if(mv != null) | |
111 | { | |
112 | mv.visitTypeInsn(opcode, desc); | |
113 | } | |
114 | } | |
115 | ||
116 | public void visitFieldInsn( | |
117 | final int opcode, | |
118 | final String owner, | |
119 | final String name, | |
120 | final String desc){ | |
121 | minSize += 3; | |
122 | maxSize += 3; | |
123 | if(mv != null) | |
124 | { | |
125 | mv.visitFieldInsn(opcode, owner, name, desc); | |
126 | } | |
127 | } | |
128 | ||
129 | public void visitMethodInsn( | |
130 | final int opcode, | |
131 | final String owner, | |
132 | final String name, | |
133 | final String desc){ | |
134 | if(opcode == INVOKEINTERFACE) | |
135 | { | |
136 | minSize += 5; | |
137 | maxSize += 5; | |
138 | } | |
139 | else | |
140 | { | |
141 | minSize += 3; | |
142 | maxSize += 3; | |
143 | } | |
144 | if(mv != null) | |
145 | { | |
146 | mv.visitMethodInsn(opcode, owner, name, desc); | |
147 | } | |
148 | } | |
149 | ||
150 | public void visitJumpInsn(final int opcode, final Label label){ | |
151 | minSize += 3; | |
152 | if(opcode == GOTO || opcode == JSR) | |
153 | { | |
154 | maxSize += 5; | |
155 | } | |
156 | else | |
157 | { | |
158 | maxSize += 8; | |
159 | } | |
160 | if(mv != null) | |
161 | { | |
162 | mv.visitJumpInsn(opcode, label); | |
163 | } | |
164 | } | |
165 | ||
166 | public void visitLdcInsn(final Object cst){ | |
167 | if(cst instanceof Long || cst instanceof Double) | |
168 | { | |
169 | minSize += 3; | |
170 | maxSize += 3; | |
171 | } | |
172 | else | |
173 | { | |
174 | minSize += 2; | |
175 | maxSize += 3; | |
176 | } | |
177 | if(mv != null) | |
178 | { | |
179 | mv.visitLdcInsn(cst); | |
180 | } | |
181 | } | |
182 | ||
183 | public void visitIincInsn(final int var, final int increment){ | |
184 | if(var > 255 || increment > 127 || increment < -128) | |
185 | { | |
186 | minSize += 6; | |
187 | maxSize += 6; | |
188 | } | |
189 | else | |
190 | { | |
191 | minSize += 3; | |
192 | maxSize += 3; | |
193 | } | |
194 | if(mv != null) | |
195 | { | |
196 | mv.visitIincInsn(var, increment); | |
197 | } | |
198 | } | |
199 | ||
200 | public void visitTableSwitchInsn( | |
201 | final int min, | |
202 | final int max, | |
203 | final Label dflt, | |
204 | final Label[] labels){ | |
205 | minSize += 13 + labels.length * 4; | |
206 | maxSize += 16 + labels.length * 4; | |
207 | if(mv != null) | |
208 | { | |
209 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
210 | } | |
211 | } | |
212 | ||
213 | public void visitLookupSwitchInsn( | |
214 | final Label dflt, | |
215 | final int[] keys, | |
216 | final Label[] labels){ | |
217 | minSize += 9 + keys.length * 8; | |
218 | maxSize += 12 + keys.length * 8; | |
219 | if(mv != null) | |
220 | { | |
221 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
222 | } | |
223 | } | |
224 | ||
225 | public void visitMultiANewArrayInsn(final String desc, final int dims){ | |
226 | minSize += 4; | |
227 | maxSize += 4; | |
228 | if(mv != null) | |
229 | { | |
230 | mv.visitMultiANewArrayInsn(desc, dims); | |
231 | } | |
232 | } | |
233 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import clojure.asm.AnnotationVisitor; | |
32 | import clojure.asm.Attribute; | |
33 | import clojure.asm.ClassVisitor; | |
34 | import clojure.asm.FieldVisitor; | |
35 | import clojure.asm.Label; | |
36 | import clojure.asm.MethodVisitor; | |
37 | ||
38 | /** | |
39 | * An empty implementation of the ASM visitor interfaces. | |
40 | * | |
41 | * @author Eric Bruneton | |
42 | */ | |
43 | public class EmptyVisitor implements | |
44 | ClassVisitor, | |
45 | FieldVisitor, | |
46 | MethodVisitor, | |
47 | AnnotationVisitor{ | |
48 | ||
49 | public void visit( | |
50 | final int version, | |
51 | final int access, | |
52 | final String name, | |
53 | final String signature, | |
54 | final String superName, | |
55 | final String[] interfaces){ | |
56 | } | |
57 | ||
58 | public void visitSource(final String source, final String debug){ | |
59 | } | |
60 | ||
61 | public void visitOuterClass( | |
62 | final String owner, | |
63 | final String name, | |
64 | final String desc){ | |
65 | } | |
66 | ||
67 | public AnnotationVisitor visitAnnotation( | |
68 | final String desc, | |
69 | final boolean visible){ | |
70 | return this; | |
71 | } | |
72 | ||
73 | public void visitAttribute(final Attribute attr){ | |
74 | } | |
75 | ||
76 | public void visitInnerClass( | |
77 | final String name, | |
78 | final String outerName, | |
79 | final String innerName, | |
80 | final int access){ | |
81 | } | |
82 | ||
83 | public FieldVisitor visitField( | |
84 | final int access, | |
85 | final String name, | |
86 | final String desc, | |
87 | final String signature, | |
88 | final Object value){ | |
89 | return this; | |
90 | } | |
91 | ||
92 | public MethodVisitor visitMethod( | |
93 | final int access, | |
94 | final String name, | |
95 | final String desc, | |
96 | final String signature, | |
97 | final String[] exceptions){ | |
98 | return this; | |
99 | } | |
100 | ||
101 | public void visitEnd(){ | |
102 | } | |
103 | ||
104 | public AnnotationVisitor visitAnnotationDefault(){ | |
105 | return this; | |
106 | } | |
107 | ||
108 | public AnnotationVisitor visitParameterAnnotation( | |
109 | final int parameter, | |
110 | final String desc, | |
111 | final boolean visible){ | |
112 | return this; | |
113 | } | |
114 | ||
115 | public void visitCode(){ | |
116 | } | |
117 | ||
118 | public void visitFrame( | |
119 | final int type, | |
120 | final int nLocal, | |
121 | final Object[] local, | |
122 | final int nStack, | |
123 | final Object[] stack){ | |
124 | } | |
125 | ||
126 | public void visitInsn(final int opcode){ | |
127 | } | |
128 | ||
129 | public void visitIntInsn(final int opcode, final int operand){ | |
130 | } | |
131 | ||
132 | public void visitVarInsn(final int opcode, final int var){ | |
133 | } | |
134 | ||
135 | public void visitTypeInsn(final int opcode, final String desc){ | |
136 | } | |
137 | ||
138 | public void visitFieldInsn( | |
139 | final int opcode, | |
140 | final String owner, | |
141 | final String name, | |
142 | final String desc){ | |
143 | } | |
144 | ||
145 | public void visitMethodInsn( | |
146 | final int opcode, | |
147 | final String owner, | |
148 | final String name, | |
149 | final String desc){ | |
150 | } | |
151 | ||
152 | public void visitJumpInsn(final int opcode, final Label label){ | |
153 | } | |
154 | ||
155 | public void visitLabel(final Label label){ | |
156 | } | |
157 | ||
158 | public void visitLdcInsn(final Object cst){ | |
159 | } | |
160 | ||
161 | public void visitIincInsn(final int var, final int increment){ | |
162 | } | |
163 | ||
164 | public void visitTableSwitchInsn( | |
165 | final int min, | |
166 | final int max, | |
167 | final Label dflt, | |
168 | final Label labels[]){ | |
169 | } | |
170 | ||
171 | public void visitLookupSwitchInsn( | |
172 | final Label dflt, | |
173 | final int keys[], | |
174 | final Label labels[]){ | |
175 | } | |
176 | ||
177 | public void visitMultiANewArrayInsn(final String desc, final int dims){ | |
178 | } | |
179 | ||
180 | public void visitTryCatchBlock( | |
181 | final Label start, | |
182 | final Label end, | |
183 | final Label handler, | |
184 | final String type){ | |
185 | } | |
186 | ||
187 | public void visitLocalVariable( | |
188 | final String name, | |
189 | final String desc, | |
190 | final String signature, | |
191 | final Label start, | |
192 | final Label end, | |
193 | final int index){ | |
194 | } | |
195 | ||
196 | public void visitLineNumber(final int line, final Label start){ | |
197 | } | |
198 | ||
199 | public void visitMaxs(final int maxStack, final int maxLocals){ | |
200 | } | |
201 | ||
202 | public void visit(final String name, final Object value){ | |
203 | } | |
204 | ||
205 | public void visitEnum( | |
206 | final String name, | |
207 | final String desc, | |
208 | final String value){ | |
209 | } | |
210 | ||
211 | public AnnotationVisitor visitAnnotation( | |
212 | final String name, | |
213 | final String desc){ | |
214 | return this; | |
215 | } | |
216 | ||
217 | public AnnotationVisitor visitArray(final String name){ | |
218 | return this; | |
219 | } | |
220 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import java.util.ArrayList; | |
32 | import java.util.Arrays; | |
33 | import java.util.List; | |
34 | ||
35 | import clojure.asm.ClassVisitor; | |
36 | import clojure.asm.Label; | |
37 | import clojure.asm.MethodVisitor; | |
38 | import clojure.asm.Opcodes; | |
39 | import clojure.asm.Type; | |
40 | ||
41 | /** | |
42 | * A {@link clojure.asm.MethodAdapter} with convenient methods to generate | |
43 | * code. For example, using this adapter, the class below | |
44 | * <p/> | |
45 | * <pre> | |
46 | * public class Example { | |
47 | * public static void main(String[] args) { | |
48 | * System.out.println("Hello world!"); | |
49 | * } | |
50 | * } | |
51 | * </pre> | |
52 | * <p/> | |
53 | * can be generated as follows: | |
54 | * <p/> | |
55 | * <pre> | |
56 | * ClassWriter cw = new ClassWriter(true); | |
57 | * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null); | |
58 | * <p/> | |
59 | * Method m = Method.getMethod("void <init> ()"); | |
60 | * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); | |
61 | * mg.loadThis(); | |
62 | * mg.invokeConstructor(Type.getType(Object.class), m); | |
63 | * mg.returnValue(); | |
64 | * mg.endMethod(); | |
65 | * <p/> | |
66 | * m = Method.getMethod("void main (String[])"); | |
67 | * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); | |
68 | * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); | |
69 | * mg.push("Hello world!"); | |
70 | * mg.invokeVirtual(Type.getType(PrintStream.class), Method.getMethod("void println (String)")); | |
71 | * mg.returnValue(); | |
72 | * mg.endMethod(); | |
73 | * <p/> | |
74 | * cw.visitEnd(); | |
75 | * </pre> | |
76 | * | |
77 | * @author Juozas Baliuka | |
78 | * @author Chris Nokleberg | |
79 | * @author Eric Bruneton | |
80 | */ | |
81 | public class GeneratorAdapter extends LocalVariablesSorter{ | |
82 | ||
83 | private final static Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); | |
84 | ||
85 | private final static Type BOOLEAN_TYPE = Type.getObjectType("java/lang/Boolean"); | |
86 | ||
87 | private final static Type SHORT_TYPE = Type.getObjectType("java/lang/Short"); | |
88 | ||
89 | private final static Type CHARACTER_TYPE = Type.getObjectType("java/lang/Character"); | |
90 | ||
91 | private final static Type INTEGER_TYPE = Type.getObjectType("java/lang/Integer"); | |
92 | ||
93 | private final static Type FLOAT_TYPE = Type.getObjectType("java/lang/Float"); | |
94 | ||
95 | private final static Type LONG_TYPE = Type.getObjectType("java/lang/Long"); | |
96 | ||
97 | private final static Type DOUBLE_TYPE = Type.getObjectType("java/lang/Double"); | |
98 | ||
99 | private final static Type NUMBER_TYPE = Type.getObjectType("java/lang/Number"); | |
100 | ||
101 | private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); | |
102 | ||
103 | private final static Method BOOLEAN_VALUE = Method.getMethod("boolean booleanValue()"); | |
104 | ||
105 | private final static Method CHAR_VALUE = Method.getMethod("char charValue()"); | |
106 | ||
107 | private final static Method INT_VALUE = Method.getMethod("int intValue()"); | |
108 | ||
109 | private final static Method FLOAT_VALUE = Method.getMethod("float floatValue()"); | |
110 | ||
111 | private final static Method LONG_VALUE = Method.getMethod("long longValue()"); | |
112 | ||
113 | private final static Method DOUBLE_VALUE = Method.getMethod("double doubleValue()"); | |
114 | ||
115 | /** | |
116 | * Constant for the {@link #math math} method. | |
117 | */ | |
118 | public final static int ADD = Opcodes.IADD; | |
119 | ||
120 | /** | |
121 | * Constant for the {@link #math math} method. | |
122 | */ | |
123 | public final static int SUB = Opcodes.ISUB; | |
124 | ||
125 | /** | |
126 | * Constant for the {@link #math math} method. | |
127 | */ | |
128 | public final static int MUL = Opcodes.IMUL; | |
129 | ||
130 | /** | |
131 | * Constant for the {@link #math math} method. | |
132 | */ | |
133 | public final static int DIV = Opcodes.IDIV; | |
134 | ||
135 | /** | |
136 | * Constant for the {@link #math math} method. | |
137 | */ | |
138 | public final static int REM = Opcodes.IREM; | |
139 | ||
140 | /** | |
141 | * Constant for the {@link #math math} method. | |
142 | */ | |
143 | public final static int NEG = Opcodes.INEG; | |
144 | ||
145 | /** | |
146 | * Constant for the {@link #math math} method. | |
147 | */ | |
148 | public final static int SHL = Opcodes.ISHL; | |
149 | ||
150 | /** | |
151 | * Constant for the {@link #math math} method. | |
152 | */ | |
153 | public final static int SHR = Opcodes.ISHR; | |
154 | ||
155 | /** | |
156 | * Constant for the {@link #math math} method. | |
157 | */ | |
158 | public final static int USHR = Opcodes.IUSHR; | |
159 | ||
160 | /** | |
161 | * Constant for the {@link #math math} method. | |
162 | */ | |
163 | public final static int AND = Opcodes.IAND; | |
164 | ||
165 | /** | |
166 | * Constant for the {@link #math math} method. | |
167 | */ | |
168 | public final static int OR = Opcodes.IOR; | |
169 | ||
170 | /** | |
171 | * Constant for the {@link #math math} method. | |
172 | */ | |
173 | public final static int XOR = Opcodes.IXOR; | |
174 | ||
175 | /** | |
176 | * Constant for the {@link #ifCmp ifCmp} method. | |
177 | */ | |
178 | public final static int EQ = Opcodes.IFEQ; | |
179 | ||
180 | /** | |
181 | * Constant for the {@link #ifCmp ifCmp} method. | |
182 | */ | |
183 | public final static int NE = Opcodes.IFNE; | |
184 | ||
185 | /** | |
186 | * Constant for the {@link #ifCmp ifCmp} method. | |
187 | */ | |
188 | public final static int LT = Opcodes.IFLT; | |
189 | ||
190 | /** | |
191 | * Constant for the {@link #ifCmp ifCmp} method. | |
192 | */ | |
193 | public final static int GE = Opcodes.IFGE; | |
194 | ||
195 | /** | |
196 | * Constant for the {@link #ifCmp ifCmp} method. | |
197 | */ | |
198 | public final static int GT = Opcodes.IFGT; | |
199 | ||
200 | /** | |
201 | * Constant for the {@link #ifCmp ifCmp} method. | |
202 | */ | |
203 | public final static int LE = Opcodes.IFLE; | |
204 | ||
205 | /** | |
206 | * Access flags of the method visited by this adapter. | |
207 | */ | |
208 | private final int access; | |
209 | ||
210 | /** | |
211 | * Return type of the method visited by this adapter. | |
212 | */ | |
213 | private final Type returnType; | |
214 | ||
215 | /** | |
216 | * Argument types of the method visited by this adapter. | |
217 | */ | |
218 | private final Type[] argumentTypes; | |
219 | ||
220 | /** | |
221 | * Types of the local variables of the method visited by this adapter. | |
222 | */ | |
223 | private final List localTypes = new ArrayList(); | |
224 | ||
225 | /** | |
226 | * Creates a new {@link GeneratorAdapter}. | |
227 | * | |
228 | * @param mv the method visitor to which this adapter delegates calls. | |
229 | * @param access the method's access flags (see {@link Opcodes}). | |
230 | * @param name the method's name. | |
231 | * @param desc the method's descriptor (see {@link Type Type}). | |
232 | */ | |
233 | public GeneratorAdapter( | |
234 | final MethodVisitor mv, | |
235 | final int access, | |
236 | final String name, | |
237 | final String desc){ | |
238 | super(access, desc, mv); | |
239 | this.access = access; | |
240 | this.returnType = Type.getReturnType(desc); | |
241 | this.argumentTypes = Type.getArgumentTypes(desc); | |
242 | } | |
243 | ||
244 | /** | |
245 | * Creates a new {@link GeneratorAdapter}. | |
246 | * | |
247 | * @param access access flags of the adapted method. | |
248 | * @param method the adapted method. | |
249 | * @param mv the method visitor to which this adapter delegates calls. | |
250 | */ | |
251 | public GeneratorAdapter( | |
252 | final int access, | |
253 | final Method method, | |
254 | final MethodVisitor mv){ | |
255 | super(access, method.getDescriptor(), mv); | |
256 | this.access = access; | |
257 | this.returnType = method.getReturnType(); | |
258 | this.argumentTypes = method.getArgumentTypes(); | |
259 | } | |
260 | ||
261 | /** | |
262 | * Creates a new {@link GeneratorAdapter}. | |
263 | * | |
264 | * @param access access flags of the adapted method. | |
265 | * @param method the adapted method. | |
266 | * @param signature the signature of the adapted method (may be | |
267 | * <tt>null</tt>). | |
268 | * @param exceptions the exceptions thrown by the adapted method (may be | |
269 | * <tt>null</tt>). | |
270 | * @param cv the class visitor to which this adapter delegates calls. | |
271 | */ | |
272 | public GeneratorAdapter( | |
273 | final int access, | |
274 | final Method method, | |
275 | final String signature, | |
276 | final Type[] exceptions, | |
277 | final ClassVisitor cv){ | |
278 | this(access, method, cv.visitMethod(access, | |
279 | method.getName(), | |
280 | method.getDescriptor(), | |
281 | signature, | |
282 | getInternalNames(exceptions))); | |
283 | } | |
284 | ||
285 | /** | |
286 | * Returns the internal names of the given types. | |
287 | * | |
288 | * @param types a set of types. | |
289 | * @return the internal names of the given types. | |
290 | */ | |
291 | private static String[] getInternalNames(final Type[] types){ | |
292 | if(types == null) | |
293 | { | |
294 | return null; | |
295 | } | |
296 | String[] names = new String[types.length]; | |
297 | for(int i = 0; i < names.length; ++i) | |
298 | { | |
299 | names[i] = types[i].getInternalName(); | |
300 | } | |
301 | return names; | |
302 | } | |
303 | ||
304 | // ------------------------------------------------------------------------ | |
305 | // Instructions to push constants on the stack | |
306 | // ------------------------------------------------------------------------ | |
307 | ||
308 | /** | |
309 | * Generates the instruction to push the given value on the stack. | |
310 | * | |
311 | * @param value the value to be pushed on the stack. | |
312 | */ | |
313 | public void push(final boolean value){ | |
314 | push(value ? 1 : 0); | |
315 | } | |
316 | ||
317 | /** | |
318 | * Generates the instruction to push the given value on the stack. | |
319 | * | |
320 | * @param value the value to be pushed on the stack. | |
321 | */ | |
322 | public void push(final int value){ | |
323 | if(value >= -1 && value <= 5) | |
324 | { | |
325 | mv.visitInsn(Opcodes.ICONST_0 + value); | |
326 | } | |
327 | else if(value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) | |
328 | { | |
329 | mv.visitIntInsn(Opcodes.BIPUSH, value); | |
330 | } | |
331 | else if(value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) | |
332 | { | |
333 | mv.visitIntInsn(Opcodes.SIPUSH, value); | |
334 | } | |
335 | else | |
336 | { | |
337 | mv.visitLdcInsn(new Integer(value)); | |
338 | } | |
339 | } | |
340 | ||
341 | /** | |
342 | * Generates the instruction to push the given value on the stack. | |
343 | * | |
344 | * @param value the value to be pushed on the stack. | |
345 | */ | |
346 | public void push(final long value){ | |
347 | if(value == 0L || value == 1L) | |
348 | { | |
349 | mv.visitInsn(Opcodes.LCONST_0 + (int) value); | |
350 | } | |
351 | else | |
352 | { | |
353 | mv.visitLdcInsn(new Long(value)); | |
354 | } | |
355 | } | |
356 | ||
357 | /** | |
358 | * Generates the instruction to push the given value on the stack. | |
359 | * | |
360 | * @param value the value to be pushed on the stack. | |
361 | */ | |
362 | public void push(final float value){ | |
363 | int bits = Float.floatToIntBits(value); | |
364 | if(bits == 0L || bits == 0x3f800000 || bits == 0x40000000) | |
365 | { // 0..2 | |
366 | mv.visitInsn(Opcodes.FCONST_0 + (int) value); | |
367 | } | |
368 | else | |
369 | { | |
370 | mv.visitLdcInsn(new Float(value)); | |
371 | } | |
372 | } | |
373 | ||
374 | /** | |
375 | * Generates the instruction to push the given value on the stack. | |
376 | * | |
377 | * @param value the value to be pushed on the stack. | |
378 | */ | |
379 | public void push(final double value){ | |
380 | long bits = Double.doubleToLongBits(value); | |
381 | if(bits == 0L || bits == 0x3ff0000000000000L) | |
382 | { // +0.0d and 1.0d | |
383 | mv.visitInsn(Opcodes.DCONST_0 + (int) value); | |
384 | } | |
385 | else | |
386 | { | |
387 | mv.visitLdcInsn(new Double(value)); | |
388 | } | |
389 | } | |
390 | ||
391 | /** | |
392 | * Generates the instruction to push the given value on the stack. | |
393 | * | |
394 | * @param value the value to be pushed on the stack. May be <tt>null</tt>. | |
395 | */ | |
396 | public void push(final String value){ | |
397 | if(value == null) | |
398 | { | |
399 | mv.visitInsn(Opcodes.ACONST_NULL); | |
400 | } | |
401 | else | |
402 | { | |
403 | mv.visitLdcInsn(value); | |
404 | } | |
405 | } | |
406 | ||
407 | /** | |
408 | * Generates the instruction to push the given value on the stack. | |
409 | * | |
410 | * @param value the value to be pushed on the stack. | |
411 | */ | |
412 | public void push(final Type value){ | |
413 | if(value == null) | |
414 | { | |
415 | mv.visitInsn(Opcodes.ACONST_NULL); | |
416 | } | |
417 | else | |
418 | { | |
419 | mv.visitLdcInsn(value); | |
420 | } | |
421 | } | |
422 | ||
423 | // ------------------------------------------------------------------------ | |
424 | // Instructions to load and store method arguments | |
425 | // ------------------------------------------------------------------------ | |
426 | ||
427 | /** | |
428 | * Returns the index of the given method argument in the frame's local | |
429 | * variables array. | |
430 | * | |
431 | * @param arg the index of a method argument. | |
432 | * @return the index of the given method argument in the frame's local | |
433 | * variables array. | |
434 | */ | |
435 | private int getArgIndex(final int arg){ | |
436 | int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; | |
437 | for(int i = 0; i < arg; i++) | |
438 | { | |
439 | index += argumentTypes[i].getSize(); | |
440 | } | |
441 | return index; | |
442 | } | |
443 | ||
444 | /** | |
445 | * Generates the instruction to push a local variable on the stack. | |
446 | * | |
447 | * @param type the type of the local variable to be loaded. | |
448 | * @param index an index in the frame's local variables array. | |
449 | */ | |
450 | private void loadInsn(final Type type, final int index){ | |
451 | mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); | |
452 | } | |
453 | ||
454 | /** | |
455 | * Generates the instruction to store the top stack value in a local | |
456 | * variable. | |
457 | * | |
458 | * @param type the type of the local variable to be stored. | |
459 | * @param index an index in the frame's local variables array. | |
460 | */ | |
461 | private void storeInsn(final Type type, final int index){ | |
462 | mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); | |
463 | } | |
464 | ||
465 | /** | |
466 | * Generates the instruction to load 'this' on the stack. | |
467 | */ | |
468 | public void loadThis(){ | |
469 | if((access & Opcodes.ACC_STATIC) != 0) | |
470 | { | |
471 | throw new IllegalStateException("no 'this' pointer within static method"); | |
472 | } | |
473 | mv.visitVarInsn(Opcodes.ALOAD, 0); | |
474 | } | |
475 | ||
476 | /** | |
477 | * Generates the instruction to load the given method argument on the stack. | |
478 | * | |
479 | * @param arg the index of a method argument. | |
480 | */ | |
481 | public void loadArg(final int arg){ | |
482 | loadInsn(argumentTypes[arg], getArgIndex(arg)); | |
483 | } | |
484 | ||
485 | /** | |
486 | * Generates the instructions to load the given method arguments on the | |
487 | * stack. | |
488 | * | |
489 | * @param arg the index of the first method argument to be loaded. | |
490 | * @param count the number of method arguments to be loaded. | |
491 | */ | |
492 | public void loadArgs(final int arg, final int count){ | |
493 | int index = getArgIndex(arg); | |
494 | for(int i = 0; i < count; ++i) | |
495 | { | |
496 | Type t = argumentTypes[arg + i]; | |
497 | loadInsn(t, index); | |
498 | index += t.getSize(); | |
499 | } | |
500 | } | |
501 | ||
502 | /** | |
503 | * Generates the instructions to load all the method arguments on the stack. | |
504 | */ | |
505 | public void loadArgs(){ | |
506 | loadArgs(0, argumentTypes.length); | |
507 | } | |
508 | ||
509 | /** | |
510 | * Generates the instructions to load all the method arguments on the stack, | |
511 | * as a single object array. | |
512 | */ | |
513 | public void loadArgArray(){ | |
514 | push(argumentTypes.length); | |
515 | newArray(OBJECT_TYPE); | |
516 | for(int i = 0; i < argumentTypes.length; i++) | |
517 | { | |
518 | dup(); | |
519 | push(i); | |
520 | loadArg(i); | |
521 | box(argumentTypes[i]); | |
522 | arrayStore(OBJECT_TYPE); | |
523 | } | |
524 | } | |
525 | ||
526 | /** | |
527 | * Generates the instruction to store the top stack value in the given | |
528 | * method argument. | |
529 | * | |
530 | * @param arg the index of a method argument. | |
531 | */ | |
532 | public void storeArg(final int arg){ | |
533 | storeInsn(argumentTypes[arg], getArgIndex(arg)); | |
534 | } | |
535 | ||
536 | // ------------------------------------------------------------------------ | |
537 | // Instructions to load and store local variables | |
538 | // ------------------------------------------------------------------------ | |
539 | ||
540 | /** | |
541 | * Returns the type of the given local variable. | |
542 | * | |
543 | * @param local a local variable identifier, as returned by | |
544 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
545 | * @return the type of the given local variable. | |
546 | */ | |
547 | public Type getLocalType(final int local){ | |
548 | return (Type) localTypes.get(local - firstLocal); | |
549 | } | |
550 | ||
551 | protected void setLocalType(final int local, final Type type){ | |
552 | int index = local - firstLocal; | |
553 | while(localTypes.size() < index + 1) | |
554 | { | |
555 | localTypes.add(null); | |
556 | } | |
557 | localTypes.set(index, type); | |
558 | } | |
559 | ||
560 | /** | |
561 | * Generates the instruction to load the given local variable on the stack. | |
562 | * | |
563 | * @param local a local variable identifier, as returned by | |
564 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
565 | */ | |
566 | public void loadLocal(final int local){ | |
567 | loadInsn(getLocalType(local), local); | |
568 | } | |
569 | ||
570 | /** | |
571 | * Generates the instruction to load the given local variable on the stack. | |
572 | * | |
573 | * @param local a local variable identifier, as returned by | |
574 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
575 | * @param type the type of this local variable. | |
576 | */ | |
577 | public void loadLocal(final int local, final Type type){ | |
578 | setLocalType(local, type); | |
579 | loadInsn(type, local); | |
580 | } | |
581 | ||
582 | /** | |
583 | * Generates the instruction to store the top stack value in the given local | |
584 | * variable. | |
585 | * | |
586 | * @param local a local variable identifier, as returned by | |
587 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
588 | */ | |
589 | public void storeLocal(final int local){ | |
590 | storeInsn(getLocalType(local), local); | |
591 | } | |
592 | ||
593 | /** | |
594 | * Generates the instruction to store the top stack value in the given local | |
595 | * variable. | |
596 | * | |
597 | * @param local a local variable identifier, as returned by | |
598 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
599 | * @param type the type of this local variable. | |
600 | */ | |
601 | public void storeLocal(final int local, final Type type){ | |
602 | setLocalType(local, type); | |
603 | storeInsn(type, local); | |
604 | } | |
605 | ||
606 | /** | |
607 | * Generates the instruction to load an element from an array. | |
608 | * | |
609 | * @param type the type of the array element to be loaded. | |
610 | */ | |
611 | public void arrayLoad(final Type type){ | |
612 | mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); | |
613 | } | |
614 | ||
615 | /** | |
616 | * Generates the instruction to store an element in an array. | |
617 | * | |
618 | * @param type the type of the array element to be stored. | |
619 | */ | |
620 | public void arrayStore(final Type type){ | |
621 | mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); | |
622 | } | |
623 | ||
624 | // ------------------------------------------------------------------------ | |
625 | // Instructions to manage the stack | |
626 | // ------------------------------------------------------------------------ | |
627 | ||
628 | /** | |
629 | * Generates a POP instruction. | |
630 | */ | |
631 | public void pop(){ | |
632 | mv.visitInsn(Opcodes.POP); | |
633 | } | |
634 | ||
635 | /** | |
636 | * Generates a POP2 instruction. | |
637 | */ | |
638 | public void pop2(){ | |
639 | mv.visitInsn(Opcodes.POP2); | |
640 | } | |
641 | ||
642 | /** | |
643 | * Generates a DUP instruction. | |
644 | */ | |
645 | public void dup(){ | |
646 | mv.visitInsn(Opcodes.DUP); | |
647 | } | |
648 | ||
649 | /** | |
650 | * Generates a DUP2 instruction. | |
651 | */ | |
652 | public void dup2(){ | |
653 | mv.visitInsn(Opcodes.DUP2); | |
654 | } | |
655 | ||
656 | /** | |
657 | * Generates a DUP_X1 instruction. | |
658 | */ | |
659 | public void dupX1(){ | |
660 | mv.visitInsn(Opcodes.DUP_X1); | |
661 | } | |
662 | ||
663 | /** | |
664 | * Generates a DUP_X2 instruction. | |
665 | */ | |
666 | public void dupX2(){ | |
667 | mv.visitInsn(Opcodes.DUP_X2); | |
668 | } | |
669 | ||
670 | /** | |
671 | * Generates a DUP2_X1 instruction. | |
672 | */ | |
673 | public void dup2X1(){ | |
674 | mv.visitInsn(Opcodes.DUP2_X1); | |
675 | } | |
676 | ||
677 | /** | |
678 | * Generates a DUP2_X2 instruction. | |
679 | */ | |
680 | public void dup2X2(){ | |
681 | mv.visitInsn(Opcodes.DUP2_X2); | |
682 | } | |
683 | ||
684 | /** | |
685 | * Generates a SWAP instruction. | |
686 | */ | |
687 | public void swap(){ | |
688 | mv.visitInsn(Opcodes.SWAP); | |
689 | } | |
690 | ||
691 | /** | |
692 | * Generates the instructions to swap the top two stack values. | |
693 | * | |
694 | * @param prev type of the top - 1 stack value. | |
695 | * @param type type of the top stack value. | |
696 | */ | |
697 | public void swap(final Type prev, final Type type){ | |
698 | if(type.getSize() == 1) | |
699 | { | |
700 | if(prev.getSize() == 1) | |
701 | { | |
702 | swap(); // same as dupX1(), pop(); | |
703 | } | |
704 | else | |
705 | { | |
706 | dupX2(); | |
707 | pop(); | |
708 | } | |
709 | } | |
710 | else | |
711 | { | |
712 | if(prev.getSize() == 1) | |
713 | { | |
714 | dup2X1(); | |
715 | pop2(); | |
716 | } | |
717 | else | |
718 | { | |
719 | dup2X2(); | |
720 | pop2(); | |
721 | } | |
722 | } | |
723 | } | |
724 | ||
725 | // ------------------------------------------------------------------------ | |
726 | // Instructions to do mathematical and logical operations | |
727 | // ------------------------------------------------------------------------ | |
728 | ||
729 | /** | |
730 | * Generates the instruction to do the specified mathematical or logical | |
731 | * operation. | |
732 | * | |
733 | * @param op a mathematical or logical operation. Must be one of ADD, SUB, | |
734 | * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR. | |
735 | * @param type the type of the operand(s) for this operation. | |
736 | */ | |
737 | public void math(final int op, final Type type){ | |
738 | mv.visitInsn(type.getOpcode(op)); | |
739 | } | |
740 | ||
741 | /** | |
742 | * Generates the instructions to compute the bitwise negation of the top | |
743 | * stack value. | |
744 | */ | |
745 | public void not(){ | |
746 | mv.visitInsn(Opcodes.ICONST_1); | |
747 | mv.visitInsn(Opcodes.IXOR); | |
748 | } | |
749 | ||
750 | /** | |
751 | * Generates the instruction to increment the given local variable. | |
752 | * | |
753 | * @param local the local variable to be incremented. | |
754 | * @param amount the amount by which the local variable must be incremented. | |
755 | */ | |
756 | public void iinc(final int local, final int amount){ | |
757 | mv.visitIincInsn(local, amount); | |
758 | } | |
759 | ||
760 | /** | |
761 | * Generates the instructions to cast a numerical value from one type to | |
762 | * another. | |
763 | * | |
764 | * @param from the type of the top stack value | |
765 | * @param to the type into which this value must be cast. | |
766 | */ | |
767 | public void cast(final Type from, final Type to){ | |
768 | if(from != to) | |
769 | { | |
770 | if(from == Type.DOUBLE_TYPE) | |
771 | { | |
772 | if(to == Type.FLOAT_TYPE) | |
773 | { | |
774 | mv.visitInsn(Opcodes.D2F); | |
775 | } | |
776 | else if(to == Type.LONG_TYPE) | |
777 | { | |
778 | mv.visitInsn(Opcodes.D2L); | |
779 | } | |
780 | else | |
781 | { | |
782 | mv.visitInsn(Opcodes.D2I); | |
783 | cast(Type.INT_TYPE, to); | |
784 | } | |
785 | } | |
786 | else if(from == Type.FLOAT_TYPE) | |
787 | { | |
788 | if(to == Type.DOUBLE_TYPE) | |
789 | { | |
790 | mv.visitInsn(Opcodes.F2D); | |
791 | } | |
792 | else if(to == Type.LONG_TYPE) | |
793 | { | |
794 | mv.visitInsn(Opcodes.F2L); | |
795 | } | |
796 | else | |
797 | { | |
798 | mv.visitInsn(Opcodes.F2I); | |
799 | cast(Type.INT_TYPE, to); | |
800 | } | |
801 | } | |
802 | else if(from == Type.LONG_TYPE) | |
803 | { | |
804 | if(to == Type.DOUBLE_TYPE) | |
805 | { | |
806 | mv.visitInsn(Opcodes.L2D); | |
807 | } | |
808 | else if(to == Type.FLOAT_TYPE) | |
809 | { | |
810 | mv.visitInsn(Opcodes.L2F); | |
811 | } | |
812 | else | |
813 | { | |
814 | mv.visitInsn(Opcodes.L2I); | |
815 | cast(Type.INT_TYPE, to); | |
816 | } | |
817 | } | |
818 | else | |
819 | { | |
820 | if(to == Type.BYTE_TYPE) | |
821 | { | |
822 | mv.visitInsn(Opcodes.I2B); | |
823 | } | |
824 | else if(to == Type.CHAR_TYPE) | |
825 | { | |
826 | mv.visitInsn(Opcodes.I2C); | |
827 | } | |
828 | else if(to == Type.DOUBLE_TYPE) | |
829 | { | |
830 | mv.visitInsn(Opcodes.I2D); | |
831 | } | |
832 | else if(to == Type.FLOAT_TYPE) | |
833 | { | |
834 | mv.visitInsn(Opcodes.I2F); | |
835 | } | |
836 | else if(to == Type.LONG_TYPE) | |
837 | { | |
838 | mv.visitInsn(Opcodes.I2L); | |
839 | } | |
840 | else if(to == Type.SHORT_TYPE) | |
841 | { | |
842 | mv.visitInsn(Opcodes.I2S); | |
843 | } | |
844 | } | |
845 | } | |
846 | } | |
847 | ||
848 | // ------------------------------------------------------------------------ | |
849 | // Instructions to do boxing and unboxing operations | |
850 | // ------------------------------------------------------------------------ | |
851 | ||
852 | /** | |
853 | * Generates the instructions to box the top stack value. This value is | |
854 | * replaced by its boxed equivalent on top of the stack. | |
855 | * | |
856 | * @param type the type of the top stack value. | |
857 | */ | |
858 | public void box(final Type type){ | |
859 | if(type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) | |
860 | { | |
861 | return; | |
862 | } | |
863 | if(type == Type.VOID_TYPE) | |
864 | { | |
865 | push((String) null); | |
866 | } | |
867 | else | |
868 | { | |
869 | Type boxed = type; | |
870 | switch(type.getSort()) | |
871 | { | |
872 | case Type.BYTE: | |
873 | boxed = BYTE_TYPE; | |
874 | break; | |
875 | case Type.BOOLEAN: | |
876 | boxed = BOOLEAN_TYPE; | |
877 | break; | |
878 | case Type.SHORT: | |
879 | boxed = SHORT_TYPE; | |
880 | break; | |
881 | case Type.CHAR: | |
882 | boxed = CHARACTER_TYPE; | |
883 | break; | |
884 | case Type.INT: | |
885 | boxed = INTEGER_TYPE; | |
886 | break; | |
887 | case Type.FLOAT: | |
888 | boxed = FLOAT_TYPE; | |
889 | break; | |
890 | case Type.LONG: | |
891 | boxed = LONG_TYPE; | |
892 | break; | |
893 | case Type.DOUBLE: | |
894 | boxed = DOUBLE_TYPE; | |
895 | break; | |
896 | } | |
897 | newInstance(boxed); | |
898 | if(type.getSize() == 2) | |
899 | { | |
900 | // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o | |
901 | dupX2(); | |
902 | dupX2(); | |
903 | pop(); | |
904 | } | |
905 | else | |
906 | { | |
907 | // p -> po -> opo -> oop -> o | |
908 | dupX1(); | |
909 | swap(); | |
910 | } | |
911 | invokeConstructor(boxed, new Method("<init>", | |
912 | Type.VOID_TYPE, | |
913 | new Type[]{type})); | |
914 | } | |
915 | } | |
916 | ||
917 | /** | |
918 | * Generates the instructions to unbox the top stack value. This value is | |
919 | * replaced by its unboxed equivalent on top of the stack. | |
920 | * | |
921 | * @param type the type of the top stack value. | |
922 | */ | |
923 | public void unbox(final Type type){ | |
924 | Type t = NUMBER_TYPE; | |
925 | Method sig = null; | |
926 | switch(type.getSort()) | |
927 | { | |
928 | case Type.VOID: | |
929 | return; | |
930 | case Type.CHAR: | |
931 | t = CHARACTER_TYPE; | |
932 | sig = CHAR_VALUE; | |
933 | break; | |
934 | case Type.BOOLEAN: | |
935 | t = BOOLEAN_TYPE; | |
936 | sig = BOOLEAN_VALUE; | |
937 | break; | |
938 | case Type.DOUBLE: | |
939 | sig = DOUBLE_VALUE; | |
940 | break; | |
941 | case Type.FLOAT: | |
942 | sig = FLOAT_VALUE; | |
943 | break; | |
944 | case Type.LONG: | |
945 | sig = LONG_VALUE; | |
946 | break; | |
947 | case Type.INT: | |
948 | case Type.SHORT: | |
949 | case Type.BYTE: | |
950 | sig = INT_VALUE; | |
951 | } | |
952 | if(sig == null) | |
953 | { | |
954 | checkCast(type); | |
955 | } | |
956 | else | |
957 | { | |
958 | checkCast(t); | |
959 | invokeVirtual(t, sig); | |
960 | } | |
961 | } | |
962 | ||
963 | // ------------------------------------------------------------------------ | |
964 | // Instructions to jump to other instructions | |
965 | // ------------------------------------------------------------------------ | |
966 | ||
967 | /** | |
968 | * Creates a new {@link Label}. | |
969 | * | |
970 | * @return a new {@link Label}. | |
971 | */ | |
972 | public Label newLabel(){ | |
973 | return new Label(); | |
974 | } | |
975 | ||
976 | /** | |
977 | * Marks the current code position with the given label. | |
978 | * | |
979 | * @param label a label. | |
980 | */ | |
981 | public void mark(final Label label){ | |
982 | mv.visitLabel(label); | |
983 | } | |
984 | ||
985 | /** | |
986 | * Marks the current code position with a new label. | |
987 | * | |
988 | * @return the label that was created to mark the current code position. | |
989 | */ | |
990 | public Label mark(){ | |
991 | Label label = new Label(); | |
992 | mv.visitLabel(label); | |
993 | return label; | |
994 | } | |
995 | ||
996 | /** | |
997 | * Generates the instructions to jump to a label based on the comparison of | |
998 | * the top two stack values. | |
999 | * | |
1000 | * @param type the type of the top two stack values. | |
1001 | * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, | |
1002 | * LE. | |
1003 | * @param label where to jump if the comparison result is <tt>true</tt>. | |
1004 | */ | |
1005 | public void ifCmp(final Type type, final int mode, final Label label){ | |
1006 | int intOp = -1; | |
1007 | switch(type.getSort()) | |
1008 | { | |
1009 | case Type.LONG: | |
1010 | mv.visitInsn(Opcodes.LCMP); | |
1011 | break; | |
1012 | case Type.DOUBLE: | |
1013 | mv.visitInsn(Opcodes.DCMPG); | |
1014 | break; | |
1015 | case Type.FLOAT: | |
1016 | mv.visitInsn(Opcodes.FCMPG); | |
1017 | break; | |
1018 | case Type.ARRAY: | |
1019 | case Type.OBJECT: | |
1020 | switch(mode) | |
1021 | { | |
1022 | case EQ: | |
1023 | mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); | |
1024 | return; | |
1025 | case NE: | |
1026 | mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); | |
1027 | return; | |
1028 | } | |
1029 | throw new IllegalArgumentException("Bad comparison for type " | |
1030 | + type); | |
1031 | default: | |
1032 | switch(mode) | |
1033 | { | |
1034 | case EQ: | |
1035 | intOp = Opcodes.IF_ICMPEQ; | |
1036 | break; | |
1037 | case NE: | |
1038 | intOp = Opcodes.IF_ICMPNE; | |
1039 | break; | |
1040 | case GE: | |
1041 | intOp = Opcodes.IF_ICMPGE; | |
1042 | break; | |
1043 | case LT: | |
1044 | intOp = Opcodes.IF_ICMPLT; | |
1045 | break; | |
1046 | case LE: | |
1047 | intOp = Opcodes.IF_ICMPLE; | |
1048 | break; | |
1049 | case GT: | |
1050 | intOp = Opcodes.IF_ICMPGT; | |
1051 | break; | |
1052 | } | |
1053 | mv.visitJumpInsn(intOp, label); | |
1054 | return; | |
1055 | } | |
1056 | int jumpMode = mode; | |
1057 | switch(mode) | |
1058 | { | |
1059 | case GE: | |
1060 | jumpMode = LT; | |
1061 | break; | |
1062 | case LE: | |
1063 | jumpMode = GT; | |
1064 | break; | |
1065 | } | |
1066 | mv.visitJumpInsn(jumpMode, label); | |
1067 | } | |
1068 | ||
1069 | /** | |
1070 | * Generates the instructions to jump to a label based on the comparison of | |
1071 | * the top two integer stack values. | |
1072 | * | |
1073 | * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, | |
1074 | * LE. | |
1075 | * @param label where to jump if the comparison result is <tt>true</tt>. | |
1076 | */ | |
1077 | public void ifICmp(final int mode, final Label label){ | |
1078 | ifCmp(Type.INT_TYPE, mode, label); | |
1079 | } | |
1080 | ||
1081 | /** | |
1082 | * Generates the instructions to jump to a label based on the comparison of | |
1083 | * the top integer stack value with zero. | |
1084 | * | |
1085 | * @param mode how these values must be compared. One of EQ, NE, LT, GE, GT, | |
1086 | * LE. | |
1087 | * @param label where to jump if the comparison result is <tt>true</tt>. | |
1088 | */ | |
1089 | public void ifZCmp(final int mode, final Label label){ | |
1090 | mv.visitJumpInsn(mode, label); | |
1091 | } | |
1092 | ||
1093 | /** | |
1094 | * Generates the instruction to jump to the given label if the top stack | |
1095 | * value is null. | |
1096 | * | |
1097 | * @param label where to jump if the condition is <tt>true</tt>. | |
1098 | */ | |
1099 | public void ifNull(final Label label){ | |
1100 | mv.visitJumpInsn(Opcodes.IFNULL, label); | |
1101 | } | |
1102 | ||
1103 | /** | |
1104 | * Generates the instruction to jump to the given label if the top stack | |
1105 | * value is not null. | |
1106 | * | |
1107 | * @param label where to jump if the condition is <tt>true</tt>. | |
1108 | */ | |
1109 | public void ifNonNull(final Label label){ | |
1110 | mv.visitJumpInsn(Opcodes.IFNONNULL, label); | |
1111 | } | |
1112 | ||
1113 | /** | |
1114 | * Generates the instruction to jump to the given label. | |
1115 | * | |
1116 | * @param label where to jump if the condition is <tt>true</tt>. | |
1117 | */ | |
1118 | public void goTo(final Label label){ | |
1119 | mv.visitJumpInsn(Opcodes.GOTO, label); | |
1120 | } | |
1121 | ||
1122 | /** | |
1123 | * Generates a RET instruction. | |
1124 | * | |
1125 | * @param local a local variable identifier, as returned by | |
1126 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
1127 | */ | |
1128 | public void ret(final int local){ | |
1129 | mv.visitVarInsn(Opcodes.RET, local); | |
1130 | } | |
1131 | ||
1132 | /** | |
1133 | * Generates the instructions for a switch statement. | |
1134 | * | |
1135 | * @param keys the switch case keys. | |
1136 | * @param generator a generator to generate the code for the switch cases. | |
1137 | */ | |
1138 | public void tableSwitch( | |
1139 | final int[] keys, | |
1140 | final TableSwitchGenerator generator){ | |
1141 | float density; | |
1142 | if(keys.length == 0) | |
1143 | { | |
1144 | density = 0; | |
1145 | } | |
1146 | else | |
1147 | { | |
1148 | density = (float) keys.length | |
1149 | / (keys[keys.length - 1] - keys[0] + 1); | |
1150 | } | |
1151 | tableSwitch(keys, generator, density >= 0.5f); | |
1152 | } | |
1153 | ||
1154 | /** | |
1155 | * Generates the instructions for a switch statement. | |
1156 | * | |
1157 | * @param keys the switch case keys. | |
1158 | * @param generator a generator to generate the code for the switch cases. | |
1159 | * @param useTable <tt>true</tt> to use a TABLESWITCH instruction, or | |
1160 | * <tt>false</tt> to use a LOOKUPSWITCH instruction. | |
1161 | */ | |
1162 | public void tableSwitch( | |
1163 | final int[] keys, | |
1164 | final TableSwitchGenerator generator, | |
1165 | final boolean useTable){ | |
1166 | for(int i = 1; i < keys.length; ++i) | |
1167 | { | |
1168 | if(keys[i] < keys[i - 1]) | |
1169 | { | |
1170 | throw new IllegalArgumentException("keys must be sorted ascending"); | |
1171 | } | |
1172 | } | |
1173 | Label def = newLabel(); | |
1174 | Label end = newLabel(); | |
1175 | if(keys.length > 0) | |
1176 | { | |
1177 | int len = keys.length; | |
1178 | int min = keys[0]; | |
1179 | int max = keys[len - 1]; | |
1180 | int range = max - min + 1; | |
1181 | if(useTable) | |
1182 | { | |
1183 | Label[] labels = new Label[range]; | |
1184 | Arrays.fill(labels, def); | |
1185 | for(int i = 0; i < len; ++i) | |
1186 | { | |
1187 | labels[keys[i] - min] = newLabel(); | |
1188 | } | |
1189 | mv.visitTableSwitchInsn(min, max, def, labels); | |
1190 | for(int i = 0; i < range; ++i) | |
1191 | { | |
1192 | Label label = labels[i]; | |
1193 | if(label != def) | |
1194 | { | |
1195 | mark(label); | |
1196 | generator.generateCase(i + min, end); | |
1197 | } | |
1198 | } | |
1199 | } | |
1200 | else | |
1201 | { | |
1202 | Label[] labels = new Label[len]; | |
1203 | for(int i = 0; i < len; ++i) | |
1204 | { | |
1205 | labels[i] = newLabel(); | |
1206 | } | |
1207 | mv.visitLookupSwitchInsn(def, keys, labels); | |
1208 | for(int i = 0; i < len; ++i) | |
1209 | { | |
1210 | mark(labels[i]); | |
1211 | generator.generateCase(keys[i], end); | |
1212 | } | |
1213 | } | |
1214 | } | |
1215 | mark(def); | |
1216 | generator.generateDefault(); | |
1217 | mark(end); | |
1218 | } | |
1219 | ||
1220 | /** | |
1221 | * Generates the instruction to return the top stack value to the caller. | |
1222 | */ | |
1223 | public void returnValue(){ | |
1224 | mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); | |
1225 | } | |
1226 | ||
1227 | // ------------------------------------------------------------------------ | |
1228 | // Instructions to load and store fields | |
1229 | // ------------------------------------------------------------------------ | |
1230 | ||
1231 | /** | |
1232 | * Generates a get field or set field instruction. | |
1233 | * | |
1234 | * @param opcode the instruction's opcode. | |
1235 | * @param ownerType the class in which the field is defined. | |
1236 | * @param name the name of the field. | |
1237 | * @param fieldType the type of the field. | |
1238 | */ | |
1239 | private void fieldInsn( | |
1240 | final int opcode, | |
1241 | final Type ownerType, | |
1242 | final String name, | |
1243 | final Type fieldType){ | |
1244 | mv.visitFieldInsn(opcode, | |
1245 | ownerType.getInternalName(), | |
1246 | name, | |
1247 | fieldType.getDescriptor()); | |
1248 | } | |
1249 | ||
1250 | /** | |
1251 | * Generates the instruction to push the value of a static field on the | |
1252 | * stack. | |
1253 | * | |
1254 | * @param owner the class in which the field is defined. | |
1255 | * @param name the name of the field. | |
1256 | * @param type the type of the field. | |
1257 | */ | |
1258 | public void getStatic(final Type owner, final String name, final Type type){ | |
1259 | fieldInsn(Opcodes.GETSTATIC, owner, name, type); | |
1260 | } | |
1261 | ||
1262 | /** | |
1263 | * Generates the instruction to store the top stack value in a static field. | |
1264 | * | |
1265 | * @param owner the class in which the field is defined. | |
1266 | * @param name the name of the field. | |
1267 | * @param type the type of the field. | |
1268 | */ | |
1269 | public void putStatic(final Type owner, final String name, final Type type){ | |
1270 | fieldInsn(Opcodes.PUTSTATIC, owner, name, type); | |
1271 | } | |
1272 | ||
1273 | /** | |
1274 | * Generates the instruction to push the value of a non static field on the | |
1275 | * stack. | |
1276 | * | |
1277 | * @param owner the class in which the field is defined. | |
1278 | * @param name the name of the field. | |
1279 | * @param type the type of the field. | |
1280 | */ | |
1281 | public void getField(final Type owner, final String name, final Type type){ | |
1282 | fieldInsn(Opcodes.GETFIELD, owner, name, type); | |
1283 | } | |
1284 | ||
1285 | /** | |
1286 | * Generates the instruction to store the top stack value in a non static | |
1287 | * field. | |
1288 | * | |
1289 | * @param owner the class in which the field is defined. | |
1290 | * @param name the name of the field. | |
1291 | * @param type the type of the field. | |
1292 | */ | |
1293 | public void putField(final Type owner, final String name, final Type type){ | |
1294 | fieldInsn(Opcodes.PUTFIELD, owner, name, type); | |
1295 | } | |
1296 | ||
1297 | // ------------------------------------------------------------------------ | |
1298 | // Instructions to invoke methods | |
1299 | // ------------------------------------------------------------------------ | |
1300 | ||
1301 | /** | |
1302 | * Generates an invoke method instruction. | |
1303 | * | |
1304 | * @param opcode the instruction's opcode. | |
1305 | * @param type the class in which the method is defined. | |
1306 | * @param method the method to be invoked. | |
1307 | */ | |
1308 | private void invokeInsn( | |
1309 | final int opcode, | |
1310 | final Type type, | |
1311 | final Method method){ | |
1312 | String owner = type.getSort() == Type.ARRAY | |
1313 | ? type.getDescriptor() | |
1314 | : type.getInternalName(); | |
1315 | mv.visitMethodInsn(opcode, | |
1316 | owner, | |
1317 | method.getName(), | |
1318 | method.getDescriptor()); | |
1319 | } | |
1320 | ||
1321 | /** | |
1322 | * Generates the instruction to invoke a normal method. | |
1323 | * | |
1324 | * @param owner the class in which the method is defined. | |
1325 | * @param method the method to be invoked. | |
1326 | */ | |
1327 | public void invokeVirtual(final Type owner, final Method method){ | |
1328 | invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); | |
1329 | } | |
1330 | ||
1331 | /** | |
1332 | * Generates the instruction to invoke a constructor. | |
1333 | * | |
1334 | * @param type the class in which the constructor is defined. | |
1335 | * @param method the constructor to be invoked. | |
1336 | */ | |
1337 | public void invokeConstructor(final Type type, final Method method){ | |
1338 | invokeInsn(Opcodes.INVOKESPECIAL, type, method); | |
1339 | } | |
1340 | ||
1341 | /** | |
1342 | * Generates the instruction to invoke a static method. | |
1343 | * | |
1344 | * @param owner the class in which the method is defined. | |
1345 | * @param method the method to be invoked. | |
1346 | */ | |
1347 | public void invokeStatic(final Type owner, final Method method){ | |
1348 | invokeInsn(Opcodes.INVOKESTATIC, owner, method); | |
1349 | } | |
1350 | ||
1351 | /** | |
1352 | * Generates the instruction to invoke an interface method. | |
1353 | * | |
1354 | * @param owner the class in which the method is defined. | |
1355 | * @param method the method to be invoked. | |
1356 | */ | |
1357 | public void invokeInterface(final Type owner, final Method method){ | |
1358 | invokeInsn(Opcodes.INVOKEINTERFACE, owner, method); | |
1359 | } | |
1360 | ||
1361 | // ------------------------------------------------------------------------ | |
1362 | // Instructions to create objects and arrays | |
1363 | // ------------------------------------------------------------------------ | |
1364 | ||
1365 | /** | |
1366 | * Generates a type dependent instruction. | |
1367 | * | |
1368 | * @param opcode the instruction's opcode. | |
1369 | * @param type the instruction's operand. | |
1370 | */ | |
1371 | private void typeInsn(final int opcode, final Type type){ | |
1372 | String desc; | |
1373 | if(type.getSort() == Type.ARRAY) | |
1374 | { | |
1375 | desc = type.getDescriptor(); | |
1376 | } | |
1377 | else | |
1378 | { | |
1379 | desc = type.getInternalName(); | |
1380 | } | |
1381 | mv.visitTypeInsn(opcode, desc); | |
1382 | } | |
1383 | ||
1384 | /** | |
1385 | * Generates the instruction to create a new object. | |
1386 | * | |
1387 | * @param type the class of the object to be created. | |
1388 | */ | |
1389 | public void newInstance(final Type type){ | |
1390 | typeInsn(Opcodes.NEW, type); | |
1391 | } | |
1392 | ||
1393 | /** | |
1394 | * Generates the instruction to create a new array. | |
1395 | * | |
1396 | * @param type the type of the array elements. | |
1397 | */ | |
1398 | public void newArray(final Type type){ | |
1399 | int typ; | |
1400 | switch(type.getSort()) | |
1401 | { | |
1402 | case Type.BOOLEAN: | |
1403 | typ = Opcodes.T_BOOLEAN; | |
1404 | break; | |
1405 | case Type.CHAR: | |
1406 | typ = Opcodes.T_CHAR; | |
1407 | break; | |
1408 | case Type.BYTE: | |
1409 | typ = Opcodes.T_BYTE; | |
1410 | break; | |
1411 | case Type.SHORT: | |
1412 | typ = Opcodes.T_SHORT; | |
1413 | break; | |
1414 | case Type.INT: | |
1415 | typ = Opcodes.T_INT; | |
1416 | break; | |
1417 | case Type.FLOAT: | |
1418 | typ = Opcodes.T_FLOAT; | |
1419 | break; | |
1420 | case Type.LONG: | |
1421 | typ = Opcodes.T_LONG; | |
1422 | break; | |
1423 | case Type.DOUBLE: | |
1424 | typ = Opcodes.T_DOUBLE; | |
1425 | break; | |
1426 | default: | |
1427 | typeInsn(Opcodes.ANEWARRAY, type); | |
1428 | return; | |
1429 | } | |
1430 | mv.visitIntInsn(Opcodes.NEWARRAY, typ); | |
1431 | } | |
1432 | ||
1433 | // ------------------------------------------------------------------------ | |
1434 | // Miscelaneous instructions | |
1435 | // ------------------------------------------------------------------------ | |
1436 | ||
1437 | /** | |
1438 | * Generates the instruction to compute the length of an array. | |
1439 | */ | |
1440 | public void arrayLength(){ | |
1441 | mv.visitInsn(Opcodes.ARRAYLENGTH); | |
1442 | } | |
1443 | ||
1444 | /** | |
1445 | * Generates the instruction to throw an exception. | |
1446 | */ | |
1447 | public void throwException(){ | |
1448 | mv.visitInsn(Opcodes.ATHROW); | |
1449 | } | |
1450 | ||
1451 | /** | |
1452 | * Generates the instructions to create and throw an exception. The | |
1453 | * exception class must have a constructor with a single String argument. | |
1454 | * | |
1455 | * @param type the class of the exception to be thrown. | |
1456 | * @param msg the detailed message of the exception. | |
1457 | */ | |
1458 | public void throwException(final Type type, final String msg){ | |
1459 | newInstance(type); | |
1460 | dup(); | |
1461 | push(msg); | |
1462 | invokeConstructor(type, Method.getMethod("void <init> (String)")); | |
1463 | throwException(); | |
1464 | } | |
1465 | ||
1466 | /** | |
1467 | * Generates the instruction to check that the top stack value is of the | |
1468 | * given type. | |
1469 | * | |
1470 | * @param type a class or interface type. | |
1471 | */ | |
1472 | public void checkCast(final Type type){ | |
1473 | if(!type.equals(OBJECT_TYPE)) | |
1474 | { | |
1475 | typeInsn(Opcodes.CHECKCAST, type); | |
1476 | } | |
1477 | } | |
1478 | ||
1479 | /** | |
1480 | * Generates the instruction to test if the top stack value is of the given | |
1481 | * type. | |
1482 | * | |
1483 | * @param type a class or interface type. | |
1484 | */ | |
1485 | public void instanceOf(final Type type){ | |
1486 | typeInsn(Opcodes.INSTANCEOF, type); | |
1487 | } | |
1488 | ||
1489 | /** | |
1490 | * Generates the instruction to get the monitor of the top stack value. | |
1491 | */ | |
1492 | public void monitorEnter(){ | |
1493 | mv.visitInsn(Opcodes.MONITORENTER); | |
1494 | } | |
1495 | ||
1496 | /** | |
1497 | * Generates the instruction to release the monitor of the top stack value. | |
1498 | */ | |
1499 | public void monitorExit(){ | |
1500 | mv.visitInsn(Opcodes.MONITOREXIT); | |
1501 | } | |
1502 | ||
1503 | // ------------------------------------------------------------------------ | |
1504 | // Non instructions | |
1505 | // ------------------------------------------------------------------------ | |
1506 | ||
1507 | /** | |
1508 | * Marks the end of the visited method. | |
1509 | */ | |
1510 | public void endMethod(){ | |
1511 | if((access & Opcodes.ACC_ABSTRACT) == 0) | |
1512 | { | |
1513 | mv.visitMaxs(0, 0); | |
1514 | } | |
1515 | mv.visitEnd(); | |
1516 | } | |
1517 | ||
1518 | /** | |
1519 | * Marks the start of an exception handler. | |
1520 | * | |
1521 | * @param start beginning of the exception handler's scope (inclusive). | |
1522 | * @param end end of the exception handler's scope (exclusive). | |
1523 | * @param exception internal name of the type of exceptions handled by the | |
1524 | * handler. | |
1525 | */ | |
1526 | public void catchException( | |
1527 | final Label start, | |
1528 | final Label end, | |
1529 | final Type exception){ | |
1530 | mv.visitTryCatchBlock(start, end, mark(), exception.getInternalName()); | |
1531 | } | |
1532 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import clojure.asm.Label; | |
32 | import clojure.asm.MethodAdapter; | |
33 | import clojure.asm.MethodVisitor; | |
34 | import clojure.asm.Opcodes; | |
35 | import clojure.asm.Type; | |
36 | ||
37 | /** | |
38 | * A {@link MethodAdapter} that renumbers local variables in their order of | |
39 | * appearance. This adapter allows one to easily add new local variables to a | |
40 | * method. It may be used by inheriting from this class, but the preferred way | |
41 | * of using it is via delegation: the next visitor in the chain can indeed add | |
42 | * new locals when needed by calling {@link #newLocal} on this adapter (this | |
43 | * requires a reference back to this {@link LocalVariablesSorter}). | |
44 | * | |
45 | * @author Chris Nokleberg | |
46 | * @author Eugene Kuleshov | |
47 | * @author Eric Bruneton | |
48 | */ | |
49 | public class LocalVariablesSorter extends MethodAdapter{ | |
50 | ||
51 | private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); | |
52 | ||
53 | /** | |
54 | * Mapping from old to new local variable indexes. A local variable at index | |
55 | * i of size 1 is remapped to 'mapping[2*i]', while a local variable at | |
56 | * index i of size 2 is remapped to 'mapping[2*i+1]'. | |
57 | */ | |
58 | private int[] mapping = new int[40]; | |
59 | ||
60 | /** | |
61 | * Array used to store stack map local variable types after remapping. | |
62 | */ | |
63 | private Object[] newLocals = new Object[20]; | |
64 | ||
65 | /** | |
66 | * Index of the first local variable, after formal parameters. | |
67 | */ | |
68 | protected final int firstLocal; | |
69 | ||
70 | /** | |
71 | * Index of the next local variable to be created by {@link #newLocal}. | |
72 | */ | |
73 | protected int nextLocal; | |
74 | ||
75 | /** | |
76 | * Indicates if at least one local variable has moved due to remapping. | |
77 | */ | |
78 | private boolean changed; | |
79 | ||
80 | /** | |
81 | * Creates a new {@link LocalVariablesSorter}. | |
82 | * | |
83 | * @param access access flags of the adapted method. | |
84 | * @param desc the method's descriptor (see {@link Type Type}). | |
85 | * @param mv the method visitor to which this adapter delegates calls. | |
86 | */ | |
87 | public LocalVariablesSorter( | |
88 | final int access, | |
89 | final String desc, | |
90 | final MethodVisitor mv){ | |
91 | super(mv); | |
92 | Type[] args = Type.getArgumentTypes(desc); | |
93 | nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1; | |
94 | for(int i = 0; i < args.length; i++) | |
95 | { | |
96 | nextLocal += args[i].getSize(); | |
97 | } | |
98 | firstLocal = nextLocal; | |
99 | } | |
100 | ||
101 | public void visitVarInsn(final int opcode, final int var){ | |
102 | Type type; | |
103 | switch(opcode) | |
104 | { | |
105 | case Opcodes.LLOAD: | |
106 | case Opcodes.LSTORE: | |
107 | type = Type.LONG_TYPE; | |
108 | break; | |
109 | ||
110 | case Opcodes.DLOAD: | |
111 | case Opcodes.DSTORE: | |
112 | type = Type.DOUBLE_TYPE; | |
113 | break; | |
114 | ||
115 | case Opcodes.FLOAD: | |
116 | case Opcodes.FSTORE: | |
117 | type = Type.FLOAT_TYPE; | |
118 | break; | |
119 | ||
120 | case Opcodes.ILOAD: | |
121 | case Opcodes.ISTORE: | |
122 | type = Type.INT_TYPE; | |
123 | break; | |
124 | ||
125 | case Opcodes.ALOAD: | |
126 | case Opcodes.ASTORE: | |
127 | type = OBJECT_TYPE; | |
128 | break; | |
129 | ||
130 | // case RET: | |
131 | default: | |
132 | type = Type.VOID_TYPE; | |
133 | } | |
134 | mv.visitVarInsn(opcode, remap(var, type)); | |
135 | } | |
136 | ||
137 | public void visitIincInsn(final int var, final int increment){ | |
138 | mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); | |
139 | } | |
140 | ||
141 | public void visitMaxs(final int maxStack, final int maxLocals){ | |
142 | mv.visitMaxs(maxStack, nextLocal); | |
143 | } | |
144 | ||
145 | public void visitLocalVariable( | |
146 | final String name, | |
147 | final String desc, | |
148 | final String signature, | |
149 | final Label start, | |
150 | final Label end, | |
151 | final int index){ | |
152 | int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1; | |
153 | int newIndex = remap(index, size); | |
154 | mv.visitLocalVariable(name, desc, signature, start, end, newIndex); | |
155 | } | |
156 | ||
157 | public void visitFrame( | |
158 | final int type, | |
159 | final int nLocal, | |
160 | final Object[] local, | |
161 | final int nStack, | |
162 | final Object[] stack){ | |
163 | if(type != Opcodes.F_NEW) | |
164 | { // uncompressed frame | |
165 | throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); | |
166 | } | |
167 | ||
168 | if(!changed) | |
169 | { // optimization for the case where mapping = identity | |
170 | mv.visitFrame(type, nLocal, local, nStack, stack); | |
171 | return; | |
172 | } | |
173 | ||
174 | // creates a copy of newLocals | |
175 | Object[] oldLocals = new Object[newLocals.length]; | |
176 | System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); | |
177 | ||
178 | // copies types from 'local' to 'newLocals' | |
179 | // 'newLocals' already contains the variables added with 'newLocal' | |
180 | ||
181 | int index = 0; // old local variable index | |
182 | int number = 0; // old local variable number | |
183 | for(; number < nLocal; ++number) | |
184 | { | |
185 | Object t = local[number]; | |
186 | int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; | |
187 | if(t != Opcodes.TOP) | |
188 | { | |
189 | setFrameLocal(remap(index, size), t); | |
190 | } | |
191 | index += size; | |
192 | } | |
193 | ||
194 | // removes TOP after long and double types as well as trailing TOPs | |
195 | ||
196 | index = 0; | |
197 | number = 0; | |
198 | for(int i = 0; index < newLocals.length; ++i) | |
199 | { | |
200 | Object t = newLocals[index++]; | |
201 | if(t != null && t != Opcodes.TOP) | |
202 | { | |
203 | newLocals[i] = t; | |
204 | number = i + 1; | |
205 | if(t == Opcodes.LONG || t == Opcodes.DOUBLE) | |
206 | { | |
207 | index += 1; | |
208 | } | |
209 | } | |
210 | else | |
211 | { | |
212 | newLocals[i] = Opcodes.TOP; | |
213 | } | |
214 | } | |
215 | ||
216 | // visits remapped frame | |
217 | mv.visitFrame(type, number, newLocals, nStack, stack); | |
218 | ||
219 | // restores original value of 'newLocals' | |
220 | newLocals = oldLocals; | |
221 | } | |
222 | ||
223 | // ------------- | |
224 | ||
225 | /** | |
226 | * Creates a new local variable of the given type. | |
227 | * | |
228 | * @param type the type of the local variable to be created. | |
229 | * @return the identifier of the newly created local variable. | |
230 | */ | |
231 | public int newLocal(final Type type){ | |
232 | Object t; | |
233 | switch(type.getSort()) | |
234 | { | |
235 | case Type.BOOLEAN: | |
236 | case Type.CHAR: | |
237 | case Type.BYTE: | |
238 | case Type.SHORT: | |
239 | case Type.INT: | |
240 | t = Opcodes.INTEGER; | |
241 | break; | |
242 | case Type.FLOAT: | |
243 | t = Opcodes.FLOAT; | |
244 | break; | |
245 | case Type.LONG: | |
246 | t = Opcodes.LONG; | |
247 | break; | |
248 | case Type.DOUBLE: | |
249 | t = Opcodes.DOUBLE; | |
250 | break; | |
251 | case Type.ARRAY: | |
252 | t = type.getDescriptor(); | |
253 | break; | |
254 | // case Type.OBJECT: | |
255 | default: | |
256 | t = type.getInternalName(); | |
257 | break; | |
258 | } | |
259 | int local = nextLocal; | |
260 | setLocalType(local, type); | |
261 | setFrameLocal(local, t); | |
262 | nextLocal += type.getSize(); | |
263 | return local; | |
264 | } | |
265 | ||
266 | /** | |
267 | * Sets the current type of the given local variable. The default | |
268 | * implementation of this method does nothing. | |
269 | * | |
270 | * @param local a local variable identifier, as returned by {@link #newLocal | |
271 | * newLocal()}. | |
272 | * @param type the type of the value being stored in the local variable | |
273 | */ | |
274 | protected void setLocalType(final int local, final Type type){ | |
275 | } | |
276 | ||
277 | private void setFrameLocal(final int local, final Object type){ | |
278 | int l = newLocals.length; | |
279 | if(local >= l) | |
280 | { | |
281 | Object[] a = new Object[Math.max(2 * l, local + 1)]; | |
282 | System.arraycopy(newLocals, 0, a, 0, l); | |
283 | newLocals = a; | |
284 | } | |
285 | newLocals[local] = type; | |
286 | } | |
287 | ||
288 | private int remap(final int var, final Type type){ | |
289 | if(var < firstLocal) | |
290 | { | |
291 | return var; | |
292 | } | |
293 | int key = 2 * var + type.getSize() - 1; | |
294 | int size = mapping.length; | |
295 | if(key >= size) | |
296 | { | |
297 | int[] newMapping = new int[Math.max(2 * size, key + 1)]; | |
298 | System.arraycopy(mapping, 0, newMapping, 0, size); | |
299 | mapping = newMapping; | |
300 | } | |
301 | int value = mapping[key]; | |
302 | if(value == 0) | |
303 | { | |
304 | value = nextLocal + 1; | |
305 | mapping[key] = value; | |
306 | setLocalType(nextLocal, type); | |
307 | nextLocal += type.getSize(); | |
308 | } | |
309 | if(value - 1 != var) | |
310 | { | |
311 | changed = true; | |
312 | } | |
313 | return value - 1; | |
314 | } | |
315 | ||
316 | private int remap(final int var, final int size){ | |
317 | if(var < firstLocal || !changed) | |
318 | { | |
319 | return var; | |
320 | } | |
321 | int key = 2 * var + size - 1; | |
322 | int value = key < mapping.length ? mapping[key] : 0; | |
323 | if(value == 0) | |
324 | { | |
325 | throw new IllegalStateException("Unknown local variable " + var); | |
326 | } | |
327 | return value - 1; | |
328 | } | |
329 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import java.util.HashMap; | |
32 | import java.util.Map; | |
33 | ||
34 | import clojure.asm.Type; | |
35 | ||
36 | /** | |
37 | * A named method descriptor. | |
38 | * | |
39 | * @author Juozas Baliuka | |
40 | * @author Chris Nokleberg | |
41 | * @author Eric Bruneton | |
42 | */ | |
43 | public class Method{ | |
44 | ||
45 | /** | |
46 | * The method name. | |
47 | */ | |
48 | private final String name; | |
49 | ||
50 | /** | |
51 | * The method descriptor. | |
52 | */ | |
53 | private final String desc; | |
54 | ||
55 | /** | |
56 | * Maps primitive Java type names to their descriptors. | |
57 | */ | |
58 | private final static Map DESCRIPTORS; | |
59 | ||
60 | static | |
61 | { | |
62 | DESCRIPTORS = new HashMap(); | |
63 | DESCRIPTORS.put("void", "V"); | |
64 | DESCRIPTORS.put("byte", "B"); | |
65 | DESCRIPTORS.put("char", "C"); | |
66 | DESCRIPTORS.put("double", "D"); | |
67 | DESCRIPTORS.put("float", "F"); | |
68 | DESCRIPTORS.put("int", "I"); | |
69 | DESCRIPTORS.put("long", "J"); | |
70 | DESCRIPTORS.put("short", "S"); | |
71 | DESCRIPTORS.put("boolean", "Z"); | |
72 | } | |
73 | ||
74 | /** | |
75 | * Creates a new {@link Method}. | |
76 | * | |
77 | * @param name the method's name. | |
78 | * @param desc the method's descriptor. | |
79 | */ | |
80 | public Method(final String name, final String desc){ | |
81 | this.name = name; | |
82 | this.desc = desc; | |
83 | } | |
84 | ||
85 | /** | |
86 | * Creates a new {@link Method}. | |
87 | * | |
88 | * @param name the method's name. | |
89 | * @param returnType the method's return type. | |
90 | * @param argumentTypes the method's argument types. | |
91 | */ | |
92 | public Method( | |
93 | final String name, | |
94 | final Type returnType, | |
95 | final Type[] argumentTypes){ | |
96 | this(name, Type.getMethodDescriptor(returnType, argumentTypes)); | |
97 | } | |
98 | ||
99 | /** | |
100 | * Returns a {@link Method} corresponding to the given Java method | |
101 | * declaration. | |
102 | * | |
103 | * @param method a Java method declaration, without argument names, of the | |
104 | * form "returnType name (argumentType1, ... argumentTypeN)", where | |
105 | * the types are in plain Java (e.g. "int", "float", | |
106 | * "java.util.List", ...). Classes of the java.lang package can be | |
107 | * specified by their unqualified name; all other classes names must | |
108 | * be fully qualified. | |
109 | * @return a {@link Method} corresponding to the given Java method | |
110 | * declaration. | |
111 | * @throws IllegalArgumentException if <code>method</code> could not get | |
112 | * parsed. | |
113 | */ | |
114 | public static Method getMethod(final String method) | |
115 | throws IllegalArgumentException{ | |
116 | return getMethod(method, false); | |
117 | } | |
118 | ||
119 | /** | |
120 | * Returns a {@link Method} corresponding to the given Java method | |
121 | * declaration. | |
122 | * | |
123 | * @param method a Java method declaration, without argument names, of the | |
124 | * form "returnType name (argumentType1, ... argumentTypeN)", where | |
125 | * the types are in plain Java (e.g. "int", "float", | |
126 | * "java.util.List", ...). Classes of the java.lang package may be | |
127 | * specified by their unqualified name, depending on the | |
128 | * defaultPackage argument; all other classes names must be fully | |
129 | * qualified. | |
130 | * @param defaultPackage true if unqualified class names belong to the | |
131 | * default package, or false if they correspond to java.lang classes. | |
132 | * For instance "Object" means "Object" if this option is true, or | |
133 | * "java.lang.Object" otherwise. | |
134 | * @return a {@link Method} corresponding to the given Java method | |
135 | * declaration. | |
136 | * @throws IllegalArgumentException if <code>method</code> could not get | |
137 | * parsed. | |
138 | */ | |
139 | public static Method getMethod( | |
140 | final String method, | |
141 | final boolean defaultPackage) throws IllegalArgumentException{ | |
142 | int space = method.indexOf(' '); | |
143 | int start = method.indexOf('(', space) + 1; | |
144 | int end = method.indexOf(')', start); | |
145 | if(space == -1 || start == -1 || end == -1) | |
146 | { | |
147 | throw new IllegalArgumentException(); | |
148 | } | |
149 | // TODO: Check validity of returnType, methodName and arguments. | |
150 | String returnType = method.substring(0, space); | |
151 | String methodName = method.substring(space + 1, start - 1).trim(); | |
152 | StringBuffer sb = new StringBuffer(); | |
153 | sb.append('('); | |
154 | int p; | |
155 | do | |
156 | { | |
157 | String s; | |
158 | p = method.indexOf(',', start); | |
159 | if(p == -1) | |
160 | { | |
161 | s = map(method.substring(start, end).trim(), defaultPackage); | |
162 | } | |
163 | else | |
164 | { | |
165 | s = map(method.substring(start, p).trim(), defaultPackage); | |
166 | start = p + 1; | |
167 | } | |
168 | sb.append(s); | |
169 | } while(p != -1); | |
170 | sb.append(')'); | |
171 | sb.append(map(returnType, defaultPackage)); | |
172 | return new Method(methodName, sb.toString()); | |
173 | } | |
174 | ||
175 | private static String map(final String type, final boolean defaultPackage){ | |
176 | if(type.equals("")) | |
177 | { | |
178 | return type; | |
179 | } | |
180 | ||
181 | StringBuffer sb = new StringBuffer(); | |
182 | int index = 0; | |
183 | while((index = type.indexOf("[]", index) + 1) > 0) | |
184 | { | |
185 | sb.append('['); | |
186 | } | |
187 | ||
188 | String t = type.substring(0, type.length() - sb.length() * 2); | |
189 | String desc = (String) DESCRIPTORS.get(t); | |
190 | if(desc != null) | |
191 | { | |
192 | sb.append(desc); | |
193 | } | |
194 | else | |
195 | { | |
196 | sb.append('L'); | |
197 | if(t.indexOf('.') < 0) | |
198 | { | |
199 | if(!defaultPackage) | |
200 | { | |
201 | sb.append("java/lang/"); | |
202 | } | |
203 | sb.append(t); | |
204 | } | |
205 | else | |
206 | { | |
207 | sb.append(t.replace('.', '/')); | |
208 | } | |
209 | sb.append(';'); | |
210 | } | |
211 | return sb.toString(); | |
212 | } | |
213 | ||
214 | /** | |
215 | * Returns the name of the method described by this object. | |
216 | * | |
217 | * @return the name of the method described by this object. | |
218 | */ | |
219 | public String getName(){ | |
220 | return name; | |
221 | } | |
222 | ||
223 | /** | |
224 | * Returns the descriptor of the method described by this object. | |
225 | * | |
226 | * @return the descriptor of the method described by this object. | |
227 | */ | |
228 | public String getDescriptor(){ | |
229 | return desc; | |
230 | } | |
231 | ||
232 | /** | |
233 | * Returns the return type of the method described by this object. | |
234 | * | |
235 | * @return the return type of the method described by this object. | |
236 | */ | |
237 | public Type getReturnType(){ | |
238 | return Type.getReturnType(desc); | |
239 | } | |
240 | ||
241 | /** | |
242 | * Returns the argument types of the method described by this object. | |
243 | * | |
244 | * @return the argument types of the method described by this object. | |
245 | */ | |
246 | public Type[] getArgumentTypes(){ | |
247 | return Type.getArgumentTypes(desc); | |
248 | } | |
249 | ||
250 | public String toString(){ | |
251 | return name + desc; | |
252 | } | |
253 | ||
254 | public boolean equals(final Object o){ | |
255 | if(!(o instanceof Method)) | |
256 | { | |
257 | return false; | |
258 | } | |
259 | Method other = (Method) o; | |
260 | return name.equals(other.name) && desc.equals(other.desc); | |
261 | } | |
262 | ||
263 | public int hashCode(){ | |
264 | return name.hashCode() ^ desc.hashCode(); | |
265 | } | |
266 | }⏎ |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import java.io.ByteArrayOutputStream; | |
32 | import java.io.DataOutputStream; | |
33 | import java.io.IOException; | |
34 | import java.security.MessageDigest; | |
35 | import java.util.ArrayList; | |
36 | import java.util.Arrays; | |
37 | import java.util.Collection; | |
38 | ||
39 | import clojure.asm.ClassAdapter; | |
40 | import clojure.asm.ClassVisitor; | |
41 | import clojure.asm.FieldVisitor; | |
42 | import clojure.asm.MethodVisitor; | |
43 | import clojure.asm.Opcodes; | |
44 | ||
45 | /** | |
46 | * A {@link ClassAdapter} that adds a serial version unique identifier to a | |
47 | * class if missing. Here is typical usage of this class: | |
48 | * <p/> | |
49 | * <pre> | |
50 | * ClassWriter cw = new ClassWriter(...); | |
51 | * ClassVisitor sv = new SerialVersionUIDAdder(cw); | |
52 | * ClassVisitor ca = new MyClassAdapter(sv); | |
53 | * new ClassReader(orginalClass).accept(ca, false); | |
54 | * </pre> | |
55 | * <p/> | |
56 | * The SVUID algorithm can be found <a href= | |
57 | * "http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html" | |
58 | * >http://java.sun.com/j2se/1.4.2/docs/guide/serialization/spec/class.html</a>: | |
59 | * <p/> | |
60 | * <pre> | |
61 | * The serialVersionUID is computed using the signature of a stream of bytes | |
62 | * that reflect the class definition. The National Institute of Standards and | |
63 | * Technology (NIST) Secure Hash Algorithm (SHA-1) is used to compute a | |
64 | * signature for the stream. The first two 32-bit quantities are used to form a | |
65 | * 64-bit hash. A java.lang.DataOutputStream is used to convert primitive data | |
66 | * types to a sequence of bytes. The values input to the stream are defined by | |
67 | * the Java Virtual Machine (VM) specification for classes. | |
68 | * <p/> | |
69 | * The sequence of items in the stream is as follows: | |
70 | * <p/> | |
71 | * 1. The class name written using UTF encoding. | |
72 | * 2. The class modifiers written as a 32-bit integer. | |
73 | * 3. The name of each interface sorted by name written using UTF encoding. | |
74 | * 4. For each field of the class sorted by field name (except private static | |
75 | * and private transient fields): | |
76 | * 1. The name of the field in UTF encoding. | |
77 | * 2. The modifiers of the field written as a 32-bit integer. | |
78 | * 3. The descriptor of the field in UTF encoding | |
79 | * 5. If a class initializer exists, write out the following: | |
80 | * 1. The name of the method, <clinit>, in UTF encoding. | |
81 | * 2. The modifier of the method, java.lang.reflect.Modifier.STATIC, | |
82 | * written as a 32-bit integer. | |
83 | * 3. The descriptor of the method, ()V, in UTF encoding. | |
84 | * 6. For each non-private constructor sorted by method name and signature: | |
85 | * 1. The name of the method, <init>, in UTF encoding. | |
86 | * 2. The modifiers of the method written as a 32-bit integer. | |
87 | * 3. The descriptor of the method in UTF encoding. | |
88 | * 7. For each non-private method sorted by method name and signature: | |
89 | * 1. The name of the method in UTF encoding. | |
90 | * 2. The modifiers of the method written as a 32-bit integer. | |
91 | * 3. The descriptor of the method in UTF encoding. | |
92 | * 8. The SHA-1 algorithm is executed on the stream of bytes produced by | |
93 | * DataOutputStream and produces five 32-bit values sha[0..4]. | |
94 | * <p/> | |
95 | * 9. The hash value is assembled from the first and second 32-bit values of | |
96 | * the SHA-1 message digest. If the result of the message digest, the five | |
97 | * 32-bit words H0 H1 H2 H3 H4, is in an array of five int values named | |
98 | * sha, the hash value would be computed as follows: | |
99 | * <p/> | |
100 | * long hash = ((sha[0] >>> 24) & 0xFF) | | |
101 | * ((sha[0] >>> 16) & 0xFF) << 8 | | |
102 | * ((sha[0] >>> 8) & 0xFF) << 16 | | |
103 | * ((sha[0] >>> 0) & 0xFF) << 24 | | |
104 | * ((sha[1] >>> 24) & 0xFF) << 32 | | |
105 | * ((sha[1] >>> 16) & 0xFF) << 40 | | |
106 | * ((sha[1] >>> 8) & 0xFF) << 48 | | |
107 | * ((sha[1] >>> 0) & 0xFF) << 56; | |
108 | * </pre> | |
109 | * | |
110 | * @author Rajendra Inamdar, Vishal Vishnoi | |
111 | */ | |
112 | public class SerialVersionUIDAdder extends ClassAdapter{ | |
113 | ||
114 | /** | |
115 | * Flag that indicates if we need to compute SVUID. | |
116 | */ | |
117 | protected boolean computeSVUID; | |
118 | ||
119 | /** | |
120 | * Set to true if the class already has SVUID. | |
121 | */ | |
122 | protected boolean hasSVUID; | |
123 | ||
124 | /** | |
125 | * Classes access flags. | |
126 | */ | |
127 | protected int access; | |
128 | ||
129 | /** | |
130 | * Internal name of the class | |
131 | */ | |
132 | protected String name; | |
133 | ||
134 | /** | |
135 | * Interfaces implemented by the class. | |
136 | */ | |
137 | protected String[] interfaces; | |
138 | ||
139 | /** | |
140 | * Collection of fields. (except private static and private transient | |
141 | * fields) | |
142 | */ | |
143 | protected Collection svuidFields; | |
144 | ||
145 | /** | |
146 | * Set to true if the class has static initializer. | |
147 | */ | |
148 | protected boolean hasStaticInitializer; | |
149 | ||
150 | /** | |
151 | * Collection of non-private constructors. | |
152 | */ | |
153 | protected Collection svuidConstructors; | |
154 | ||
155 | /** | |
156 | * Collection of non-private methods. | |
157 | */ | |
158 | protected Collection svuidMethods; | |
159 | ||
160 | /** | |
161 | * Creates a new {@link SerialVersionUIDAdder}. | |
162 | * | |
163 | * @param cv a {@link ClassVisitor} to which this visitor will delegate | |
164 | * calls. | |
165 | */ | |
166 | public SerialVersionUIDAdder(final ClassVisitor cv){ | |
167 | super(cv); | |
168 | svuidFields = new ArrayList(); | |
169 | svuidConstructors = new ArrayList(); | |
170 | svuidMethods = new ArrayList(); | |
171 | } | |
172 | ||
173 | // ------------------------------------------------------------------------ | |
174 | // Overriden methods | |
175 | // ------------------------------------------------------------------------ | |
176 | ||
177 | /* | |
178 | * Visit class header and get class name, access , and intefraces | |
179 | * informatoin (step 1,2, and 3) for SVUID computation. | |
180 | */ | |
181 | ||
182 | public void visit( | |
183 | final int version, | |
184 | final int access, | |
185 | final String name, | |
186 | final String signature, | |
187 | final String superName, | |
188 | final String[] interfaces){ | |
189 | computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0; | |
190 | ||
191 | if(computeSVUID) | |
192 | { | |
193 | this.name = name; | |
194 | this.access = access; | |
195 | this.interfaces = interfaces; | |
196 | } | |
197 | ||
198 | super.visit(version, access, name, signature, superName, interfaces); | |
199 | } | |
200 | ||
201 | /* | |
202 | * Visit the methods and get constructor and method information (step 5 and | |
203 | * 7). Also determince if there is a class initializer (step 6). | |
204 | */ | |
205 | public MethodVisitor visitMethod( | |
206 | final int access, | |
207 | final String name, | |
208 | final String desc, | |
209 | final String signature, | |
210 | final String[] exceptions){ | |
211 | if(computeSVUID) | |
212 | { | |
213 | if(name.equals("<clinit>")) | |
214 | { | |
215 | hasStaticInitializer = true; | |
216 | } | |
217 | /* | |
218 | * Remembers non private constructors and methods for SVUID | |
219 | * computation For constructor and method modifiers, only the | |
220 | * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, | |
221 | * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags | |
222 | * are used. | |
223 | */ | |
224 | int mods = access | |
225 | & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | |
226 | | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC | |
227 | | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED | |
228 | | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT); | |
229 | ||
230 | // all non private methods | |
231 | if((access & Opcodes.ACC_PRIVATE) == 0) | |
232 | { | |
233 | if(name.equals("<init>")) | |
234 | { | |
235 | svuidConstructors.add(new Item(name, mods, desc)); | |
236 | } | |
237 | else if(!name.equals("<clinit>")) | |
238 | { | |
239 | svuidMethods.add(new Item(name, mods, desc)); | |
240 | } | |
241 | } | |
242 | } | |
243 | ||
244 | return cv.visitMethod(access, name, desc, signature, exceptions); | |
245 | } | |
246 | ||
247 | /* | |
248 | * Gets class field information for step 4 of the alogrithm. Also determines | |
249 | * if the class already has a SVUID. | |
250 | */ | |
251 | public FieldVisitor visitField( | |
252 | final int access, | |
253 | final String name, | |
254 | final String desc, | |
255 | final String signature, | |
256 | final Object value){ | |
257 | if(computeSVUID) | |
258 | { | |
259 | if(name.equals("serialVersionUID")) | |
260 | { | |
261 | // since the class already has SVUID, we won't be computing it. | |
262 | computeSVUID = false; | |
263 | hasSVUID = true; | |
264 | } | |
265 | /* | |
266 | * Remember field for SVUID computation For field modifiers, only | |
267 | * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, | |
268 | * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when | |
269 | * computing serialVersionUID values. | |
270 | */ | |
271 | int mods = access | |
272 | & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | |
273 | | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC | |
274 | | Opcodes.ACC_FINAL | Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT); | |
275 | ||
276 | if((access & Opcodes.ACC_PRIVATE) == 0 | |
277 | || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) | |
278 | { | |
279 | svuidFields.add(new Item(name, mods, desc)); | |
280 | } | |
281 | } | |
282 | ||
283 | return super.visitField(access, name, desc, signature, value); | |
284 | } | |
285 | ||
286 | /* | |
287 | * Add the SVUID if class doesn't have one | |
288 | */ | |
289 | public void visitEnd(){ | |
290 | // compute SVUID and add it to the class | |
291 | if(computeSVUID && !hasSVUID) | |
292 | { | |
293 | try | |
294 | { | |
295 | cv.visitField(Opcodes.ACC_FINAL + Opcodes.ACC_STATIC, | |
296 | "serialVersionUID", | |
297 | "J", | |
298 | null, | |
299 | new Long(computeSVUID())); | |
300 | } | |
301 | catch(Throwable e) | |
302 | { | |
303 | throw new RuntimeException("Error while computing SVUID for " | |
304 | + name, e); | |
305 | } | |
306 | } | |
307 | ||
308 | super.visitEnd(); | |
309 | } | |
310 | ||
311 | // ------------------------------------------------------------------------ | |
312 | // Utility methods | |
313 | // ------------------------------------------------------------------------ | |
314 | ||
315 | /** | |
316 | * Returns the value of SVUID if the class doesn't have one already. Please | |
317 | * note that 0 is returned if the class already has SVUID, thus use | |
318 | * <code>isHasSVUID</code> to determine if the class already had an SVUID. | |
319 | * | |
320 | * @return Returns the serial version UID | |
321 | * @throws IOException | |
322 | */ | |
323 | protected long computeSVUID() throws IOException{ | |
324 | ByteArrayOutputStream bos = null; | |
325 | DataOutputStream dos = null; | |
326 | long svuid = 0; | |
327 | ||
328 | try | |
329 | { | |
330 | bos = new ByteArrayOutputStream(); | |
331 | dos = new DataOutputStream(bos); | |
332 | ||
333 | /* | |
334 | * 1. The class name written using UTF encoding. | |
335 | */ | |
336 | dos.writeUTF(name.replace('/', '.')); | |
337 | ||
338 | /* | |
339 | * 2. The class modifiers written as a 32-bit integer. | |
340 | */ | |
341 | dos.writeInt(access | |
342 | & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | |
343 | | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)); | |
344 | ||
345 | /* | |
346 | * 3. The name of each interface sorted by name written using UTF | |
347 | * encoding. | |
348 | */ | |
349 | Arrays.sort(interfaces); | |
350 | for(int i = 0; i < interfaces.length; i++) | |
351 | { | |
352 | dos.writeUTF(interfaces[i].replace('/', '.')); | |
353 | } | |
354 | ||
355 | /* | |
356 | * 4. For each field of the class sorted by field name (except | |
357 | * private static and private transient fields): | |
358 | * | |
359 | * 1. The name of the field in UTF encoding. 2. The modifiers of the | |
360 | * field written as a 32-bit integer. 3. The descriptor of the field | |
361 | * in UTF encoding | |
362 | * | |
363 | * Note that field signatutes are not dot separated. Method and | |
364 | * constructor signatures are dot separated. Go figure... | |
365 | */ | |
366 | writeItems(svuidFields, dos, false); | |
367 | ||
368 | /* | |
369 | * 5. If a class initializer exists, write out the following: 1. The | |
370 | * name of the method, <clinit>, in UTF encoding. 2. The modifier of | |
371 | * the method, java.lang.reflect.Modifier.STATIC, written as a | |
372 | * 32-bit integer. 3. The descriptor of the method, ()V, in UTF | |
373 | * encoding. | |
374 | */ | |
375 | if(hasStaticInitializer) | |
376 | { | |
377 | dos.writeUTF("<clinit>"); | |
378 | dos.writeInt(Opcodes.ACC_STATIC); | |
379 | dos.writeUTF("()V"); | |
380 | } // if.. | |
381 | ||
382 | /* | |
383 | * 6. For each non-private constructor sorted by method name and | |
384 | * signature: 1. The name of the method, <init>, in UTF encoding. 2. | |
385 | * The modifiers of the method written as a 32-bit integer. 3. The | |
386 | * descriptor of the method in UTF encoding. | |
387 | */ | |
388 | writeItems(svuidConstructors, dos, true); | |
389 | ||
390 | /* | |
391 | * 7. For each non-private method sorted by method name and | |
392 | * signature: 1. The name of the method in UTF encoding. 2. The | |
393 | * modifiers of the method written as a 32-bit integer. 3. The | |
394 | * descriptor of the method in UTF encoding. | |
395 | */ | |
396 | writeItems(svuidMethods, dos, true); | |
397 | ||
398 | dos.flush(); | |
399 | ||
400 | /* | |
401 | * 8. The SHA-1 algorithm is executed on the stream of bytes | |
402 | * produced by DataOutputStream and produces five 32-bit values | |
403 | * sha[0..4]. | |
404 | */ | |
405 | byte[] hashBytes = computeSHAdigest(bos.toByteArray()); | |
406 | ||
407 | /* | |
408 | * 9. The hash value is assembled from the first and second 32-bit | |
409 | * values of the SHA-1 message digest. If the result of the message | |
410 | * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of | |
411 | * five int values named sha, the hash value would be computed as | |
412 | * follows: | |
413 | * | |
414 | * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) << | |
415 | * 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) << | |
416 | * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) << | |
417 | * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) << | |
418 | * 56; | |
419 | */ | |
420 | for(int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) | |
421 | { | |
422 | svuid = (svuid << 8) | (hashBytes[i] & 0xFF); | |
423 | } | |
424 | } | |
425 | finally | |
426 | { | |
427 | // close the stream (if open) | |
428 | if(dos != null) | |
429 | { | |
430 | dos.close(); | |
431 | } | |
432 | } | |
433 | ||
434 | return svuid; | |
435 | } | |
436 | ||
437 | /** | |
438 | * Returns the SHA-1 message digest of the given value. | |
439 | * | |
440 | * @param value the value whose SHA message digest must be computed. | |
441 | * @return the SHA-1 message digest of the given value. | |
442 | */ | |
443 | protected byte[] computeSHAdigest(final byte[] value){ | |
444 | try | |
445 | { | |
446 | return MessageDigest.getInstance("SHA").digest(value); | |
447 | } | |
448 | catch(Exception e) | |
449 | { | |
450 | throw new UnsupportedOperationException(e); | |
451 | } | |
452 | } | |
453 | ||
454 | /** | |
455 | * Sorts the items in the collection and writes it to the data output stream | |
456 | * | |
457 | * @param itemCollection collection of items | |
458 | * @param dos a <code>DataOutputStream</code> value | |
459 | * @param dotted a <code>boolean</code> value | |
460 | * @throws IOException if an error occurs | |
461 | */ | |
462 | private void writeItems( | |
463 | final Collection itemCollection, | |
464 | final DataOutputStream dos, | |
465 | final boolean dotted) throws IOException{ | |
466 | int size = itemCollection.size(); | |
467 | Item items[] = (Item[]) itemCollection.toArray(new Item[size]); | |
468 | Arrays.sort(items); | |
469 | for(int i = 0; i < size; i++) | |
470 | { | |
471 | dos.writeUTF(items[i].name); | |
472 | dos.writeInt(items[i].access); | |
473 | dos.writeUTF(dotted | |
474 | ? items[i].desc.replace('/', '.') | |
475 | : items[i].desc); | |
476 | } | |
477 | } | |
478 | ||
479 | // ------------------------------------------------------------------------ | |
480 | // Inner classes | |
481 | // ------------------------------------------------------------------------ | |
482 | ||
483 | static class Item implements Comparable{ | |
484 | ||
485 | String name; | |
486 | ||
487 | int access; | |
488 | ||
489 | String desc; | |
490 | ||
491 | Item(final String name, final int access, final String desc){ | |
492 | this.name = name; | |
493 | this.access = access; | |
494 | this.desc = desc; | |
495 | } | |
496 | ||
497 | public int compareTo(final Object o){ | |
498 | Item other = (Item) o; | |
499 | int retVal = name.compareTo(other.name); | |
500 | if(retVal == 0) | |
501 | { | |
502 | retVal = desc.compareTo(other.desc); | |
503 | } | |
504 | return retVal; | |
505 | } | |
506 | } | |
507 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import clojure.asm.ClassAdapter; | |
32 | import clojure.asm.ClassVisitor; | |
33 | import clojure.asm.MethodVisitor; | |
34 | import clojure.asm.Opcodes; | |
35 | ||
36 | /** | |
37 | * A {@link ClassAdapter} that merges clinit methods into a single one. | |
38 | * | |
39 | * @author Eric Bruneton | |
40 | */ | |
41 | public class StaticInitMerger extends ClassAdapter{ | |
42 | ||
43 | private String name; | |
44 | ||
45 | private MethodVisitor clinit; | |
46 | ||
47 | private String prefix; | |
48 | ||
49 | private int counter; | |
50 | ||
51 | public StaticInitMerger(final String prefix, final ClassVisitor cv){ | |
52 | super(cv); | |
53 | this.prefix = prefix; | |
54 | } | |
55 | ||
56 | public void visit( | |
57 | final int version, | |
58 | final int access, | |
59 | final String name, | |
60 | final String signature, | |
61 | final String superName, | |
62 | final String[] interfaces){ | |
63 | cv.visit(version, access, name, signature, superName, interfaces); | |
64 | this.name = name; | |
65 | } | |
66 | ||
67 | public MethodVisitor visitMethod( | |
68 | final int access, | |
69 | final String name, | |
70 | final String desc, | |
71 | final String signature, | |
72 | final String[] exceptions){ | |
73 | MethodVisitor mv; | |
74 | if(name.equals("<clinit>")) | |
75 | { | |
76 | int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC; | |
77 | String n = prefix + counter++; | |
78 | mv = cv.visitMethod(a, n, desc, signature, exceptions); | |
79 | ||
80 | if(clinit == null) | |
81 | { | |
82 | clinit = cv.visitMethod(a, name, desc, null, null); | |
83 | } | |
84 | clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc); | |
85 | } | |
86 | else | |
87 | { | |
88 | mv = cv.visitMethod(access, name, desc, signature, exceptions); | |
89 | } | |
90 | return mv; | |
91 | } | |
92 | ||
93 | public void visitEnd(){ | |
94 | if(clinit != null) | |
95 | { | |
96 | clinit.visitInsn(Opcodes.RETURN); | |
97 | clinit.visitMaxs(0, 0); | |
98 | } | |
99 | cv.visitEnd(); | |
100 | } | |
101 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2005 INRIA, France Telecom | |
3 | * All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. Neither the name of the copyright holders nor the names of its | |
14 | * contributors may be used to endorse or promote products derived from | |
15 | * this software without specific prior written permission. | |
16 | * | |
17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
18 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
21 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
22 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
23 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
24 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
25 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
26 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
27 | * THE POSSIBILITY OF SUCH DAMAGE. | |
28 | */ | |
29 | package clojure.asm.commons; | |
30 | ||
31 | import clojure.asm.Label; | |
32 | ||
33 | /** | |
34 | * A code generator for switch statements. | |
35 | * | |
36 | * @author Juozas Baliuka | |
37 | * @author Chris Nokleberg | |
38 | * @author Eric Bruneton | |
39 | */ | |
40 | public interface TableSwitchGenerator{ | |
41 | ||
42 | /** | |
43 | * Generates the code for a switch case. | |
44 | * | |
45 | * @param key the switch case key. | |
46 | * @param end a label that corresponds to the end of the switch statement. | |
47 | */ | |
48 | void generateCase(int key, Label end); | |
49 | ||
50 | /** | |
51 | * Generates the code for the default switch case. | |
52 | */ | |
53 | void generateDefault(); | |
54 | } |
0 | <html> | |
1 | <!-- | |
2 | * ASM: a very small and fast Java bytecode manipulation framework | |
3 | * Copyright (c) 2000-2005 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 | <body> | |
31 | Provides some useful class and method adapters. <i>The preferred way of using | |
32 | these adapters is by chaining them together and to custom adapters (instead of | |
33 | inheriting from them)</i>. Indeed this approach provides more combination | |
34 | possibilities than inheritance. For instance, suppose you want to implement an | |
35 | adapter MyAdapter than needs sorted local variables and intermediate stack map | |
36 | frame values taking into account the local variables sort. By using inheritance, | |
37 | this would require MyAdapter to extend AnalyzerAdapter, itself extending | |
38 | LocalVariablesSorter. But AnalyzerAdapter is not a subclass of | |
39 | LocalVariablesSorter, so this is not possible. On the contrary, by using | |
40 | delegation, you can make LocalVariablesSorter delegate to AnalyzerAdapter, | |
41 | itself delegating to MyAdapter. In this case AnalyzerAdapter computes | |
42 | intermediate frames based on the output of LocalVariablesSorter, and MyAdapter | |
43 | can add new locals by calling the newLocal method on LocalVariablesSorter, and | |
44 | can get the stack map frame state before each instruction by reading the locals | |
45 | and stack fields in AnalyzerAdapter (this requires references from MyAdapter | |
46 | back to LocalVariablesSorter and AnalyzerAdapter). | |
47 | </body>⏎ |
0 | <html> | |
1 | <!-- | |
2 | * ASM: a very small and fast Java bytecode manipulation framework | |
3 | * Copyright (c) 2000-2005 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 | <body> | |
31 | Provides a small and fast bytecode manipulation framework. | |
32 | ||
33 | <p> | |
34 | The <a href="http://www.objectweb.org/asm">ASM</a> framework is organized | |
35 | around the {@link clojure.asm.ClassVisitor ClassVisitor}, | |
36 | {@link clojure.asm.FieldVisitor FieldVisitor} and | |
37 | {@link clojure.asm.MethodVisitor MethodVisitor} interfaces, which allow | |
38 | one to visit the fields and methods of a class, including the bytecode | |
39 | instructions of each method. | |
40 | ||
41 | <p> | |
42 | In addition to these main interfaces, ASM provides a {@link | |
43 | clojure.asm.ClassReader ClassReader} class, that can parse an | |
44 | existing class and make a given visitor visit it. ASM also provides | |
45 | a {@link clojure.asm.ClassWriter ClassWriter} class, which is | |
46 | a visitor that generates Java class files. | |
47 | ||
48 | <p> | |
49 | In order to generate a class from scratch, only the {@link | |
50 | clojure.asm.ClassWriter ClassWriter} class is necessary. Indeed, | |
51 | in order to generate a class, one must just call its visit<i>XXX</i> | |
52 | methods with the appropriate arguments to generate the desired fields | |
53 | and methods. See the "helloworld" example in the ASM distribution for | |
54 | more details about class generation. | |
55 | ||
56 | <p> | |
57 | In order to modify existing classes, one must use a {@link | |
58 | clojure.asm.ClassReader ClassReader} class to analyze | |
59 | the original class, a class modifier, and a {@link clojure.asm.ClassWriter | |
60 | ClassWriter} to construct the modified class. The class modifier | |
61 | is just a {@link clojure.asm.ClassVisitor ClassVisitor} | |
62 | that delegates most of the work to another {@link clojure.asm.ClassVisitor | |
63 | ClassVisitor}, but that sometimes changes some parameter values, | |
64 | or call additional methods, in order to implement the desired | |
65 | modification process. In order to make it easier to implement such | |
66 | class modifiers, ASM provides the {@link clojure.asm.ClassAdapter | |
67 | ClassAdapter} and {@link clojure.asm.MethodAdapter MethodAdapter} | |
68 | classes, which implement the {@link clojure.asm.ClassVisitor ClassVisitor} | |
69 | and {@link clojure.asm.MethodVisitor MethodVisitor} interfaces by | |
70 | delegating all work to other visitors. See the "adapt" example in the ASM | |
71 | distribution for more details about class modification. | |
72 | ||
73 | <p> | |
74 | The size of the core ASM library, <tt>asm.jar</tt>, is only 42KB, which is much | |
75 | smaller than the size of the | |
76 | <a href="http://jakarta.apache.org/bcel">BCEL</a> library (504KB), and than the | |
77 | size of the | |
78 | <a href="http://serp.sourceforge.net">SERP</a> library (150KB). ASM is also | |
79 | much faster than these tools. Indeed the overhead of a load time class | |
80 | transformation process is of the order of 60% with ASM, 700% or more with BCEL, | |
81 | and 1100% or more with SERP (see the <tt>test/perf</tt> directory in the ASM | |
82 | distribution)! | |
83 | ||
84 | @since ASM 1.3 | |
85 | </body> | |
86 | </html> |