Removed third-party files
Daigo Moriwaki
9 years ago
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 class 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 abstract class AnnotationVisitor { | |
40 | ||
41 | /** | |
42 | * The ASM API version implemented by this visitor. The value of this field | |
43 | * must be one of {@link Opcodes#ASM4}. | |
44 | */ | |
45 | protected final int api; | |
46 | ||
47 | /** | |
48 | * The annotation visitor to which this visitor must delegate method calls. | |
49 | * May be null. | |
50 | */ | |
51 | protected AnnotationVisitor av; | |
52 | ||
53 | /** | |
54 | * Constructs a new {@link AnnotationVisitor}. | |
55 | * | |
56 | * @param api | |
57 | * the ASM API version implemented by this visitor. Must be one | |
58 | * of {@link Opcodes#ASM4}. | |
59 | */ | |
60 | public AnnotationVisitor(final int api) { | |
61 | this(api, null); | |
62 | } | |
63 | ||
64 | /** | |
65 | * Constructs a new {@link AnnotationVisitor}. | |
66 | * | |
67 | * @param api | |
68 | * the ASM API version implemented by this visitor. Must be one | |
69 | * of {@link Opcodes#ASM4}. | |
70 | * @param av | |
71 | * the annotation visitor to which this visitor must delegate | |
72 | * method calls. May be null. | |
73 | */ | |
74 | public AnnotationVisitor(final int api, final AnnotationVisitor av) { | |
75 | if (api != Opcodes.ASM4) { | |
76 | throw new IllegalArgumentException(); | |
77 | } | |
78 | this.api = api; | |
79 | this.av = av; | |
80 | } | |
81 | ||
82 | /** | |
83 | * Visits a primitive value of the annotation. | |
84 | * | |
85 | * @param name | |
86 | * the value name. | |
87 | * @param value | |
88 | * the actual value, whose type must be {@link Byte}, | |
89 | * {@link Boolean}, {@link Character}, {@link Short}, | |
90 | * {@link Integer} , {@link Long}, {@link Float}, {@link Double}, | |
91 | * {@link String} or {@link Type} or OBJECT or ARRAY sort. This | |
92 | * value can also be an array of byte, boolean, short, char, int, | |
93 | * long, float or double values (this is equivalent to using | |
94 | * {@link #visitArray visitArray} and visiting each array element | |
95 | * in turn, but is more convenient). | |
96 | */ | |
97 | public void visit(String name, Object value) { | |
98 | if (av != null) { | |
99 | av.visit(name, value); | |
100 | } | |
101 | } | |
102 | ||
103 | /** | |
104 | * Visits an enumeration value of the annotation. | |
105 | * | |
106 | * @param name | |
107 | * the value name. | |
108 | * @param desc | |
109 | * the class descriptor of the enumeration class. | |
110 | * @param value | |
111 | * the actual enumeration value. | |
112 | */ | |
113 | public void visitEnum(String name, String desc, String value) { | |
114 | if (av != null) { | |
115 | av.visitEnum(name, desc, value); | |
116 | } | |
117 | } | |
118 | ||
119 | /** | |
120 | * Visits a nested annotation value of the annotation. | |
121 | * | |
122 | * @param name | |
123 | * the value name. | |
124 | * @param desc | |
125 | * the class descriptor of the nested annotation class. | |
126 | * @return a visitor to visit the actual nested annotation value, or | |
127 | * <tt>null</tt> if this visitor is not interested in visiting this | |
128 | * nested annotation. <i>The nested annotation value must be fully | |
129 | * visited before calling other methods on this annotation | |
130 | * visitor</i>. | |
131 | */ | |
132 | public AnnotationVisitor visitAnnotation(String name, String desc) { | |
133 | if (av != null) { | |
134 | return av.visitAnnotation(name, desc); | |
135 | } | |
136 | return null; | |
137 | } | |
138 | ||
139 | /** | |
140 | * Visits an array value of the annotation. Note that arrays of primitive | |
141 | * types (such as byte, boolean, short, char, int, long, float or double) | |
142 | * can be passed as value to {@link #visit visit}. This is what | |
143 | * {@link ClassReader} does. | |
144 | * | |
145 | * @param name | |
146 | * the value name. | |
147 | * @return a visitor to visit the actual array value elements, or | |
148 | * <tt>null</tt> if this visitor is not interested in visiting these | |
149 | * values. The 'name' parameters passed to the methods of this | |
150 | * visitor are ignored. <i>All the array values must be visited | |
151 | * before calling other methods on this annotation visitor</i>. | |
152 | */ | |
153 | public AnnotationVisitor visitArray(String name) { | |
154 | if (av != null) { | |
155 | return av.visitArray(name); | |
156 | } | |
157 | return null; | |
158 | } | |
159 | ||
160 | /** | |
161 | * Visits the end of the annotation. | |
162 | */ | |
163 | public void visitEnd() { | |
164 | if (av != null) { | |
165 | av.visitEnd(); | |
166 | } | |
167 | } | |
168 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 extends 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 | |
93 | * the class writer to which this annotation must be added. | |
94 | * @param named | |
95 | * <tt>true<tt> if values are named, <tt>false</tt> otherwise. | |
96 | * @param bv | |
97 | * where the annotation values must be stored. | |
98 | * @param parent | |
99 | * where the number of annotation values must be stored. | |
100 | * @param offset | |
101 | * where in <tt>parent</tt> the number of annotation values must | |
102 | * be stored. | |
103 | */ | |
104 | AnnotationWriter(final ClassWriter cw, final boolean named, | |
105 | final ByteVector bv, final ByteVector parent, final int offset) { | |
106 | super(Opcodes.ASM4); | |
107 | this.cw = cw; | |
108 | this.named = named; | |
109 | this.bv = bv; | |
110 | this.parent = parent; | |
111 | this.offset = offset; | |
112 | } | |
113 | ||
114 | // ------------------------------------------------------------------------ | |
115 | // Implementation of the AnnotationVisitor abstract class | |
116 | // ------------------------------------------------------------------------ | |
117 | ||
118 | @Override | |
119 | public void visit(final String name, final Object value) { | |
120 | ++size; | |
121 | if (named) { | |
122 | bv.putShort(cw.newUTF8(name)); | |
123 | } | |
124 | if (value instanceof String) { | |
125 | bv.put12('s', cw.newUTF8((String) value)); | |
126 | } else if (value instanceof Byte) { | |
127 | bv.put12('B', cw.newInteger(((Byte) value).byteValue()).index); | |
128 | } else if (value instanceof Boolean) { | |
129 | int v = ((Boolean) value).booleanValue() ? 1 : 0; | |
130 | bv.put12('Z', cw.newInteger(v).index); | |
131 | } else if (value instanceof Character) { | |
132 | bv.put12('C', cw.newInteger(((Character) value).charValue()).index); | |
133 | } else if (value instanceof Short) { | |
134 | bv.put12('S', cw.newInteger(((Short) value).shortValue()).index); | |
135 | } else if (value instanceof Type) { | |
136 | bv.put12('c', cw.newUTF8(((Type) value).getDescriptor())); | |
137 | } else if (value instanceof byte[]) { | |
138 | byte[] v = (byte[]) value; | |
139 | bv.put12('[', v.length); | |
140 | for (int i = 0; i < v.length; i++) { | |
141 | bv.put12('B', cw.newInteger(v[i]).index); | |
142 | } | |
143 | } else if (value instanceof boolean[]) { | |
144 | boolean[] v = (boolean[]) value; | |
145 | bv.put12('[', v.length); | |
146 | for (int i = 0; i < v.length; i++) { | |
147 | bv.put12('Z', cw.newInteger(v[i] ? 1 : 0).index); | |
148 | } | |
149 | } else if (value instanceof short[]) { | |
150 | short[] v = (short[]) value; | |
151 | bv.put12('[', v.length); | |
152 | for (int i = 0; i < v.length; i++) { | |
153 | bv.put12('S', cw.newInteger(v[i]).index); | |
154 | } | |
155 | } else if (value instanceof char[]) { | |
156 | char[] v = (char[]) value; | |
157 | bv.put12('[', v.length); | |
158 | for (int i = 0; i < v.length; i++) { | |
159 | bv.put12('C', cw.newInteger(v[i]).index); | |
160 | } | |
161 | } else if (value instanceof int[]) { | |
162 | int[] v = (int[]) value; | |
163 | bv.put12('[', v.length); | |
164 | for (int i = 0; i < v.length; i++) { | |
165 | bv.put12('I', cw.newInteger(v[i]).index); | |
166 | } | |
167 | } else if (value instanceof long[]) { | |
168 | long[] v = (long[]) value; | |
169 | bv.put12('[', v.length); | |
170 | for (int i = 0; i < v.length; i++) { | |
171 | bv.put12('J', cw.newLong(v[i]).index); | |
172 | } | |
173 | } else if (value instanceof float[]) { | |
174 | float[] v = (float[]) value; | |
175 | bv.put12('[', v.length); | |
176 | for (int i = 0; i < v.length; i++) { | |
177 | bv.put12('F', cw.newFloat(v[i]).index); | |
178 | } | |
179 | } else if (value instanceof double[]) { | |
180 | double[] v = (double[]) value; | |
181 | bv.put12('[', v.length); | |
182 | for (int i = 0; i < v.length; i++) { | |
183 | bv.put12('D', cw.newDouble(v[i]).index); | |
184 | } | |
185 | } else { | |
186 | Item i = cw.newConstItem(value); | |
187 | bv.put12(".s.IFJDCS".charAt(i.type), i.index); | |
188 | } | |
189 | } | |
190 | ||
191 | @Override | |
192 | public void visitEnum(final String name, final String desc, | |
193 | final String value) { | |
194 | ++size; | |
195 | if (named) { | |
196 | bv.putShort(cw.newUTF8(name)); | |
197 | } | |
198 | bv.put12('e', cw.newUTF8(desc)).putShort(cw.newUTF8(value)); | |
199 | } | |
200 | ||
201 | @Override | |
202 | public AnnotationVisitor visitAnnotation(final String name, | |
203 | final String desc) { | |
204 | ++size; | |
205 | if (named) { | |
206 | bv.putShort(cw.newUTF8(name)); | |
207 | } | |
208 | // write tag and type, and reserve space for values count | |
209 | bv.put12('@', cw.newUTF8(desc)).putShort(0); | |
210 | return new AnnotationWriter(cw, true, bv, bv, bv.length - 2); | |
211 | } | |
212 | ||
213 | @Override | |
214 | public AnnotationVisitor visitArray(final String name) { | |
215 | ++size; | |
216 | if (named) { | |
217 | bv.putShort(cw.newUTF8(name)); | |
218 | } | |
219 | // write tag, and reserve space for array size | |
220 | bv.put12('[', 0); | |
221 | return new AnnotationWriter(cw, false, bv, bv, bv.length - 2); | |
222 | } | |
223 | ||
224 | @Override | |
225 | public void visitEnd() { | |
226 | if (parent != null) { | |
227 | byte[] data = parent.data; | |
228 | data[offset] = (byte) (size >>> 8); | |
229 | data[offset + 1] = (byte) size; | |
230 | } | |
231 | } | |
232 | ||
233 | // ------------------------------------------------------------------------ | |
234 | // Utility methods | |
235 | // ------------------------------------------------------------------------ | |
236 | ||
237 | /** | |
238 | * Returns the size of this annotation writer list. | |
239 | * | |
240 | * @return the size of this annotation writer list. | |
241 | */ | |
242 | int getSize() { | |
243 | int size = 0; | |
244 | AnnotationWriter aw = this; | |
245 | while (aw != null) { | |
246 | size += aw.bv.length; | |
247 | aw = aw.next; | |
248 | } | |
249 | return size; | |
250 | } | |
251 | ||
252 | /** | |
253 | * Puts the annotations of this annotation writer list into the given byte | |
254 | * vector. | |
255 | * | |
256 | * @param out | |
257 | * where the annotations must be put. | |
258 | */ | |
259 | void put(final ByteVector out) { | |
260 | int n = 0; | |
261 | int size = 2; | |
262 | AnnotationWriter aw = this; | |
263 | AnnotationWriter last = null; | |
264 | while (aw != null) { | |
265 | ++n; | |
266 | size += aw.bv.length; | |
267 | aw.visitEnd(); // in case user forgot to call visitEnd | |
268 | aw.prev = last; | |
269 | last = aw; | |
270 | aw = aw.next; | |
271 | } | |
272 | out.putInt(size); | |
273 | out.putShort(n); | |
274 | aw = last; | |
275 | while (aw != null) { | |
276 | out.putByteArray(aw.bv.data, 0, aw.bv.length); | |
277 | aw = aw.prev; | |
278 | } | |
279 | } | |
280 | ||
281 | /** | |
282 | * Puts the given annotation lists into the given byte vector. | |
283 | * | |
284 | * @param panns | |
285 | * an array of annotation writer lists. | |
286 | * @param off | |
287 | * index of the first annotation to be written. | |
288 | * @param out | |
289 | * where the annotations must be put. | |
290 | */ | |
291 | static void put(final AnnotationWriter[] panns, final int off, | |
292 | final ByteVector out) { | |
293 | int size = 1 + 2 * (panns.length - off); | |
294 | for (int i = off; i < panns.length; ++i) { | |
295 | size += panns[i] == null ? 0 : panns[i].getSize(); | |
296 | } | |
297 | out.putInt(size).putByte(panns.length - off); | |
298 | for (int i = off; i < panns.length; ++i) { | |
299 | AnnotationWriter aw = panns[i]; | |
300 | AnnotationWriter last = null; | |
301 | int n = 0; | |
302 | while (aw != null) { | |
303 | ++n; | |
304 | aw.visitEnd(); // in case user forgot to call visitEnd | |
305 | aw.prev = last; | |
306 | last = aw; | |
307 | aw = aw.next; | |
308 | } | |
309 | out.putShort(n); | |
310 | aw = last; | |
311 | while (aw != null) { | |
312 | out.putByteArray(aw.bv.data, 0, aw.bv.length); | |
313 | aw = aw.prev; | |
314 | } | |
315 | } | |
316 | } | |
317 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | |
58 | * the type of the attribute. | |
59 | */ | |
60 | protected Attribute(final String type) { | |
61 | this.type = type; | |
62 | } | |
63 | ||
64 | /** | |
65 | * Returns <tt>true</tt> if this type of attribute is unknown. The default | |
66 | * implementation of this method always returns <tt>true</tt>. | |
67 | * | |
68 | * @return <tt>true</tt> if this type of attribute is unknown. | |
69 | */ | |
70 | public boolean isUnknown() { | |
71 | return true; | |
72 | } | |
73 | ||
74 | /** | |
75 | * Returns <tt>true</tt> if this type of attribute is a code attribute. | |
76 | * | |
77 | * @return <tt>true</tt> if this type of attribute is a code attribute. | |
78 | */ | |
79 | public boolean isCodeAttribute() { | |
80 | return false; | |
81 | } | |
82 | ||
83 | /** | |
84 | * Returns the labels corresponding to this attribute. | |
85 | * | |
86 | * @return the labels corresponding to this attribute, or <tt>null</tt> if | |
87 | * this attribute is not a code attribute that contains labels. | |
88 | */ | |
89 | protected Label[] getLabels() { | |
90 | return null; | |
91 | } | |
92 | ||
93 | /** | |
94 | * Reads a {@link #type type} attribute. This method must return a | |
95 | * <i>new</i> {@link Attribute} object, of type {@link #type type}, | |
96 | * corresponding to the <tt>len</tt> bytes starting at the given offset, in | |
97 | * the given class reader. | |
98 | * | |
99 | * @param cr | |
100 | * the class that contains the attribute to be read. | |
101 | * @param off | |
102 | * index of the first byte of the attribute's content in | |
103 | * {@link ClassReader#b cr.b}. The 6 attribute header bytes, | |
104 | * containing the type and the length of the attribute, are not | |
105 | * taken into account here. | |
106 | * @param len | |
107 | * the length of the attribute's content. | |
108 | * @param buf | |
109 | * buffer to be used to call {@link ClassReader#readUTF8 | |
110 | * readUTF8}, {@link ClassReader#readClass(int,char[]) readClass} | |
111 | * or {@link ClassReader#readConst readConst}. | |
112 | * @param codeOff | |
113 | * index of the first byte of code's attribute content in | |
114 | * {@link ClassReader#b cr.b}, or -1 if the attribute to be read | |
115 | * is not a code attribute. The 6 attribute header bytes, | |
116 | * containing the type and the length of the attribute, are not | |
117 | * taken into account here. | |
118 | * @param labels | |
119 | * the labels of the method's code, or <tt>null</tt> if the | |
120 | * attribute to be read is not a code attribute. | |
121 | * @return a <i>new</i> {@link Attribute} object corresponding to the given | |
122 | * bytes. | |
123 | */ | |
124 | protected Attribute read(final ClassReader cr, final int off, | |
125 | final int len, final char[] buf, final int codeOff, | |
126 | final Label[] labels) { | |
127 | Attribute attr = new Attribute(type); | |
128 | attr.value = new byte[len]; | |
129 | System.arraycopy(cr.b, off, attr.value, 0, len); | |
130 | return attr; | |
131 | } | |
132 | ||
133 | /** | |
134 | * Returns the byte array form of this attribute. | |
135 | * | |
136 | * @param cw | |
137 | * the class to which this attribute must be added. This | |
138 | * parameter can be used to add to the constant pool of this | |
139 | * class the items that corresponds to this attribute. | |
140 | * @param code | |
141 | * the bytecode of the method corresponding to this code | |
142 | * attribute, or <tt>null</tt> if this attribute is not a code | |
143 | * attributes. | |
144 | * @param len | |
145 | * the length of the bytecode of the method corresponding to this | |
146 | * code attribute, or <tt>null</tt> if this attribute is not a | |
147 | * code attribute. | |
148 | * @param maxStack | |
149 | * the maximum stack size of the method corresponding to this | |
150 | * code attribute, or -1 if this attribute is not a code | |
151 | * attribute. | |
152 | * @param maxLocals | |
153 | * the maximum number of local variables of the method | |
154 | * corresponding to this code attribute, or -1 if this attribute | |
155 | * is not a code attribute. | |
156 | * @return the byte array form of this attribute. | |
157 | */ | |
158 | protected ByteVector write(final ClassWriter cw, final byte[] code, | |
159 | final int len, final int maxStack, final int maxLocals) { | |
160 | ByteVector v = new ByteVector(); | |
161 | v.data = value; | |
162 | v.length = value.length; | |
163 | return v; | |
164 | } | |
165 | ||
166 | /** | |
167 | * Returns the length of the attribute list that begins with this attribute. | |
168 | * | |
169 | * @return the length of the attribute list that begins with this attribute. | |
170 | */ | |
171 | final int getCount() { | |
172 | int count = 0; | |
173 | Attribute attr = this; | |
174 | while (attr != null) { | |
175 | count += 1; | |
176 | attr = attr.next; | |
177 | } | |
178 | return count; | |
179 | } | |
180 | ||
181 | /** | |
182 | * Returns the size of all the attributes in this attribute list. | |
183 | * | |
184 | * @param cw | |
185 | * the class writer to be used to convert the attributes into | |
186 | * byte arrays, with the {@link #write write} method. | |
187 | * @param code | |
188 | * the bytecode of the method corresponding to these code | |
189 | * attributes, or <tt>null</tt> if these attributes are not code | |
190 | * attributes. | |
191 | * @param len | |
192 | * the length of the bytecode of the method corresponding to | |
193 | * these code attributes, or <tt>null</tt> if these attributes | |
194 | * are not code attributes. | |
195 | * @param maxStack | |
196 | * the maximum stack size of the method corresponding to these | |
197 | * code attributes, or -1 if these attributes are not code | |
198 | * attributes. | |
199 | * @param maxLocals | |
200 | * the maximum number of local variables of the method | |
201 | * corresponding to these code attributes, or -1 if these | |
202 | * attributes are not code attributes. | |
203 | * @return the size of all the attributes in this attribute list. This size | |
204 | * includes the size of the attribute headers. | |
205 | */ | |
206 | final int getSize(final ClassWriter cw, final byte[] code, final int len, | |
207 | final int maxStack, final int maxLocals) { | |
208 | Attribute attr = this; | |
209 | int size = 0; | |
210 | while (attr != null) { | |
211 | cw.newUTF8(attr.type); | |
212 | size += attr.write(cw, code, len, maxStack, maxLocals).length + 6; | |
213 | attr = attr.next; | |
214 | } | |
215 | return size; | |
216 | } | |
217 | ||
218 | /** | |
219 | * Writes all the attributes of this attribute list in the given byte | |
220 | * vector. | |
221 | * | |
222 | * @param cw | |
223 | * the class writer to be used to convert the attributes into | |
224 | * byte arrays, with the {@link #write write} method. | |
225 | * @param code | |
226 | * the bytecode of the method corresponding to these code | |
227 | * attributes, or <tt>null</tt> if these attributes are not code | |
228 | * attributes. | |
229 | * @param len | |
230 | * the length of the bytecode of the method corresponding to | |
231 | * these code attributes, or <tt>null</tt> if these attributes | |
232 | * are not code attributes. | |
233 | * @param maxStack | |
234 | * the maximum stack size of the method corresponding to these | |
235 | * code attributes, or -1 if these attributes are not code | |
236 | * attributes. | |
237 | * @param maxLocals | |
238 | * the maximum number of local variables of the method | |
239 | * corresponding to these code attributes, or -1 if these | |
240 | * attributes are not code attributes. | |
241 | * @param out | |
242 | * where the attributes must be written. | |
243 | */ | |
244 | final void put(final ClassWriter cw, final byte[] code, final int len, | |
245 | final int maxStack, final int maxLocals, final ByteVector out) { | |
246 | Attribute attr = this; | |
247 | while (attr != null) { | |
248 | ByteVector b = attr.write(cw, code, len, maxStack, maxLocals); | |
249 | out.putShort(cw.newUTF8(attr.type)).putInt(b.length); | |
250 | out.putByteArray(b.data, 0, b.length); | |
251 | attr = attr.next; | |
252 | } | |
253 | } | |
254 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | |
62 | * the initial size of the byte vector to be constructed. | |
63 | */ | |
64 | public ByteVector(final int initialSize) { | |
65 | data = new byte[initialSize]; | |
66 | } | |
67 | ||
68 | /** | |
69 | * Puts a byte into this byte vector. The byte vector is automatically | |
70 | * enlarged if necessary. | |
71 | * | |
72 | * @param b | |
73 | * a byte. | |
74 | * @return this byte vector. | |
75 | */ | |
76 | public ByteVector putByte(final int b) { | |
77 | int length = this.length; | |
78 | if (length + 1 > data.length) { | |
79 | enlarge(1); | |
80 | } | |
81 | data[length++] = (byte) b; | |
82 | this.length = length; | |
83 | return this; | |
84 | } | |
85 | ||
86 | /** | |
87 | * Puts two bytes into this byte vector. The byte vector is automatically | |
88 | * enlarged if necessary. | |
89 | * | |
90 | * @param b1 | |
91 | * a byte. | |
92 | * @param b2 | |
93 | * another byte. | |
94 | * @return this byte vector. | |
95 | */ | |
96 | ByteVector put11(final int b1, final int b2) { | |
97 | int length = this.length; | |
98 | if (length + 2 > data.length) { | |
99 | enlarge(2); | |
100 | } | |
101 | byte[] data = this.data; | |
102 | data[length++] = (byte) b1; | |
103 | data[length++] = (byte) b2; | |
104 | this.length = length; | |
105 | return this; | |
106 | } | |
107 | ||
108 | /** | |
109 | * Puts a short into this byte vector. The byte vector is automatically | |
110 | * enlarged if necessary. | |
111 | * | |
112 | * @param s | |
113 | * a short. | |
114 | * @return this byte vector. | |
115 | */ | |
116 | public ByteVector putShort(final int s) { | |
117 | int length = this.length; | |
118 | if (length + 2 > data.length) { | |
119 | enlarge(2); | |
120 | } | |
121 | byte[] data = this.data; | |
122 | data[length++] = (byte) (s >>> 8); | |
123 | data[length++] = (byte) s; | |
124 | this.length = length; | |
125 | return this; | |
126 | } | |
127 | ||
128 | /** | |
129 | * Puts a byte and a short into this byte vector. The byte vector is | |
130 | * automatically enlarged if necessary. | |
131 | * | |
132 | * @param b | |
133 | * a byte. | |
134 | * @param s | |
135 | * a short. | |
136 | * @return this byte vector. | |
137 | */ | |
138 | ByteVector put12(final int b, final int s) { | |
139 | int length = this.length; | |
140 | if (length + 3 > data.length) { | |
141 | enlarge(3); | |
142 | } | |
143 | byte[] data = this.data; | |
144 | data[length++] = (byte) b; | |
145 | data[length++] = (byte) (s >>> 8); | |
146 | data[length++] = (byte) s; | |
147 | this.length = length; | |
148 | return this; | |
149 | } | |
150 | ||
151 | /** | |
152 | * Puts an int into this byte vector. The byte vector is automatically | |
153 | * enlarged if necessary. | |
154 | * | |
155 | * @param i | |
156 | * an int. | |
157 | * @return this byte vector. | |
158 | */ | |
159 | public ByteVector putInt(final int i) { | |
160 | int length = this.length; | |
161 | if (length + 4 > data.length) { | |
162 | enlarge(4); | |
163 | } | |
164 | byte[] data = this.data; | |
165 | data[length++] = (byte) (i >>> 24); | |
166 | data[length++] = (byte) (i >>> 16); | |
167 | data[length++] = (byte) (i >>> 8); | |
168 | data[length++] = (byte) i; | |
169 | this.length = length; | |
170 | return this; | |
171 | } | |
172 | ||
173 | /** | |
174 | * Puts a long into this byte vector. The byte vector is automatically | |
175 | * enlarged if necessary. | |
176 | * | |
177 | * @param l | |
178 | * a long. | |
179 | * @return this byte vector. | |
180 | */ | |
181 | public ByteVector putLong(final long l) { | |
182 | int length = this.length; | |
183 | if (length + 8 > data.length) { | |
184 | enlarge(8); | |
185 | } | |
186 | byte[] data = this.data; | |
187 | int i = (int) (l >>> 32); | |
188 | data[length++] = (byte) (i >>> 24); | |
189 | data[length++] = (byte) (i >>> 16); | |
190 | data[length++] = (byte) (i >>> 8); | |
191 | data[length++] = (byte) i; | |
192 | i = (int) l; | |
193 | data[length++] = (byte) (i >>> 24); | |
194 | data[length++] = (byte) (i >>> 16); | |
195 | data[length++] = (byte) (i >>> 8); | |
196 | data[length++] = (byte) i; | |
197 | this.length = length; | |
198 | return this; | |
199 | } | |
200 | ||
201 | /** | |
202 | * Puts an UTF8 string into this byte vector. The byte vector is | |
203 | * automatically enlarged if necessary. | |
204 | * | |
205 | * @param s | |
206 | * a String. | |
207 | * @return this byte vector. | |
208 | */ | |
209 | public ByteVector putUTF8(final String s) { | |
210 | int charLength = s.length(); | |
211 | int len = length; | |
212 | if (len + 2 + charLength > data.length) { | |
213 | enlarge(2 + charLength); | |
214 | } | |
215 | byte[] data = this.data; | |
216 | // optimistic algorithm: instead of computing the byte length and then | |
217 | // serializing the string (which requires two loops), we assume the byte | |
218 | // length is equal to char length (which is the most frequent case), and | |
219 | // we start serializing the string right away. During the serialization, | |
220 | // if we find that this assumption is wrong, we continue with the | |
221 | // general method. | |
222 | data[len++] = (byte) (charLength >>> 8); | |
223 | data[len++] = (byte) charLength; | |
224 | for (int i = 0; i < charLength; ++i) { | |
225 | char c = s.charAt(i); | |
226 | if (c >= '\001' && c <= '\177') { | |
227 | data[len++] = (byte) c; | |
228 | } else { | |
229 | int byteLength = i; | |
230 | for (int j = i; j < charLength; ++j) { | |
231 | c = s.charAt(j); | |
232 | if (c >= '\001' && c <= '\177') { | |
233 | byteLength++; | |
234 | } else if (c > '\u07FF') { | |
235 | byteLength += 3; | |
236 | } else { | |
237 | byteLength += 2; | |
238 | } | |
239 | } | |
240 | data[length] = (byte) (byteLength >>> 8); | |
241 | data[length + 1] = (byte) byteLength; | |
242 | if (length + 2 + byteLength > data.length) { | |
243 | length = len; | |
244 | enlarge(2 + byteLength); | |
245 | data = this.data; | |
246 | } | |
247 | for (int j = i; j < charLength; ++j) { | |
248 | c = s.charAt(j); | |
249 | if (c >= '\001' && c <= '\177') { | |
250 | data[len++] = (byte) c; | |
251 | } else if (c > '\u07FF') { | |
252 | data[len++] = (byte) (0xE0 | c >> 12 & 0xF); | |
253 | data[len++] = (byte) (0x80 | c >> 6 & 0x3F); | |
254 | data[len++] = (byte) (0x80 | c & 0x3F); | |
255 | } else { | |
256 | data[len++] = (byte) (0xC0 | c >> 6 & 0x1F); | |
257 | data[len++] = (byte) (0x80 | c & 0x3F); | |
258 | } | |
259 | } | |
260 | break; | |
261 | } | |
262 | } | |
263 | length = len; | |
264 | return this; | |
265 | } | |
266 | ||
267 | /** | |
268 | * Puts an array of bytes into this byte vector. The byte vector is | |
269 | * automatically enlarged if necessary. | |
270 | * | |
271 | * @param b | |
272 | * an array of bytes. May be <tt>null</tt> to put <tt>len</tt> | |
273 | * null bytes into this byte vector. | |
274 | * @param off | |
275 | * index of the fist byte of b that must be copied. | |
276 | * @param len | |
277 | * number of bytes of b that must be copied. | |
278 | * @return this byte vector. | |
279 | */ | |
280 | public ByteVector putByteArray(final byte[] b, final int off, final int len) { | |
281 | if (length + len > data.length) { | |
282 | enlarge(len); | |
283 | } | |
284 | if (b != null) { | |
285 | System.arraycopy(b, off, data, length, len); | |
286 | } | |
287 | length += len; | |
288 | return this; | |
289 | } | |
290 | ||
291 | /** | |
292 | * Enlarge this byte vector so that it can receive n more bytes. | |
293 | * | |
294 | * @param size | |
295 | * number of additional bytes that this byte vector should be | |
296 | * able to receive. | |
297 | */ | |
298 | private void enlarge(final int size) { | |
299 | int length1 = 2 * data.length; | |
300 | int length2 = length + size; | |
301 | byte[] newData = new byte[length1 > length2 ? length1 : length2]; | |
302 | System.arraycopy(data, 0, newData, 0, length); | |
303 | data = newData; | |
304 | } | |
305 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.IOException; | |
32 | import java.io.InputStream; | |
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 | * True to enable signatures support. | |
47 | */ | |
48 | static final boolean SIGNATURES = true; | |
49 | ||
50 | /** | |
51 | * True to enable annotations support. | |
52 | */ | |
53 | static final boolean ANNOTATIONS = true; | |
54 | ||
55 | /** | |
56 | * True to enable stack map frames support. | |
57 | */ | |
58 | static final boolean FRAMES = true; | |
59 | ||
60 | /** | |
61 | * True to enable bytecode writing support. | |
62 | */ | |
63 | static final boolean WRITER = true; | |
64 | ||
65 | /** | |
66 | * True to enable JSR_W and GOTO_W support. | |
67 | */ | |
68 | static final boolean RESIZE = true; | |
69 | ||
70 | /** | |
71 | * Flag to skip method code. If this class is set <code>CODE</code> | |
72 | * attribute won't be visited. This can be used, for example, to retrieve | |
73 | * annotations for methods and method parameters. | |
74 | */ | |
75 | public static final int SKIP_CODE = 1; | |
76 | ||
77 | /** | |
78 | * Flag to skip the debug information in the class. If this flag is set the | |
79 | * debug information of the class is not visited, i.e. the | |
80 | * {@link MethodVisitor#visitLocalVariable visitLocalVariable} and | |
81 | * {@link MethodVisitor#visitLineNumber visitLineNumber} methods will not be | |
82 | * called. | |
83 | */ | |
84 | public static final int SKIP_DEBUG = 2; | |
85 | ||
86 | /** | |
87 | * Flag to skip the stack map frames in the class. If this flag is set the | |
88 | * stack map frames of the class is not visited, i.e. the | |
89 | * {@link MethodVisitor#visitFrame visitFrame} method will not be called. | |
90 | * This flag is useful when the {@link ClassWriter#COMPUTE_FRAMES} option is | |
91 | * used: it avoids visiting frames that will be ignored and recomputed from | |
92 | * scratch in the class writer. | |
93 | */ | |
94 | public static final int SKIP_FRAMES = 4; | |
95 | ||
96 | /** | |
97 | * Flag to expand the stack map frames. By default stack map frames are | |
98 | * visited in their original format (i.e. "expanded" for classes whose | |
99 | * version is less than V1_6, and "compressed" for the other classes). If | |
100 | * this flag is set, stack map frames are always visited in expanded format | |
101 | * (this option adds a decompression/recompression step in ClassReader and | |
102 | * ClassWriter which degrades performances quite a lot). | |
103 | */ | |
104 | public static final int EXPAND_FRAMES = 8; | |
105 | ||
106 | /** | |
107 | * The class to be parsed. <i>The content of this array must not be | |
108 | * modified. This field is intended for {@link Attribute} sub classes, and | |
109 | * is normally not needed by class generators or adapters.</i> | |
110 | */ | |
111 | public final byte[] b; | |
112 | ||
113 | /** | |
114 | * The start index of each constant pool item in {@link #b b}, plus one. The | |
115 | * one byte offset skips the constant pool item tag that indicates its type. | |
116 | */ | |
117 | private final int[] items; | |
118 | ||
119 | /** | |
120 | * The String objects corresponding to the CONSTANT_Utf8 items. This cache | |
121 | * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item, | |
122 | * which GREATLY improves performances (by a factor 2 to 3). This caching | |
123 | * strategy could be extended to all constant pool items, but its benefit | |
124 | * would not be so great for these items (because they are much less | |
125 | * expensive to parse than CONSTANT_Utf8 items). | |
126 | */ | |
127 | private final String[] strings; | |
128 | ||
129 | /** | |
130 | * Maximum length of the strings contained in the constant pool of the | |
131 | * class. | |
132 | */ | |
133 | private final int maxStringLength; | |
134 | ||
135 | /** | |
136 | * Start index of the class header information (access, name...) in | |
137 | * {@link #b b}. | |
138 | */ | |
139 | public final int header; | |
140 | ||
141 | // ------------------------------------------------------------------------ | |
142 | // Constructors | |
143 | // ------------------------------------------------------------------------ | |
144 | ||
145 | /** | |
146 | * Constructs a new {@link ClassReader} object. | |
147 | * | |
148 | * @param b | |
149 | * the bytecode of the class to be read. | |
150 | */ | |
151 | public ClassReader(final byte[] b) { | |
152 | this(b, 0, b.length); | |
153 | } | |
154 | ||
155 | /** | |
156 | * Constructs a new {@link ClassReader} object. | |
157 | * | |
158 | * @param b | |
159 | * the bytecode of the class to be read. | |
160 | * @param off | |
161 | * the start offset of the class data. | |
162 | * @param len | |
163 | * the length of the class data. | |
164 | */ | |
165 | public ClassReader(final byte[] b, final int off, final int len) { | |
166 | this.b = b; | |
167 | // checks the class version | |
168 | if (readShort(off + 6) > Opcodes.V1_7) { | |
169 | throw new IllegalArgumentException(); | |
170 | } | |
171 | // parses the constant pool | |
172 | items = new int[readUnsignedShort(off + 8)]; | |
173 | int n = items.length; | |
174 | strings = new String[n]; | |
175 | int max = 0; | |
176 | int index = off + 10; | |
177 | for (int i = 1; i < n; ++i) { | |
178 | items[i] = index + 1; | |
179 | int size; | |
180 | switch (b[index]) { | |
181 | case ClassWriter.FIELD: | |
182 | case ClassWriter.METH: | |
183 | case ClassWriter.IMETH: | |
184 | case ClassWriter.INT: | |
185 | case ClassWriter.FLOAT: | |
186 | case ClassWriter.NAME_TYPE: | |
187 | case ClassWriter.INDY: | |
188 | size = 5; | |
189 | break; | |
190 | case ClassWriter.LONG: | |
191 | case ClassWriter.DOUBLE: | |
192 | size = 9; | |
193 | ++i; | |
194 | break; | |
195 | case ClassWriter.UTF8: | |
196 | size = 3 + readUnsignedShort(index + 1); | |
197 | if (size > max) { | |
198 | max = size; | |
199 | } | |
200 | break; | |
201 | case ClassWriter.HANDLE: | |
202 | size = 4; | |
203 | break; | |
204 | // case ClassWriter.CLASS: | |
205 | // case ClassWriter.STR: | |
206 | // case ClassWriter.MTYPE | |
207 | default: | |
208 | size = 3; | |
209 | break; | |
210 | } | |
211 | index += size; | |
212 | } | |
213 | maxStringLength = max; | |
214 | // the class header information starts just after the constant pool | |
215 | header = index; | |
216 | } | |
217 | ||
218 | /** | |
219 | * Returns the class's access flags (see {@link Opcodes}). This value may | |
220 | * not reflect Deprecated and Synthetic flags when bytecode is before 1.5 | |
221 | * and those flags are represented by attributes. | |
222 | * | |
223 | * @return the class access flags | |
224 | * | |
225 | * @see ClassVisitor#visit(int, int, String, String, String, String[]) | |
226 | */ | |
227 | public int getAccess() { | |
228 | return readUnsignedShort(header); | |
229 | } | |
230 | ||
231 | /** | |
232 | * Returns the internal name of the class (see | |
233 | * {@link Type#getInternalName() getInternalName}). | |
234 | * | |
235 | * @return the internal class name | |
236 | * | |
237 | * @see ClassVisitor#visit(int, int, String, String, String, String[]) | |
238 | */ | |
239 | public String getClassName() { | |
240 | return readClass(header + 2, new char[maxStringLength]); | |
241 | } | |
242 | ||
243 | /** | |
244 | * Returns the internal of name of the super class (see | |
245 | * {@link Type#getInternalName() getInternalName}). For interfaces, the | |
246 | * super class is {@link Object}. | |
247 | * | |
248 | * @return the internal name of super class, or <tt>null</tt> for | |
249 | * {@link Object} class. | |
250 | * | |
251 | * @see ClassVisitor#visit(int, int, String, String, String, String[]) | |
252 | */ | |
253 | public String getSuperName() { | |
254 | return readClass(header + 4, new char[maxStringLength]); | |
255 | } | |
256 | ||
257 | /** | |
258 | * Returns the internal names of the class's interfaces (see | |
259 | * {@link Type#getInternalName() getInternalName}). | |
260 | * | |
261 | * @return the array of internal names for all implemented interfaces or | |
262 | * <tt>null</tt>. | |
263 | * | |
264 | * @see ClassVisitor#visit(int, int, String, String, String, String[]) | |
265 | */ | |
266 | public String[] getInterfaces() { | |
267 | int index = header + 6; | |
268 | int n = readUnsignedShort(index); | |
269 | String[] interfaces = new String[n]; | |
270 | if (n > 0) { | |
271 | char[] buf = new char[maxStringLength]; | |
272 | for (int i = 0; i < n; ++i) { | |
273 | index += 2; | |
274 | interfaces[i] = readClass(index, buf); | |
275 | } | |
276 | } | |
277 | return interfaces; | |
278 | } | |
279 | ||
280 | /** | |
281 | * Copies the constant pool data into the given {@link ClassWriter}. Should | |
282 | * be called before the {@link #accept(ClassVisitor,int)} method. | |
283 | * | |
284 | * @param classWriter | |
285 | * the {@link ClassWriter} to copy constant pool into. | |
286 | */ | |
287 | void copyPool(final ClassWriter classWriter) { | |
288 | char[] buf = new char[maxStringLength]; | |
289 | int ll = items.length; | |
290 | Item[] items2 = new Item[ll]; | |
291 | for (int i = 1; i < ll; i++) { | |
292 | int index = items[i]; | |
293 | int tag = b[index - 1]; | |
294 | Item item = new Item(i); | |
295 | int nameType; | |
296 | switch (tag) { | |
297 | case ClassWriter.FIELD: | |
298 | case ClassWriter.METH: | |
299 | case ClassWriter.IMETH: | |
300 | nameType = items[readUnsignedShort(index + 2)]; | |
301 | item.set(tag, readClass(index, buf), readUTF8(nameType, buf), | |
302 | readUTF8(nameType + 2, buf)); | |
303 | break; | |
304 | case ClassWriter.INT: | |
305 | item.set(readInt(index)); | |
306 | break; | |
307 | case ClassWriter.FLOAT: | |
308 | item.set(Float.intBitsToFloat(readInt(index))); | |
309 | break; | |
310 | case ClassWriter.NAME_TYPE: | |
311 | item.set(tag, readUTF8(index, buf), readUTF8(index + 2, buf), | |
312 | null); | |
313 | break; | |
314 | case ClassWriter.LONG: | |
315 | item.set(readLong(index)); | |
316 | ++i; | |
317 | break; | |
318 | case ClassWriter.DOUBLE: | |
319 | item.set(Double.longBitsToDouble(readLong(index))); | |
320 | ++i; | |
321 | break; | |
322 | case ClassWriter.UTF8: { | |
323 | String s = strings[i]; | |
324 | if (s == null) { | |
325 | index = items[i]; | |
326 | s = strings[i] = readUTF(index + 2, | |
327 | readUnsignedShort(index), buf); | |
328 | } | |
329 | item.set(tag, s, null, null); | |
330 | break; | |
331 | } | |
332 | case ClassWriter.HANDLE: { | |
333 | int fieldOrMethodRef = items[readUnsignedShort(index + 1)]; | |
334 | nameType = items[readUnsignedShort(fieldOrMethodRef + 2)]; | |
335 | item.set(ClassWriter.HANDLE_BASE + readByte(index), | |
336 | readClass(fieldOrMethodRef, buf), | |
337 | readUTF8(nameType, buf), readUTF8(nameType + 2, buf)); | |
338 | break; | |
339 | } | |
340 | case ClassWriter.INDY: | |
341 | if (classWriter.bootstrapMethods == null) { | |
342 | copyBootstrapMethods(classWriter, items2, buf); | |
343 | } | |
344 | nameType = items[readUnsignedShort(index + 2)]; | |
345 | item.set(readUTF8(nameType, buf), readUTF8(nameType + 2, buf), | |
346 | readUnsignedShort(index)); | |
347 | break; | |
348 | // case ClassWriter.STR: | |
349 | // case ClassWriter.CLASS: | |
350 | // case ClassWriter.MTYPE | |
351 | default: | |
352 | item.set(tag, readUTF8(index, buf), null, null); | |
353 | break; | |
354 | } | |
355 | ||
356 | int index2 = item.hashCode % items2.length; | |
357 | item.next = items2[index2]; | |
358 | items2[index2] = item; | |
359 | } | |
360 | ||
361 | int off = items[1] - 1; | |
362 | classWriter.pool.putByteArray(b, off, header - off); | |
363 | classWriter.items = items2; | |
364 | classWriter.threshold = (int) (0.75d * ll); | |
365 | classWriter.index = ll; | |
366 | } | |
367 | ||
368 | /** | |
369 | * Copies the bootstrap method data into the given {@link ClassWriter}. | |
370 | * Should be called before the {@link #accept(ClassVisitor,int)} method. | |
371 | * | |
372 | * @param classWriter | |
373 | * the {@link ClassWriter} to copy bootstrap methods into. | |
374 | */ | |
375 | private void copyBootstrapMethods(final ClassWriter classWriter, | |
376 | final Item[] items, final char[] c) { | |
377 | // finds the "BootstrapMethods" attribute | |
378 | int u = getAttributes(); | |
379 | boolean found = false; | |
380 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
381 | String attrName = readUTF8(u + 2, c); | |
382 | if ("BootstrapMethods".equals(attrName)) { | |
383 | found = true; | |
384 | break; | |
385 | } | |
386 | u += 6 + readInt(u + 4); | |
387 | } | |
388 | if (!found) { | |
389 | return; | |
390 | } | |
391 | // copies the bootstrap methods in the class writer | |
392 | int boostrapMethodCount = readUnsignedShort(u + 8); | |
393 | for (int j = 0, v = u + 10; j < boostrapMethodCount; j++) { | |
394 | int position = v - u - 10; | |
395 | int hashCode = readConst(readUnsignedShort(v), c).hashCode(); | |
396 | for (int k = readUnsignedShort(v + 2); k > 0; --k) { | |
397 | hashCode ^= readConst(readUnsignedShort(v + 4), c).hashCode(); | |
398 | v += 2; | |
399 | } | |
400 | v += 4; | |
401 | Item item = new Item(j); | |
402 | item.set(position, hashCode & 0x7FFFFFFF); | |
403 | int index = item.hashCode % items.length; | |
404 | item.next = items[index]; | |
405 | items[index] = item; | |
406 | } | |
407 | int attrSize = readInt(u + 4); | |
408 | ByteVector bootstrapMethods = new ByteVector(attrSize + 62); | |
409 | bootstrapMethods.putByteArray(b, u + 10, attrSize - 2); | |
410 | classWriter.bootstrapMethodsCount = boostrapMethodCount; | |
411 | classWriter.bootstrapMethods = bootstrapMethods; | |
412 | } | |
413 | ||
414 | /** | |
415 | * Constructs a new {@link ClassReader} object. | |
416 | * | |
417 | * @param is | |
418 | * an input stream from which to read the class. | |
419 | * @throws IOException | |
420 | * if a problem occurs during reading. | |
421 | */ | |
422 | public ClassReader(final InputStream is) throws IOException { | |
423 | this(readClass(is, false)); | |
424 | } | |
425 | ||
426 | /** | |
427 | * Constructs a new {@link ClassReader} object. | |
428 | * | |
429 | * @param name | |
430 | * the binary qualified name of the class to be read. | |
431 | * @throws IOException | |
432 | * if an exception occurs during reading. | |
433 | */ | |
434 | public ClassReader(final String name) throws IOException { | |
435 | this(readClass( | |
436 | ClassLoader.getSystemResourceAsStream(name.replace('.', '/') | |
437 | + ".class"), true)); | |
438 | } | |
439 | ||
440 | /** | |
441 | * Reads the bytecode of a class. | |
442 | * | |
443 | * @param is | |
444 | * an input stream from which to read the class. | |
445 | * @param close | |
446 | * true to close the input stream after reading. | |
447 | * @return the bytecode read from the given input stream. | |
448 | * @throws IOException | |
449 | * if a problem occurs during reading. | |
450 | */ | |
451 | private static byte[] readClass(final InputStream is, boolean close) | |
452 | throws IOException { | |
453 | if (is == null) { | |
454 | throw new IOException("Class not found"); | |
455 | } | |
456 | try { | |
457 | byte[] b = new byte[is.available()]; | |
458 | int len = 0; | |
459 | while (true) { | |
460 | int n = is.read(b, len, b.length - len); | |
461 | if (n == -1) { | |
462 | if (len < b.length) { | |
463 | byte[] c = new byte[len]; | |
464 | System.arraycopy(b, 0, c, 0, len); | |
465 | b = c; | |
466 | } | |
467 | return b; | |
468 | } | |
469 | len += n; | |
470 | if (len == b.length) { | |
471 | int last = is.read(); | |
472 | if (last < 0) { | |
473 | return b; | |
474 | } | |
475 | byte[] c = new byte[b.length + 1000]; | |
476 | System.arraycopy(b, 0, c, 0, len); | |
477 | c[len++] = (byte) last; | |
478 | b = c; | |
479 | } | |
480 | } | |
481 | } finally { | |
482 | if (close) { | |
483 | is.close(); | |
484 | } | |
485 | } | |
486 | } | |
487 | ||
488 | // ------------------------------------------------------------------------ | |
489 | // Public methods | |
490 | // ------------------------------------------------------------------------ | |
491 | ||
492 | /** | |
493 | * Makes the given visitor visit the Java class of this {@link ClassReader} | |
494 | * . This class is the one specified in the constructor (see | |
495 | * {@link #ClassReader(byte[]) ClassReader}). | |
496 | * | |
497 | * @param classVisitor | |
498 | * the visitor that must visit this class. | |
499 | * @param flags | |
500 | * option flags that can be used to modify the default behavior | |
501 | * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} | |
502 | * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. | |
503 | */ | |
504 | public void accept(final ClassVisitor classVisitor, final int flags) { | |
505 | accept(classVisitor, new Attribute[0], flags); | |
506 | } | |
507 | ||
508 | /** | |
509 | * Makes the given visitor visit the Java class of this {@link ClassReader}. | |
510 | * This class is the one specified in the constructor (see | |
511 | * {@link #ClassReader(byte[]) ClassReader}). | |
512 | * | |
513 | * @param classVisitor | |
514 | * the visitor that must visit this class. | |
515 | * @param attrs | |
516 | * prototypes of the attributes that must be parsed during the | |
517 | * visit of the class. Any attribute whose type is not equal to | |
518 | * the type of one the prototypes will not be parsed: its byte | |
519 | * array value will be passed unchanged to the ClassWriter. | |
520 | * <i>This may corrupt it if this value contains references to | |
521 | * the constant pool, or has syntactic or semantic links with a | |
522 | * class element that has been transformed by a class adapter | |
523 | * between the reader and the writer</i>. | |
524 | * @param flags | |
525 | * option flags that can be used to modify the default behavior | |
526 | * of this class. See {@link #SKIP_DEBUG}, {@link #EXPAND_FRAMES} | |
527 | * , {@link #SKIP_FRAMES}, {@link #SKIP_CODE}. | |
528 | */ | |
529 | public void accept(final ClassVisitor classVisitor, | |
530 | final Attribute[] attrs, final int flags) { | |
531 | int u = header; // current offset in the class file | |
532 | char[] c = new char[maxStringLength]; // buffer used to read strings | |
533 | ||
534 | Context context = new Context(); | |
535 | context.attrs = attrs; | |
536 | context.flags = flags; | |
537 | context.buffer = c; | |
538 | ||
539 | // reads the class declaration | |
540 | int access = readUnsignedShort(u); | |
541 | String name = readClass(u + 2, c); | |
542 | String superClass = readClass(u + 4, c); | |
543 | String[] interfaces = new String[readUnsignedShort(u + 6)]; | |
544 | u += 8; | |
545 | for (int i = 0; i < interfaces.length; ++i) { | |
546 | interfaces[i] = readClass(u, c); | |
547 | u += 2; | |
548 | } | |
549 | ||
550 | // reads the class attributes | |
551 | String signature = null; | |
552 | String sourceFile = null; | |
553 | String sourceDebug = null; | |
554 | String enclosingOwner = null; | |
555 | String enclosingName = null; | |
556 | String enclosingDesc = null; | |
557 | int anns = 0; | |
558 | int ianns = 0; | |
559 | int innerClasses = 0; | |
560 | Attribute attributes = null; | |
561 | ||
562 | u = getAttributes(); | |
563 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
564 | String attrName = readUTF8(u + 2, c); | |
565 | // tests are sorted in decreasing frequency order | |
566 | // (based on frequencies observed on typical classes) | |
567 | if ("SourceFile".equals(attrName)) { | |
568 | sourceFile = readUTF8(u + 8, c); | |
569 | } else if ("InnerClasses".equals(attrName)) { | |
570 | innerClasses = u + 8; | |
571 | } else if ("EnclosingMethod".equals(attrName)) { | |
572 | enclosingOwner = readClass(u + 8, c); | |
573 | int item = readUnsignedShort(u + 10); | |
574 | if (item != 0) { | |
575 | enclosingName = readUTF8(items[item], c); | |
576 | enclosingDesc = readUTF8(items[item] + 2, c); | |
577 | } | |
578 | } else if (SIGNATURES && "Signature".equals(attrName)) { | |
579 | signature = readUTF8(u + 8, c); | |
580 | } else if (ANNOTATIONS | |
581 | && "RuntimeVisibleAnnotations".equals(attrName)) { | |
582 | anns = u + 8; | |
583 | } else if ("Deprecated".equals(attrName)) { | |
584 | access |= Opcodes.ACC_DEPRECATED; | |
585 | } else if ("Synthetic".equals(attrName)) { | |
586 | access |= Opcodes.ACC_SYNTHETIC | |
587 | | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; | |
588 | } else if ("SourceDebugExtension".equals(attrName)) { | |
589 | int len = readInt(u + 4); | |
590 | sourceDebug = readUTF(u + 8, len, new char[len]); | |
591 | } else if (ANNOTATIONS | |
592 | && "RuntimeInvisibleAnnotations".equals(attrName)) { | |
593 | ianns = u + 8; | |
594 | } else if ("BootstrapMethods".equals(attrName)) { | |
595 | int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; | |
596 | for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { | |
597 | bootstrapMethods[j] = v; | |
598 | v += 2 + readUnsignedShort(v + 2) << 1; | |
599 | } | |
600 | context.bootstrapMethods = bootstrapMethods; | |
601 | } else { | |
602 | Attribute attr = readAttribute(attrs, attrName, u + 8, | |
603 | readInt(u + 4), c, -1, null); | |
604 | if (attr != null) { | |
605 | attr.next = attributes; | |
606 | attributes = attr; | |
607 | } | |
608 | } | |
609 | u += 6 + readInt(u + 4); | |
610 | } | |
611 | ||
612 | // visits the class declaration | |
613 | classVisitor.visit(readInt(items[1] - 7), access, name, signature, | |
614 | superClass, interfaces); | |
615 | ||
616 | // visits the source and debug info | |
617 | if ((flags & SKIP_DEBUG) == 0 | |
618 | && (sourceFile != null || sourceDebug != null)) { | |
619 | classVisitor.visitSource(sourceFile, sourceDebug); | |
620 | } | |
621 | ||
622 | // visits the outer class | |
623 | if (enclosingOwner != null) { | |
624 | classVisitor.visitOuterClass(enclosingOwner, enclosingName, | |
625 | enclosingDesc); | |
626 | } | |
627 | ||
628 | // visits the class annotations | |
629 | if (ANNOTATIONS && anns != 0) { | |
630 | for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { | |
631 | v = readAnnotationValues(v + 2, c, true, | |
632 | classVisitor.visitAnnotation(readUTF8(v, c), true)); | |
633 | } | |
634 | } | |
635 | if (ANNOTATIONS && ianns != 0) { | |
636 | for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { | |
637 | v = readAnnotationValues(v + 2, c, true, | |
638 | classVisitor.visitAnnotation(readUTF8(v, c), false)); | |
639 | } | |
640 | } | |
641 | ||
642 | // visits the attributes | |
643 | while (attributes != null) { | |
644 | Attribute attr = attributes.next; | |
645 | attributes.next = null; | |
646 | classVisitor.visitAttribute(attributes); | |
647 | attributes = attr; | |
648 | } | |
649 | ||
650 | // visits the inner classes | |
651 | if (innerClasses != 0) { | |
652 | int v = innerClasses + 2; | |
653 | for (int i = readUnsignedShort(innerClasses); i > 0; --i) { | |
654 | classVisitor.visitInnerClass(readClass(v, c), | |
655 | readClass(v + 2, c), readUTF8(v + 4, c), | |
656 | readUnsignedShort(v + 6)); | |
657 | v += 8; | |
658 | } | |
659 | } | |
660 | ||
661 | // visits the fields and methods | |
662 | u = header + 10 + 2 * interfaces.length; | |
663 | for (int i = readUnsignedShort(u - 2); i > 0; --i) { | |
664 | u = readField(classVisitor, context, u); | |
665 | } | |
666 | u += 2; | |
667 | for (int i = readUnsignedShort(u - 2); i > 0; --i) { | |
668 | u = readMethod(classVisitor, context, u); | |
669 | } | |
670 | ||
671 | // visits the end of the class | |
672 | classVisitor.visitEnd(); | |
673 | } | |
674 | ||
675 | /** | |
676 | * Reads a field and makes the given visitor visit it. | |
677 | * | |
678 | * @param classVisitor | |
679 | * the visitor that must visit the field. | |
680 | * @param context | |
681 | * information about the class being parsed. | |
682 | * @param u | |
683 | * the start offset of the field in the class file. | |
684 | * @return the offset of the first byte following the field in the class. | |
685 | */ | |
686 | private int readField(final ClassVisitor classVisitor, | |
687 | final Context context, int u) { | |
688 | // reads the field declaration | |
689 | char[] c = context.buffer; | |
690 | int access = readUnsignedShort(u); | |
691 | String name = readUTF8(u + 2, c); | |
692 | String desc = readUTF8(u + 4, c); | |
693 | u += 6; | |
694 | ||
695 | // reads the field attributes | |
696 | String signature = null; | |
697 | int anns = 0; | |
698 | int ianns = 0; | |
699 | Object value = null; | |
700 | Attribute attributes = null; | |
701 | ||
702 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
703 | String attrName = readUTF8(u + 2, c); | |
704 | // tests are sorted in decreasing frequency order | |
705 | // (based on frequencies observed on typical classes) | |
706 | if ("ConstantValue".equals(attrName)) { | |
707 | int item = readUnsignedShort(u + 8); | |
708 | value = item == 0 ? null : readConst(item, c); | |
709 | } else if (SIGNATURES && "Signature".equals(attrName)) { | |
710 | signature = readUTF8(u + 8, c); | |
711 | } else if ("Deprecated".equals(attrName)) { | |
712 | access |= Opcodes.ACC_DEPRECATED; | |
713 | } else if ("Synthetic".equals(attrName)) { | |
714 | access |= Opcodes.ACC_SYNTHETIC | |
715 | | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; | |
716 | } else if (ANNOTATIONS | |
717 | && "RuntimeVisibleAnnotations".equals(attrName)) { | |
718 | anns = u + 8; | |
719 | } else if (ANNOTATIONS | |
720 | && "RuntimeInvisibleAnnotations".equals(attrName)) { | |
721 | ianns = u + 8; | |
722 | } else { | |
723 | Attribute attr = readAttribute(context.attrs, attrName, u + 8, | |
724 | readInt(u + 4), c, -1, null); | |
725 | if (attr != null) { | |
726 | attr.next = attributes; | |
727 | attributes = attr; | |
728 | } | |
729 | } | |
730 | u += 6 + readInt(u + 4); | |
731 | } | |
732 | u += 2; | |
733 | ||
734 | // visits the field declaration | |
735 | FieldVisitor fv = classVisitor.visitField(access, name, desc, | |
736 | signature, value); | |
737 | if (fv == null) { | |
738 | return u; | |
739 | } | |
740 | ||
741 | // visits the field annotations | |
742 | if (ANNOTATIONS && anns != 0) { | |
743 | for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { | |
744 | v = readAnnotationValues(v + 2, c, true, | |
745 | fv.visitAnnotation(readUTF8(v, c), true)); | |
746 | } | |
747 | } | |
748 | if (ANNOTATIONS && ianns != 0) { | |
749 | for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { | |
750 | v = readAnnotationValues(v + 2, c, true, | |
751 | fv.visitAnnotation(readUTF8(v, c), false)); | |
752 | } | |
753 | } | |
754 | ||
755 | // visits the field attributes | |
756 | while (attributes != null) { | |
757 | Attribute attr = attributes.next; | |
758 | attributes.next = null; | |
759 | fv.visitAttribute(attributes); | |
760 | attributes = attr; | |
761 | } | |
762 | ||
763 | // visits the end of the field | |
764 | fv.visitEnd(); | |
765 | ||
766 | return u; | |
767 | } | |
768 | ||
769 | /** | |
770 | * Reads a method and makes the given visitor visit it. | |
771 | * | |
772 | * @param classVisitor | |
773 | * the visitor that must visit the method. | |
774 | * @param context | |
775 | * information about the class being parsed. | |
776 | * @param u | |
777 | * the start offset of the method in the class file. | |
778 | * @return the offset of the first byte following the method in the class. | |
779 | */ | |
780 | private int readMethod(final ClassVisitor classVisitor, | |
781 | final Context context, int u) { | |
782 | // reads the method declaration | |
783 | char[] c = context.buffer; | |
784 | int access = readUnsignedShort(u); | |
785 | String name = readUTF8(u + 2, c); | |
786 | String desc = readUTF8(u + 4, c); | |
787 | u += 6; | |
788 | ||
789 | // reads the method attributes | |
790 | int code = 0; | |
791 | int exception = 0; | |
792 | String[] exceptions = null; | |
793 | String signature = null; | |
794 | int anns = 0; | |
795 | int ianns = 0; | |
796 | int dann = 0; | |
797 | int mpanns = 0; | |
798 | int impanns = 0; | |
799 | int firstAttribute = u; | |
800 | Attribute attributes = null; | |
801 | ||
802 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
803 | String attrName = readUTF8(u + 2, c); | |
804 | // tests are sorted in decreasing frequency order | |
805 | // (based on frequencies observed on typical classes) | |
806 | if ("Code".equals(attrName)) { | |
807 | if ((context.flags & SKIP_CODE) == 0) { | |
808 | code = u + 8; | |
809 | } | |
810 | } else if ("Exceptions".equals(attrName)) { | |
811 | exceptions = new String[readUnsignedShort(u + 8)]; | |
812 | exception = u + 10; | |
813 | for (int j = 0; j < exceptions.length; ++j) { | |
814 | exceptions[j] = readClass(exception, c); | |
815 | exception += 2; | |
816 | } | |
817 | } else if (SIGNATURES && "Signature".equals(attrName)) { | |
818 | signature = readUTF8(u + 8, c); | |
819 | } else if ("Deprecated".equals(attrName)) { | |
820 | access |= Opcodes.ACC_DEPRECATED; | |
821 | } else if (ANNOTATIONS | |
822 | && "RuntimeVisibleAnnotations".equals(attrName)) { | |
823 | anns = u + 8; | |
824 | } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { | |
825 | dann = u + 8; | |
826 | } else if ("Synthetic".equals(attrName)) { | |
827 | access |= Opcodes.ACC_SYNTHETIC | |
828 | | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; | |
829 | } else if (ANNOTATIONS | |
830 | && "RuntimeInvisibleAnnotations".equals(attrName)) { | |
831 | ianns = u + 8; | |
832 | } else if (ANNOTATIONS | |
833 | && "RuntimeVisibleParameterAnnotations".equals(attrName)) { | |
834 | mpanns = u + 8; | |
835 | } else if (ANNOTATIONS | |
836 | && "RuntimeInvisibleParameterAnnotations".equals(attrName)) { | |
837 | impanns = u + 8; | |
838 | } else { | |
839 | Attribute attr = readAttribute(context.attrs, attrName, u + 8, | |
840 | readInt(u + 4), c, -1, null); | |
841 | if (attr != null) { | |
842 | attr.next = attributes; | |
843 | attributes = attr; | |
844 | } | |
845 | } | |
846 | u += 6 + readInt(u + 4); | |
847 | } | |
848 | u += 2; | |
849 | ||
850 | // visits the method declaration | |
851 | MethodVisitor mv = classVisitor.visitMethod(access, name, desc, | |
852 | signature, exceptions); | |
853 | if (mv == null) { | |
854 | return u; | |
855 | } | |
856 | ||
857 | /* | |
858 | * if the returned MethodVisitor is in fact a MethodWriter, it means | |
859 | * there is no method adapter between the reader and the writer. If, in | |
860 | * addition, the writer's constant pool was copied from this reader | |
861 | * (mw.cw.cr == this), and the signature and exceptions of the method | |
862 | * have not been changed, then it is possible to skip all visit events | |
863 | * and just copy the original code of the method to the writer (the | |
864 | * access, name and descriptor can have been changed, this is not | |
865 | * important since they are not copied as is from the reader). | |
866 | */ | |
867 | if (WRITER && mv instanceof MethodWriter) { | |
868 | MethodWriter mw = (MethodWriter) mv; | |
869 | if (mw.cw.cr == this && signature == mw.signature) { | |
870 | boolean sameExceptions = false; | |
871 | if (exceptions == null) { | |
872 | sameExceptions = mw.exceptionCount == 0; | |
873 | } else if (exceptions.length == mw.exceptionCount) { | |
874 | sameExceptions = true; | |
875 | for (int j = exceptions.length - 1; j >= 0; --j) { | |
876 | exception -= 2; | |
877 | if (mw.exceptions[j] != readUnsignedShort(exception)) { | |
878 | sameExceptions = false; | |
879 | break; | |
880 | } | |
881 | } | |
882 | } | |
883 | if (sameExceptions) { | |
884 | /* | |
885 | * we do not copy directly the code into MethodWriter to | |
886 | * save a byte array copy operation. The real copy will be | |
887 | * done in ClassWriter.toByteArray(). | |
888 | */ | |
889 | mw.classReaderOffset = firstAttribute; | |
890 | mw.classReaderLength = u - firstAttribute; | |
891 | return u; | |
892 | } | |
893 | } | |
894 | } | |
895 | ||
896 | // visits the method annotations | |
897 | if (ANNOTATIONS && dann != 0) { | |
898 | AnnotationVisitor dv = mv.visitAnnotationDefault(); | |
899 | readAnnotationValue(dann, c, null, dv); | |
900 | if (dv != null) { | |
901 | dv.visitEnd(); | |
902 | } | |
903 | } | |
904 | if (ANNOTATIONS && anns != 0) { | |
905 | for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { | |
906 | v = readAnnotationValues(v + 2, c, true, | |
907 | mv.visitAnnotation(readUTF8(v, c), true)); | |
908 | } | |
909 | } | |
910 | if (ANNOTATIONS && ianns != 0) { | |
911 | for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { | |
912 | v = readAnnotationValues(v + 2, c, true, | |
913 | mv.visitAnnotation(readUTF8(v, c), false)); | |
914 | } | |
915 | } | |
916 | if (ANNOTATIONS && mpanns != 0) { | |
917 | readParameterAnnotations(mpanns, desc, c, true, mv); | |
918 | } | |
919 | if (ANNOTATIONS && impanns != 0) { | |
920 | readParameterAnnotations(impanns, desc, c, false, mv); | |
921 | } | |
922 | ||
923 | // visits the method attributes | |
924 | while (attributes != null) { | |
925 | Attribute attr = attributes.next; | |
926 | attributes.next = null; | |
927 | mv.visitAttribute(attributes); | |
928 | attributes = attr; | |
929 | } | |
930 | ||
931 | // visits the method code | |
932 | if (code != 0) { | |
933 | context.access = access; | |
934 | context.name = name; | |
935 | context.desc = desc; | |
936 | mv.visitCode(); | |
937 | readCode(mv, context, code); | |
938 | } | |
939 | ||
940 | // visits the end of the method | |
941 | mv.visitEnd(); | |
942 | ||
943 | return u; | |
944 | } | |
945 | ||
946 | /** | |
947 | * Reads the bytecode of a method and makes the given visitor visit it. | |
948 | * | |
949 | * @param mv | |
950 | * the visitor that must visit the method's code. | |
951 | * @param context | |
952 | * information about the class being parsed. | |
953 | * @param u | |
954 | * the start offset of the code attribute in the class file. | |
955 | */ | |
956 | private void readCode(final MethodVisitor mv, final Context context, int u) { | |
957 | // reads the header | |
958 | byte[] b = this.b; | |
959 | char[] c = context.buffer; | |
960 | int maxStack = readUnsignedShort(u); | |
961 | int maxLocals = readUnsignedShort(u + 2); | |
962 | int codeLength = readInt(u + 4); | |
963 | u += 8; | |
964 | ||
965 | // reads the bytecode to find the labels | |
966 | int codeStart = u; | |
967 | int codeEnd = u + codeLength; | |
968 | Label[] labels = new Label[codeLength + 2]; | |
969 | readLabel(codeLength + 1, labels); | |
970 | while (u < codeEnd) { | |
971 | int offset = u - codeStart; | |
972 | int opcode = b[u] & 0xFF; | |
973 | switch (ClassWriter.TYPE[opcode]) { | |
974 | case ClassWriter.NOARG_INSN: | |
975 | case ClassWriter.IMPLVAR_INSN: | |
976 | u += 1; | |
977 | break; | |
978 | case ClassWriter.LABEL_INSN: | |
979 | readLabel(offset + readShort(u + 1), labels); | |
980 | u += 3; | |
981 | break; | |
982 | case ClassWriter.LABELW_INSN: | |
983 | readLabel(offset + readInt(u + 1), labels); | |
984 | u += 5; | |
985 | break; | |
986 | case ClassWriter.WIDE_INSN: | |
987 | opcode = b[u + 1] & 0xFF; | |
988 | if (opcode == Opcodes.IINC) { | |
989 | u += 6; | |
990 | } else { | |
991 | u += 4; | |
992 | } | |
993 | break; | |
994 | case ClassWriter.TABL_INSN: | |
995 | // skips 0 to 3 padding bytes | |
996 | u = u + 4 - (offset & 3); | |
997 | // reads instruction | |
998 | readLabel(offset + readInt(u), labels); | |
999 | for (int i = readInt(u + 8) - readInt(u + 4) + 1; i > 0; --i) { | |
1000 | readLabel(offset + readInt(u + 12), labels); | |
1001 | u += 4; | |
1002 | } | |
1003 | u += 12; | |
1004 | break; | |
1005 | case ClassWriter.LOOK_INSN: | |
1006 | // skips 0 to 3 padding bytes | |
1007 | u = u + 4 - (offset & 3); | |
1008 | // reads instruction | |
1009 | readLabel(offset + readInt(u), labels); | |
1010 | for (int i = readInt(u + 4); i > 0; --i) { | |
1011 | readLabel(offset + readInt(u + 12), labels); | |
1012 | u += 8; | |
1013 | } | |
1014 | u += 8; | |
1015 | break; | |
1016 | case ClassWriter.VAR_INSN: | |
1017 | case ClassWriter.SBYTE_INSN: | |
1018 | case ClassWriter.LDC_INSN: | |
1019 | u += 2; | |
1020 | break; | |
1021 | case ClassWriter.SHORT_INSN: | |
1022 | case ClassWriter.LDCW_INSN: | |
1023 | case ClassWriter.FIELDORMETH_INSN: | |
1024 | case ClassWriter.TYPE_INSN: | |
1025 | case ClassWriter.IINC_INSN: | |
1026 | u += 3; | |
1027 | break; | |
1028 | case ClassWriter.ITFMETH_INSN: | |
1029 | case ClassWriter.INDYMETH_INSN: | |
1030 | u += 5; | |
1031 | break; | |
1032 | // case MANA_INSN: | |
1033 | default: | |
1034 | u += 4; | |
1035 | break; | |
1036 | } | |
1037 | } | |
1038 | ||
1039 | // reads the try catch entries to find the labels, and also visits them | |
1040 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
1041 | Label start = readLabel(readUnsignedShort(u + 2), labels); | |
1042 | Label end = readLabel(readUnsignedShort(u + 4), labels); | |
1043 | Label handler = readLabel(readUnsignedShort(u + 6), labels); | |
1044 | String type = readUTF8(items[readUnsignedShort(u + 8)], c); | |
1045 | mv.visitTryCatchBlock(start, end, handler, type); | |
1046 | u += 8; | |
1047 | } | |
1048 | u += 2; | |
1049 | ||
1050 | // reads the code attributes | |
1051 | int varTable = 0; | |
1052 | int varTypeTable = 0; | |
1053 | boolean zip = true; | |
1054 | boolean unzip = (context.flags & EXPAND_FRAMES) != 0; | |
1055 | int stackMap = 0; | |
1056 | int stackMapSize = 0; | |
1057 | int frameCount = 0; | |
1058 | Context frame = null; | |
1059 | Attribute attributes = null; | |
1060 | ||
1061 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
1062 | String attrName = readUTF8(u + 2, c); | |
1063 | if ("LocalVariableTable".equals(attrName)) { | |
1064 | if ((context.flags & SKIP_DEBUG) == 0) { | |
1065 | varTable = u + 8; | |
1066 | for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { | |
1067 | int label = readUnsignedShort(v + 10); | |
1068 | if (labels[label] == null) { | |
1069 | readLabel(label, labels).status |= Label.DEBUG; | |
1070 | } | |
1071 | label += readUnsignedShort(v + 12); | |
1072 | if (labels[label] == null) { | |
1073 | readLabel(label, labels).status |= Label.DEBUG; | |
1074 | } | |
1075 | v += 10; | |
1076 | } | |
1077 | } | |
1078 | } else if ("LocalVariableTypeTable".equals(attrName)) { | |
1079 | varTypeTable = u + 8; | |
1080 | } else if ("LineNumberTable".equals(attrName)) { | |
1081 | if ((context.flags & SKIP_DEBUG) == 0) { | |
1082 | for (int j = readUnsignedShort(u + 8), v = u; j > 0; --j) { | |
1083 | int label = readUnsignedShort(v + 10); | |
1084 | if (labels[label] == null) { | |
1085 | readLabel(label, labels).status |= Label.DEBUG; | |
1086 | } | |
1087 | labels[label].line = readUnsignedShort(v + 12); | |
1088 | v += 4; | |
1089 | } | |
1090 | } | |
1091 | } else if (FRAMES && "StackMapTable".equals(attrName)) { | |
1092 | if ((context.flags & SKIP_FRAMES) == 0) { | |
1093 | stackMap = u + 10; | |
1094 | stackMapSize = readInt(u + 4); | |
1095 | frameCount = readUnsignedShort(u + 8); | |
1096 | } | |
1097 | /* | |
1098 | * here we do not extract the labels corresponding to the | |
1099 | * attribute content. This would require a full parsing of the | |
1100 | * attribute, which would need to be repeated in the second | |
1101 | * phase (see below). Instead the content of the attribute is | |
1102 | * read one frame at a time (i.e. after a frame has been | |
1103 | * visited, the next frame is read), and the labels it contains | |
1104 | * are also extracted one frame at a time. Thanks to the | |
1105 | * ordering of frames, having only a "one frame lookahead" is | |
1106 | * not a problem, i.e. it is not possible to see an offset | |
1107 | * smaller than the offset of the current insn and for which no | |
1108 | * Label exist. | |
1109 | */ | |
1110 | /* | |
1111 | * This is not true for UNINITIALIZED type offsets. We solve | |
1112 | * this by parsing the stack map table without a full decoding | |
1113 | * (see below). | |
1114 | */ | |
1115 | } else if (FRAMES && "StackMap".equals(attrName)) { | |
1116 | if ((context.flags & SKIP_FRAMES) == 0) { | |
1117 | zip = false; | |
1118 | stackMap = u + 10; | |
1119 | stackMapSize = readInt(u + 4); | |
1120 | frameCount = readUnsignedShort(u + 8); | |
1121 | } | |
1122 | /* | |
1123 | * IMPORTANT! here we assume that the frames are ordered, as in | |
1124 | * the StackMapTable attribute, although this is not guaranteed | |
1125 | * by the attribute format. | |
1126 | */ | |
1127 | } else { | |
1128 | for (int j = 0; j < context.attrs.length; ++j) { | |
1129 | if (context.attrs[j].type.equals(attrName)) { | |
1130 | Attribute attr = context.attrs[j].read(this, u + 8, | |
1131 | readInt(u + 4), c, codeStart - 8, labels); | |
1132 | if (attr != null) { | |
1133 | attr.next = attributes; | |
1134 | attributes = attr; | |
1135 | } | |
1136 | } | |
1137 | } | |
1138 | } | |
1139 | u += 6 + readInt(u + 4); | |
1140 | } | |
1141 | u += 2; | |
1142 | ||
1143 | // generates the first (implicit) stack map frame | |
1144 | if (FRAMES && stackMap != 0) { | |
1145 | /* | |
1146 | * for the first explicit frame the offset is not offset_delta + 1 | |
1147 | * but only offset_delta; setting the implicit frame offset to -1 | |
1148 | * allow the use of the "offset_delta + 1" rule in all cases | |
1149 | */ | |
1150 | frame = context; | |
1151 | frame.offset = -1; | |
1152 | frame.mode = 0; | |
1153 | frame.localCount = 0; | |
1154 | frame.localDiff = 0; | |
1155 | frame.stackCount = 0; | |
1156 | frame.local = new Object[maxLocals]; | |
1157 | frame.stack = new Object[maxStack]; | |
1158 | if (unzip) { | |
1159 | getImplicitFrame(context); | |
1160 | } | |
1161 | /* | |
1162 | * Finds labels for UNINITIALIZED frame types. Instead of decoding | |
1163 | * each element of the stack map table, we look for 3 consecutive | |
1164 | * bytes that "look like" an UNINITIALIZED type (tag 8, offset | |
1165 | * within code bounds, NEW instruction at this offset). We may find | |
1166 | * false positives (i.e. not real UNINITIALIZED types), but this | |
1167 | * should be rare, and the only consequence will be the creation of | |
1168 | * an unneeded label. This is better than creating a label for each | |
1169 | * NEW instruction, and faster than fully decoding the whole stack | |
1170 | * map table. | |
1171 | */ | |
1172 | for (int i = stackMap; i < stackMap + stackMapSize - 2; ++i) { | |
1173 | if (b[i] == 8) { // UNINITIALIZED FRAME TYPE | |
1174 | int v = readUnsignedShort(i + 1); | |
1175 | if (v >= 0 && v < codeLength) { | |
1176 | if ((b[codeStart + v] & 0xFF) == Opcodes.NEW) { | |
1177 | readLabel(v, labels); | |
1178 | } | |
1179 | } | |
1180 | } | |
1181 | } | |
1182 | } | |
1183 | ||
1184 | // visits the instructions | |
1185 | u = codeStart; | |
1186 | while (u < codeEnd) { | |
1187 | int offset = u - codeStart; | |
1188 | ||
1189 | // visits the label and line number for this offset, if any | |
1190 | Label l = labels[offset]; | |
1191 | if (l != null) { | |
1192 | mv.visitLabel(l); | |
1193 | if ((context.flags & SKIP_DEBUG) == 0 && l.line > 0) { | |
1194 | mv.visitLineNumber(l.line, l); | |
1195 | } | |
1196 | } | |
1197 | ||
1198 | // visits the frame for this offset, if any | |
1199 | while (FRAMES && frame != null | |
1200 | && (frame.offset == offset || frame.offset == -1)) { | |
1201 | // if there is a frame for this offset, makes the visitor visit | |
1202 | // it, and reads the next frame if there is one. | |
1203 | if (frame.offset != -1) { | |
1204 | if (!zip || unzip) { | |
1205 | mv.visitFrame(Opcodes.F_NEW, frame.localCount, | |
1206 | frame.local, frame.stackCount, frame.stack); | |
1207 | } else { | |
1208 | mv.visitFrame(frame.mode, frame.localDiff, frame.local, | |
1209 | frame.stackCount, frame.stack); | |
1210 | } | |
1211 | } | |
1212 | if (frameCount > 0) { | |
1213 | stackMap = readFrame(stackMap, zip, unzip, labels, frame); | |
1214 | --frameCount; | |
1215 | } else { | |
1216 | frame = null; | |
1217 | } | |
1218 | } | |
1219 | ||
1220 | // visits the instruction at this offset | |
1221 | int opcode = b[u] & 0xFF; | |
1222 | switch (ClassWriter.TYPE[opcode]) { | |
1223 | case ClassWriter.NOARG_INSN: | |
1224 | mv.visitInsn(opcode); | |
1225 | u += 1; | |
1226 | break; | |
1227 | case ClassWriter.IMPLVAR_INSN: | |
1228 | if (opcode > Opcodes.ISTORE) { | |
1229 | opcode -= 59; // ISTORE_0 | |
1230 | mv.visitVarInsn(Opcodes.ISTORE + (opcode >> 2), | |
1231 | opcode & 0x3); | |
1232 | } else { | |
1233 | opcode -= 26; // ILOAD_0 | |
1234 | mv.visitVarInsn(Opcodes.ILOAD + (opcode >> 2), opcode & 0x3); | |
1235 | } | |
1236 | u += 1; | |
1237 | break; | |
1238 | case ClassWriter.LABEL_INSN: | |
1239 | mv.visitJumpInsn(opcode, labels[offset + readShort(u + 1)]); | |
1240 | u += 3; | |
1241 | break; | |
1242 | case ClassWriter.LABELW_INSN: | |
1243 | mv.visitJumpInsn(opcode - 33, labels[offset + readInt(u + 1)]); | |
1244 | u += 5; | |
1245 | break; | |
1246 | case ClassWriter.WIDE_INSN: | |
1247 | opcode = b[u + 1] & 0xFF; | |
1248 | if (opcode == Opcodes.IINC) { | |
1249 | mv.visitIincInsn(readUnsignedShort(u + 2), readShort(u + 4)); | |
1250 | u += 6; | |
1251 | } else { | |
1252 | mv.visitVarInsn(opcode, readUnsignedShort(u + 2)); | |
1253 | u += 4; | |
1254 | } | |
1255 | break; | |
1256 | case ClassWriter.TABL_INSN: { | |
1257 | // skips 0 to 3 padding bytes | |
1258 | u = u + 4 - (offset & 3); | |
1259 | // reads instruction | |
1260 | int label = offset + readInt(u); | |
1261 | int min = readInt(u + 4); | |
1262 | int max = readInt(u + 8); | |
1263 | Label[] table = new Label[max - min + 1]; | |
1264 | u += 12; | |
1265 | for (int i = 0; i < table.length; ++i) { | |
1266 | table[i] = labels[offset + readInt(u)]; | |
1267 | u += 4; | |
1268 | } | |
1269 | mv.visitTableSwitchInsn(min, max, labels[label], table); | |
1270 | break; | |
1271 | } | |
1272 | case ClassWriter.LOOK_INSN: { | |
1273 | // skips 0 to 3 padding bytes | |
1274 | u = u + 4 - (offset & 3); | |
1275 | // reads instruction | |
1276 | int label = offset + readInt(u); | |
1277 | int len = readInt(u + 4); | |
1278 | int[] keys = new int[len]; | |
1279 | Label[] values = new Label[len]; | |
1280 | u += 8; | |
1281 | for (int i = 0; i < len; ++i) { | |
1282 | keys[i] = readInt(u); | |
1283 | values[i] = labels[offset + readInt(u + 4)]; | |
1284 | u += 8; | |
1285 | } | |
1286 | mv.visitLookupSwitchInsn(labels[label], keys, values); | |
1287 | break; | |
1288 | } | |
1289 | case ClassWriter.VAR_INSN: | |
1290 | mv.visitVarInsn(opcode, b[u + 1] & 0xFF); | |
1291 | u += 2; | |
1292 | break; | |
1293 | case ClassWriter.SBYTE_INSN: | |
1294 | mv.visitIntInsn(opcode, b[u + 1]); | |
1295 | u += 2; | |
1296 | break; | |
1297 | case ClassWriter.SHORT_INSN: | |
1298 | mv.visitIntInsn(opcode, readShort(u + 1)); | |
1299 | u += 3; | |
1300 | break; | |
1301 | case ClassWriter.LDC_INSN: | |
1302 | mv.visitLdcInsn(readConst(b[u + 1] & 0xFF, c)); | |
1303 | u += 2; | |
1304 | break; | |
1305 | case ClassWriter.LDCW_INSN: | |
1306 | mv.visitLdcInsn(readConst(readUnsignedShort(u + 1), c)); | |
1307 | u += 3; | |
1308 | break; | |
1309 | case ClassWriter.FIELDORMETH_INSN: | |
1310 | case ClassWriter.ITFMETH_INSN: { | |
1311 | int cpIndex = items[readUnsignedShort(u + 1)]; | |
1312 | String iowner = readClass(cpIndex, c); | |
1313 | cpIndex = items[readUnsignedShort(cpIndex + 2)]; | |
1314 | String iname = readUTF8(cpIndex, c); | |
1315 | String idesc = readUTF8(cpIndex + 2, c); | |
1316 | if (opcode < Opcodes.INVOKEVIRTUAL) { | |
1317 | mv.visitFieldInsn(opcode, iowner, iname, idesc); | |
1318 | } else { | |
1319 | mv.visitMethodInsn(opcode, iowner, iname, idesc); | |
1320 | } | |
1321 | if (opcode == Opcodes.INVOKEINTERFACE) { | |
1322 | u += 5; | |
1323 | } else { | |
1324 | u += 3; | |
1325 | } | |
1326 | break; | |
1327 | } | |
1328 | case ClassWriter.INDYMETH_INSN: { | |
1329 | int cpIndex = items[readUnsignedShort(u + 1)]; | |
1330 | int bsmIndex = context.bootstrapMethods[readUnsignedShort(cpIndex)]; | |
1331 | Handle bsm = (Handle) readConst(readUnsignedShort(bsmIndex), c); | |
1332 | int bsmArgCount = readUnsignedShort(bsmIndex + 2); | |
1333 | Object[] bsmArgs = new Object[bsmArgCount]; | |
1334 | bsmIndex += 4; | |
1335 | for (int i = 0; i < bsmArgCount; i++) { | |
1336 | bsmArgs[i] = readConst(readUnsignedShort(bsmIndex), c); | |
1337 | bsmIndex += 2; | |
1338 | } | |
1339 | cpIndex = items[readUnsignedShort(cpIndex + 2)]; | |
1340 | String iname = readUTF8(cpIndex, c); | |
1341 | String idesc = readUTF8(cpIndex + 2, c); | |
1342 | mv.visitInvokeDynamicInsn(iname, idesc, bsm, bsmArgs); | |
1343 | u += 5; | |
1344 | break; | |
1345 | } | |
1346 | case ClassWriter.TYPE_INSN: | |
1347 | mv.visitTypeInsn(opcode, readClass(u + 1, c)); | |
1348 | u += 3; | |
1349 | break; | |
1350 | case ClassWriter.IINC_INSN: | |
1351 | mv.visitIincInsn(b[u + 1] & 0xFF, b[u + 2]); | |
1352 | u += 3; | |
1353 | break; | |
1354 | // case MANA_INSN: | |
1355 | default: | |
1356 | mv.visitMultiANewArrayInsn(readClass(u + 1, c), b[u + 3] & 0xFF); | |
1357 | u += 4; | |
1358 | break; | |
1359 | } | |
1360 | } | |
1361 | if (labels[codeLength] != null) { | |
1362 | mv.visitLabel(labels[codeLength]); | |
1363 | } | |
1364 | ||
1365 | // visits the local variable tables | |
1366 | if ((context.flags & SKIP_DEBUG) == 0 && varTable != 0) { | |
1367 | int[] typeTable = null; | |
1368 | if (varTypeTable != 0) { | |
1369 | u = varTypeTable + 2; | |
1370 | typeTable = new int[readUnsignedShort(varTypeTable) * 3]; | |
1371 | for (int i = typeTable.length; i > 0;) { | |
1372 | typeTable[--i] = u + 6; // signature | |
1373 | typeTable[--i] = readUnsignedShort(u + 8); // index | |
1374 | typeTable[--i] = readUnsignedShort(u); // start | |
1375 | u += 10; | |
1376 | } | |
1377 | } | |
1378 | u = varTable + 2; | |
1379 | for (int i = readUnsignedShort(varTable); i > 0; --i) { | |
1380 | int start = readUnsignedShort(u); | |
1381 | int length = readUnsignedShort(u + 2); | |
1382 | int index = readUnsignedShort(u + 8); | |
1383 | String vsignature = null; | |
1384 | if (typeTable != null) { | |
1385 | for (int j = 0; j < typeTable.length; j += 3) { | |
1386 | if (typeTable[j] == start && typeTable[j + 1] == index) { | |
1387 | vsignature = readUTF8(typeTable[j + 2], c); | |
1388 | break; | |
1389 | } | |
1390 | } | |
1391 | } | |
1392 | mv.visitLocalVariable(readUTF8(u + 4, c), readUTF8(u + 6, c), | |
1393 | vsignature, labels[start], labels[start + length], | |
1394 | index); | |
1395 | u += 10; | |
1396 | } | |
1397 | } | |
1398 | ||
1399 | // visits the code attributes | |
1400 | while (attributes != null) { | |
1401 | Attribute attr = attributes.next; | |
1402 | attributes.next = null; | |
1403 | mv.visitAttribute(attributes); | |
1404 | attributes = attr; | |
1405 | } | |
1406 | ||
1407 | // visits the max stack and max locals values | |
1408 | mv.visitMaxs(maxStack, maxLocals); | |
1409 | } | |
1410 | ||
1411 | /** | |
1412 | * Reads parameter annotations and makes the given visitor visit them. | |
1413 | * | |
1414 | * @param v | |
1415 | * start offset in {@link #b b} of the annotations to be read. | |
1416 | * @param desc | |
1417 | * the method descriptor. | |
1418 | * @param buf | |
1419 | * buffer to be used to call {@link #readUTF8 readUTF8}, | |
1420 | * {@link #readClass(int,char[]) readClass} or {@link #readConst | |
1421 | * readConst}. | |
1422 | * @param visible | |
1423 | * <tt>true</tt> if the annotations to be read are visible at | |
1424 | * runtime. | |
1425 | * @param mv | |
1426 | * the visitor that must visit the annotations. | |
1427 | */ | |
1428 | private void readParameterAnnotations(int v, final String desc, | |
1429 | final char[] buf, final boolean visible, final MethodVisitor mv) { | |
1430 | int i; | |
1431 | int n = b[v++] & 0xFF; | |
1432 | // workaround for a bug in javac (javac compiler generates a parameter | |
1433 | // annotation array whose size is equal to the number of parameters in | |
1434 | // the Java source file, while it should generate an array whose size is | |
1435 | // equal to the number of parameters in the method descriptor - which | |
1436 | // includes the synthetic parameters added by the compiler). This work- | |
1437 | // around supposes that the synthetic parameters are the first ones. | |
1438 | int synthetics = Type.getArgumentTypes(desc).length - n; | |
1439 | AnnotationVisitor av; | |
1440 | for (i = 0; i < synthetics; ++i) { | |
1441 | // virtual annotation to detect synthetic parameters in MethodWriter | |
1442 | av = mv.visitParameterAnnotation(i, "Ljava/lang/Synthetic;", false); | |
1443 | if (av != null) { | |
1444 | av.visitEnd(); | |
1445 | } | |
1446 | } | |
1447 | for (; i < n + synthetics; ++i) { | |
1448 | int j = readUnsignedShort(v); | |
1449 | v += 2; | |
1450 | for (; j > 0; --j) { | |
1451 | av = mv.visitParameterAnnotation(i, readUTF8(v, buf), visible); | |
1452 | v = readAnnotationValues(v + 2, buf, true, av); | |
1453 | } | |
1454 | } | |
1455 | } | |
1456 | ||
1457 | /** | |
1458 | * Reads the values of an annotation and makes the given visitor visit them. | |
1459 | * | |
1460 | * @param v | |
1461 | * the start offset in {@link #b b} of the values to be read | |
1462 | * (including the unsigned short that gives the number of | |
1463 | * values). | |
1464 | * @param buf | |
1465 | * buffer to be used to call {@link #readUTF8 readUTF8}, | |
1466 | * {@link #readClass(int,char[]) readClass} or {@link #readConst | |
1467 | * readConst}. | |
1468 | * @param named | |
1469 | * if the annotation values are named or not. | |
1470 | * @param av | |
1471 | * the visitor that must visit the values. | |
1472 | * @return the end offset of the annotation values. | |
1473 | */ | |
1474 | private int readAnnotationValues(int v, final char[] buf, | |
1475 | final boolean named, final AnnotationVisitor av) { | |
1476 | int i = readUnsignedShort(v); | |
1477 | v += 2; | |
1478 | if (named) { | |
1479 | for (; i > 0; --i) { | |
1480 | v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); | |
1481 | } | |
1482 | } else { | |
1483 | for (; i > 0; --i) { | |
1484 | v = readAnnotationValue(v, buf, null, av); | |
1485 | } | |
1486 | } | |
1487 | if (av != null) { | |
1488 | av.visitEnd(); | |
1489 | } | |
1490 | return v; | |
1491 | } | |
1492 | ||
1493 | /** | |
1494 | * Reads a value of an annotation and makes the given visitor visit it. | |
1495 | * | |
1496 | * @param v | |
1497 | * the start offset in {@link #b b} of the value to be read | |
1498 | * (<i>not including the value name constant pool index</i>). | |
1499 | * @param buf | |
1500 | * buffer to be used to call {@link #readUTF8 readUTF8}, | |
1501 | * {@link #readClass(int,char[]) readClass} or {@link #readConst | |
1502 | * readConst}. | |
1503 | * @param name | |
1504 | * the name of the value to be read. | |
1505 | * @param av | |
1506 | * the visitor that must visit the value. | |
1507 | * @return the end offset of the annotation value. | |
1508 | */ | |
1509 | private int readAnnotationValue(int v, final char[] buf, final String name, | |
1510 | final AnnotationVisitor av) { | |
1511 | int i; | |
1512 | if (av == null) { | |
1513 | switch (b[v] & 0xFF) { | |
1514 | case 'e': // enum_const_value | |
1515 | return v + 5; | |
1516 | case '@': // annotation_value | |
1517 | return readAnnotationValues(v + 3, buf, true, null); | |
1518 | case '[': // array_value | |
1519 | return readAnnotationValues(v + 1, buf, false, null); | |
1520 | default: | |
1521 | return v + 3; | |
1522 | } | |
1523 | } | |
1524 | switch (b[v++] & 0xFF) { | |
1525 | case 'I': // pointer to CONSTANT_Integer | |
1526 | case 'J': // pointer to CONSTANT_Long | |
1527 | case 'F': // pointer to CONSTANT_Float | |
1528 | case 'D': // pointer to CONSTANT_Double | |
1529 | av.visit(name, readConst(readUnsignedShort(v), buf)); | |
1530 | v += 2; | |
1531 | break; | |
1532 | case 'B': // pointer to CONSTANT_Byte | |
1533 | av.visit(name, | |
1534 | new Byte((byte) readInt(items[readUnsignedShort(v)]))); | |
1535 | v += 2; | |
1536 | break; | |
1537 | case 'Z': // pointer to CONSTANT_Boolean | |
1538 | av.visit(name, | |
1539 | readInt(items[readUnsignedShort(v)]) == 0 ? Boolean.FALSE | |
1540 | : Boolean.TRUE); | |
1541 | v += 2; | |
1542 | break; | |
1543 | case 'S': // pointer to CONSTANT_Short | |
1544 | av.visit(name, new Short( | |
1545 | (short) readInt(items[readUnsignedShort(v)]))); | |
1546 | v += 2; | |
1547 | break; | |
1548 | case 'C': // pointer to CONSTANT_Char | |
1549 | av.visit(name, new Character( | |
1550 | (char) readInt(items[readUnsignedShort(v)]))); | |
1551 | v += 2; | |
1552 | break; | |
1553 | case 's': // pointer to CONSTANT_Utf8 | |
1554 | av.visit(name, readUTF8(v, buf)); | |
1555 | v += 2; | |
1556 | break; | |
1557 | case 'e': // enum_const_value | |
1558 | av.visitEnum(name, readUTF8(v, buf), readUTF8(v + 2, buf)); | |
1559 | v += 4; | |
1560 | break; | |
1561 | case 'c': // class_info | |
1562 | av.visit(name, Type.getType(readUTF8(v, buf))); | |
1563 | v += 2; | |
1564 | break; | |
1565 | case '@': // annotation_value | |
1566 | v = readAnnotationValues(v + 2, buf, true, | |
1567 | av.visitAnnotation(name, readUTF8(v, buf))); | |
1568 | break; | |
1569 | case '[': // array_value | |
1570 | int size = readUnsignedShort(v); | |
1571 | v += 2; | |
1572 | if (size == 0) { | |
1573 | return readAnnotationValues(v - 2, buf, false, | |
1574 | av.visitArray(name)); | |
1575 | } | |
1576 | switch (this.b[v++] & 0xFF) { | |
1577 | case 'B': | |
1578 | byte[] bv = new byte[size]; | |
1579 | for (i = 0; i < size; i++) { | |
1580 | bv[i] = (byte) readInt(items[readUnsignedShort(v)]); | |
1581 | v += 3; | |
1582 | } | |
1583 | av.visit(name, bv); | |
1584 | --v; | |
1585 | break; | |
1586 | case 'Z': | |
1587 | boolean[] zv = new boolean[size]; | |
1588 | for (i = 0; i < size; i++) { | |
1589 | zv[i] = readInt(items[readUnsignedShort(v)]) != 0; | |
1590 | v += 3; | |
1591 | } | |
1592 | av.visit(name, zv); | |
1593 | --v; | |
1594 | break; | |
1595 | case 'S': | |
1596 | short[] sv = new short[size]; | |
1597 | for (i = 0; i < size; i++) { | |
1598 | sv[i] = (short) readInt(items[readUnsignedShort(v)]); | |
1599 | v += 3; | |
1600 | } | |
1601 | av.visit(name, sv); | |
1602 | --v; | |
1603 | break; | |
1604 | case 'C': | |
1605 | char[] cv = new char[size]; | |
1606 | for (i = 0; i < size; i++) { | |
1607 | cv[i] = (char) readInt(items[readUnsignedShort(v)]); | |
1608 | v += 3; | |
1609 | } | |
1610 | av.visit(name, cv); | |
1611 | --v; | |
1612 | break; | |
1613 | case 'I': | |
1614 | int[] iv = new int[size]; | |
1615 | for (i = 0; i < size; i++) { | |
1616 | iv[i] = readInt(items[readUnsignedShort(v)]); | |
1617 | v += 3; | |
1618 | } | |
1619 | av.visit(name, iv); | |
1620 | --v; | |
1621 | break; | |
1622 | case 'J': | |
1623 | long[] lv = new long[size]; | |
1624 | for (i = 0; i < size; i++) { | |
1625 | lv[i] = readLong(items[readUnsignedShort(v)]); | |
1626 | v += 3; | |
1627 | } | |
1628 | av.visit(name, lv); | |
1629 | --v; | |
1630 | break; | |
1631 | case 'F': | |
1632 | float[] fv = new float[size]; | |
1633 | for (i = 0; i < size; i++) { | |
1634 | fv[i] = Float | |
1635 | .intBitsToFloat(readInt(items[readUnsignedShort(v)])); | |
1636 | v += 3; | |
1637 | } | |
1638 | av.visit(name, fv); | |
1639 | --v; | |
1640 | break; | |
1641 | case 'D': | |
1642 | double[] dv = new double[size]; | |
1643 | for (i = 0; i < size; i++) { | |
1644 | dv[i] = Double | |
1645 | .longBitsToDouble(readLong(items[readUnsignedShort(v)])); | |
1646 | v += 3; | |
1647 | } | |
1648 | av.visit(name, dv); | |
1649 | --v; | |
1650 | break; | |
1651 | default: | |
1652 | v = readAnnotationValues(v - 3, buf, false, av.visitArray(name)); | |
1653 | } | |
1654 | } | |
1655 | return v; | |
1656 | } | |
1657 | ||
1658 | /** | |
1659 | * Computes the implicit frame of the method currently being parsed (as | |
1660 | * defined in the given {@link Context}) and stores it in the given context. | |
1661 | * | |
1662 | * @param frame | |
1663 | * information about the class being parsed. | |
1664 | */ | |
1665 | private void getImplicitFrame(final Context frame) { | |
1666 | String desc = frame.desc; | |
1667 | Object[] locals = frame.local; | |
1668 | int local = 0; | |
1669 | if ((frame.access & Opcodes.ACC_STATIC) == 0) { | |
1670 | if ("<init>".equals(frame.name)) { | |
1671 | locals[local++] = Opcodes.UNINITIALIZED_THIS; | |
1672 | } else { | |
1673 | locals[local++] = readClass(header + 2, frame.buffer); | |
1674 | } | |
1675 | } | |
1676 | int i = 1; | |
1677 | loop: while (true) { | |
1678 | int j = i; | |
1679 | switch (desc.charAt(i++)) { | |
1680 | case 'Z': | |
1681 | case 'C': | |
1682 | case 'B': | |
1683 | case 'S': | |
1684 | case 'I': | |
1685 | locals[local++] = Opcodes.INTEGER; | |
1686 | break; | |
1687 | case 'F': | |
1688 | locals[local++] = Opcodes.FLOAT; | |
1689 | break; | |
1690 | case 'J': | |
1691 | locals[local++] = Opcodes.LONG; | |
1692 | break; | |
1693 | case 'D': | |
1694 | locals[local++] = Opcodes.DOUBLE; | |
1695 | break; | |
1696 | case '[': | |
1697 | while (desc.charAt(i) == '[') { | |
1698 | ++i; | |
1699 | } | |
1700 | if (desc.charAt(i) == 'L') { | |
1701 | ++i; | |
1702 | while (desc.charAt(i) != ';') { | |
1703 | ++i; | |
1704 | } | |
1705 | } | |
1706 | locals[local++] = desc.substring(j, ++i); | |
1707 | break; | |
1708 | case 'L': | |
1709 | while (desc.charAt(i) != ';') { | |
1710 | ++i; | |
1711 | } | |
1712 | locals[local++] = desc.substring(j + 1, i++); | |
1713 | break; | |
1714 | default: | |
1715 | break loop; | |
1716 | } | |
1717 | } | |
1718 | frame.localCount = local; | |
1719 | } | |
1720 | ||
1721 | /** | |
1722 | * Reads a stack map frame and stores the result in the given | |
1723 | * {@link Context} object. | |
1724 | * | |
1725 | * @param stackMap | |
1726 | * the start offset of a stack map frame in the class file. | |
1727 | * @param zip | |
1728 | * if the stack map frame at stackMap is compressed or not. | |
1729 | * @param unzip | |
1730 | * if the stack map frame must be uncompressed. | |
1731 | * @param labels | |
1732 | * the labels of the method currently being parsed, indexed by | |
1733 | * their offset. A new label for the parsed stack map frame is | |
1734 | * stored in this array if it does not already exist. | |
1735 | * @param frame | |
1736 | * where the parsed stack map frame must be stored. | |
1737 | * @return the offset of the first byte following the parsed frame. | |
1738 | */ | |
1739 | private int readFrame(int stackMap, boolean zip, boolean unzip, | |
1740 | Label[] labels, Context frame) { | |
1741 | char[] c = frame.buffer; | |
1742 | int tag; | |
1743 | int delta; | |
1744 | if (zip) { | |
1745 | tag = b[stackMap++] & 0xFF; | |
1746 | } else { | |
1747 | tag = MethodWriter.FULL_FRAME; | |
1748 | frame.offset = -1; | |
1749 | } | |
1750 | frame.localDiff = 0; | |
1751 | if (tag < MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME) { | |
1752 | delta = tag; | |
1753 | frame.mode = Opcodes.F_SAME; | |
1754 | frame.stackCount = 0; | |
1755 | } else if (tag < MethodWriter.RESERVED) { | |
1756 | delta = tag - MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME; | |
1757 | stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); | |
1758 | frame.mode = Opcodes.F_SAME1; | |
1759 | frame.stackCount = 1; | |
1760 | } else { | |
1761 | delta = readUnsignedShort(stackMap); | |
1762 | stackMap += 2; | |
1763 | if (tag == MethodWriter.SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) { | |
1764 | stackMap = readFrameType(frame.stack, 0, stackMap, c, labels); | |
1765 | frame.mode = Opcodes.F_SAME1; | |
1766 | frame.stackCount = 1; | |
1767 | } else if (tag >= MethodWriter.CHOP_FRAME | |
1768 | && tag < MethodWriter.SAME_FRAME_EXTENDED) { | |
1769 | frame.mode = Opcodes.F_CHOP; | |
1770 | frame.localDiff = MethodWriter.SAME_FRAME_EXTENDED - tag; | |
1771 | frame.localCount -= frame.localDiff; | |
1772 | frame.stackCount = 0; | |
1773 | } else if (tag == MethodWriter.SAME_FRAME_EXTENDED) { | |
1774 | frame.mode = Opcodes.F_SAME; | |
1775 | frame.stackCount = 0; | |
1776 | } else if (tag < MethodWriter.FULL_FRAME) { | |
1777 | int local = unzip ? frame.localCount : 0; | |
1778 | for (int i = tag - MethodWriter.SAME_FRAME_EXTENDED; i > 0; i--) { | |
1779 | stackMap = readFrameType(frame.local, local++, stackMap, c, | |
1780 | labels); | |
1781 | } | |
1782 | frame.mode = Opcodes.F_APPEND; | |
1783 | frame.localDiff = tag - MethodWriter.SAME_FRAME_EXTENDED; | |
1784 | frame.localCount += frame.localDiff; | |
1785 | frame.stackCount = 0; | |
1786 | } else { // if (tag == FULL_FRAME) { | |
1787 | frame.mode = Opcodes.F_FULL; | |
1788 | int n = readUnsignedShort(stackMap); | |
1789 | stackMap += 2; | |
1790 | frame.localDiff = n; | |
1791 | frame.localCount = n; | |
1792 | for (int local = 0; n > 0; n--) { | |
1793 | stackMap = readFrameType(frame.local, local++, stackMap, c, | |
1794 | labels); | |
1795 | } | |
1796 | n = readUnsignedShort(stackMap); | |
1797 | stackMap += 2; | |
1798 | frame.stackCount = n; | |
1799 | for (int stack = 0; n > 0; n--) { | |
1800 | stackMap = readFrameType(frame.stack, stack++, stackMap, c, | |
1801 | labels); | |
1802 | } | |
1803 | } | |
1804 | } | |
1805 | frame.offset += delta + 1; | |
1806 | readLabel(frame.offset, labels); | |
1807 | return stackMap; | |
1808 | } | |
1809 | ||
1810 | /** | |
1811 | * Reads a stack map frame type and stores it at the given index in the | |
1812 | * given array. | |
1813 | * | |
1814 | * @param frame | |
1815 | * the array where the parsed type must be stored. | |
1816 | * @param index | |
1817 | * the index in 'frame' where the parsed type must be stored. | |
1818 | * @param v | |
1819 | * the start offset of the stack map frame type to read. | |
1820 | * @param buf | |
1821 | * a buffer to read strings. | |
1822 | * @param labels | |
1823 | * the labels of the method currently being parsed, indexed by | |
1824 | * their offset. If the parsed type is an Uninitialized type, a | |
1825 | * new label for the corresponding NEW instruction is stored in | |
1826 | * this array if it does not already exist. | |
1827 | * @return the offset of the first byte after the parsed type. | |
1828 | */ | |
1829 | private int readFrameType(final Object[] frame, final int index, int v, | |
1830 | final char[] buf, final Label[] labels) { | |
1831 | int type = b[v++] & 0xFF; | |
1832 | switch (type) { | |
1833 | case 0: | |
1834 | frame[index] = Opcodes.TOP; | |
1835 | break; | |
1836 | case 1: | |
1837 | frame[index] = Opcodes.INTEGER; | |
1838 | break; | |
1839 | case 2: | |
1840 | frame[index] = Opcodes.FLOAT; | |
1841 | break; | |
1842 | case 3: | |
1843 | frame[index] = Opcodes.DOUBLE; | |
1844 | break; | |
1845 | case 4: | |
1846 | frame[index] = Opcodes.LONG; | |
1847 | break; | |
1848 | case 5: | |
1849 | frame[index] = Opcodes.NULL; | |
1850 | break; | |
1851 | case 6: | |
1852 | frame[index] = Opcodes.UNINITIALIZED_THIS; | |
1853 | break; | |
1854 | case 7: // Object | |
1855 | frame[index] = readClass(v, buf); | |
1856 | v += 2; | |
1857 | break; | |
1858 | default: // Uninitialized | |
1859 | frame[index] = readLabel(readUnsignedShort(v), labels); | |
1860 | v += 2; | |
1861 | } | |
1862 | return v; | |
1863 | } | |
1864 | ||
1865 | /** | |
1866 | * Returns the label corresponding to the given offset. The default | |
1867 | * implementation of this method creates a label for the given offset if it | |
1868 | * has not been already created. | |
1869 | * | |
1870 | * @param offset | |
1871 | * a bytecode offset in a method. | |
1872 | * @param labels | |
1873 | * the already created labels, indexed by their offset. If a | |
1874 | * label already exists for offset this method must not create a | |
1875 | * new one. Otherwise it must store the new label in this array. | |
1876 | * @return a non null Label, which must be equal to labels[offset]. | |
1877 | */ | |
1878 | protected Label readLabel(int offset, Label[] labels) { | |
1879 | if (labels[offset] == null) { | |
1880 | labels[offset] = new Label(); | |
1881 | } | |
1882 | return labels[offset]; | |
1883 | } | |
1884 | ||
1885 | /** | |
1886 | * Returns the start index of the attribute_info structure of this class. | |
1887 | * | |
1888 | * @return the start index of the attribute_info structure of this class. | |
1889 | */ | |
1890 | private int getAttributes() { | |
1891 | // skips the header | |
1892 | int u = header + 8 + readUnsignedShort(header + 6) * 2; | |
1893 | // skips fields and methods | |
1894 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
1895 | for (int j = readUnsignedShort(u + 8); j > 0; --j) { | |
1896 | u += 6 + readInt(u + 12); | |
1897 | } | |
1898 | u += 8; | |
1899 | } | |
1900 | u += 2; | |
1901 | for (int i = readUnsignedShort(u); i > 0; --i) { | |
1902 | for (int j = readUnsignedShort(u + 8); j > 0; --j) { | |
1903 | u += 6 + readInt(u + 12); | |
1904 | } | |
1905 | u += 8; | |
1906 | } | |
1907 | // the attribute_info structure starts just after the methods | |
1908 | return u + 2; | |
1909 | } | |
1910 | ||
1911 | /** | |
1912 | * Reads an attribute in {@link #b b}. | |
1913 | * | |
1914 | * @param attrs | |
1915 | * prototypes of the attributes that must be parsed during the | |
1916 | * visit of the class. Any attribute whose type is not equal to | |
1917 | * the type of one the prototypes is ignored (i.e. an empty | |
1918 | * {@link Attribute} instance is returned). | |
1919 | * @param type | |
1920 | * the type of the attribute. | |
1921 | * @param off | |
1922 | * index of the first byte of the attribute's content in | |
1923 | * {@link #b b}. The 6 attribute header bytes, containing the | |
1924 | * type and the length of the attribute, are not taken into | |
1925 | * account here (they have already been read). | |
1926 | * @param len | |
1927 | * the length of the attribute's content. | |
1928 | * @param buf | |
1929 | * buffer to be used to call {@link #readUTF8 readUTF8}, | |
1930 | * {@link #readClass(int,char[]) readClass} or {@link #readConst | |
1931 | * readConst}. | |
1932 | * @param codeOff | |
1933 | * index of the first byte of code's attribute content in | |
1934 | * {@link #b b}, or -1 if the attribute to be read is not a code | |
1935 | * attribute. The 6 attribute header bytes, containing the type | |
1936 | * and the length of the attribute, are not taken into account | |
1937 | * here. | |
1938 | * @param labels | |
1939 | * the labels of the method's code, or <tt>null</tt> if the | |
1940 | * attribute to be read is not a code attribute. | |
1941 | * @return the attribute that has been read, or <tt>null</tt> to skip this | |
1942 | * attribute. | |
1943 | */ | |
1944 | private Attribute readAttribute(final Attribute[] attrs, final String type, | |
1945 | final int off, final int len, final char[] buf, final int codeOff, | |
1946 | final Label[] labels) { | |
1947 | for (int i = 0; i < attrs.length; ++i) { | |
1948 | if (attrs[i].type.equals(type)) { | |
1949 | return attrs[i].read(this, off, len, buf, codeOff, labels); | |
1950 | } | |
1951 | } | |
1952 | return new Attribute(type).read(this, off, len, null, -1, null); | |
1953 | } | |
1954 | ||
1955 | // ------------------------------------------------------------------------ | |
1956 | // Utility methods: low level parsing | |
1957 | // ------------------------------------------------------------------------ | |
1958 | ||
1959 | /** | |
1960 | * Returns the number of constant pool items in {@link #b b}. | |
1961 | * | |
1962 | * @return the number of constant pool items in {@link #b b}. | |
1963 | */ | |
1964 | public int getItemCount() { | |
1965 | return items.length; | |
1966 | } | |
1967 | ||
1968 | /** | |
1969 | * Returns the start index of the constant pool item in {@link #b b}, plus | |
1970 | * one. <i>This method is intended for {@link Attribute} sub classes, and is | |
1971 | * normally not needed by class generators or adapters.</i> | |
1972 | * | |
1973 | * @param item | |
1974 | * the index a constant pool item. | |
1975 | * @return the start index of the constant pool item in {@link #b b}, plus | |
1976 | * one. | |
1977 | */ | |
1978 | public int getItem(final int item) { | |
1979 | return items[item]; | |
1980 | } | |
1981 | ||
1982 | /** | |
1983 | * Returns the maximum length of the strings contained in the constant pool | |
1984 | * of the class. | |
1985 | * | |
1986 | * @return the maximum length of the strings contained in the constant pool | |
1987 | * of the class. | |
1988 | */ | |
1989 | public int getMaxStringLength() { | |
1990 | return maxStringLength; | |
1991 | } | |
1992 | ||
1993 | /** | |
1994 | * Reads a byte value in {@link #b b}. <i>This method is intended for | |
1995 | * {@link Attribute} sub classes, and is normally not needed by class | |
1996 | * generators or adapters.</i> | |
1997 | * | |
1998 | * @param index | |
1999 | * the start index of the value to be read in {@link #b b}. | |
2000 | * @return the read value. | |
2001 | */ | |
2002 | public int readByte(final int index) { | |
2003 | return b[index] & 0xFF; | |
2004 | } | |
2005 | ||
2006 | /** | |
2007 | * Reads an unsigned short value in {@link #b b}. <i>This method is intended | |
2008 | * for {@link Attribute} sub classes, and is normally not needed by class | |
2009 | * generators or adapters.</i> | |
2010 | * | |
2011 | * @param index | |
2012 | * the start index of the value to be read in {@link #b b}. | |
2013 | * @return the read value. | |
2014 | */ | |
2015 | public int readUnsignedShort(final int index) { | |
2016 | byte[] b = this.b; | |
2017 | return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); | |
2018 | } | |
2019 | ||
2020 | /** | |
2021 | * Reads a signed short value in {@link #b b}. <i>This method is intended | |
2022 | * for {@link Attribute} sub classes, and is normally not needed by class | |
2023 | * generators or adapters.</i> | |
2024 | * | |
2025 | * @param index | |
2026 | * the start index of the value to be read in {@link #b b}. | |
2027 | * @return the read value. | |
2028 | */ | |
2029 | public short readShort(final int index) { | |
2030 | byte[] b = this.b; | |
2031 | return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); | |
2032 | } | |
2033 | ||
2034 | /** | |
2035 | * Reads a signed int value in {@link #b b}. <i>This method is intended for | |
2036 | * {@link Attribute} sub classes, and is normally not needed by class | |
2037 | * generators or adapters.</i> | |
2038 | * | |
2039 | * @param index | |
2040 | * the start index of the value to be read in {@link #b b}. | |
2041 | * @return the read value. | |
2042 | */ | |
2043 | public int readInt(final int index) { | |
2044 | byte[] b = this.b; | |
2045 | return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) | |
2046 | | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); | |
2047 | } | |
2048 | ||
2049 | /** | |
2050 | * Reads a signed long value in {@link #b b}. <i>This method is intended for | |
2051 | * {@link Attribute} sub classes, and is normally not needed by class | |
2052 | * generators or adapters.</i> | |
2053 | * | |
2054 | * @param index | |
2055 | * the start index of the value to be read in {@link #b b}. | |
2056 | * @return the read value. | |
2057 | */ | |
2058 | public long readLong(final int index) { | |
2059 | long l1 = readInt(index); | |
2060 | long l0 = readInt(index + 4) & 0xFFFFFFFFL; | |
2061 | return (l1 << 32) | l0; | |
2062 | } | |
2063 | ||
2064 | /** | |
2065 | * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method | |
2066 | * is intended for {@link Attribute} sub classes, and is normally not needed | |
2067 | * by class generators or adapters.</i> | |
2068 | * | |
2069 | * @param index | |
2070 | * the start index of an unsigned short value in {@link #b b}, | |
2071 | * whose value is the index of an UTF8 constant pool item. | |
2072 | * @param buf | |
2073 | * buffer to be used to read the item. This buffer must be | |
2074 | * sufficiently large. It is not automatically resized. | |
2075 | * @return the String corresponding to the specified UTF8 item. | |
2076 | */ | |
2077 | public String readUTF8(int index, final char[] buf) { | |
2078 | int item = readUnsignedShort(index); | |
2079 | if (index == 0 || item == 0) { | |
2080 | return null; | |
2081 | } | |
2082 | String s = strings[item]; | |
2083 | if (s != null) { | |
2084 | return s; | |
2085 | } | |
2086 | index = items[item]; | |
2087 | return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf); | |
2088 | } | |
2089 | ||
2090 | /** | |
2091 | * Reads UTF8 string in {@link #b b}. | |
2092 | * | |
2093 | * @param index | |
2094 | * start offset of the UTF8 string to be read. | |
2095 | * @param utfLen | |
2096 | * length of the UTF8 string to be read. | |
2097 | * @param buf | |
2098 | * buffer to be used to read the string. This buffer must be | |
2099 | * sufficiently large. It is not automatically resized. | |
2100 | * @return the String corresponding to the specified UTF8 string. | |
2101 | */ | |
2102 | private String readUTF(int index, final int utfLen, final char[] buf) { | |
2103 | int endIndex = index + utfLen; | |
2104 | byte[] b = this.b; | |
2105 | int strLen = 0; | |
2106 | int c; | |
2107 | int st = 0; | |
2108 | char cc = 0; | |
2109 | while (index < endIndex) { | |
2110 | c = b[index++]; | |
2111 | switch (st) { | |
2112 | case 0: | |
2113 | c = c & 0xFF; | |
2114 | if (c < 0x80) { // 0xxxxxxx | |
2115 | buf[strLen++] = (char) c; | |
2116 | } else if (c < 0xE0 && c > 0xBF) { // 110x xxxx 10xx xxxx | |
2117 | cc = (char) (c & 0x1F); | |
2118 | st = 1; | |
2119 | } else { // 1110 xxxx 10xx xxxx 10xx xxxx | |
2120 | cc = (char) (c & 0x0F); | |
2121 | st = 2; | |
2122 | } | |
2123 | break; | |
2124 | ||
2125 | case 1: // byte 2 of 2-byte char or byte 3 of 3-byte char | |
2126 | buf[strLen++] = (char) ((cc << 6) | (c & 0x3F)); | |
2127 | st = 0; | |
2128 | break; | |
2129 | ||
2130 | case 2: // byte 2 of 3-byte char | |
2131 | cc = (char) ((cc << 6) | (c & 0x3F)); | |
2132 | st = 1; | |
2133 | break; | |
2134 | } | |
2135 | } | |
2136 | return new String(buf, 0, strLen); | |
2137 | } | |
2138 | ||
2139 | /** | |
2140 | * Reads a class constant pool item in {@link #b b}. <i>This method is | |
2141 | * intended for {@link Attribute} sub classes, and is normally not needed by | |
2142 | * class generators or adapters.</i> | |
2143 | * | |
2144 | * @param index | |
2145 | * the start index of an unsigned short value in {@link #b b}, | |
2146 | * whose value is the index of a class constant pool item. | |
2147 | * @param buf | |
2148 | * buffer to be used to read the item. This buffer must be | |
2149 | * sufficiently large. It is not automatically resized. | |
2150 | * @return the String corresponding to the specified class item. | |
2151 | */ | |
2152 | public String readClass(final int index, final char[] buf) { | |
2153 | // computes the start index of the CONSTANT_Class item in b | |
2154 | // and reads the CONSTANT_Utf8 item designated by | |
2155 | // the first two bytes of this CONSTANT_Class item | |
2156 | return readUTF8(items[readUnsignedShort(index)], buf); | |
2157 | } | |
2158 | ||
2159 | /** | |
2160 | * Reads a numeric or string constant pool item in {@link #b b}. <i>This | |
2161 | * method is intended for {@link Attribute} sub classes, and is normally not | |
2162 | * needed by class generators or adapters.</i> | |
2163 | * | |
2164 | * @param item | |
2165 | * the index of a constant pool item. | |
2166 | * @param buf | |
2167 | * buffer to be used to read the item. This buffer must be | |
2168 | * sufficiently large. It is not automatically resized. | |
2169 | * @return the {@link Integer}, {@link Float}, {@link Long}, {@link Double}, | |
2170 | * {@link String}, {@link Type} or {@link Handle} corresponding to | |
2171 | * the given constant pool item. | |
2172 | */ | |
2173 | public Object readConst(final int item, final char[] buf) { | |
2174 | int index = items[item]; | |
2175 | switch (b[index - 1]) { | |
2176 | case ClassWriter.INT: | |
2177 | return new Integer(readInt(index)); | |
2178 | case ClassWriter.FLOAT: | |
2179 | return new Float(Float.intBitsToFloat(readInt(index))); | |
2180 | case ClassWriter.LONG: | |
2181 | return new Long(readLong(index)); | |
2182 | case ClassWriter.DOUBLE: | |
2183 | return new Double(Double.longBitsToDouble(readLong(index))); | |
2184 | case ClassWriter.CLASS: | |
2185 | return Type.getObjectType(readUTF8(index, buf)); | |
2186 | case ClassWriter.STR: | |
2187 | return readUTF8(index, buf); | |
2188 | case ClassWriter.MTYPE: | |
2189 | return Type.getMethodType(readUTF8(index, buf)); | |
2190 | default: // case ClassWriter.HANDLE_BASE + [1..9]: | |
2191 | int tag = readByte(index); | |
2192 | int[] items = this.items; | |
2193 | int cpIndex = items[readUnsignedShort(index + 1)]; | |
2194 | String owner = readClass(cpIndex, buf); | |
2195 | cpIndex = items[readUnsignedShort(cpIndex + 2)]; | |
2196 | String name = readUTF8(cpIndex, buf); | |
2197 | String desc = readUTF8(cpIndex + 2, buf); | |
2198 | return new Handle(tag, owner, name, desc); | |
2199 | } | |
2200 | } | |
2201 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 class must be called in | |
33 | * the following order: <tt>visit</tt> [ <tt>visitSource</tt> ] [ | |
34 | * <tt>visitOuterClass</tt> ] ( <tt>visitAnnotation</tt> | | |
35 | * <tt>visitAttribute</tt> )* ( <tt>visitInnerClass</tt> | <tt>visitField</tt> | | |
36 | * <tt>visitMethod</tt> )* <tt>visitEnd</tt>. | |
37 | * | |
38 | * @author Eric Bruneton | |
39 | */ | |
40 | public abstract class ClassVisitor { | |
41 | ||
42 | /** | |
43 | * The ASM API version implemented by this visitor. The value of this field | |
44 | * must be one of {@link Opcodes#ASM4}. | |
45 | */ | |
46 | protected final int api; | |
47 | ||
48 | /** | |
49 | * The class visitor to which this visitor must delegate method calls. May | |
50 | * be null. | |
51 | */ | |
52 | protected ClassVisitor cv; | |
53 | ||
54 | /** | |
55 | * Constructs a new {@link ClassVisitor}. | |
56 | * | |
57 | * @param api | |
58 | * the ASM API version implemented by this visitor. Must be one | |
59 | * of {@link Opcodes#ASM4}. | |
60 | */ | |
61 | public ClassVisitor(final int api) { | |
62 | this(api, null); | |
63 | } | |
64 | ||
65 | /** | |
66 | * Constructs a new {@link ClassVisitor}. | |
67 | * | |
68 | * @param api | |
69 | * the ASM API version implemented by this visitor. Must be one | |
70 | * of {@link Opcodes#ASM4}. | |
71 | * @param cv | |
72 | * the class visitor to which this visitor must delegate method | |
73 | * calls. May be null. | |
74 | */ | |
75 | public ClassVisitor(final int api, final ClassVisitor cv) { | |
76 | if (api != Opcodes.ASM4) { | |
77 | throw new IllegalArgumentException(); | |
78 | } | |
79 | this.api = api; | |
80 | this.cv = cv; | |
81 | } | |
82 | ||
83 | /** | |
84 | * Visits the header of the class. | |
85 | * | |
86 | * @param version | |
87 | * the class version. | |
88 | * @param access | |
89 | * the class's access flags (see {@link Opcodes}). This parameter | |
90 | * also indicates if the class is deprecated. | |
91 | * @param name | |
92 | * the internal name of the class (see | |
93 | * {@link Type#getInternalName() getInternalName}). | |
94 | * @param signature | |
95 | * the signature of this class. May be <tt>null</tt> if the class | |
96 | * is not a generic one, and does not extend or implement generic | |
97 | * classes or interfaces. | |
98 | * @param superName | |
99 | * the internal of name of the super class (see | |
100 | * {@link Type#getInternalName() getInternalName}). For | |
101 | * interfaces, the super class is {@link Object}. May be | |
102 | * <tt>null</tt>, but only for the {@link Object} class. | |
103 | * @param interfaces | |
104 | * the internal names of the class's interfaces (see | |
105 | * {@link Type#getInternalName() getInternalName}). May be | |
106 | * <tt>null</tt>. | |
107 | */ | |
108 | public void visit(int version, int access, String name, String signature, | |
109 | String superName, String[] interfaces) { | |
110 | if (cv != null) { | |
111 | cv.visit(version, access, name, signature, superName, interfaces); | |
112 | } | |
113 | } | |
114 | ||
115 | /** | |
116 | * Visits the source of the class. | |
117 | * | |
118 | * @param source | |
119 | * the name of the source file from which the class was compiled. | |
120 | * May be <tt>null</tt>. | |
121 | * @param debug | |
122 | * additional debug information to compute the correspondance | |
123 | * between source and compiled elements of the class. May be | |
124 | * <tt>null</tt>. | |
125 | */ | |
126 | public void visitSource(String source, String debug) { | |
127 | if (cv != null) { | |
128 | cv.visitSource(source, debug); | |
129 | } | |
130 | } | |
131 | ||
132 | /** | |
133 | * Visits the enclosing class of the class. This method must be called only | |
134 | * if the class has an enclosing class. | |
135 | * | |
136 | * @param owner | |
137 | * internal name of the enclosing class of the class. | |
138 | * @param name | |
139 | * the name of the method that contains the class, or | |
140 | * <tt>null</tt> if the class is not enclosed in a method of its | |
141 | * enclosing class. | |
142 | * @param desc | |
143 | * the descriptor of the method that contains the class, or | |
144 | * <tt>null</tt> if the class is not enclosed in a method of its | |
145 | * enclosing class. | |
146 | */ | |
147 | public void visitOuterClass(String owner, String name, String desc) { | |
148 | if (cv != null) { | |
149 | cv.visitOuterClass(owner, name, desc); | |
150 | } | |
151 | } | |
152 | ||
153 | /** | |
154 | * Visits an annotation of the class. | |
155 | * | |
156 | * @param desc | |
157 | * the class descriptor of the annotation class. | |
158 | * @param visible | |
159 | * <tt>true</tt> if the annotation is visible at runtime. | |
160 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
161 | * this visitor is not interested in visiting this annotation. | |
162 | */ | |
163 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { | |
164 | if (cv != null) { | |
165 | return cv.visitAnnotation(desc, visible); | |
166 | } | |
167 | return null; | |
168 | } | |
169 | ||
170 | /** | |
171 | * Visits a non standard attribute of the class. | |
172 | * | |
173 | * @param attr | |
174 | * an attribute. | |
175 | */ | |
176 | public void visitAttribute(Attribute attr) { | |
177 | if (cv != null) { | |
178 | cv.visitAttribute(attr); | |
179 | } | |
180 | } | |
181 | ||
182 | /** | |
183 | * Visits information about an inner class. This inner class is not | |
184 | * necessarily a member of the class being visited. | |
185 | * | |
186 | * @param name | |
187 | * the internal name of an inner class (see | |
188 | * {@link Type#getInternalName() getInternalName}). | |
189 | * @param outerName | |
190 | * the internal name of the class to which the inner class | |
191 | * belongs (see {@link Type#getInternalName() getInternalName}). | |
192 | * May be <tt>null</tt> for not member classes. | |
193 | * @param innerName | |
194 | * the (simple) name of the inner class inside its enclosing | |
195 | * class. May be <tt>null</tt> for anonymous inner classes. | |
196 | * @param access | |
197 | * the access flags of the inner class as originally declared in | |
198 | * the enclosing class. | |
199 | */ | |
200 | public void visitInnerClass(String name, String outerName, | |
201 | String innerName, int access) { | |
202 | if (cv != null) { | |
203 | cv.visitInnerClass(name, outerName, innerName, access); | |
204 | } | |
205 | } | |
206 | ||
207 | /** | |
208 | * Visits a field of the class. | |
209 | * | |
210 | * @param access | |
211 | * the field's access flags (see {@link Opcodes}). This parameter | |
212 | * also indicates if the field is synthetic and/or deprecated. | |
213 | * @param name | |
214 | * the field's name. | |
215 | * @param desc | |
216 | * the field's descriptor (see {@link Type Type}). | |
217 | * @param signature | |
218 | * the field's signature. May be <tt>null</tt> if the field's | |
219 | * type does not use generic types. | |
220 | * @param value | |
221 | * the field's initial value. This parameter, which may be | |
222 | * <tt>null</tt> if the field does not have an initial value, | |
223 | * must be an {@link Integer}, a {@link Float}, a {@link Long}, a | |
224 | * {@link Double} or a {@link String} (for <tt>int</tt>, | |
225 | * <tt>float</tt>, <tt>long</tt> or <tt>String</tt> fields | |
226 | * respectively). <i>This parameter is only used for static | |
227 | * fields</i>. Its value is ignored for non static fields, which | |
228 | * must be initialized through bytecode instructions in | |
229 | * constructors or methods. | |
230 | * @return a visitor to visit field annotations and attributes, or | |
231 | * <tt>null</tt> if this class visitor is not interested in visiting | |
232 | * these annotations and attributes. | |
233 | */ | |
234 | public FieldVisitor visitField(int access, String name, String desc, | |
235 | String signature, Object value) { | |
236 | if (cv != null) { | |
237 | return cv.visitField(access, name, desc, signature, value); | |
238 | } | |
239 | return null; | |
240 | } | |
241 | ||
242 | /** | |
243 | * Visits a method of the class. This method <i>must</i> return a new | |
244 | * {@link MethodVisitor} instance (or <tt>null</tt>) each time it is called, | |
245 | * i.e., it should not return a previously returned visitor. | |
246 | * | |
247 | * @param access | |
248 | * the method's access flags (see {@link Opcodes}). This | |
249 | * parameter also indicates if the method is synthetic and/or | |
250 | * deprecated. | |
251 | * @param name | |
252 | * the method's name. | |
253 | * @param desc | |
254 | * the method's descriptor (see {@link Type Type}). | |
255 | * @param signature | |
256 | * the method's signature. May be <tt>null</tt> if the method | |
257 | * parameters, return type and exceptions do not use generic | |
258 | * types. | |
259 | * @param exceptions | |
260 | * the internal names of the method's exception classes (see | |
261 | * {@link Type#getInternalName() getInternalName}). May be | |
262 | * <tt>null</tt>. | |
263 | * @return an object to visit the byte code of the method, or <tt>null</tt> | |
264 | * if this class visitor is not interested in visiting the code of | |
265 | * this method. | |
266 | */ | |
267 | public MethodVisitor visitMethod(int access, String name, String desc, | |
268 | String signature, String[] exceptions) { | |
269 | if (cv != null) { | |
270 | return cv.visitMethod(access, name, desc, signature, exceptions); | |
271 | } | |
272 | return null; | |
273 | } | |
274 | ||
275 | /** | |
276 | * Visits the end of the class. This method, which is the last one to be | |
277 | * called, is used to inform the visitor that all the fields and methods of | |
278 | * the class have been visited. | |
279 | */ | |
280 | public void visitEnd() { | |
281 | if (cv != null) { | |
282 | cv.visitEnd(); | |
283 | } | |
284 | } | |
285 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 extends 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 static final 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 static final int COMPUTE_FRAMES = 2; | |
66 | ||
67 | /** | |
68 | * Pseudo access flag to distinguish between the synthetic attribute and the | |
69 | * synthetic access flag. | |
70 | */ | |
71 | static final int ACC_SYNTHETIC_ATTRIBUTE = 0x40000; | |
72 | ||
73 | /** | |
74 | * Factor to convert from ACC_SYNTHETIC_ATTRIBUTE to Opcode.ACC_SYNTHETIC. | |
75 | */ | |
76 | static final int TO_ACC_SYNTHETIC = ACC_SYNTHETIC_ATTRIBUTE | |
77 | / Opcodes.ACC_SYNTHETIC; | |
78 | ||
79 | /** | |
80 | * The type of instructions without any argument. | |
81 | */ | |
82 | static final int NOARG_INSN = 0; | |
83 | ||
84 | /** | |
85 | * The type of instructions with an signed byte argument. | |
86 | */ | |
87 | static final int SBYTE_INSN = 1; | |
88 | ||
89 | /** | |
90 | * The type of instructions with an signed short argument. | |
91 | */ | |
92 | static final int SHORT_INSN = 2; | |
93 | ||
94 | /** | |
95 | * The type of instructions with a local variable index argument. | |
96 | */ | |
97 | static final int VAR_INSN = 3; | |
98 | ||
99 | /** | |
100 | * The type of instructions with an implicit local variable index argument. | |
101 | */ | |
102 | static final int IMPLVAR_INSN = 4; | |
103 | ||
104 | /** | |
105 | * The type of instructions with a type descriptor argument. | |
106 | */ | |
107 | static final int TYPE_INSN = 5; | |
108 | ||
109 | /** | |
110 | * The type of field and method invocations instructions. | |
111 | */ | |
112 | static final int FIELDORMETH_INSN = 6; | |
113 | ||
114 | /** | |
115 | * The type of the INVOKEINTERFACE/INVOKEDYNAMIC instruction. | |
116 | */ | |
117 | static final int ITFMETH_INSN = 7; | |
118 | ||
119 | /** | |
120 | * The type of the INVOKEDYNAMIC instruction. | |
121 | */ | |
122 | static final int INDYMETH_INSN = 8; | |
123 | ||
124 | /** | |
125 | * The type of instructions with a 2 bytes bytecode offset label. | |
126 | */ | |
127 | static final int LABEL_INSN = 9; | |
128 | ||
129 | /** | |
130 | * The type of instructions with a 4 bytes bytecode offset label. | |
131 | */ | |
132 | static final int LABELW_INSN = 10; | |
133 | ||
134 | /** | |
135 | * The type of the LDC instruction. | |
136 | */ | |
137 | static final int LDC_INSN = 11; | |
138 | ||
139 | /** | |
140 | * The type of the LDC_W and LDC2_W instructions. | |
141 | */ | |
142 | static final int LDCW_INSN = 12; | |
143 | ||
144 | /** | |
145 | * The type of the IINC instruction. | |
146 | */ | |
147 | static final int IINC_INSN = 13; | |
148 | ||
149 | /** | |
150 | * The type of the TABLESWITCH instruction. | |
151 | */ | |
152 | static final int TABL_INSN = 14; | |
153 | ||
154 | /** | |
155 | * The type of the LOOKUPSWITCH instruction. | |
156 | */ | |
157 | static final int LOOK_INSN = 15; | |
158 | ||
159 | /** | |
160 | * The type of the MULTIANEWARRAY instruction. | |
161 | */ | |
162 | static final int MANA_INSN = 16; | |
163 | ||
164 | /** | |
165 | * The type of the WIDE instruction. | |
166 | */ | |
167 | static final int WIDE_INSN = 17; | |
168 | ||
169 | /** | |
170 | * The instruction types of all JVM opcodes. | |
171 | */ | |
172 | static final byte[] TYPE; | |
173 | ||
174 | /** | |
175 | * The type of CONSTANT_Class constant pool items. | |
176 | */ | |
177 | static final int CLASS = 7; | |
178 | ||
179 | /** | |
180 | * The type of CONSTANT_Fieldref constant pool items. | |
181 | */ | |
182 | static final int FIELD = 9; | |
183 | ||
184 | /** | |
185 | * The type of CONSTANT_Methodref constant pool items. | |
186 | */ | |
187 | static final int METH = 10; | |
188 | ||
189 | /** | |
190 | * The type of CONSTANT_InterfaceMethodref constant pool items. | |
191 | */ | |
192 | static final int IMETH = 11; | |
193 | ||
194 | /** | |
195 | * The type of CONSTANT_String constant pool items. | |
196 | */ | |
197 | static final int STR = 8; | |
198 | ||
199 | /** | |
200 | * The type of CONSTANT_Integer constant pool items. | |
201 | */ | |
202 | static final int INT = 3; | |
203 | ||
204 | /** | |
205 | * The type of CONSTANT_Float constant pool items. | |
206 | */ | |
207 | static final int FLOAT = 4; | |
208 | ||
209 | /** | |
210 | * The type of CONSTANT_Long constant pool items. | |
211 | */ | |
212 | static final int LONG = 5; | |
213 | ||
214 | /** | |
215 | * The type of CONSTANT_Double constant pool items. | |
216 | */ | |
217 | static final int DOUBLE = 6; | |
218 | ||
219 | /** | |
220 | * The type of CONSTANT_NameAndType constant pool items. | |
221 | */ | |
222 | static final int NAME_TYPE = 12; | |
223 | ||
224 | /** | |
225 | * The type of CONSTANT_Utf8 constant pool items. | |
226 | */ | |
227 | static final int UTF8 = 1; | |
228 | ||
229 | /** | |
230 | * The type of CONSTANT_MethodType constant pool items. | |
231 | */ | |
232 | static final int MTYPE = 16; | |
233 | ||
234 | /** | |
235 | * The type of CONSTANT_MethodHandle constant pool items. | |
236 | */ | |
237 | static final int HANDLE = 15; | |
238 | ||
239 | /** | |
240 | * The type of CONSTANT_InvokeDynamic constant pool items. | |
241 | */ | |
242 | static final int INDY = 18; | |
243 | ||
244 | /** | |
245 | * The base value for all CONSTANT_MethodHandle constant pool items. | |
246 | * Internally, ASM store the 9 variations of CONSTANT_MethodHandle into 9 | |
247 | * different items. | |
248 | */ | |
249 | static final int HANDLE_BASE = 20; | |
250 | ||
251 | /** | |
252 | * Normal type Item stored in the ClassWriter {@link ClassWriter#typeTable}, | |
253 | * instead of the constant pool, in order to avoid clashes with normal | |
254 | * constant pool items in the ClassWriter constant pool's hash table. | |
255 | */ | |
256 | static final int TYPE_NORMAL = 30; | |
257 | ||
258 | /** | |
259 | * Uninitialized type Item stored in the ClassWriter | |
260 | * {@link ClassWriter#typeTable}, instead of the constant pool, in order to | |
261 | * avoid clashes with normal constant pool items in the ClassWriter constant | |
262 | * pool's hash table. | |
263 | */ | |
264 | static final int TYPE_UNINIT = 31; | |
265 | ||
266 | /** | |
267 | * Merged type Item stored in the ClassWriter {@link ClassWriter#typeTable}, | |
268 | * instead of the constant pool, in order to avoid clashes with normal | |
269 | * constant pool items in the ClassWriter constant pool's hash table. | |
270 | */ | |
271 | static final int TYPE_MERGED = 32; | |
272 | ||
273 | /** | |
274 | * The type of BootstrapMethods items. These items are stored in a special | |
275 | * class attribute named BootstrapMethods and not in the constant pool. | |
276 | */ | |
277 | static final int BSM = 33; | |
278 | ||
279 | /** | |
280 | * The class reader from which this class writer was constructed, if any. | |
281 | */ | |
282 | ClassReader cr; | |
283 | ||
284 | /** | |
285 | * Minor and major version numbers of the class to be generated. | |
286 | */ | |
287 | int version; | |
288 | ||
289 | /** | |
290 | * Index of the next item to be added in the constant pool. | |
291 | */ | |
292 | int index; | |
293 | ||
294 | /** | |
295 | * The constant pool of this class. | |
296 | */ | |
297 | final ByteVector pool; | |
298 | ||
299 | /** | |
300 | * The constant pool's hash table data. | |
301 | */ | |
302 | Item[] items; | |
303 | ||
304 | /** | |
305 | * The threshold of the constant pool's hash table. | |
306 | */ | |
307 | int threshold; | |
308 | ||
309 | /** | |
310 | * A reusable key used to look for items in the {@link #items} hash table. | |
311 | */ | |
312 | final Item key; | |
313 | ||
314 | /** | |
315 | * A reusable key used to look for items in the {@link #items} hash table. | |
316 | */ | |
317 | final Item key2; | |
318 | ||
319 | /** | |
320 | * A reusable key used to look for items in the {@link #items} hash table. | |
321 | */ | |
322 | final Item key3; | |
323 | ||
324 | /** | |
325 | * A reusable key used to look for items in the {@link #items} hash table. | |
326 | */ | |
327 | final Item key4; | |
328 | ||
329 | /** | |
330 | * A type table used to temporarily store internal names that will not | |
331 | * necessarily be stored in the constant pool. This type table is used by | |
332 | * the control flow and data flow analysis algorithm used to compute stack | |
333 | * map frames from scratch. This array associates to each index <tt>i</tt> | |
334 | * the Item whose index is <tt>i</tt>. All Item objects stored in this array | |
335 | * are also stored in the {@link #items} hash table. These two arrays allow | |
336 | * to retrieve an Item from its index or, conversely, to get the index of an | |
337 | * Item from its value. Each Item stores an internal name in its | |
338 | * {@link Item#strVal1} field. | |
339 | */ | |
340 | Item[] typeTable; | |
341 | ||
342 | /** | |
343 | * Number of elements in the {@link #typeTable} array. | |
344 | */ | |
345 | private short typeCount; | |
346 | ||
347 | /** | |
348 | * The access flags of this class. | |
349 | */ | |
350 | private int access; | |
351 | ||
352 | /** | |
353 | * The constant pool item that contains the internal name of this class. | |
354 | */ | |
355 | private int name; | |
356 | ||
357 | /** | |
358 | * The internal name of this class. | |
359 | */ | |
360 | String thisName; | |
361 | ||
362 | /** | |
363 | * The constant pool item that contains the signature of this class. | |
364 | */ | |
365 | private int signature; | |
366 | ||
367 | /** | |
368 | * The constant pool item that contains the internal name of the super class | |
369 | * of this class. | |
370 | */ | |
371 | private int superName; | |
372 | ||
373 | /** | |
374 | * Number of interfaces implemented or extended by this class or interface. | |
375 | */ | |
376 | private int interfaceCount; | |
377 | ||
378 | /** | |
379 | * The interfaces implemented or extended by this class or interface. More | |
380 | * precisely, this array contains the indexes of the constant pool items | |
381 | * that contain the internal names of these interfaces. | |
382 | */ | |
383 | private int[] interfaces; | |
384 | ||
385 | /** | |
386 | * The index of the constant pool item that contains the name of the source | |
387 | * file from which this class was compiled. | |
388 | */ | |
389 | private int sourceFile; | |
390 | ||
391 | /** | |
392 | * The SourceDebug attribute of this class. | |
393 | */ | |
394 | private ByteVector sourceDebug; | |
395 | ||
396 | /** | |
397 | * The constant pool item that contains the name of the enclosing class of | |
398 | * this class. | |
399 | */ | |
400 | private int enclosingMethodOwner; | |
401 | ||
402 | /** | |
403 | * The constant pool item that contains the name and descriptor of the | |
404 | * enclosing method of this class. | |
405 | */ | |
406 | private int enclosingMethod; | |
407 | ||
408 | /** | |
409 | * The runtime visible annotations of this class. | |
410 | */ | |
411 | private AnnotationWriter anns; | |
412 | ||
413 | /** | |
414 | * The runtime invisible annotations of this class. | |
415 | */ | |
416 | private AnnotationWriter ianns; | |
417 | ||
418 | /** | |
419 | * The non standard attributes of this class. | |
420 | */ | |
421 | private Attribute attrs; | |
422 | ||
423 | /** | |
424 | * The number of entries in the InnerClasses attribute. | |
425 | */ | |
426 | private int innerClassesCount; | |
427 | ||
428 | /** | |
429 | * The InnerClasses attribute. | |
430 | */ | |
431 | private ByteVector innerClasses; | |
432 | ||
433 | /** | |
434 | * The number of entries in the BootstrapMethods attribute. | |
435 | */ | |
436 | int bootstrapMethodsCount; | |
437 | ||
438 | /** | |
439 | * The BootstrapMethods attribute. | |
440 | */ | |
441 | ByteVector bootstrapMethods; | |
442 | ||
443 | /** | |
444 | * The fields of this class. These fields are stored in a linked list of | |
445 | * {@link FieldWriter} objects, linked to each other by their | |
446 | * {@link FieldWriter#fv} field. This field stores the first element of this | |
447 | * list. | |
448 | */ | |
449 | FieldWriter firstField; | |
450 | ||
451 | /** | |
452 | * The fields of this class. These fields are stored in a linked list of | |
453 | * {@link FieldWriter} objects, linked to each other by their | |
454 | * {@link FieldWriter#fv} field. This field stores the last element of this | |
455 | * list. | |
456 | */ | |
457 | FieldWriter lastField; | |
458 | ||
459 | /** | |
460 | * The methods of this class. These methods are stored in a linked list of | |
461 | * {@link MethodWriter} objects, linked to each other by their | |
462 | * {@link MethodWriter#mv} field. This field stores the first element of | |
463 | * this list. | |
464 | */ | |
465 | MethodWriter firstMethod; | |
466 | ||
467 | /** | |
468 | * The methods of this class. These methods are stored in a linked list of | |
469 | * {@link MethodWriter} objects, linked to each other by their | |
470 | * {@link MethodWriter#mv} field. This field stores the last element of this | |
471 | * list. | |
472 | */ | |
473 | MethodWriter lastMethod; | |
474 | ||
475 | /** | |
476 | * <tt>true</tt> if the maximum stack size and number of local variables | |
477 | * must be automatically computed. | |
478 | */ | |
479 | private final boolean computeMaxs; | |
480 | ||
481 | /** | |
482 | * <tt>true</tt> if the stack map frames must be recomputed from scratch. | |
483 | */ | |
484 | private final boolean computeFrames; | |
485 | ||
486 | /** | |
487 | * <tt>true</tt> if the stack map tables of this class are invalid. The | |
488 | * {@link MethodWriter#resizeInstructions} method cannot transform existing | |
489 | * stack map tables, and so produces potentially invalid classes when it is | |
490 | * executed. In this case the class is reread and rewritten with the | |
491 | * {@link #COMPUTE_FRAMES} option (the resizeInstructions method can resize | |
492 | * stack map tables when this option is used). | |
493 | */ | |
494 | boolean invalidFrames; | |
495 | ||
496 | // ------------------------------------------------------------------------ | |
497 | // Static initializer | |
498 | // ------------------------------------------------------------------------ | |
499 | ||
500 | /** | |
501 | * Computes the instruction types of JVM opcodes. | |
502 | */ | |
503 | static { | |
504 | int i; | |
505 | byte[] b = new byte[220]; | |
506 | String s = "AAAAAAAAAAAAAAAABCLMMDDDDDEEEEEEEEEEEEEEEEEEEEAAAAAAAADD" | |
507 | + "DDDEEEEEEEEEEEEEEEEEEEEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" | |
508 | + "AAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAAJJJJJJJJJJJJJJJJDOPAA" | |
509 | + "AAAAGGGGGGGHIFBFAAFFAARQJJKKJJJJJJJJJJJJJJJJJJ"; | |
510 | for (i = 0; i < b.length; ++i) { | |
511 | b[i] = (byte) (s.charAt(i) - 'A'); | |
512 | } | |
513 | TYPE = b; | |
514 | ||
515 | // code to generate the above string | |
516 | // | |
517 | // // SBYTE_INSN instructions | |
518 | // b[Constants.NEWARRAY] = SBYTE_INSN; | |
519 | // b[Constants.BIPUSH] = SBYTE_INSN; | |
520 | // | |
521 | // // SHORT_INSN instructions | |
522 | // b[Constants.SIPUSH] = SHORT_INSN; | |
523 | // | |
524 | // // (IMPL)VAR_INSN instructions | |
525 | // b[Constants.RET] = VAR_INSN; | |
526 | // for (i = Constants.ILOAD; i <= Constants.ALOAD; ++i) { | |
527 | // b[i] = VAR_INSN; | |
528 | // } | |
529 | // for (i = Constants.ISTORE; i <= Constants.ASTORE; ++i) { | |
530 | // b[i] = VAR_INSN; | |
531 | // } | |
532 | // for (i = 26; i <= 45; ++i) { // ILOAD_0 to ALOAD_3 | |
533 | // b[i] = IMPLVAR_INSN; | |
534 | // } | |
535 | // for (i = 59; i <= 78; ++i) { // ISTORE_0 to ASTORE_3 | |
536 | // b[i] = IMPLVAR_INSN; | |
537 | // } | |
538 | // | |
539 | // // TYPE_INSN instructions | |
540 | // b[Constants.NEW] = TYPE_INSN; | |
541 | // b[Constants.ANEWARRAY] = TYPE_INSN; | |
542 | // b[Constants.CHECKCAST] = TYPE_INSN; | |
543 | // b[Constants.INSTANCEOF] = TYPE_INSN; | |
544 | // | |
545 | // // (Set)FIELDORMETH_INSN instructions | |
546 | // for (i = Constants.GETSTATIC; i <= Constants.INVOKESTATIC; ++i) { | |
547 | // b[i] = FIELDORMETH_INSN; | |
548 | // } | |
549 | // b[Constants.INVOKEINTERFACE] = ITFMETH_INSN; | |
550 | // b[Constants.INVOKEDYNAMIC] = INDYMETH_INSN; | |
551 | // | |
552 | // // LABEL(W)_INSN instructions | |
553 | // for (i = Constants.IFEQ; i <= Constants.JSR; ++i) { | |
554 | // b[i] = LABEL_INSN; | |
555 | // } | |
556 | // b[Constants.IFNULL] = LABEL_INSN; | |
557 | // b[Constants.IFNONNULL] = LABEL_INSN; | |
558 | // b[200] = LABELW_INSN; // GOTO_W | |
559 | // b[201] = LABELW_INSN; // JSR_W | |
560 | // // temporary opcodes used internally by ASM - see Label and | |
561 | // MethodWriter | |
562 | // for (i = 202; i < 220; ++i) { | |
563 | // b[i] = LABEL_INSN; | |
564 | // } | |
565 | // | |
566 | // // LDC(_W) instructions | |
567 | // b[Constants.LDC] = LDC_INSN; | |
568 | // b[19] = LDCW_INSN; // LDC_W | |
569 | // b[20] = LDCW_INSN; // LDC2_W | |
570 | // | |
571 | // // special instructions | |
572 | // b[Constants.IINC] = IINC_INSN; | |
573 | // b[Constants.TABLESWITCH] = TABL_INSN; | |
574 | // b[Constants.LOOKUPSWITCH] = LOOK_INSN; | |
575 | // b[Constants.MULTIANEWARRAY] = MANA_INSN; | |
576 | // b[196] = WIDE_INSN; // WIDE | |
577 | // | |
578 | // for (i = 0; i < b.length; ++i) { | |
579 | // System.err.print((char)('A' + b[i])); | |
580 | // } | |
581 | // System.err.println(); | |
582 | } | |
583 | ||
584 | // ------------------------------------------------------------------------ | |
585 | // Constructor | |
586 | // ------------------------------------------------------------------------ | |
587 | ||
588 | /** | |
589 | * Constructs a new {@link ClassWriter} object. | |
590 | * | |
591 | * @param flags | |
592 | * option flags that can be used to modify the default behavior | |
593 | * of this class. See {@link #COMPUTE_MAXS}, | |
594 | * {@link #COMPUTE_FRAMES}. | |
595 | */ | |
596 | public ClassWriter(final int flags) { | |
597 | super(Opcodes.ASM4); | |
598 | index = 1; | |
599 | pool = new ByteVector(); | |
600 | items = new Item[256]; | |
601 | threshold = (int) (0.75d * items.length); | |
602 | key = new Item(); | |
603 | key2 = new Item(); | |
604 | key3 = new Item(); | |
605 | key4 = new Item(); | |
606 | this.computeMaxs = (flags & COMPUTE_MAXS) != 0; | |
607 | this.computeFrames = (flags & COMPUTE_FRAMES) != 0; | |
608 | } | |
609 | ||
610 | /** | |
611 | * Constructs a new {@link ClassWriter} object and enables optimizations for | |
612 | * "mostly add" bytecode transformations. These optimizations are the | |
613 | * following: | |
614 | * | |
615 | * <ul> | |
616 | * <li>The constant pool from the original class is copied as is in the new | |
617 | * class, which saves time. New constant pool entries will be added at the | |
618 | * end if necessary, but unused constant pool entries <i>won't be | |
619 | * removed</i>.</li> | |
620 | * <li>Methods that are not transformed are copied as is in the new class, | |
621 | * directly from the original class bytecode (i.e. without emitting visit | |
622 | * events for all the method instructions), which saves a <i>lot</i> of | |
623 | * time. Untransformed methods are detected by the fact that the | |
624 | * {@link ClassReader} receives {@link MethodVisitor} objects that come from | |
625 | * a {@link ClassWriter} (and not from any other {@link ClassVisitor} | |
626 | * instance).</li> | |
627 | * </ul> | |
628 | * | |
629 | * @param classReader | |
630 | * the {@link ClassReader} used to read the original class. It | |
631 | * will be used to copy the entire constant pool from the | |
632 | * original class and also to copy other fragments of original | |
633 | * bytecode where applicable. | |
634 | * @param flags | |
635 | * option flags that can be used to modify the default behavior | |
636 | * of this class. <i>These option flags do not affect methods | |
637 | * that are copied as is in the new class. This means that the | |
638 | * maximum stack size nor the stack frames will be computed for | |
639 | * these methods</i>. See {@link #COMPUTE_MAXS}, | |
640 | * {@link #COMPUTE_FRAMES}. | |
641 | */ | |
642 | public ClassWriter(final ClassReader classReader, final int flags) { | |
643 | this(flags); | |
644 | classReader.copyPool(this); | |
645 | this.cr = classReader; | |
646 | } | |
647 | ||
648 | // ------------------------------------------------------------------------ | |
649 | // Implementation of the ClassVisitor abstract class | |
650 | // ------------------------------------------------------------------------ | |
651 | ||
652 | @Override | |
653 | public final void visit(final int version, final int access, | |
654 | final String name, final String signature, final String superName, | |
655 | final String[] interfaces) { | |
656 | this.version = version; | |
657 | this.access = access; | |
658 | this.name = newClass(name); | |
659 | thisName = name; | |
660 | if (ClassReader.SIGNATURES && signature != null) { | |
661 | this.signature = newUTF8(signature); | |
662 | } | |
663 | this.superName = superName == null ? 0 : newClass(superName); | |
664 | if (interfaces != null && interfaces.length > 0) { | |
665 | interfaceCount = interfaces.length; | |
666 | this.interfaces = new int[interfaceCount]; | |
667 | for (int i = 0; i < interfaceCount; ++i) { | |
668 | this.interfaces[i] = newClass(interfaces[i]); | |
669 | } | |
670 | } | |
671 | } | |
672 | ||
673 | @Override | |
674 | public final void visitSource(final String file, final String debug) { | |
675 | if (file != null) { | |
676 | sourceFile = newUTF8(file); | |
677 | } | |
678 | if (debug != null) { | |
679 | sourceDebug = new ByteVector().putUTF8(debug); | |
680 | } | |
681 | } | |
682 | ||
683 | @Override | |
684 | public final void visitOuterClass(final String owner, final String name, | |
685 | final String desc) { | |
686 | enclosingMethodOwner = newClass(owner); | |
687 | if (name != null && desc != null) { | |
688 | enclosingMethod = newNameType(name, desc); | |
689 | } | |
690 | } | |
691 | ||
692 | @Override | |
693 | public final AnnotationVisitor visitAnnotation(final String desc, | |
694 | final boolean visible) { | |
695 | if (!ClassReader.ANNOTATIONS) { | |
696 | return null; | |
697 | } | |
698 | ByteVector bv = new ByteVector(); | |
699 | // write type, and reserve space for values count | |
700 | bv.putShort(newUTF8(desc)).putShort(0); | |
701 | AnnotationWriter aw = new AnnotationWriter(this, true, bv, bv, 2); | |
702 | if (visible) { | |
703 | aw.next = anns; | |
704 | anns = aw; | |
705 | } else { | |
706 | aw.next = ianns; | |
707 | ianns = aw; | |
708 | } | |
709 | return aw; | |
710 | } | |
711 | ||
712 | @Override | |
713 | public final void visitAttribute(final Attribute attr) { | |
714 | attr.next = attrs; | |
715 | attrs = attr; | |
716 | } | |
717 | ||
718 | @Override | |
719 | public final void visitInnerClass(final String name, | |
720 | final String outerName, final String innerName, final int access) { | |
721 | if (innerClasses == null) { | |
722 | innerClasses = new ByteVector(); | |
723 | } | |
724 | ++innerClassesCount; | |
725 | innerClasses.putShort(name == null ? 0 : newClass(name)); | |
726 | innerClasses.putShort(outerName == null ? 0 : newClass(outerName)); | |
727 | innerClasses.putShort(innerName == null ? 0 : newUTF8(innerName)); | |
728 | innerClasses.putShort(access); | |
729 | } | |
730 | ||
731 | @Override | |
732 | public final FieldVisitor visitField(final int access, final String name, | |
733 | final String desc, final String signature, final Object value) { | |
734 | return new FieldWriter(this, access, name, desc, signature, value); | |
735 | } | |
736 | ||
737 | @Override | |
738 | public final MethodVisitor visitMethod(final int access, final String name, | |
739 | final String desc, final String signature, final String[] exceptions) { | |
740 | return new MethodWriter(this, access, name, desc, signature, | |
741 | exceptions, computeMaxs, computeFrames); | |
742 | } | |
743 | ||
744 | @Override | |
745 | public final void visitEnd() { | |
746 | } | |
747 | ||
748 | // ------------------------------------------------------------------------ | |
749 | // Other public methods | |
750 | // ------------------------------------------------------------------------ | |
751 | ||
752 | /** | |
753 | * Returns the bytecode of the class that was build with this class writer. | |
754 | * | |
755 | * @return the bytecode of the class that was build with this class writer. | |
756 | */ | |
757 | public byte[] toByteArray() { | |
758 | if (index > 0xFFFF) { | |
759 | throw new RuntimeException("Class file too large!"); | |
760 | } | |
761 | // computes the real size of the bytecode of this class | |
762 | int size = 24 + 2 * interfaceCount; | |
763 | int nbFields = 0; | |
764 | FieldWriter fb = firstField; | |
765 | while (fb != null) { | |
766 | ++nbFields; | |
767 | size += fb.getSize(); | |
768 | fb = (FieldWriter) fb.fv; | |
769 | } | |
770 | int nbMethods = 0; | |
771 | MethodWriter mb = firstMethod; | |
772 | while (mb != null) { | |
773 | ++nbMethods; | |
774 | size += mb.getSize(); | |
775 | mb = (MethodWriter) mb.mv; | |
776 | } | |
777 | int attributeCount = 0; | |
778 | if (bootstrapMethods != null) { | |
779 | // we put it as first attribute in order to improve a bit | |
780 | // ClassReader.copyBootstrapMethods | |
781 | ++attributeCount; | |
782 | size += 8 + bootstrapMethods.length; | |
783 | newUTF8("BootstrapMethods"); | |
784 | } | |
785 | if (ClassReader.SIGNATURES && signature != 0) { | |
786 | ++attributeCount; | |
787 | size += 8; | |
788 | newUTF8("Signature"); | |
789 | } | |
790 | if (sourceFile != 0) { | |
791 | ++attributeCount; | |
792 | size += 8; | |
793 | newUTF8("SourceFile"); | |
794 | } | |
795 | if (sourceDebug != null) { | |
796 | ++attributeCount; | |
797 | size += sourceDebug.length + 4; | |
798 | newUTF8("SourceDebugExtension"); | |
799 | } | |
800 | if (enclosingMethodOwner != 0) { | |
801 | ++attributeCount; | |
802 | size += 10; | |
803 | newUTF8("EnclosingMethod"); | |
804 | } | |
805 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
806 | ++attributeCount; | |
807 | size += 6; | |
808 | newUTF8("Deprecated"); | |
809 | } | |
810 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
811 | if ((version & 0xFFFF) < Opcodes.V1_5 | |
812 | || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
813 | ++attributeCount; | |
814 | size += 6; | |
815 | newUTF8("Synthetic"); | |
816 | } | |
817 | } | |
818 | if (innerClasses != null) { | |
819 | ++attributeCount; | |
820 | size += 8 + innerClasses.length; | |
821 | newUTF8("InnerClasses"); | |
822 | } | |
823 | if (ClassReader.ANNOTATIONS && anns != null) { | |
824 | ++attributeCount; | |
825 | size += 8 + anns.getSize(); | |
826 | newUTF8("RuntimeVisibleAnnotations"); | |
827 | } | |
828 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
829 | ++attributeCount; | |
830 | size += 8 + ianns.getSize(); | |
831 | newUTF8("RuntimeInvisibleAnnotations"); | |
832 | } | |
833 | if (attrs != null) { | |
834 | attributeCount += attrs.getCount(); | |
835 | size += attrs.getSize(this, null, 0, -1, -1); | |
836 | } | |
837 | size += pool.length; | |
838 | // allocates a byte vector of this size, in order to avoid unnecessary | |
839 | // arraycopy operations in the ByteVector.enlarge() method | |
840 | ByteVector out = new ByteVector(size); | |
841 | out.putInt(0xCAFEBABE).putInt(version); | |
842 | out.putShort(index).putByteArray(pool.data, 0, pool.length); | |
843 | int mask = Opcodes.ACC_DEPRECATED | ACC_SYNTHETIC_ATTRIBUTE | |
844 | | ((access & ACC_SYNTHETIC_ATTRIBUTE) / TO_ACC_SYNTHETIC); | |
845 | out.putShort(access & ~mask).putShort(name).putShort(superName); | |
846 | out.putShort(interfaceCount); | |
847 | for (int i = 0; i < interfaceCount; ++i) { | |
848 | out.putShort(interfaces[i]); | |
849 | } | |
850 | out.putShort(nbFields); | |
851 | fb = firstField; | |
852 | while (fb != null) { | |
853 | fb.put(out); | |
854 | fb = (FieldWriter) fb.fv; | |
855 | } | |
856 | out.putShort(nbMethods); | |
857 | mb = firstMethod; | |
858 | while (mb != null) { | |
859 | mb.put(out); | |
860 | mb = (MethodWriter) mb.mv; | |
861 | } | |
862 | out.putShort(attributeCount); | |
863 | if (bootstrapMethods != null) { | |
864 | out.putShort(newUTF8("BootstrapMethods")); | |
865 | out.putInt(bootstrapMethods.length + 2).putShort( | |
866 | bootstrapMethodsCount); | |
867 | out.putByteArray(bootstrapMethods.data, 0, bootstrapMethods.length); | |
868 | } | |
869 | if (ClassReader.SIGNATURES && signature != 0) { | |
870 | out.putShort(newUTF8("Signature")).putInt(2).putShort(signature); | |
871 | } | |
872 | if (sourceFile != 0) { | |
873 | out.putShort(newUTF8("SourceFile")).putInt(2).putShort(sourceFile); | |
874 | } | |
875 | if (sourceDebug != null) { | |
876 | int len = sourceDebug.length - 2; | |
877 | out.putShort(newUTF8("SourceDebugExtension")).putInt(len); | |
878 | out.putByteArray(sourceDebug.data, 2, len); | |
879 | } | |
880 | if (enclosingMethodOwner != 0) { | |
881 | out.putShort(newUTF8("EnclosingMethod")).putInt(4); | |
882 | out.putShort(enclosingMethodOwner).putShort(enclosingMethod); | |
883 | } | |
884 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
885 | out.putShort(newUTF8("Deprecated")).putInt(0); | |
886 | } | |
887 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
888 | if ((version & 0xFFFF) < Opcodes.V1_5 | |
889 | || (access & ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
890 | out.putShort(newUTF8("Synthetic")).putInt(0); | |
891 | } | |
892 | } | |
893 | if (innerClasses != null) { | |
894 | out.putShort(newUTF8("InnerClasses")); | |
895 | out.putInt(innerClasses.length + 2).putShort(innerClassesCount); | |
896 | out.putByteArray(innerClasses.data, 0, innerClasses.length); | |
897 | } | |
898 | if (ClassReader.ANNOTATIONS && anns != null) { | |
899 | out.putShort(newUTF8("RuntimeVisibleAnnotations")); | |
900 | anns.put(out); | |
901 | } | |
902 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
903 | out.putShort(newUTF8("RuntimeInvisibleAnnotations")); | |
904 | ianns.put(out); | |
905 | } | |
906 | if (attrs != null) { | |
907 | attrs.put(this, null, 0, -1, -1, out); | |
908 | } | |
909 | if (invalidFrames) { | |
910 | ClassWriter cw = new ClassWriter(COMPUTE_FRAMES); | |
911 | new ClassReader(out.data).accept(cw, ClassReader.SKIP_FRAMES); | |
912 | return cw.toByteArray(); | |
913 | } | |
914 | return out.data; | |
915 | } | |
916 | ||
917 | // ------------------------------------------------------------------------ | |
918 | // Utility methods: constant pool management | |
919 | // ------------------------------------------------------------------------ | |
920 | ||
921 | /** | |
922 | * Adds a number or string constant to the constant pool of the class being | |
923 | * build. Does nothing if the constant pool already contains a similar item. | |
924 | * | |
925 | * @param cst | |
926 | * the value of the constant to be added to the constant pool. | |
927 | * This parameter must be an {@link Integer}, a {@link Float}, a | |
928 | * {@link Long}, a {@link Double}, a {@link String} or a | |
929 | * {@link Type}. | |
930 | * @return a new or already existing constant item with the given value. | |
931 | */ | |
932 | Item newConstItem(final Object cst) { | |
933 | if (cst instanceof Integer) { | |
934 | int val = ((Integer) cst).intValue(); | |
935 | return newInteger(val); | |
936 | } else if (cst instanceof Byte) { | |
937 | int val = ((Byte) cst).intValue(); | |
938 | return newInteger(val); | |
939 | } else if (cst instanceof Character) { | |
940 | int val = ((Character) cst).charValue(); | |
941 | return newInteger(val); | |
942 | } else if (cst instanceof Short) { | |
943 | int val = ((Short) cst).intValue(); | |
944 | return newInteger(val); | |
945 | } else if (cst instanceof Boolean) { | |
946 | int val = ((Boolean) cst).booleanValue() ? 1 : 0; | |
947 | return newInteger(val); | |
948 | } else if (cst instanceof Float) { | |
949 | float val = ((Float) cst).floatValue(); | |
950 | return newFloat(val); | |
951 | } else if (cst instanceof Long) { | |
952 | long val = ((Long) cst).longValue(); | |
953 | return newLong(val); | |
954 | } else if (cst instanceof Double) { | |
955 | double val = ((Double) cst).doubleValue(); | |
956 | return newDouble(val); | |
957 | } else if (cst instanceof String) { | |
958 | return newString((String) cst); | |
959 | } else if (cst instanceof Type) { | |
960 | Type t = (Type) cst; | |
961 | int s = t.getSort(); | |
962 | if (s == Type.OBJECT) { | |
963 | return newClassItem(t.getInternalName()); | |
964 | } else if (s == Type.METHOD) { | |
965 | return newMethodTypeItem(t.getDescriptor()); | |
966 | } else { // s == primitive type or array | |
967 | return newClassItem(t.getDescriptor()); | |
968 | } | |
969 | } else if (cst instanceof Handle) { | |
970 | Handle h = (Handle) cst; | |
971 | return newHandleItem(h.tag, h.owner, h.name, h.desc); | |
972 | } else { | |
973 | throw new IllegalArgumentException("value " + cst); | |
974 | } | |
975 | } | |
976 | ||
977 | /** | |
978 | * Adds a number or string constant to the constant pool of the class being | |
979 | * build. Does nothing if the constant pool already contains a similar item. | |
980 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
981 | * normally not needed by class generators or adapters.</i> | |
982 | * | |
983 | * @param cst | |
984 | * the value of the constant to be added to the constant pool. | |
985 | * This parameter must be an {@link Integer}, a {@link Float}, a | |
986 | * {@link Long}, a {@link Double} or a {@link String}. | |
987 | * @return the index of a new or already existing constant item with the | |
988 | * given value. | |
989 | */ | |
990 | public int newConst(final Object cst) { | |
991 | return newConstItem(cst).index; | |
992 | } | |
993 | ||
994 | /** | |
995 | * Adds an UTF8 string to the constant pool of the class being build. Does | |
996 | * nothing if the constant pool already contains a similar item. <i>This | |
997 | * method is intended for {@link Attribute} sub classes, and is normally not | |
998 | * needed by class generators or adapters.</i> | |
999 | * | |
1000 | * @param value | |
1001 | * the String value. | |
1002 | * @return the index of a new or already existing UTF8 item. | |
1003 | */ | |
1004 | public int newUTF8(final String value) { | |
1005 | key.set(UTF8, value, null, null); | |
1006 | Item result = get(key); | |
1007 | if (result == null) { | |
1008 | pool.putByte(UTF8).putUTF8(value); | |
1009 | result = new Item(index++, key); | |
1010 | put(result); | |
1011 | } | |
1012 | return result.index; | |
1013 | } | |
1014 | ||
1015 | /** | |
1016 | * Adds a class reference to the constant pool of the class being build. | |
1017 | * Does nothing if the constant pool already contains a similar item. | |
1018 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1019 | * normally not needed by class generators or adapters.</i> | |
1020 | * | |
1021 | * @param value | |
1022 | * the internal name of the class. | |
1023 | * @return a new or already existing class reference item. | |
1024 | */ | |
1025 | Item newClassItem(final String value) { | |
1026 | key2.set(CLASS, value, null, null); | |
1027 | Item result = get(key2); | |
1028 | if (result == null) { | |
1029 | pool.put12(CLASS, newUTF8(value)); | |
1030 | result = new Item(index++, key2); | |
1031 | put(result); | |
1032 | } | |
1033 | return result; | |
1034 | } | |
1035 | ||
1036 | /** | |
1037 | * Adds a class 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 value | |
1043 | * the internal name of the class. | |
1044 | * @return the index of a new or already existing class reference item. | |
1045 | */ | |
1046 | public int newClass(final String value) { | |
1047 | return newClassItem(value).index; | |
1048 | } | |
1049 | ||
1050 | /** | |
1051 | * Adds a method type reference to the constant pool of the class being | |
1052 | * build. Does nothing if the constant pool already contains a similar item. | |
1053 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1054 | * normally not needed by class generators or adapters.</i> | |
1055 | * | |
1056 | * @param methodDesc | |
1057 | * method descriptor of the method type. | |
1058 | * @return a new or already existing method type reference item. | |
1059 | */ | |
1060 | Item newMethodTypeItem(final String methodDesc) { | |
1061 | key2.set(MTYPE, methodDesc, null, null); | |
1062 | Item result = get(key2); | |
1063 | if (result == null) { | |
1064 | pool.put12(MTYPE, newUTF8(methodDesc)); | |
1065 | result = new Item(index++, key2); | |
1066 | put(result); | |
1067 | } | |
1068 | return result; | |
1069 | } | |
1070 | ||
1071 | /** | |
1072 | * Adds a method type reference to the constant pool of the class being | |
1073 | * build. Does nothing if the constant pool already contains a similar item. | |
1074 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1075 | * normally not needed by class generators or adapters.</i> | |
1076 | * | |
1077 | * @param methodDesc | |
1078 | * method descriptor of the method type. | |
1079 | * @return the index of a new or already existing method type reference | |
1080 | * item. | |
1081 | */ | |
1082 | public int newMethodType(final String methodDesc) { | |
1083 | return newMethodTypeItem(methodDesc).index; | |
1084 | } | |
1085 | ||
1086 | /** | |
1087 | * Adds a handle to the constant pool of the class being build. Does nothing | |
1088 | * if the constant pool already contains a similar item. <i>This method is | |
1089 | * intended for {@link Attribute} sub classes, and is normally not needed by | |
1090 | * class generators or adapters.</i> | |
1091 | * | |
1092 | * @param tag | |
1093 | * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, | |
1094 | * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, | |
1095 | * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, | |
1096 | * {@link Opcodes#H_INVOKESTATIC}, | |
1097 | * {@link Opcodes#H_INVOKESPECIAL}, | |
1098 | * {@link Opcodes#H_NEWINVOKESPECIAL} or | |
1099 | * {@link Opcodes#H_INVOKEINTERFACE}. | |
1100 | * @param owner | |
1101 | * the internal name of the field or method owner class. | |
1102 | * @param name | |
1103 | * the name of the field or method. | |
1104 | * @param desc | |
1105 | * the descriptor of the field or method. | |
1106 | * @return a new or an already existing method type reference item. | |
1107 | */ | |
1108 | Item newHandleItem(final int tag, final String owner, final String name, | |
1109 | final String desc) { | |
1110 | key4.set(HANDLE_BASE + tag, owner, name, desc); | |
1111 | Item result = get(key4); | |
1112 | if (result == null) { | |
1113 | if (tag <= Opcodes.H_PUTSTATIC) { | |
1114 | put112(HANDLE, tag, newField(owner, name, desc)); | |
1115 | } else { | |
1116 | put112(HANDLE, | |
1117 | tag, | |
1118 | newMethod(owner, name, desc, | |
1119 | tag == Opcodes.H_INVOKEINTERFACE)); | |
1120 | } | |
1121 | result = new Item(index++, key4); | |
1122 | put(result); | |
1123 | } | |
1124 | return result; | |
1125 | } | |
1126 | ||
1127 | /** | |
1128 | * Adds a handle to the constant pool of the class being build. Does nothing | |
1129 | * if the constant pool already contains a similar item. <i>This method is | |
1130 | * intended for {@link Attribute} sub classes, and is normally not needed by | |
1131 | * class generators or adapters.</i> | |
1132 | * | |
1133 | * @param tag | |
1134 | * the kind of this handle. Must be {@link Opcodes#H_GETFIELD}, | |
1135 | * {@link Opcodes#H_GETSTATIC}, {@link Opcodes#H_PUTFIELD}, | |
1136 | * {@link Opcodes#H_PUTSTATIC}, {@link Opcodes#H_INVOKEVIRTUAL}, | |
1137 | * {@link Opcodes#H_INVOKESTATIC}, | |
1138 | * {@link Opcodes#H_INVOKESPECIAL}, | |
1139 | * {@link Opcodes#H_NEWINVOKESPECIAL} or | |
1140 | * {@link Opcodes#H_INVOKEINTERFACE}. | |
1141 | * @param owner | |
1142 | * the internal name of the field or method owner class. | |
1143 | * @param name | |
1144 | * the name of the field or method. | |
1145 | * @param desc | |
1146 | * the descriptor of the field or method. | |
1147 | * @return the index of a new or already existing method type reference | |
1148 | * item. | |
1149 | */ | |
1150 | public int newHandle(final int tag, final String owner, final String name, | |
1151 | final String desc) { | |
1152 | return newHandleItem(tag, owner, name, desc).index; | |
1153 | } | |
1154 | ||
1155 | /** | |
1156 | * Adds an invokedynamic reference to the constant pool of the class being | |
1157 | * build. Does nothing if the constant pool already contains a similar item. | |
1158 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1159 | * normally not needed by class generators or adapters.</i> | |
1160 | * | |
1161 | * @param name | |
1162 | * name of the invoked method. | |
1163 | * @param desc | |
1164 | * descriptor of the invoke method. | |
1165 | * @param bsm | |
1166 | * the bootstrap method. | |
1167 | * @param bsmArgs | |
1168 | * the bootstrap method constant arguments. | |
1169 | * | |
1170 | * @return a new or an already existing invokedynamic type reference item. | |
1171 | */ | |
1172 | Item newInvokeDynamicItem(final String name, final String desc, | |
1173 | final Handle bsm, final Object... bsmArgs) { | |
1174 | // cache for performance | |
1175 | ByteVector bootstrapMethods = this.bootstrapMethods; | |
1176 | if (bootstrapMethods == null) { | |
1177 | bootstrapMethods = this.bootstrapMethods = new ByteVector(); | |
1178 | } | |
1179 | ||
1180 | int position = bootstrapMethods.length; // record current position | |
1181 | ||
1182 | int hashCode = bsm.hashCode(); | |
1183 | bootstrapMethods.putShort(newHandle(bsm.tag, bsm.owner, bsm.name, | |
1184 | bsm.desc)); | |
1185 | ||
1186 | int argsLength = bsmArgs.length; | |
1187 | bootstrapMethods.putShort(argsLength); | |
1188 | ||
1189 | for (int i = 0; i < argsLength; i++) { | |
1190 | Object bsmArg = bsmArgs[i]; | |
1191 | hashCode ^= bsmArg.hashCode(); | |
1192 | bootstrapMethods.putShort(newConst(bsmArg)); | |
1193 | } | |
1194 | ||
1195 | byte[] data = bootstrapMethods.data; | |
1196 | int length = (1 + 1 + argsLength) << 1; // (bsm + argCount + arguments) | |
1197 | hashCode &= 0x7FFFFFFF; | |
1198 | Item result = items[hashCode % items.length]; | |
1199 | loop: while (result != null) { | |
1200 | if (result.type != BSM || result.hashCode != hashCode) { | |
1201 | result = result.next; | |
1202 | continue; | |
1203 | } | |
1204 | ||
1205 | // because the data encode the size of the argument | |
1206 | // we don't need to test if these size are equals | |
1207 | int resultPosition = result.intVal; | |
1208 | for (int p = 0; p < length; p++) { | |
1209 | if (data[position + p] != data[resultPosition + p]) { | |
1210 | result = result.next; | |
1211 | continue loop; | |
1212 | } | |
1213 | } | |
1214 | break; | |
1215 | } | |
1216 | ||
1217 | int bootstrapMethodIndex; | |
1218 | if (result != null) { | |
1219 | bootstrapMethodIndex = result.index; | |
1220 | bootstrapMethods.length = position; // revert to old position | |
1221 | } else { | |
1222 | bootstrapMethodIndex = bootstrapMethodsCount++; | |
1223 | result = new Item(bootstrapMethodIndex); | |
1224 | result.set(position, hashCode); | |
1225 | put(result); | |
1226 | } | |
1227 | ||
1228 | // now, create the InvokeDynamic constant | |
1229 | key3.set(name, desc, bootstrapMethodIndex); | |
1230 | result = get(key3); | |
1231 | if (result == null) { | |
1232 | put122(INDY, bootstrapMethodIndex, newNameType(name, desc)); | |
1233 | result = new Item(index++, key3); | |
1234 | put(result); | |
1235 | } | |
1236 | return result; | |
1237 | } | |
1238 | ||
1239 | /** | |
1240 | * Adds an invokedynamic reference to the constant pool of the class being | |
1241 | * build. Does nothing if the constant pool already contains a similar item. | |
1242 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1243 | * normally not needed by class generators or adapters.</i> | |
1244 | * | |
1245 | * @param name | |
1246 | * name of the invoked method. | |
1247 | * @param desc | |
1248 | * descriptor of the invoke method. | |
1249 | * @param bsm | |
1250 | * the bootstrap method. | |
1251 | * @param bsmArgs | |
1252 | * the bootstrap method constant arguments. | |
1253 | * | |
1254 | * @return the index of a new or already existing invokedynamic reference | |
1255 | * item. | |
1256 | */ | |
1257 | public int newInvokeDynamic(final String name, final String desc, | |
1258 | final Handle bsm, final Object... bsmArgs) { | |
1259 | return newInvokeDynamicItem(name, desc, bsm, bsmArgs).index; | |
1260 | } | |
1261 | ||
1262 | /** | |
1263 | * Adds a field reference to the constant pool of the class being build. | |
1264 | * Does nothing if the constant pool already contains a similar item. | |
1265 | * | |
1266 | * @param owner | |
1267 | * the internal name of the field's owner class. | |
1268 | * @param name | |
1269 | * the field's name. | |
1270 | * @param desc | |
1271 | * the field's descriptor. | |
1272 | * @return a new or already existing field reference item. | |
1273 | */ | |
1274 | Item newFieldItem(final String owner, final String name, final String desc) { | |
1275 | key3.set(FIELD, owner, name, desc); | |
1276 | Item result = get(key3); | |
1277 | if (result == null) { | |
1278 | put122(FIELD, newClass(owner), newNameType(name, desc)); | |
1279 | result = new Item(index++, key3); | |
1280 | put(result); | |
1281 | } | |
1282 | return result; | |
1283 | } | |
1284 | ||
1285 | /** | |
1286 | * Adds a field reference to the constant pool of the class being build. | |
1287 | * Does nothing if the constant pool already contains a similar item. | |
1288 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1289 | * normally not needed by class generators or adapters.</i> | |
1290 | * | |
1291 | * @param owner | |
1292 | * the internal name of the field's owner class. | |
1293 | * @param name | |
1294 | * the field's name. | |
1295 | * @param desc | |
1296 | * the field's descriptor. | |
1297 | * @return the index of a new or already existing field reference item. | |
1298 | */ | |
1299 | public int newField(final String owner, final String name, final String desc) { | |
1300 | return newFieldItem(owner, name, desc).index; | |
1301 | } | |
1302 | ||
1303 | /** | |
1304 | * Adds a method reference to the constant pool of the class being build. | |
1305 | * Does nothing if the constant pool already contains a similar item. | |
1306 | * | |
1307 | * @param owner | |
1308 | * the internal name of the method's owner class. | |
1309 | * @param name | |
1310 | * the method's name. | |
1311 | * @param desc | |
1312 | * the method's descriptor. | |
1313 | * @param itf | |
1314 | * <tt>true</tt> if <tt>owner</tt> is an interface. | |
1315 | * @return a new or already existing method reference item. | |
1316 | */ | |
1317 | Item newMethodItem(final String owner, final String name, | |
1318 | final String desc, final boolean itf) { | |
1319 | int type = itf ? IMETH : METH; | |
1320 | key3.set(type, owner, name, desc); | |
1321 | Item result = get(key3); | |
1322 | if (result == null) { | |
1323 | put122(type, newClass(owner), newNameType(name, desc)); | |
1324 | result = new Item(index++, key3); | |
1325 | put(result); | |
1326 | } | |
1327 | return result; | |
1328 | } | |
1329 | ||
1330 | /** | |
1331 | * Adds a method reference to the constant pool of the class being build. | |
1332 | * Does nothing if the constant pool already contains a similar item. | |
1333 | * <i>This method is intended for {@link Attribute} sub classes, and is | |
1334 | * normally not needed by class generators or adapters.</i> | |
1335 | * | |
1336 | * @param owner | |
1337 | * the internal name of the method's owner class. | |
1338 | * @param name | |
1339 | * the method's name. | |
1340 | * @param desc | |
1341 | * the method's descriptor. | |
1342 | * @param itf | |
1343 | * <tt>true</tt> if <tt>owner</tt> is an interface. | |
1344 | * @return the index of a new or already existing method reference item. | |
1345 | */ | |
1346 | public int newMethod(final String owner, final String name, | |
1347 | final String desc, final boolean itf) { | |
1348 | return newMethodItem(owner, name, desc, itf).index; | |
1349 | } | |
1350 | ||
1351 | /** | |
1352 | * Adds an integer to the constant pool of the class being build. Does | |
1353 | * nothing if the constant pool already contains a similar item. | |
1354 | * | |
1355 | * @param value | |
1356 | * the int value. | |
1357 | * @return a new or already existing int item. | |
1358 | */ | |
1359 | Item newInteger(final int value) { | |
1360 | key.set(value); | |
1361 | Item result = get(key); | |
1362 | if (result == null) { | |
1363 | pool.putByte(INT).putInt(value); | |
1364 | result = new Item(index++, key); | |
1365 | put(result); | |
1366 | } | |
1367 | return result; | |
1368 | } | |
1369 | ||
1370 | /** | |
1371 | * Adds a float to the constant pool of the class being build. Does nothing | |
1372 | * if the constant pool already contains a similar item. | |
1373 | * | |
1374 | * @param value | |
1375 | * the float value. | |
1376 | * @return a new or already existing float item. | |
1377 | */ | |
1378 | Item newFloat(final float value) { | |
1379 | key.set(value); | |
1380 | Item result = get(key); | |
1381 | if (result == null) { | |
1382 | pool.putByte(FLOAT).putInt(key.intVal); | |
1383 | result = new Item(index++, key); | |
1384 | put(result); | |
1385 | } | |
1386 | return result; | |
1387 | } | |
1388 | ||
1389 | /** | |
1390 | * Adds a long to the constant pool of the class being build. Does nothing | |
1391 | * if the constant pool already contains a similar item. | |
1392 | * | |
1393 | * @param value | |
1394 | * the long value. | |
1395 | * @return a new or already existing long item. | |
1396 | */ | |
1397 | Item newLong(final long value) { | |
1398 | key.set(value); | |
1399 | Item result = get(key); | |
1400 | if (result == null) { | |
1401 | pool.putByte(LONG).putLong(value); | |
1402 | result = new Item(index, key); | |
1403 | index += 2; | |
1404 | put(result); | |
1405 | } | |
1406 | return result; | |
1407 | } | |
1408 | ||
1409 | /** | |
1410 | * Adds a double to the constant pool of the class being build. Does nothing | |
1411 | * if the constant pool already contains a similar item. | |
1412 | * | |
1413 | * @param value | |
1414 | * the double value. | |
1415 | * @return a new or already existing double item. | |
1416 | */ | |
1417 | Item newDouble(final double value) { | |
1418 | key.set(value); | |
1419 | Item result = get(key); | |
1420 | if (result == null) { | |
1421 | pool.putByte(DOUBLE).putLong(key.longVal); | |
1422 | result = new Item(index, key); | |
1423 | index += 2; | |
1424 | put(result); | |
1425 | } | |
1426 | return result; | |
1427 | } | |
1428 | ||
1429 | /** | |
1430 | * Adds a string to the constant pool of the class being build. Does nothing | |
1431 | * if the constant pool already contains a similar item. | |
1432 | * | |
1433 | * @param value | |
1434 | * the String value. | |
1435 | * @return a new or already existing string item. | |
1436 | */ | |
1437 | private Item newString(final String value) { | |
1438 | key2.set(STR, value, null, null); | |
1439 | Item result = get(key2); | |
1440 | if (result == null) { | |
1441 | pool.put12(STR, newUTF8(value)); | |
1442 | result = new Item(index++, key2); | |
1443 | put(result); | |
1444 | } | |
1445 | return result; | |
1446 | } | |
1447 | ||
1448 | /** | |
1449 | * Adds a name and type to the constant pool of the class being build. Does | |
1450 | * nothing if the constant pool already contains a similar item. <i>This | |
1451 | * method is intended for {@link Attribute} sub classes, and is normally not | |
1452 | * needed by class generators or adapters.</i> | |
1453 | * | |
1454 | * @param name | |
1455 | * a name. | |
1456 | * @param desc | |
1457 | * a type descriptor. | |
1458 | * @return the index of a new or already existing name and type item. | |
1459 | */ | |
1460 | public int newNameType(final String name, final String desc) { | |
1461 | return newNameTypeItem(name, desc).index; | |
1462 | } | |
1463 | ||
1464 | /** | |
1465 | * Adds a name and type to the constant pool of the class being build. Does | |
1466 | * nothing if the constant pool already contains a similar item. | |
1467 | * | |
1468 | * @param name | |
1469 | * a name. | |
1470 | * @param desc | |
1471 | * a type descriptor. | |
1472 | * @return a new or already existing name and type item. | |
1473 | */ | |
1474 | Item newNameTypeItem(final String name, final String desc) { | |
1475 | key2.set(NAME_TYPE, name, desc, null); | |
1476 | Item result = get(key2); | |
1477 | if (result == null) { | |
1478 | put122(NAME_TYPE, newUTF8(name), newUTF8(desc)); | |
1479 | result = new Item(index++, key2); | |
1480 | put(result); | |
1481 | } | |
1482 | return result; | |
1483 | } | |
1484 | ||
1485 | /** | |
1486 | * Adds the given internal name to {@link #typeTable} and returns its index. | |
1487 | * Does nothing if the type table already contains this internal name. | |
1488 | * | |
1489 | * @param type | |
1490 | * the internal name to be added to the type table. | |
1491 | * @return the index of this internal name in the type table. | |
1492 | */ | |
1493 | int addType(final String type) { | |
1494 | key.set(TYPE_NORMAL, type, null, null); | |
1495 | Item result = get(key); | |
1496 | if (result == null) { | |
1497 | result = addType(key); | |
1498 | } | |
1499 | return result.index; | |
1500 | } | |
1501 | ||
1502 | /** | |
1503 | * Adds the given "uninitialized" type to {@link #typeTable} and returns its | |
1504 | * index. This method is used for UNINITIALIZED types, made of an internal | |
1505 | * name and a bytecode offset. | |
1506 | * | |
1507 | * @param type | |
1508 | * the internal name to be added to the type table. | |
1509 | * @param offset | |
1510 | * the bytecode offset of the NEW instruction that created this | |
1511 | * UNINITIALIZED type value. | |
1512 | * @return the index of this internal name in the type table. | |
1513 | */ | |
1514 | int addUninitializedType(final String type, final int offset) { | |
1515 | key.type = TYPE_UNINIT; | |
1516 | key.intVal = offset; | |
1517 | key.strVal1 = type; | |
1518 | key.hashCode = 0x7FFFFFFF & (TYPE_UNINIT + type.hashCode() + offset); | |
1519 | Item result = get(key); | |
1520 | if (result == null) { | |
1521 | result = addType(key); | |
1522 | } | |
1523 | return result.index; | |
1524 | } | |
1525 | ||
1526 | /** | |
1527 | * Adds the given Item to {@link #typeTable}. | |
1528 | * | |
1529 | * @param item | |
1530 | * the value to be added to the type table. | |
1531 | * @return the added Item, which a new Item instance with the same value as | |
1532 | * the given Item. | |
1533 | */ | |
1534 | private Item addType(final Item item) { | |
1535 | ++typeCount; | |
1536 | Item result = new Item(typeCount, key); | |
1537 | put(result); | |
1538 | if (typeTable == null) { | |
1539 | typeTable = new Item[16]; | |
1540 | } | |
1541 | if (typeCount == typeTable.length) { | |
1542 | Item[] newTable = new Item[2 * typeTable.length]; | |
1543 | System.arraycopy(typeTable, 0, newTable, 0, typeTable.length); | |
1544 | typeTable = newTable; | |
1545 | } | |
1546 | typeTable[typeCount] = result; | |
1547 | return result; | |
1548 | } | |
1549 | ||
1550 | /** | |
1551 | * Returns the index of the common super type of the two given types. This | |
1552 | * method calls {@link #getCommonSuperClass} and caches the result in the | |
1553 | * {@link #items} hash table to speedup future calls with the same | |
1554 | * parameters. | |
1555 | * | |
1556 | * @param type1 | |
1557 | * index of an internal name in {@link #typeTable}. | |
1558 | * @param type2 | |
1559 | * index of an internal name in {@link #typeTable}. | |
1560 | * @return the index of the common super type of the two given types. | |
1561 | */ | |
1562 | int getMergedType(final int type1, final int type2) { | |
1563 | key2.type = TYPE_MERGED; | |
1564 | key2.longVal = type1 | (((long) type2) << 32); | |
1565 | key2.hashCode = 0x7FFFFFFF & (TYPE_MERGED + type1 + type2); | |
1566 | Item result = get(key2); | |
1567 | if (result == null) { | |
1568 | String t = typeTable[type1].strVal1; | |
1569 | String u = typeTable[type2].strVal1; | |
1570 | key2.intVal = addType(getCommonSuperClass(t, u)); | |
1571 | result = new Item((short) 0, key2); | |
1572 | put(result); | |
1573 | } | |
1574 | return result.intVal; | |
1575 | } | |
1576 | ||
1577 | /** | |
1578 | * Returns the common super type of the two given types. The default | |
1579 | * implementation of this method <i>loads<i> the two given classes and uses | |
1580 | * the java.lang.Class methods to find the common super class. It can be | |
1581 | * overridden to compute this common super type in other ways, in particular | |
1582 | * without actually loading any class, or to take into account the class | |
1583 | * that is currently being generated by this ClassWriter, which can of | |
1584 | * course not be loaded since it is under construction. | |
1585 | * | |
1586 | * @param type1 | |
1587 | * the internal name of a class. | |
1588 | * @param type2 | |
1589 | * the internal name of another class. | |
1590 | * @return the internal name of the common super class of the two given | |
1591 | * classes. | |
1592 | */ | |
1593 | protected String getCommonSuperClass(final String type1, final String type2) { | |
1594 | Class<?> c, d; | |
1595 | ClassLoader classLoader = getClass().getClassLoader(); | |
1596 | try { | |
1597 | c = Class.forName(type1.replace('/', '.'), false, classLoader); | |
1598 | d = Class.forName(type2.replace('/', '.'), false, classLoader); | |
1599 | } catch (Exception e) { | |
1600 | throw new RuntimeException(e.toString()); | |
1601 | } | |
1602 | if (c.isAssignableFrom(d)) { | |
1603 | return type1; | |
1604 | } | |
1605 | if (d.isAssignableFrom(c)) { | |
1606 | return type2; | |
1607 | } | |
1608 | if (c.isInterface() || d.isInterface()) { | |
1609 | return "java/lang/Object"; | |
1610 | } else { | |
1611 | do { | |
1612 | c = c.getSuperclass(); | |
1613 | } while (!c.isAssignableFrom(d)); | |
1614 | return c.getName().replace('.', '/'); | |
1615 | } | |
1616 | } | |
1617 | ||
1618 | /** | |
1619 | * Returns the constant pool's hash table item which is equal to the given | |
1620 | * item. | |
1621 | * | |
1622 | * @param key | |
1623 | * a constant pool item. | |
1624 | * @return the constant pool's hash table item which is equal to the given | |
1625 | * item, or <tt>null</tt> if there is no such item. | |
1626 | */ | |
1627 | private Item get(final Item key) { | |
1628 | Item i = items[key.hashCode % items.length]; | |
1629 | while (i != null && (i.type != key.type || !key.isEqualTo(i))) { | |
1630 | i = i.next; | |
1631 | } | |
1632 | return i; | |
1633 | } | |
1634 | ||
1635 | /** | |
1636 | * Puts the given item in the constant pool's hash table. The hash table | |
1637 | * <i>must</i> not already contains this item. | |
1638 | * | |
1639 | * @param i | |
1640 | * the item to be added to the constant pool's hash table. | |
1641 | */ | |
1642 | private void put(final Item i) { | |
1643 | if (index + typeCount > threshold) { | |
1644 | int ll = items.length; | |
1645 | int nl = ll * 2 + 1; | |
1646 | Item[] newItems = new Item[nl]; | |
1647 | for (int l = ll - 1; l >= 0; --l) { | |
1648 | Item j = items[l]; | |
1649 | while (j != null) { | |
1650 | int index = j.hashCode % newItems.length; | |
1651 | Item k = j.next; | |
1652 | j.next = newItems[index]; | |
1653 | newItems[index] = j; | |
1654 | j = k; | |
1655 | } | |
1656 | } | |
1657 | items = newItems; | |
1658 | threshold = (int) (nl * 0.75); | |
1659 | } | |
1660 | int index = i.hashCode % items.length; | |
1661 | i.next = items[index]; | |
1662 | items[index] = i; | |
1663 | } | |
1664 | ||
1665 | /** | |
1666 | * Puts one byte and two shorts into the constant pool. | |
1667 | * | |
1668 | * @param b | |
1669 | * a byte. | |
1670 | * @param s1 | |
1671 | * a short. | |
1672 | * @param s2 | |
1673 | * another short. | |
1674 | */ | |
1675 | private void put122(final int b, final int s1, final int s2) { | |
1676 | pool.put12(b, s1).putShort(s2); | |
1677 | } | |
1678 | ||
1679 | /** | |
1680 | * Puts two bytes and one short into the constant pool. | |
1681 | * | |
1682 | * @param b1 | |
1683 | * a byte. | |
1684 | * @param b2 | |
1685 | * another byte. | |
1686 | * @param s | |
1687 | * a short. | |
1688 | */ | |
1689 | private void put112(final int b1, final int b2, final int s) { | |
1690 | pool.put11(b1, b2).putShort(s); | |
1691 | } | |
1692 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | ||
30 | package clojure.asm; | |
31 | ||
32 | /** | |
33 | * Information about a class being parsed in a {@link ClassReader}. | |
34 | * | |
35 | * @author Eric Bruneton | |
36 | */ | |
37 | class Context { | |
38 | ||
39 | /** | |
40 | * Prototypes of the attributes that must be parsed for this class. | |
41 | */ | |
42 | Attribute[] attrs; | |
43 | ||
44 | /** | |
45 | * The {@link ClassReader} option flags for the parsing of this class. | |
46 | */ | |
47 | int flags; | |
48 | ||
49 | /** | |
50 | * The buffer used to read strings. | |
51 | */ | |
52 | char[] buffer; | |
53 | ||
54 | /** | |
55 | * The start index of each bootstrap method. | |
56 | */ | |
57 | int[] bootstrapMethods; | |
58 | ||
59 | /** | |
60 | * The access flags of the method currently being parsed. | |
61 | */ | |
62 | int access; | |
63 | ||
64 | /** | |
65 | * The name of the method currently being parsed. | |
66 | */ | |
67 | String name; | |
68 | ||
69 | /** | |
70 | * The descriptor of the method currently being parsed. | |
71 | */ | |
72 | String desc; | |
73 | ||
74 | /** | |
75 | * The offset of the latest stack map frame that has been parsed. | |
76 | */ | |
77 | int offset; | |
78 | ||
79 | /** | |
80 | * The encoding of the latest stack map frame that has been parsed. | |
81 | */ | |
82 | int mode; | |
83 | ||
84 | /** | |
85 | * The number of locals in the latest stack map frame that has been parsed. | |
86 | */ | |
87 | int localCount; | |
88 | ||
89 | /** | |
90 | * The number locals in the latest stack map frame that has been parsed, | |
91 | * minus the number of locals in the previous frame. | |
92 | */ | |
93 | int localDiff; | |
94 | ||
95 | /** | |
96 | * The local values of the latest stack map frame that has been parsed. | |
97 | */ | |
98 | Object[] local; | |
99 | ||
100 | /** | |
101 | * The stack size of the latest stack map frame that has been parsed. | |
102 | */ | |
103 | int stackCount; | |
104 | ||
105 | /** | |
106 | * The stack values of the latest stack map frame that has been parsed. | |
107 | */ | |
108 | Object[] stack; | |
109 | }⏎ |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | static final 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 | static final 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-2011 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 class must be called in | |
33 | * the following order: ( <tt>visitAnnotation</tt> | <tt>visitAttribute</tt> )* | |
34 | * <tt>visitEnd</tt>. | |
35 | * | |
36 | * @author Eric Bruneton | |
37 | */ | |
38 | public abstract class FieldVisitor { | |
39 | ||
40 | /** | |
41 | * The ASM API version implemented by this visitor. The value of this field | |
42 | * must be one of {@link Opcodes#ASM4}. | |
43 | */ | |
44 | protected final int api; | |
45 | ||
46 | /** | |
47 | * The field visitor to which this visitor must delegate method calls. May | |
48 | * be null. | |
49 | */ | |
50 | protected FieldVisitor fv; | |
51 | ||
52 | /** | |
53 | * Constructs a new {@link FieldVisitor}. | |
54 | * | |
55 | * @param api | |
56 | * the ASM API version implemented by this visitor. Must be one | |
57 | * of {@link Opcodes#ASM4}. | |
58 | */ | |
59 | public FieldVisitor(final int api) { | |
60 | this(api, null); | |
61 | } | |
62 | ||
63 | /** | |
64 | * Constructs a new {@link FieldVisitor}. | |
65 | * | |
66 | * @param api | |
67 | * the ASM API version implemented by this visitor. Must be one | |
68 | * of {@link Opcodes#ASM4}. | |
69 | * @param fv | |
70 | * the field visitor to which this visitor must delegate method | |
71 | * calls. May be null. | |
72 | */ | |
73 | public FieldVisitor(final int api, final FieldVisitor fv) { | |
74 | if (api != Opcodes.ASM4) { | |
75 | throw new IllegalArgumentException(); | |
76 | } | |
77 | this.api = api; | |
78 | this.fv = fv; | |
79 | } | |
80 | ||
81 | /** | |
82 | * Visits an annotation of the field. | |
83 | * | |
84 | * @param desc | |
85 | * the class descriptor of the annotation class. | |
86 | * @param visible | |
87 | * <tt>true</tt> if the annotation is visible at runtime. | |
88 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
89 | * this visitor is not interested in visiting this annotation. | |
90 | */ | |
91 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { | |
92 | if (fv != null) { | |
93 | return fv.visitAnnotation(desc, visible); | |
94 | } | |
95 | return null; | |
96 | } | |
97 | ||
98 | /** | |
99 | * Visits a non standard attribute of the field. | |
100 | * | |
101 | * @param attr | |
102 | * an attribute. | |
103 | */ | |
104 | public void visitAttribute(Attribute attr) { | |
105 | if (fv != null) { | |
106 | fv.visitAttribute(attr); | |
107 | } | |
108 | } | |
109 | ||
110 | /** | |
111 | * Visits the end of the field. This method, which is the last one to be | |
112 | * called, is used to inform the visitor that all the annotations and | |
113 | * attributes of the field have been visited. | |
114 | */ | |
115 | public void visitEnd() { | |
116 | if (fv != null) { | |
117 | fv.visitEnd(); | |
118 | } | |
119 | } | |
120 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 extends FieldVisitor { | |
37 | ||
38 | /** | |
39 | * The class writer to which this field must be added. | |
40 | */ | |
41 | private final ClassWriter cw; | |
42 | ||
43 | /** | |
44 | * Access flags of this field. | |
45 | */ | |
46 | private final int access; | |
47 | ||
48 | /** | |
49 | * The index of the constant pool item that contains the name of this | |
50 | * method. | |
51 | */ | |
52 | private final int name; | |
53 | ||
54 | /** | |
55 | * The index of the constant pool item that contains the descriptor of this | |
56 | * field. | |
57 | */ | |
58 | private final int desc; | |
59 | ||
60 | /** | |
61 | * The index of the constant pool item that contains the signature of this | |
62 | * field. | |
63 | */ | |
64 | private int signature; | |
65 | ||
66 | /** | |
67 | * The index of the constant pool item that contains the constant value of | |
68 | * this field. | |
69 | */ | |
70 | private int value; | |
71 | ||
72 | /** | |
73 | * The runtime visible annotations of this field. May be <tt>null</tt>. | |
74 | */ | |
75 | private AnnotationWriter anns; | |
76 | ||
77 | /** | |
78 | * The runtime invisible annotations of this field. May be <tt>null</tt>. | |
79 | */ | |
80 | private AnnotationWriter ianns; | |
81 | ||
82 | /** | |
83 | * The non standard attributes of this field. May be <tt>null</tt>. | |
84 | */ | |
85 | private Attribute attrs; | |
86 | ||
87 | // ------------------------------------------------------------------------ | |
88 | // Constructor | |
89 | // ------------------------------------------------------------------------ | |
90 | ||
91 | /** | |
92 | * Constructs a new {@link FieldWriter}. | |
93 | * | |
94 | * @param cw | |
95 | * the class writer to which this field must be added. | |
96 | * @param access | |
97 | * the field's access flags (see {@link Opcodes}). | |
98 | * @param name | |
99 | * the field's name. | |
100 | * @param desc | |
101 | * the field's descriptor (see {@link Type}). | |
102 | * @param signature | |
103 | * the field's signature. May be <tt>null</tt>. | |
104 | * @param value | |
105 | * the field's constant value. May be <tt>null</tt>. | |
106 | */ | |
107 | FieldWriter(final ClassWriter cw, final int access, final String name, | |
108 | final String desc, final String signature, final Object value) { | |
109 | super(Opcodes.ASM4); | |
110 | if (cw.firstField == null) { | |
111 | cw.firstField = this; | |
112 | } else { | |
113 | cw.lastField.fv = this; | |
114 | } | |
115 | cw.lastField = this; | |
116 | this.cw = cw; | |
117 | this.access = access; | |
118 | this.name = cw.newUTF8(name); | |
119 | this.desc = cw.newUTF8(desc); | |
120 | if (ClassReader.SIGNATURES && signature != null) { | |
121 | this.signature = cw.newUTF8(signature); | |
122 | } | |
123 | if (value != null) { | |
124 | this.value = cw.newConstItem(value).index; | |
125 | } | |
126 | } | |
127 | ||
128 | // ------------------------------------------------------------------------ | |
129 | // Implementation of the FieldVisitor abstract class | |
130 | // ------------------------------------------------------------------------ | |
131 | ||
132 | @Override | |
133 | public AnnotationVisitor visitAnnotation(final String desc, | |
134 | final boolean visible) { | |
135 | if (!ClassReader.ANNOTATIONS) { | |
136 | return null; | |
137 | } | |
138 | ByteVector bv = new ByteVector(); | |
139 | // write type, and reserve space for values count | |
140 | bv.putShort(cw.newUTF8(desc)).putShort(0); | |
141 | AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); | |
142 | if (visible) { | |
143 | aw.next = anns; | |
144 | anns = aw; | |
145 | } else { | |
146 | aw.next = ianns; | |
147 | ianns = aw; | |
148 | } | |
149 | return aw; | |
150 | } | |
151 | ||
152 | @Override | |
153 | public void visitAttribute(final Attribute attr) { | |
154 | attr.next = attrs; | |
155 | attrs = attr; | |
156 | } | |
157 | ||
158 | @Override | |
159 | public void visitEnd() { | |
160 | } | |
161 | ||
162 | // ------------------------------------------------------------------------ | |
163 | // Utility methods | |
164 | // ------------------------------------------------------------------------ | |
165 | ||
166 | /** | |
167 | * Returns the size of this field. | |
168 | * | |
169 | * @return the size of this field. | |
170 | */ | |
171 | int getSize() { | |
172 | int size = 8; | |
173 | if (value != 0) { | |
174 | cw.newUTF8("ConstantValue"); | |
175 | size += 8; | |
176 | } | |
177 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
178 | if ((cw.version & 0xFFFF) < Opcodes.V1_5 | |
179 | || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
180 | cw.newUTF8("Synthetic"); | |
181 | size += 6; | |
182 | } | |
183 | } | |
184 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
185 | cw.newUTF8("Deprecated"); | |
186 | size += 6; | |
187 | } | |
188 | if (ClassReader.SIGNATURES && signature != 0) { | |
189 | cw.newUTF8("Signature"); | |
190 | size += 8; | |
191 | } | |
192 | if (ClassReader.ANNOTATIONS && anns != null) { | |
193 | cw.newUTF8("RuntimeVisibleAnnotations"); | |
194 | size += 8 + anns.getSize(); | |
195 | } | |
196 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
197 | cw.newUTF8("RuntimeInvisibleAnnotations"); | |
198 | size += 8 + ianns.getSize(); | |
199 | } | |
200 | if (attrs != null) { | |
201 | size += attrs.getSize(cw, null, 0, -1, -1); | |
202 | } | |
203 | return size; | |
204 | } | |
205 | ||
206 | /** | |
207 | * Puts the content of this field into the given byte vector. | |
208 | * | |
209 | * @param out | |
210 | * where the content of this field must be put. | |
211 | */ | |
212 | void put(final ByteVector out) { | |
213 | final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; | |
214 | int mask = Opcodes.ACC_DEPRECATED | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE | |
215 | | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); | |
216 | out.putShort(access & ~mask).putShort(name).putShort(desc); | |
217 | int attributeCount = 0; | |
218 | if (value != 0) { | |
219 | ++attributeCount; | |
220 | } | |
221 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
222 | if ((cw.version & 0xFFFF) < Opcodes.V1_5 | |
223 | || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
224 | ++attributeCount; | |
225 | } | |
226 | } | |
227 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
228 | ++attributeCount; | |
229 | } | |
230 | if (ClassReader.SIGNATURES && signature != 0) { | |
231 | ++attributeCount; | |
232 | } | |
233 | if (ClassReader.ANNOTATIONS && anns != null) { | |
234 | ++attributeCount; | |
235 | } | |
236 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
237 | ++attributeCount; | |
238 | } | |
239 | if (attrs != null) { | |
240 | attributeCount += attrs.getCount(); | |
241 | } | |
242 | out.putShort(attributeCount); | |
243 | if (value != 0) { | |
244 | out.putShort(cw.newUTF8("ConstantValue")); | |
245 | out.putInt(2).putShort(value); | |
246 | } | |
247 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
248 | if ((cw.version & 0xFFFF) < Opcodes.V1_5 | |
249 | || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
250 | out.putShort(cw.newUTF8("Synthetic")).putInt(0); | |
251 | } | |
252 | } | |
253 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
254 | out.putShort(cw.newUTF8("Deprecated")).putInt(0); | |
255 | } | |
256 | if (ClassReader.SIGNATURES && signature != 0) { | |
257 | out.putShort(cw.newUTF8("Signature")); | |
258 | out.putInt(2).putShort(signature); | |
259 | } | |
260 | if (ClassReader.ANNOTATIONS && anns != null) { | |
261 | out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); | |
262 | anns.put(out); | |
263 | } | |
264 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
265 | out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); | |
266 | ianns.put(out); | |
267 | } | |
268 | if (attrs != null) { | |
269 | attrs.put(cw, null, 0, -1, -1, out); | |
270 | } | |
271 | } | |
272 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 beginning 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 | |
84 | * operand stack. This is necessary to be able to simulate DUPx_y | |
85 | * instructions, whose effect would be dependent on the actual type values | |
86 | * if types were always represented by a single slot in the stack (and this | |
87 | * is not possible, since actual type values are not always known - cf LOCAL | |
88 | * and 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 | static final int DIM = 0xF0000000; | |
96 | ||
97 | /** | |
98 | * Constant to be added to a type to get a type with one more dimension. | |
99 | */ | |
100 | static final int ARRAY_OF = 0x10000000; | |
101 | ||
102 | /** | |
103 | * Constant to be added to a type to get a type with one less dimension. | |
104 | */ | |
105 | static final 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 | static final int KIND = 0xF000000; | |
115 | ||
116 | /** | |
117 | * Flag used for LOCAL and STACK types. Indicates that if this type happens | |
118 | * to be a long or double type (during the computations of input frames), | |
119 | * then it must be set to TOP because the second word of this value has been | |
120 | * reused to store other data in the basic block. Hence the first word no | |
121 | * longer stores a valid long or double value. | |
122 | */ | |
123 | static final int TOP_IF_LONG_OR_DOUBLE = 0x800000; | |
124 | ||
125 | /** | |
126 | * Mask to get the value of a frame type. | |
127 | */ | |
128 | static final int VALUE = 0x7FFFFF; | |
129 | ||
130 | /** | |
131 | * Mask to get the kind of base types. | |
132 | */ | |
133 | static final int BASE_KIND = 0xFF00000; | |
134 | ||
135 | /** | |
136 | * Mask to get the value of base types. | |
137 | */ | |
138 | static final int BASE_VALUE = 0xFFFFF; | |
139 | ||
140 | /** | |
141 | * Kind of the types that are not relative to an input stack map frame. | |
142 | */ | |
143 | static final int BASE = 0x1000000; | |
144 | ||
145 | /** | |
146 | * Base kind of the base reference types. The BASE_VALUE of such types is an | |
147 | * index into the type table. | |
148 | */ | |
149 | static final int OBJECT = BASE | 0x700000; | |
150 | ||
151 | /** | |
152 | * Base kind of the uninitialized base types. The BASE_VALUE of such types | |
153 | * in an index into the type table (the Item at that index contains both an | |
154 | * instruction offset and an internal class name). | |
155 | */ | |
156 | static final int UNINITIALIZED = BASE | 0x800000; | |
157 | ||
158 | /** | |
159 | * Kind of the types that are relative to the local variable types of an | |
160 | * input stack map frame. The value of such types is a local variable index. | |
161 | */ | |
162 | private static final int LOCAL = 0x2000000; | |
163 | ||
164 | /** | |
165 | * Kind of the the types that are relative to the stack of an input stack | |
166 | * map frame. The value of such types is a position relatively to the top of | |
167 | * this stack. | |
168 | */ | |
169 | private static final int STACK = 0x3000000; | |
170 | ||
171 | /** | |
172 | * The TOP type. This is a BASE type. | |
173 | */ | |
174 | static final int TOP = BASE | 0; | |
175 | ||
176 | /** | |
177 | * The BOOLEAN type. This is a BASE type mainly used for array types. | |
178 | */ | |
179 | static final int BOOLEAN = BASE | 9; | |
180 | ||
181 | /** | |
182 | * The BYTE type. This is a BASE type mainly used for array types. | |
183 | */ | |
184 | static final int BYTE = BASE | 10; | |
185 | ||
186 | /** | |
187 | * The CHAR type. This is a BASE type mainly used for array types. | |
188 | */ | |
189 | static final int CHAR = BASE | 11; | |
190 | ||
191 | /** | |
192 | * The SHORT type. This is a BASE type mainly used for array types. | |
193 | */ | |
194 | static final int SHORT = BASE | 12; | |
195 | ||
196 | /** | |
197 | * The INTEGER type. This is a BASE type. | |
198 | */ | |
199 | static final int INTEGER = BASE | 1; | |
200 | ||
201 | /** | |
202 | * The FLOAT type. This is a BASE type. | |
203 | */ | |
204 | static final int FLOAT = BASE | 2; | |
205 | ||
206 | /** | |
207 | * The DOUBLE type. This is a BASE type. | |
208 | */ | |
209 | static final int DOUBLE = BASE | 3; | |
210 | ||
211 | /** | |
212 | * The LONG type. This is a BASE type. | |
213 | */ | |
214 | static final int LONG = BASE | 4; | |
215 | ||
216 | /** | |
217 | * The NULL type. This is a BASE type. | |
218 | */ | |
219 | static final int NULL = BASE | 5; | |
220 | ||
221 | /** | |
222 | * The UNINITIALIZED_THIS type. This is a BASE type. | |
223 | */ | |
224 | static final int UNINITIALIZED_THIS = BASE | 6; | |
225 | ||
226 | /** | |
227 | * The stack size variation corresponding to each JVM instruction. This | |
228 | * stack variation is equal to the size of the values produced by an | |
229 | * instruction, minus the size of the values consumed by this instruction. | |
230 | */ | |
231 | static final int[] SIZE; | |
232 | ||
233 | /** | |
234 | * Computes the stack size variation corresponding to each JVM instruction. | |
235 | */ | |
236 | static { | |
237 | int i; | |
238 | int[] b = new int[202]; | |
239 | String s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" | |
240 | + "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" | |
241 | + "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" | |
242 | + "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"; | |
243 | for (i = 0; i < b.length; ++i) { | |
244 | b[i] = s.charAt(i) - 'E'; | |
245 | } | |
246 | SIZE = b; | |
247 | ||
248 | // code to generate the above string | |
249 | // | |
250 | // int NA = 0; // not applicable (unused opcode or variable size opcode) | |
251 | // | |
252 | // b = new int[] { | |
253 | // 0, //NOP, // visitInsn | |
254 | // 1, //ACONST_NULL, // - | |
255 | // 1, //ICONST_M1, // - | |
256 | // 1, //ICONST_0, // - | |
257 | // 1, //ICONST_1, // - | |
258 | // 1, //ICONST_2, // - | |
259 | // 1, //ICONST_3, // - | |
260 | // 1, //ICONST_4, // - | |
261 | // 1, //ICONST_5, // - | |
262 | // 2, //LCONST_0, // - | |
263 | // 2, //LCONST_1, // - | |
264 | // 1, //FCONST_0, // - | |
265 | // 1, //FCONST_1, // - | |
266 | // 1, //FCONST_2, // - | |
267 | // 2, //DCONST_0, // - | |
268 | // 2, //DCONST_1, // - | |
269 | // 1, //BIPUSH, // visitIntInsn | |
270 | // 1, //SIPUSH, // - | |
271 | // 1, //LDC, // visitLdcInsn | |
272 | // NA, //LDC_W, // - | |
273 | // NA, //LDC2_W, // - | |
274 | // 1, //ILOAD, // visitVarInsn | |
275 | // 2, //LLOAD, // - | |
276 | // 1, //FLOAD, // - | |
277 | // 2, //DLOAD, // - | |
278 | // 1, //ALOAD, // - | |
279 | // NA, //ILOAD_0, // - | |
280 | // NA, //ILOAD_1, // - | |
281 | // NA, //ILOAD_2, // - | |
282 | // NA, //ILOAD_3, // - | |
283 | // NA, //LLOAD_0, // - | |
284 | // NA, //LLOAD_1, // - | |
285 | // NA, //LLOAD_2, // - | |
286 | // NA, //LLOAD_3, // - | |
287 | // NA, //FLOAD_0, // - | |
288 | // NA, //FLOAD_1, // - | |
289 | // NA, //FLOAD_2, // - | |
290 | // NA, //FLOAD_3, // - | |
291 | // NA, //DLOAD_0, // - | |
292 | // NA, //DLOAD_1, // - | |
293 | // NA, //DLOAD_2, // - | |
294 | // NA, //DLOAD_3, // - | |
295 | // NA, //ALOAD_0, // - | |
296 | // NA, //ALOAD_1, // - | |
297 | // NA, //ALOAD_2, // - | |
298 | // NA, //ALOAD_3, // - | |
299 | // -1, //IALOAD, // visitInsn | |
300 | // 0, //LALOAD, // - | |
301 | // -1, //FALOAD, // - | |
302 | // 0, //DALOAD, // - | |
303 | // -1, //AALOAD, // - | |
304 | // -1, //BALOAD, // - | |
305 | // -1, //CALOAD, // - | |
306 | // -1, //SALOAD, // - | |
307 | // -1, //ISTORE, // visitVarInsn | |
308 | // -2, //LSTORE, // - | |
309 | // -1, //FSTORE, // - | |
310 | // -2, //DSTORE, // - | |
311 | // -1, //ASTORE, // - | |
312 | // NA, //ISTORE_0, // - | |
313 | // NA, //ISTORE_1, // - | |
314 | // NA, //ISTORE_2, // - | |
315 | // NA, //ISTORE_3, // - | |
316 | // NA, //LSTORE_0, // - | |
317 | // NA, //LSTORE_1, // - | |
318 | // NA, //LSTORE_2, // - | |
319 | // NA, //LSTORE_3, // - | |
320 | // NA, //FSTORE_0, // - | |
321 | // NA, //FSTORE_1, // - | |
322 | // NA, //FSTORE_2, // - | |
323 | // NA, //FSTORE_3, // - | |
324 | // NA, //DSTORE_0, // - | |
325 | // NA, //DSTORE_1, // - | |
326 | // NA, //DSTORE_2, // - | |
327 | // NA, //DSTORE_3, // - | |
328 | // NA, //ASTORE_0, // - | |
329 | // NA, //ASTORE_1, // - | |
330 | // NA, //ASTORE_2, // - | |
331 | // NA, //ASTORE_3, // - | |
332 | // -3, //IASTORE, // visitInsn | |
333 | // -4, //LASTORE, // - | |
334 | // -3, //FASTORE, // - | |
335 | // -4, //DASTORE, // - | |
336 | // -3, //AASTORE, // - | |
337 | // -3, //BASTORE, // - | |
338 | // -3, //CASTORE, // - | |
339 | // -3, //SASTORE, // - | |
340 | // -1, //POP, // - | |
341 | // -2, //POP2, // - | |
342 | // 1, //DUP, // - | |
343 | // 1, //DUP_X1, // - | |
344 | // 1, //DUP_X2, // - | |
345 | // 2, //DUP2, // - | |
346 | // 2, //DUP2_X1, // - | |
347 | // 2, //DUP2_X2, // - | |
348 | // 0, //SWAP, // - | |
349 | // -1, //IADD, // - | |
350 | // -2, //LADD, // - | |
351 | // -1, //FADD, // - | |
352 | // -2, //DADD, // - | |
353 | // -1, //ISUB, // - | |
354 | // -2, //LSUB, // - | |
355 | // -1, //FSUB, // - | |
356 | // -2, //DSUB, // - | |
357 | // -1, //IMUL, // - | |
358 | // -2, //LMUL, // - | |
359 | // -1, //FMUL, // - | |
360 | // -2, //DMUL, // - | |
361 | // -1, //IDIV, // - | |
362 | // -2, //LDIV, // - | |
363 | // -1, //FDIV, // - | |
364 | // -2, //DDIV, // - | |
365 | // -1, //IREM, // - | |
366 | // -2, //LREM, // - | |
367 | // -1, //FREM, // - | |
368 | // -2, //DREM, // - | |
369 | // 0, //INEG, // - | |
370 | // 0, //LNEG, // - | |
371 | // 0, //FNEG, // - | |
372 | // 0, //DNEG, // - | |
373 | // -1, //ISHL, // - | |
374 | // -1, //LSHL, // - | |
375 | // -1, //ISHR, // - | |
376 | // -1, //LSHR, // - | |
377 | // -1, //IUSHR, // - | |
378 | // -1, //LUSHR, // - | |
379 | // -1, //IAND, // - | |
380 | // -2, //LAND, // - | |
381 | // -1, //IOR, // - | |
382 | // -2, //LOR, // - | |
383 | // -1, //IXOR, // - | |
384 | // -2, //LXOR, // - | |
385 | // 0, //IINC, // visitIincInsn | |
386 | // 1, //I2L, // visitInsn | |
387 | // 0, //I2F, // - | |
388 | // 1, //I2D, // - | |
389 | // -1, //L2I, // - | |
390 | // -1, //L2F, // - | |
391 | // 0, //L2D, // - | |
392 | // 0, //F2I, // - | |
393 | // 1, //F2L, // - | |
394 | // 1, //F2D, // - | |
395 | // -1, //D2I, // - | |
396 | // 0, //D2L, // - | |
397 | // -1, //D2F, // - | |
398 | // 0, //I2B, // - | |
399 | // 0, //I2C, // - | |
400 | // 0, //I2S, // - | |
401 | // -3, //LCMP, // - | |
402 | // -1, //FCMPL, // - | |
403 | // -1, //FCMPG, // - | |
404 | // -3, //DCMPL, // - | |
405 | // -3, //DCMPG, // - | |
406 | // -1, //IFEQ, // visitJumpInsn | |
407 | // -1, //IFNE, // - | |
408 | // -1, //IFLT, // - | |
409 | // -1, //IFGE, // - | |
410 | // -1, //IFGT, // - | |
411 | // -1, //IFLE, // - | |
412 | // -2, //IF_ICMPEQ, // - | |
413 | // -2, //IF_ICMPNE, // - | |
414 | // -2, //IF_ICMPLT, // - | |
415 | // -2, //IF_ICMPGE, // - | |
416 | // -2, //IF_ICMPGT, // - | |
417 | // -2, //IF_ICMPLE, // - | |
418 | // -2, //IF_ACMPEQ, // - | |
419 | // -2, //IF_ACMPNE, // - | |
420 | // 0, //GOTO, // - | |
421 | // 1, //JSR, // - | |
422 | // 0, //RET, // visitVarInsn | |
423 | // -1, //TABLESWITCH, // visiTableSwitchInsn | |
424 | // -1, //LOOKUPSWITCH, // visitLookupSwitch | |
425 | // -1, //IRETURN, // visitInsn | |
426 | // -2, //LRETURN, // - | |
427 | // -1, //FRETURN, // - | |
428 | // -2, //DRETURN, // - | |
429 | // -1, //ARETURN, // - | |
430 | // 0, //RETURN, // - | |
431 | // NA, //GETSTATIC, // visitFieldInsn | |
432 | // NA, //PUTSTATIC, // - | |
433 | // NA, //GETFIELD, // - | |
434 | // NA, //PUTFIELD, // - | |
435 | // NA, //INVOKEVIRTUAL, // visitMethodInsn | |
436 | // NA, //INVOKESPECIAL, // - | |
437 | // NA, //INVOKESTATIC, // - | |
438 | // NA, //INVOKEINTERFACE, // - | |
439 | // NA, //INVOKEDYNAMIC, // visitInvokeDynamicInsn | |
440 | // 1, //NEW, // visitTypeInsn | |
441 | // 0, //NEWARRAY, // visitIntInsn | |
442 | // 0, //ANEWARRAY, // visitTypeInsn | |
443 | // 0, //ARRAYLENGTH, // visitInsn | |
444 | // NA, //ATHROW, // - | |
445 | // 0, //CHECKCAST, // visitTypeInsn | |
446 | // 0, //INSTANCEOF, // - | |
447 | // -1, //MONITORENTER, // visitInsn | |
448 | // -1, //MONITOREXIT, // - | |
449 | // NA, //WIDE, // NOT VISITED | |
450 | // NA, //MULTIANEWARRAY, // visitMultiANewArrayInsn | |
451 | // -1, //IFNULL, // visitJumpInsn | |
452 | // -1, //IFNONNULL, // - | |
453 | // NA, //GOTO_W, // - | |
454 | // NA, //JSR_W, // - | |
455 | // }; | |
456 | // for (i = 0; i < b.length; ++i) { | |
457 | // System.err.print((char)('E' + b[i])); | |
458 | // } | |
459 | // System.err.println(); | |
460 | } | |
461 | ||
462 | /** | |
463 | * The label (i.e. basic block) to which these input and output stack map | |
464 | * frames correspond. | |
465 | */ | |
466 | Label owner; | |
467 | ||
468 | /** | |
469 | * The input stack map frame locals. | |
470 | */ | |
471 | int[] inputLocals; | |
472 | ||
473 | /** | |
474 | * The input stack map frame stack. | |
475 | */ | |
476 | int[] inputStack; | |
477 | ||
478 | /** | |
479 | * The output stack map frame locals. | |
480 | */ | |
481 | private int[] outputLocals; | |
482 | ||
483 | /** | |
484 | * The output stack map frame stack. | |
485 | */ | |
486 | private int[] outputStack; | |
487 | ||
488 | /** | |
489 | * Relative size of the output stack. The exact semantics of this field | |
490 | * depends on the algorithm that is used. | |
491 | * | |
492 | * When only the maximum stack size is computed, this field is the size of | |
493 | * the output stack relatively to the top of the input stack. | |
494 | * | |
495 | * When the stack map frames are completely computed, this field is the | |
496 | * actual number of types in {@link #outputStack}. | |
497 | */ | |
498 | private int outputStackTop; | |
499 | ||
500 | /** | |
501 | * Number of types that are initialized in the basic block. | |
502 | * | |
503 | * @see #initializations | |
504 | */ | |
505 | private int initializationCount; | |
506 | ||
507 | /** | |
508 | * The types that are initialized in the basic block. A constructor | |
509 | * invocation on an UNINITIALIZED or UNINITIALIZED_THIS type must replace | |
510 | * <i>every occurence</i> of this type in the local variables and in the | |
511 | * operand stack. This cannot be done during the first phase of the | |
512 | * algorithm since, during this phase, the local variables and the operand | |
513 | * stack are not completely computed. It is therefore necessary to store the | |
514 | * types on which constructors are invoked in the basic block, in order to | |
515 | * do this replacement during the second phase of the algorithm, where the | |
516 | * frames are fully computed. Note that this array can contain types that | |
517 | * are relative to input locals or to the input stack (see below for the | |
518 | * description of the algorithm). | |
519 | */ | |
520 | private int[] initializations; | |
521 | ||
522 | /** | |
523 | * Returns the output frame local variable type at the given index. | |
524 | * | |
525 | * @param local | |
526 | * the index of the local that must be returned. | |
527 | * @return the output frame local variable type at the given index. | |
528 | */ | |
529 | private int get(final int local) { | |
530 | if (outputLocals == null || local >= outputLocals.length) { | |
531 | // this local has never been assigned in this basic block, | |
532 | // so it is still equal to its value in the input frame | |
533 | return LOCAL | local; | |
534 | } else { | |
535 | int type = outputLocals[local]; | |
536 | if (type == 0) { | |
537 | // this local has never been assigned in this basic block, | |
538 | // so it is still equal to its value in the input frame | |
539 | type = outputLocals[local] = LOCAL | local; | |
540 | } | |
541 | return type; | |
542 | } | |
543 | } | |
544 | ||
545 | /** | |
546 | * Sets the output frame local variable type at the given index. | |
547 | * | |
548 | * @param local | |
549 | * the index of the local that must be set. | |
550 | * @param type | |
551 | * the value of the local that must be set. | |
552 | */ | |
553 | private void set(final int local, final int type) { | |
554 | // creates and/or resizes the output local variables array if necessary | |
555 | if (outputLocals == null) { | |
556 | outputLocals = new int[10]; | |
557 | } | |
558 | int n = outputLocals.length; | |
559 | if (local >= n) { | |
560 | int[] t = new int[Math.max(local + 1, 2 * n)]; | |
561 | System.arraycopy(outputLocals, 0, t, 0, n); | |
562 | outputLocals = t; | |
563 | } | |
564 | // sets the local variable | |
565 | outputLocals[local] = type; | |
566 | } | |
567 | ||
568 | /** | |
569 | * Pushes a new type onto the output frame stack. | |
570 | * | |
571 | * @param type | |
572 | * the type that must be pushed. | |
573 | */ | |
574 | private void push(final int type) { | |
575 | // creates and/or resizes the output stack array if necessary | |
576 | if (outputStack == null) { | |
577 | outputStack = new int[10]; | |
578 | } | |
579 | int n = outputStack.length; | |
580 | if (outputStackTop >= n) { | |
581 | int[] t = new int[Math.max(outputStackTop + 1, 2 * n)]; | |
582 | System.arraycopy(outputStack, 0, t, 0, n); | |
583 | outputStack = t; | |
584 | } | |
585 | // pushes the type on the output stack | |
586 | outputStack[outputStackTop++] = type; | |
587 | // updates the maximun height reached by the output stack, if needed | |
588 | int top = owner.inputStackTop + outputStackTop; | |
589 | if (top > owner.outputStackMax) { | |
590 | owner.outputStackMax = top; | |
591 | } | |
592 | } | |
593 | ||
594 | /** | |
595 | * Pushes a new type onto the output frame stack. | |
596 | * | |
597 | * @param cw | |
598 | * the ClassWriter to which this label belongs. | |
599 | * @param desc | |
600 | * the descriptor of the type to be pushed. Can also be a method | |
601 | * descriptor (in this case this method pushes its return type | |
602 | * onto the output frame stack). | |
603 | */ | |
604 | private void push(final ClassWriter cw, final String desc) { | |
605 | int type = type(cw, desc); | |
606 | if (type != 0) { | |
607 | push(type); | |
608 | if (type == LONG || type == DOUBLE) { | |
609 | push(TOP); | |
610 | } | |
611 | } | |
612 | } | |
613 | ||
614 | /** | |
615 | * Returns the int encoding of the given type. | |
616 | * | |
617 | * @param cw | |
618 | * the ClassWriter to which this label belongs. | |
619 | * @param desc | |
620 | * a type descriptor. | |
621 | * @return the int encoding of the given type. | |
622 | */ | |
623 | private static int type(final ClassWriter cw, final String desc) { | |
624 | String t; | |
625 | int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; | |
626 | switch (desc.charAt(index)) { | |
627 | case 'V': | |
628 | return 0; | |
629 | case 'Z': | |
630 | case 'C': | |
631 | case 'B': | |
632 | case 'S': | |
633 | case 'I': | |
634 | return INTEGER; | |
635 | case 'F': | |
636 | return FLOAT; | |
637 | case 'J': | |
638 | return LONG; | |
639 | case 'D': | |
640 | return DOUBLE; | |
641 | case 'L': | |
642 | // stores the internal name, not the descriptor! | |
643 | t = desc.substring(index + 1, desc.length() - 1); | |
644 | return OBJECT | cw.addType(t); | |
645 | // case '[': | |
646 | default: | |
647 | // extracts the dimensions and the element type | |
648 | int data; | |
649 | int dims = index + 1; | |
650 | while (desc.charAt(dims) == '[') { | |
651 | ++dims; | |
652 | } | |
653 | switch (desc.charAt(dims)) { | |
654 | case 'Z': | |
655 | data = BOOLEAN; | |
656 | break; | |
657 | case 'C': | |
658 | data = CHAR; | |
659 | break; | |
660 | case 'B': | |
661 | data = BYTE; | |
662 | break; | |
663 | case 'S': | |
664 | data = SHORT; | |
665 | break; | |
666 | case 'I': | |
667 | data = INTEGER; | |
668 | break; | |
669 | case 'F': | |
670 | data = FLOAT; | |
671 | break; | |
672 | case 'J': | |
673 | data = LONG; | |
674 | break; | |
675 | case 'D': | |
676 | data = DOUBLE; | |
677 | break; | |
678 | // case 'L': | |
679 | default: | |
680 | // stores the internal name, not the descriptor | |
681 | t = desc.substring(dims + 1, desc.length() - 1); | |
682 | data = OBJECT | cw.addType(t); | |
683 | } | |
684 | return (dims - index) << 28 | data; | |
685 | } | |
686 | } | |
687 | ||
688 | /** | |
689 | * Pops a type from the output frame stack and returns its value. | |
690 | * | |
691 | * @return the type that has been popped from the output frame stack. | |
692 | */ | |
693 | private int pop() { | |
694 | if (outputStackTop > 0) { | |
695 | return outputStack[--outputStackTop]; | |
696 | } else { | |
697 | // if the output frame stack is empty, pops from the input stack | |
698 | return STACK | -(--owner.inputStackTop); | |
699 | } | |
700 | } | |
701 | ||
702 | /** | |
703 | * Pops the given number of types from the output frame stack. | |
704 | * | |
705 | * @param elements | |
706 | * the number of types that must be popped. | |
707 | */ | |
708 | private void pop(final int elements) { | |
709 | if (outputStackTop >= elements) { | |
710 | outputStackTop -= elements; | |
711 | } else { | |
712 | // if the number of elements to be popped is greater than the number | |
713 | // of elements in the output stack, clear it, and pops the remaining | |
714 | // elements from the input stack. | |
715 | owner.inputStackTop -= elements - outputStackTop; | |
716 | outputStackTop = 0; | |
717 | } | |
718 | } | |
719 | ||
720 | /** | |
721 | * Pops a type from the output frame stack. | |
722 | * | |
723 | * @param desc | |
724 | * the descriptor of the type to be popped. Can also be a method | |
725 | * descriptor (in this case this method pops the types | |
726 | * corresponding to the method arguments). | |
727 | */ | |
728 | private void pop(final String desc) { | |
729 | char c = desc.charAt(0); | |
730 | if (c == '(') { | |
731 | pop((Type.getArgumentsAndReturnSizes(desc) >> 2) - 1); | |
732 | } else if (c == 'J' || c == 'D') { | |
733 | pop(2); | |
734 | } else { | |
735 | pop(1); | |
736 | } | |
737 | } | |
738 | ||
739 | /** | |
740 | * Adds a new type to the list of types on which a constructor is invoked in | |
741 | * the basic block. | |
742 | * | |
743 | * @param var | |
744 | * a type on a which a constructor is invoked. | |
745 | */ | |
746 | private void init(final int var) { | |
747 | // creates and/or resizes the initializations array if necessary | |
748 | if (initializations == null) { | |
749 | initializations = new int[2]; | |
750 | } | |
751 | int n = initializations.length; | |
752 | if (initializationCount >= n) { | |
753 | int[] t = new int[Math.max(initializationCount + 1, 2 * n)]; | |
754 | System.arraycopy(initializations, 0, t, 0, n); | |
755 | initializations = t; | |
756 | } | |
757 | // stores the type to be initialized | |
758 | initializations[initializationCount++] = var; | |
759 | } | |
760 | ||
761 | /** | |
762 | * Replaces the given type with the appropriate type if it is one of the | |
763 | * types on which a constructor is invoked in the basic block. | |
764 | * | |
765 | * @param cw | |
766 | * the ClassWriter to which this label belongs. | |
767 | * @param t | |
768 | * a type | |
769 | * @return t or, if t is one of the types on which a constructor is invoked | |
770 | * in the basic block, the type corresponding to this constructor. | |
771 | */ | |
772 | private int init(final ClassWriter cw, final int t) { | |
773 | int s; | |
774 | if (t == UNINITIALIZED_THIS) { | |
775 | s = OBJECT | cw.addType(cw.thisName); | |
776 | } else if ((t & (DIM | BASE_KIND)) == UNINITIALIZED) { | |
777 | String type = cw.typeTable[t & BASE_VALUE].strVal1; | |
778 | s = OBJECT | cw.addType(type); | |
779 | } else { | |
780 | return t; | |
781 | } | |
782 | for (int j = 0; j < initializationCount; ++j) { | |
783 | int u = initializations[j]; | |
784 | int dim = u & DIM; | |
785 | int kind = u & KIND; | |
786 | if (kind == LOCAL) { | |
787 | u = dim + inputLocals[u & VALUE]; | |
788 | } else if (kind == STACK) { | |
789 | u = dim + inputStack[inputStack.length - (u & VALUE)]; | |
790 | } | |
791 | if (t == u) { | |
792 | return s; | |
793 | } | |
794 | } | |
795 | return t; | |
796 | } | |
797 | ||
798 | /** | |
799 | * Initializes the input frame of the first basic block from the method | |
800 | * descriptor. | |
801 | * | |
802 | * @param cw | |
803 | * the ClassWriter to which this label belongs. | |
804 | * @param access | |
805 | * the access flags of the method to which this label belongs. | |
806 | * @param args | |
807 | * the formal parameter types of this method. | |
808 | * @param maxLocals | |
809 | * the maximum number of local variables of this method. | |
810 | */ | |
811 | void initInputFrame(final ClassWriter cw, final int access, | |
812 | final Type[] args, final int maxLocals) { | |
813 | inputLocals = new int[maxLocals]; | |
814 | inputStack = new int[0]; | |
815 | int i = 0; | |
816 | if ((access & Opcodes.ACC_STATIC) == 0) { | |
817 | if ((access & MethodWriter.ACC_CONSTRUCTOR) == 0) { | |
818 | inputLocals[i++] = OBJECT | cw.addType(cw.thisName); | |
819 | } else { | |
820 | inputLocals[i++] = UNINITIALIZED_THIS; | |
821 | } | |
822 | } | |
823 | for (int j = 0; j < args.length; ++j) { | |
824 | int t = type(cw, args[j].getDescriptor()); | |
825 | inputLocals[i++] = t; | |
826 | if (t == LONG || t == DOUBLE) { | |
827 | inputLocals[i++] = TOP; | |
828 | } | |
829 | } | |
830 | while (i < maxLocals) { | |
831 | inputLocals[i++] = TOP; | |
832 | } | |
833 | } | |
834 | ||
835 | /** | |
836 | * Simulates the action of the given instruction on the output stack frame. | |
837 | * | |
838 | * @param opcode | |
839 | * the opcode of the instruction. | |
840 | * @param arg | |
841 | * the operand of the instruction, if any. | |
842 | * @param cw | |
843 | * the class writer to which this label belongs. | |
844 | * @param item | |
845 | * the operand of the instructions, if any. | |
846 | */ | |
847 | void execute(final int opcode, final int arg, final ClassWriter cw, | |
848 | final Item item) { | |
849 | int t1, t2, t3, t4; | |
850 | switch (opcode) { | |
851 | case Opcodes.NOP: | |
852 | case Opcodes.INEG: | |
853 | case Opcodes.LNEG: | |
854 | case Opcodes.FNEG: | |
855 | case Opcodes.DNEG: | |
856 | case Opcodes.I2B: | |
857 | case Opcodes.I2C: | |
858 | case Opcodes.I2S: | |
859 | case Opcodes.GOTO: | |
860 | case Opcodes.RETURN: | |
861 | break; | |
862 | case Opcodes.ACONST_NULL: | |
863 | push(NULL); | |
864 | break; | |
865 | case Opcodes.ICONST_M1: | |
866 | case Opcodes.ICONST_0: | |
867 | case Opcodes.ICONST_1: | |
868 | case Opcodes.ICONST_2: | |
869 | case Opcodes.ICONST_3: | |
870 | case Opcodes.ICONST_4: | |
871 | case Opcodes.ICONST_5: | |
872 | case Opcodes.BIPUSH: | |
873 | case Opcodes.SIPUSH: | |
874 | case Opcodes.ILOAD: | |
875 | push(INTEGER); | |
876 | break; | |
877 | case Opcodes.LCONST_0: | |
878 | case Opcodes.LCONST_1: | |
879 | case Opcodes.LLOAD: | |
880 | push(LONG); | |
881 | push(TOP); | |
882 | break; | |
883 | case Opcodes.FCONST_0: | |
884 | case Opcodes.FCONST_1: | |
885 | case Opcodes.FCONST_2: | |
886 | case Opcodes.FLOAD: | |
887 | push(FLOAT); | |
888 | break; | |
889 | case Opcodes.DCONST_0: | |
890 | case Opcodes.DCONST_1: | |
891 | case Opcodes.DLOAD: | |
892 | push(DOUBLE); | |
893 | push(TOP); | |
894 | break; | |
895 | case Opcodes.LDC: | |
896 | switch (item.type) { | |
897 | case ClassWriter.INT: | |
898 | push(INTEGER); | |
899 | break; | |
900 | case ClassWriter.LONG: | |
901 | push(LONG); | |
902 | push(TOP); | |
903 | break; | |
904 | case ClassWriter.FLOAT: | |
905 | push(FLOAT); | |
906 | break; | |
907 | case ClassWriter.DOUBLE: | |
908 | push(DOUBLE); | |
909 | push(TOP); | |
910 | break; | |
911 | case ClassWriter.CLASS: | |
912 | push(OBJECT | cw.addType("java/lang/Class")); | |
913 | break; | |
914 | case ClassWriter.STR: | |
915 | push(OBJECT | cw.addType("java/lang/String")); | |
916 | break; | |
917 | case ClassWriter.MTYPE: | |
918 | push(OBJECT | cw.addType("java/lang/invoke/MethodType")); | |
919 | break; | |
920 | // case ClassWriter.HANDLE_BASE + [1..9]: | |
921 | default: | |
922 | push(OBJECT | cw.addType("java/lang/invoke/MethodHandle")); | |
923 | } | |
924 | break; | |
925 | case Opcodes.ALOAD: | |
926 | push(get(arg)); | |
927 | break; | |
928 | case Opcodes.IALOAD: | |
929 | case Opcodes.BALOAD: | |
930 | case Opcodes.CALOAD: | |
931 | case Opcodes.SALOAD: | |
932 | pop(2); | |
933 | push(INTEGER); | |
934 | break; | |
935 | case Opcodes.LALOAD: | |
936 | case Opcodes.D2L: | |
937 | pop(2); | |
938 | push(LONG); | |
939 | push(TOP); | |
940 | break; | |
941 | case Opcodes.FALOAD: | |
942 | pop(2); | |
943 | push(FLOAT); | |
944 | break; | |
945 | case Opcodes.DALOAD: | |
946 | case Opcodes.L2D: | |
947 | pop(2); | |
948 | push(DOUBLE); | |
949 | push(TOP); | |
950 | break; | |
951 | case Opcodes.AALOAD: | |
952 | pop(1); | |
953 | t1 = pop(); | |
954 | push(ELEMENT_OF + t1); | |
955 | break; | |
956 | case Opcodes.ISTORE: | |
957 | case Opcodes.FSTORE: | |
958 | case Opcodes.ASTORE: | |
959 | t1 = pop(); | |
960 | set(arg, t1); | |
961 | if (arg > 0) { | |
962 | t2 = get(arg - 1); | |
963 | // if t2 is of kind STACK or LOCAL we cannot know its size! | |
964 | if (t2 == LONG || t2 == DOUBLE) { | |
965 | set(arg - 1, TOP); | |
966 | } else if ((t2 & KIND) != BASE) { | |
967 | set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); | |
968 | } | |
969 | } | |
970 | break; | |
971 | case Opcodes.LSTORE: | |
972 | case Opcodes.DSTORE: | |
973 | pop(1); | |
974 | t1 = pop(); | |
975 | set(arg, t1); | |
976 | set(arg + 1, TOP); | |
977 | if (arg > 0) { | |
978 | t2 = get(arg - 1); | |
979 | // if t2 is of kind STACK or LOCAL we cannot know its size! | |
980 | if (t2 == LONG || t2 == DOUBLE) { | |
981 | set(arg - 1, TOP); | |
982 | } else if ((t2 & KIND) != BASE) { | |
983 | set(arg - 1, t2 | TOP_IF_LONG_OR_DOUBLE); | |
984 | } | |
985 | } | |
986 | break; | |
987 | case Opcodes.IASTORE: | |
988 | case Opcodes.BASTORE: | |
989 | case Opcodes.CASTORE: | |
990 | case Opcodes.SASTORE: | |
991 | case Opcodes.FASTORE: | |
992 | case Opcodes.AASTORE: | |
993 | pop(3); | |
994 | break; | |
995 | case Opcodes.LASTORE: | |
996 | case Opcodes.DASTORE: | |
997 | pop(4); | |
998 | break; | |
999 | case Opcodes.POP: | |
1000 | case Opcodes.IFEQ: | |
1001 | case Opcodes.IFNE: | |
1002 | case Opcodes.IFLT: | |
1003 | case Opcodes.IFGE: | |
1004 | case Opcodes.IFGT: | |
1005 | case Opcodes.IFLE: | |
1006 | case Opcodes.IRETURN: | |
1007 | case Opcodes.FRETURN: | |
1008 | case Opcodes.ARETURN: | |
1009 | case Opcodes.TABLESWITCH: | |
1010 | case Opcodes.LOOKUPSWITCH: | |
1011 | case Opcodes.ATHROW: | |
1012 | case Opcodes.MONITORENTER: | |
1013 | case Opcodes.MONITOREXIT: | |
1014 | case Opcodes.IFNULL: | |
1015 | case Opcodes.IFNONNULL: | |
1016 | pop(1); | |
1017 | break; | |
1018 | case Opcodes.POP2: | |
1019 | case Opcodes.IF_ICMPEQ: | |
1020 | case Opcodes.IF_ICMPNE: | |
1021 | case Opcodes.IF_ICMPLT: | |
1022 | case Opcodes.IF_ICMPGE: | |
1023 | case Opcodes.IF_ICMPGT: | |
1024 | case Opcodes.IF_ICMPLE: | |
1025 | case Opcodes.IF_ACMPEQ: | |
1026 | case Opcodes.IF_ACMPNE: | |
1027 | case Opcodes.LRETURN: | |
1028 | case Opcodes.DRETURN: | |
1029 | pop(2); | |
1030 | break; | |
1031 | case Opcodes.DUP: | |
1032 | t1 = pop(); | |
1033 | push(t1); | |
1034 | push(t1); | |
1035 | break; | |
1036 | case Opcodes.DUP_X1: | |
1037 | t1 = pop(); | |
1038 | t2 = pop(); | |
1039 | push(t1); | |
1040 | push(t2); | |
1041 | push(t1); | |
1042 | break; | |
1043 | case Opcodes.DUP_X2: | |
1044 | t1 = pop(); | |
1045 | t2 = pop(); | |
1046 | t3 = pop(); | |
1047 | push(t1); | |
1048 | push(t3); | |
1049 | push(t2); | |
1050 | push(t1); | |
1051 | break; | |
1052 | case Opcodes.DUP2: | |
1053 | t1 = pop(); | |
1054 | t2 = pop(); | |
1055 | push(t2); | |
1056 | push(t1); | |
1057 | push(t2); | |
1058 | push(t1); | |
1059 | break; | |
1060 | case Opcodes.DUP2_X1: | |
1061 | t1 = pop(); | |
1062 | t2 = pop(); | |
1063 | t3 = pop(); | |
1064 | push(t2); | |
1065 | push(t1); | |
1066 | push(t3); | |
1067 | push(t2); | |
1068 | push(t1); | |
1069 | break; | |
1070 | case Opcodes.DUP2_X2: | |
1071 | t1 = pop(); | |
1072 | t2 = pop(); | |
1073 | t3 = pop(); | |
1074 | t4 = pop(); | |
1075 | push(t2); | |
1076 | push(t1); | |
1077 | push(t4); | |
1078 | push(t3); | |
1079 | push(t2); | |
1080 | push(t1); | |
1081 | break; | |
1082 | case Opcodes.SWAP: | |
1083 | t1 = pop(); | |
1084 | t2 = pop(); | |
1085 | push(t1); | |
1086 | push(t2); | |
1087 | break; | |
1088 | case Opcodes.IADD: | |
1089 | case Opcodes.ISUB: | |
1090 | case Opcodes.IMUL: | |
1091 | case Opcodes.IDIV: | |
1092 | case Opcodes.IREM: | |
1093 | case Opcodes.IAND: | |
1094 | case Opcodes.IOR: | |
1095 | case Opcodes.IXOR: | |
1096 | case Opcodes.ISHL: | |
1097 | case Opcodes.ISHR: | |
1098 | case Opcodes.IUSHR: | |
1099 | case Opcodes.L2I: | |
1100 | case Opcodes.D2I: | |
1101 | case Opcodes.FCMPL: | |
1102 | case Opcodes.FCMPG: | |
1103 | pop(2); | |
1104 | push(INTEGER); | |
1105 | break; | |
1106 | case Opcodes.LADD: | |
1107 | case Opcodes.LSUB: | |
1108 | case Opcodes.LMUL: | |
1109 | case Opcodes.LDIV: | |
1110 | case Opcodes.LREM: | |
1111 | case Opcodes.LAND: | |
1112 | case Opcodes.LOR: | |
1113 | case Opcodes.LXOR: | |
1114 | pop(4); | |
1115 | push(LONG); | |
1116 | push(TOP); | |
1117 | break; | |
1118 | case Opcodes.FADD: | |
1119 | case Opcodes.FSUB: | |
1120 | case Opcodes.FMUL: | |
1121 | case Opcodes.FDIV: | |
1122 | case Opcodes.FREM: | |
1123 | case Opcodes.L2F: | |
1124 | case Opcodes.D2F: | |
1125 | pop(2); | |
1126 | push(FLOAT); | |
1127 | break; | |
1128 | case Opcodes.DADD: | |
1129 | case Opcodes.DSUB: | |
1130 | case Opcodes.DMUL: | |
1131 | case Opcodes.DDIV: | |
1132 | case Opcodes.DREM: | |
1133 | pop(4); | |
1134 | push(DOUBLE); | |
1135 | push(TOP); | |
1136 | break; | |
1137 | case Opcodes.LSHL: | |
1138 | case Opcodes.LSHR: | |
1139 | case Opcodes.LUSHR: | |
1140 | pop(3); | |
1141 | push(LONG); | |
1142 | push(TOP); | |
1143 | break; | |
1144 | case Opcodes.IINC: | |
1145 | set(arg, INTEGER); | |
1146 | break; | |
1147 | case Opcodes.I2L: | |
1148 | case Opcodes.F2L: | |
1149 | pop(1); | |
1150 | push(LONG); | |
1151 | push(TOP); | |
1152 | break; | |
1153 | case Opcodes.I2F: | |
1154 | pop(1); | |
1155 | push(FLOAT); | |
1156 | break; | |
1157 | case Opcodes.I2D: | |
1158 | case Opcodes.F2D: | |
1159 | pop(1); | |
1160 | push(DOUBLE); | |
1161 | push(TOP); | |
1162 | break; | |
1163 | case Opcodes.F2I: | |
1164 | case Opcodes.ARRAYLENGTH: | |
1165 | case Opcodes.INSTANCEOF: | |
1166 | pop(1); | |
1167 | push(INTEGER); | |
1168 | break; | |
1169 | case Opcodes.LCMP: | |
1170 | case Opcodes.DCMPL: | |
1171 | case Opcodes.DCMPG: | |
1172 | pop(4); | |
1173 | push(INTEGER); | |
1174 | break; | |
1175 | case Opcodes.JSR: | |
1176 | case Opcodes.RET: | |
1177 | throw new RuntimeException( | |
1178 | "JSR/RET are not supported with computeFrames option"); | |
1179 | case Opcodes.GETSTATIC: | |
1180 | push(cw, item.strVal3); | |
1181 | break; | |
1182 | case Opcodes.PUTSTATIC: | |
1183 | pop(item.strVal3); | |
1184 | break; | |
1185 | case Opcodes.GETFIELD: | |
1186 | pop(1); | |
1187 | push(cw, item.strVal3); | |
1188 | break; | |
1189 | case Opcodes.PUTFIELD: | |
1190 | pop(item.strVal3); | |
1191 | pop(); | |
1192 | break; | |
1193 | case Opcodes.INVOKEVIRTUAL: | |
1194 | case Opcodes.INVOKESPECIAL: | |
1195 | case Opcodes.INVOKESTATIC: | |
1196 | case Opcodes.INVOKEINTERFACE: | |
1197 | pop(item.strVal3); | |
1198 | if (opcode != Opcodes.INVOKESTATIC) { | |
1199 | t1 = pop(); | |
1200 | if (opcode == Opcodes.INVOKESPECIAL | |
1201 | && item.strVal2.charAt(0) == '<') { | |
1202 | init(t1); | |
1203 | } | |
1204 | } | |
1205 | push(cw, item.strVal3); | |
1206 | break; | |
1207 | case Opcodes.INVOKEDYNAMIC: | |
1208 | pop(item.strVal2); | |
1209 | push(cw, item.strVal2); | |
1210 | break; | |
1211 | case Opcodes.NEW: | |
1212 | push(UNINITIALIZED | cw.addUninitializedType(item.strVal1, arg)); | |
1213 | break; | |
1214 | case Opcodes.NEWARRAY: | |
1215 | pop(); | |
1216 | switch (arg) { | |
1217 | case Opcodes.T_BOOLEAN: | |
1218 | push(ARRAY_OF | BOOLEAN); | |
1219 | break; | |
1220 | case Opcodes.T_CHAR: | |
1221 | push(ARRAY_OF | CHAR); | |
1222 | break; | |
1223 | case Opcodes.T_BYTE: | |
1224 | push(ARRAY_OF | BYTE); | |
1225 | break; | |
1226 | case Opcodes.T_SHORT: | |
1227 | push(ARRAY_OF | SHORT); | |
1228 | break; | |
1229 | case Opcodes.T_INT: | |
1230 | push(ARRAY_OF | INTEGER); | |
1231 | break; | |
1232 | case Opcodes.T_FLOAT: | |
1233 | push(ARRAY_OF | FLOAT); | |
1234 | break; | |
1235 | case Opcodes.T_DOUBLE: | |
1236 | push(ARRAY_OF | DOUBLE); | |
1237 | break; | |
1238 | // case Opcodes.T_LONG: | |
1239 | default: | |
1240 | push(ARRAY_OF | LONG); | |
1241 | break; | |
1242 | } | |
1243 | break; | |
1244 | case Opcodes.ANEWARRAY: | |
1245 | String s = item.strVal1; | |
1246 | pop(); | |
1247 | if (s.charAt(0) == '[') { | |
1248 | push(cw, '[' + s); | |
1249 | } else { | |
1250 | push(ARRAY_OF | OBJECT | cw.addType(s)); | |
1251 | } | |
1252 | break; | |
1253 | case Opcodes.CHECKCAST: | |
1254 | s = item.strVal1; | |
1255 | pop(); | |
1256 | if (s.charAt(0) == '[') { | |
1257 | push(cw, s); | |
1258 | } else { | |
1259 | push(OBJECT | cw.addType(s)); | |
1260 | } | |
1261 | break; | |
1262 | // case Opcodes.MULTIANEWARRAY: | |
1263 | default: | |
1264 | pop(arg); | |
1265 | push(cw, item.strVal1); | |
1266 | break; | |
1267 | } | |
1268 | } | |
1269 | ||
1270 | /** | |
1271 | * Merges the input frame of the given basic block with the input and output | |
1272 | * frames of this basic block. Returns <tt>true</tt> if the input frame of | |
1273 | * the given label has been changed by this operation. | |
1274 | * | |
1275 | * @param cw | |
1276 | * the ClassWriter to which this label belongs. | |
1277 | * @param frame | |
1278 | * the basic block whose input frame must be updated. | |
1279 | * @param edge | |
1280 | * the kind of the {@link Edge} between this label and 'label'. | |
1281 | * See {@link Edge#info}. | |
1282 | * @return <tt>true</tt> if the input frame of the given label has been | |
1283 | * changed by this operation. | |
1284 | */ | |
1285 | boolean merge(final ClassWriter cw, final Frame frame, final int edge) { | |
1286 | boolean changed = false; | |
1287 | int i, s, dim, kind, t; | |
1288 | ||
1289 | int nLocal = inputLocals.length; | |
1290 | int nStack = inputStack.length; | |
1291 | if (frame.inputLocals == null) { | |
1292 | frame.inputLocals = new int[nLocal]; | |
1293 | changed = true; | |
1294 | } | |
1295 | ||
1296 | for (i = 0; i < nLocal; ++i) { | |
1297 | if (outputLocals != null && i < outputLocals.length) { | |
1298 | s = outputLocals[i]; | |
1299 | if (s == 0) { | |
1300 | t = inputLocals[i]; | |
1301 | } else { | |
1302 | dim = s & DIM; | |
1303 | kind = s & KIND; | |
1304 | if (kind == BASE) { | |
1305 | t = s; | |
1306 | } else { | |
1307 | if (kind == LOCAL) { | |
1308 | t = dim + inputLocals[s & VALUE]; | |
1309 | } else { | |
1310 | t = dim + inputStack[nStack - (s & VALUE)]; | |
1311 | } | |
1312 | if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 | |
1313 | && (t == LONG || t == DOUBLE)) { | |
1314 | t = TOP; | |
1315 | } | |
1316 | } | |
1317 | } | |
1318 | } else { | |
1319 | t = inputLocals[i]; | |
1320 | } | |
1321 | if (initializations != null) { | |
1322 | t = init(cw, t); | |
1323 | } | |
1324 | changed |= merge(cw, t, frame.inputLocals, i); | |
1325 | } | |
1326 | ||
1327 | if (edge > 0) { | |
1328 | for (i = 0; i < nLocal; ++i) { | |
1329 | t = inputLocals[i]; | |
1330 | changed |= merge(cw, t, frame.inputLocals, i); | |
1331 | } | |
1332 | if (frame.inputStack == null) { | |
1333 | frame.inputStack = new int[1]; | |
1334 | changed = true; | |
1335 | } | |
1336 | changed |= merge(cw, edge, frame.inputStack, 0); | |
1337 | return changed; | |
1338 | } | |
1339 | ||
1340 | int nInputStack = inputStack.length + owner.inputStackTop; | |
1341 | if (frame.inputStack == null) { | |
1342 | frame.inputStack = new int[nInputStack + outputStackTop]; | |
1343 | changed = true; | |
1344 | } | |
1345 | ||
1346 | for (i = 0; i < nInputStack; ++i) { | |
1347 | t = inputStack[i]; | |
1348 | if (initializations != null) { | |
1349 | t = init(cw, t); | |
1350 | } | |
1351 | changed |= merge(cw, t, frame.inputStack, i); | |
1352 | } | |
1353 | for (i = 0; i < outputStackTop; ++i) { | |
1354 | s = outputStack[i]; | |
1355 | dim = s & DIM; | |
1356 | kind = s & KIND; | |
1357 | if (kind == BASE) { | |
1358 | t = s; | |
1359 | } else { | |
1360 | if (kind == LOCAL) { | |
1361 | t = dim + inputLocals[s & VALUE]; | |
1362 | } else { | |
1363 | t = dim + inputStack[nStack - (s & VALUE)]; | |
1364 | } | |
1365 | if ((s & TOP_IF_LONG_OR_DOUBLE) != 0 | |
1366 | && (t == LONG || t == DOUBLE)) { | |
1367 | t = TOP; | |
1368 | } | |
1369 | } | |
1370 | if (initializations != null) { | |
1371 | t = init(cw, t); | |
1372 | } | |
1373 | changed |= merge(cw, t, frame.inputStack, nInputStack + i); | |
1374 | } | |
1375 | return changed; | |
1376 | } | |
1377 | ||
1378 | /** | |
1379 | * Merges the type at the given index in the given type array with the given | |
1380 | * type. Returns <tt>true</tt> if the type array has been modified by this | |
1381 | * operation. | |
1382 | * | |
1383 | * @param cw | |
1384 | * the ClassWriter to which this label belongs. | |
1385 | * @param t | |
1386 | * the type with which the type array element must be merged. | |
1387 | * @param types | |
1388 | * an array of types. | |
1389 | * @param index | |
1390 | * the index of the type that must be merged in 'types'. | |
1391 | * @return <tt>true</tt> if the type array has been modified by this | |
1392 | * operation. | |
1393 | */ | |
1394 | private static boolean merge(final ClassWriter cw, int t, | |
1395 | final int[] types, final int index) { | |
1396 | int u = types[index]; | |
1397 | if (u == t) { | |
1398 | // if the types are equal, merge(u,t)=u, so there is no change | |
1399 | return false; | |
1400 | } | |
1401 | if ((t & ~DIM) == NULL) { | |
1402 | if (u == NULL) { | |
1403 | return false; | |
1404 | } | |
1405 | t = NULL; | |
1406 | } | |
1407 | if (u == 0) { | |
1408 | // if types[index] has never been assigned, merge(u,t)=t | |
1409 | types[index] = t; | |
1410 | return true; | |
1411 | } | |
1412 | int v; | |
1413 | if ((u & BASE_KIND) == OBJECT || (u & DIM) != 0) { | |
1414 | // if u is a reference type of any dimension | |
1415 | if (t == NULL) { | |
1416 | // if t is the NULL type, merge(u,t)=u, so there is no change | |
1417 | return false; | |
1418 | } else if ((t & (DIM | BASE_KIND)) == (u & (DIM | BASE_KIND))) { | |
1419 | if ((u & BASE_KIND) == OBJECT) { | |
1420 | // if t is also a reference type, and if u and t have the | |
1421 | // same dimension merge(u,t) = dim(t) | common parent of the | |
1422 | // element types of u and t | |
1423 | v = (t & DIM) | OBJECT | |
1424 | | cw.getMergedType(t & BASE_VALUE, u & BASE_VALUE); | |
1425 | } else { | |
1426 | // if u and t are array types, but not with the same element | |
1427 | // type, merge(u,t)=java/lang/Object | |
1428 | v = OBJECT | cw.addType("java/lang/Object"); | |
1429 | } | |
1430 | } else if ((t & BASE_KIND) == OBJECT || (t & DIM) != 0) { | |
1431 | // if t is any other reference or array type, | |
1432 | // merge(u,t)=java/lang/Object | |
1433 | v = OBJECT | cw.addType("java/lang/Object"); | |
1434 | } else { | |
1435 | // if t is any other type, merge(u,t)=TOP | |
1436 | v = TOP; | |
1437 | } | |
1438 | } else if (u == NULL) { | |
1439 | // if u is the NULL type, merge(u,t)=t, | |
1440 | // or TOP if t is not a reference type | |
1441 | v = (t & BASE_KIND) == OBJECT || (t & DIM) != 0 ? t : TOP; | |
1442 | } else { | |
1443 | // if u is any other type, merge(u,t)=TOP whatever t | |
1444 | v = TOP; | |
1445 | } | |
1446 | if (u != v) { | |
1447 | types[index] = v; | |
1448 | return true; | |
1449 | } | |
1450 | return false; | |
1451 | } | |
1452 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | ||
30 | package clojure.asm; | |
31 | ||
32 | /** | |
33 | * A reference to a field or a method. | |
34 | * | |
35 | * @author Remi Forax | |
36 | * @author Eric Bruneton | |
37 | */ | |
38 | public final class Handle { | |
39 | ||
40 | /** | |
41 | * The kind of field or method designated by this Handle. Should be | |
42 | * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, | |
43 | * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, | |
44 | * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, | |
45 | * {@link Opcodes#H_INVOKESPECIAL}, {@link Opcodes#H_NEWINVOKESPECIAL} or | |
46 | * {@link Opcodes#H_INVOKEINTERFACE}. | |
47 | */ | |
48 | final int tag; | |
49 | ||
50 | /** | |
51 | * The internal name of the field or method designed by this handle. | |
52 | */ | |
53 | final String owner; | |
54 | ||
55 | /** | |
56 | * The name of the field or method designated by this handle. | |
57 | */ | |
58 | final String name; | |
59 | ||
60 | /** | |
61 | * The descriptor of the field or method designated by this handle. | |
62 | */ | |
63 | final String desc; | |
64 | ||
65 | /** | |
66 | * Constructs a new field or method handle. | |
67 | * | |
68 | * @param tag | |
69 | * the kind of field or method designated by this Handle. Must be | |
70 | * {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, | |
71 | * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, | |
72 | * {@link Opcodes#H_INVOKEVIRTUAL}, | |
73 | * {@link Opcodes#H_INVOKESTATIC}, | |
74 | * {@link Opcodes#H_INVOKESPECIAL}, | |
75 | * {@link Opcodes#H_NEWINVOKESPECIAL} or | |
76 | * {@link Opcodes#H_INVOKEINTERFACE}. | |
77 | * @param owner | |
78 | * the internal name of the field or method designed by this | |
79 | * handle. | |
80 | * @param name | |
81 | * the name of the field or method designated by this handle. | |
82 | * @param desc | |
83 | * the descriptor of the field or method designated by this | |
84 | * handle. | |
85 | */ | |
86 | public Handle(int tag, String owner, String name, String desc) { | |
87 | this.tag = tag; | |
88 | this.owner = owner; | |
89 | this.name = name; | |
90 | this.desc = desc; | |
91 | } | |
92 | ||
93 | /** | |
94 | * Returns the kind of field or method designated by this handle. | |
95 | * | |
96 | * @return {@link Opcodes#H_GETFIELD}, {@link Opcodes#H_GETSTATIC}, | |
97 | * {@link Opcodes#H_PUTFIELD}, {@link Opcodes#H_PUTSTATIC}, | |
98 | * {@link Opcodes#H_INVOKEVIRTUAL}, {@link Opcodes#H_INVOKESTATIC}, | |
99 | * {@link Opcodes#H_INVOKESPECIAL}, | |
100 | * {@link Opcodes#H_NEWINVOKESPECIAL} or | |
101 | * {@link Opcodes#H_INVOKEINTERFACE}. | |
102 | */ | |
103 | public int getTag() { | |
104 | return tag; | |
105 | } | |
106 | ||
107 | /** | |
108 | * Returns the internal name of the field or method designed by this handle. | |
109 | * | |
110 | * @return the internal name of the field or method designed by this handle. | |
111 | */ | |
112 | public String getOwner() { | |
113 | return owner; | |
114 | } | |
115 | ||
116 | /** | |
117 | * Returns the name of the field or method designated by this handle. | |
118 | * | |
119 | * @return the name of the field or method designated by this handle. | |
120 | */ | |
121 | public String getName() { | |
122 | return name; | |
123 | } | |
124 | ||
125 | /** | |
126 | * Returns the descriptor of the field or method designated by this handle. | |
127 | * | |
128 | * @return the descriptor of the field or method designated by this handle. | |
129 | */ | |
130 | public String getDesc() { | |
131 | return desc; | |
132 | } | |
133 | ||
134 | @Override | |
135 | public boolean equals(Object obj) { | |
136 | if (obj == this) { | |
137 | return true; | |
138 | } | |
139 | if (!(obj instanceof Handle)) { | |
140 | return false; | |
141 | } | |
142 | Handle h = (Handle) obj; | |
143 | return tag == h.tag && owner.equals(h.owner) && name.equals(h.name) | |
144 | && desc.equals(h.desc); | |
145 | } | |
146 | ||
147 | @Override | |
148 | public int hashCode() { | |
149 | return tag + owner.hashCode() * name.hashCode() * desc.hashCode(); | |
150 | } | |
151 | ||
152 | /** | |
153 | * Returns the textual representation of this handle. The textual | |
154 | * representation is: | |
155 | * | |
156 | * <pre> | |
157 | * owner '.' name desc ' ' '(' tag ')' | |
158 | * </pre> | |
159 | * | |
160 | * . As this format is unambiguous, it can be parsed if necessary. | |
161 | */ | |
162 | @Override | |
163 | public String toString() { | |
164 | return owner + '.' + name + desc + " (" + tag + ')'; | |
165 | } | |
166 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | ||
70 | /** | |
71 | * Removes the range between start and end from the given exception | |
72 | * handlers. | |
73 | * | |
74 | * @param h | |
75 | * an exception handler list. | |
76 | * @param start | |
77 | * the start of the range to be removed. | |
78 | * @param end | |
79 | * the end of the range to be removed. Maybe null. | |
80 | * @return the exception handler list with the start-end range removed. | |
81 | */ | |
82 | static Handler remove(Handler h, Label start, Label end) { | |
83 | if (h == null) { | |
84 | return null; | |
85 | } else { | |
86 | h.next = remove(h.next, start, end); | |
87 | } | |
88 | int hstart = h.start.position; | |
89 | int hend = h.end.position; | |
90 | int s = start.position; | |
91 | int e = end == null ? Integer.MAX_VALUE : end.position; | |
92 | // if [hstart,hend[ and [s,e[ intervals intersect... | |
93 | if (s < hend && e > hstart) { | |
94 | if (s <= hstart) { | |
95 | if (e >= hend) { | |
96 | // [hstart,hend[ fully included in [s,e[, h removed | |
97 | h = h.next; | |
98 | } else { | |
99 | // [hstart,hend[ minus [s,e[ = [e,hend[ | |
100 | h.start = end; | |
101 | } | |
102 | } else if (e >= hend) { | |
103 | // [hstart,hend[ minus [s,e[ = [hstart,s[ | |
104 | h.end = start; | |
105 | } else { | |
106 | // [hstart,hend[ minus [s,e[ = [hstart,s[ + [e,hend[ | |
107 | Handler g = new Handler(); | |
108 | g.start = end; | |
109 | g.end = h.end; | |
110 | g.handler = h.handler; | |
111 | g.desc = h.desc; | |
112 | g.type = h.type; | |
113 | g.next = h.next; | |
114 | h.end = start; | |
115 | h.next = g; | |
116 | } | |
117 | } | |
118 | return h; | |
119 | } | |
120 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | * {@link ClassWriter#MTYPE}, {@link ClassWriter#INDY}. | |
54 | * | |
55 | * MethodHandle constant 9 variations are stored using a range of 9 values | |
56 | * from {@link ClassWriter#HANDLE_BASE} + 1 to | |
57 | * {@link ClassWriter#HANDLE_BASE} + 9. | |
58 | * | |
59 | * Special Item types are used for Items that are stored in the ClassWriter | |
60 | * {@link ClassWriter#typeTable}, instead of the constant pool, in order to | |
61 | * avoid clashes with normal constant pool items in the ClassWriter constant | |
62 | * pool's hash table. These special item types are | |
63 | * {@link ClassWriter#TYPE_NORMAL}, {@link ClassWriter#TYPE_UNINIT} and | |
64 | * {@link ClassWriter#TYPE_MERGED}. | |
65 | */ | |
66 | int type; | |
67 | ||
68 | /** | |
69 | * Value of this item, for an integer item. | |
70 | */ | |
71 | int intVal; | |
72 | ||
73 | /** | |
74 | * Value of this item, for a long item. | |
75 | */ | |
76 | long longVal; | |
77 | ||
78 | /** | |
79 | * First part of the value of this item, for items that do not hold a | |
80 | * primitive value. | |
81 | */ | |
82 | String strVal1; | |
83 | ||
84 | /** | |
85 | * Second part of the value of this item, for items that do not hold a | |
86 | * primitive value. | |
87 | */ | |
88 | String strVal2; | |
89 | ||
90 | /** | |
91 | * Third part of the value of this item, for items that do not hold a | |
92 | * primitive value. | |
93 | */ | |
94 | String strVal3; | |
95 | ||
96 | /** | |
97 | * The hash code value of this constant pool item. | |
98 | */ | |
99 | int hashCode; | |
100 | ||
101 | /** | |
102 | * Link to another constant pool item, used for collision lists in the | |
103 | * constant pool's hash table. | |
104 | */ | |
105 | Item next; | |
106 | ||
107 | /** | |
108 | * Constructs an uninitialized {@link Item}. | |
109 | */ | |
110 | Item() { | |
111 | } | |
112 | ||
113 | /** | |
114 | * Constructs an uninitialized {@link Item} for constant pool element at | |
115 | * given position. | |
116 | * | |
117 | * @param index | |
118 | * index of the item to be constructed. | |
119 | */ | |
120 | Item(final int index) { | |
121 | this.index = index; | |
122 | } | |
123 | ||
124 | /** | |
125 | * Constructs a copy of the given item. | |
126 | * | |
127 | * @param index | |
128 | * index of the item to be constructed. | |
129 | * @param i | |
130 | * the item that must be copied into the item to be constructed. | |
131 | */ | |
132 | Item(final int index, final Item i) { | |
133 | this.index = index; | |
134 | type = i.type; | |
135 | intVal = i.intVal; | |
136 | longVal = i.longVal; | |
137 | strVal1 = i.strVal1; | |
138 | strVal2 = i.strVal2; | |
139 | strVal3 = i.strVal3; | |
140 | hashCode = i.hashCode; | |
141 | } | |
142 | ||
143 | /** | |
144 | * Sets this item to an integer item. | |
145 | * | |
146 | * @param intVal | |
147 | * the value of this item. | |
148 | */ | |
149 | void set(final int intVal) { | |
150 | this.type = ClassWriter.INT; | |
151 | this.intVal = intVal; | |
152 | this.hashCode = 0x7FFFFFFF & (type + intVal); | |
153 | } | |
154 | ||
155 | /** | |
156 | * Sets this item to a long item. | |
157 | * | |
158 | * @param longVal | |
159 | * the value of this item. | |
160 | */ | |
161 | void set(final long longVal) { | |
162 | this.type = ClassWriter.LONG; | |
163 | this.longVal = longVal; | |
164 | this.hashCode = 0x7FFFFFFF & (type + (int) longVal); | |
165 | } | |
166 | ||
167 | /** | |
168 | * Sets this item to a float item. | |
169 | * | |
170 | * @param floatVal | |
171 | * the value of this item. | |
172 | */ | |
173 | void set(final float floatVal) { | |
174 | this.type = ClassWriter.FLOAT; | |
175 | this.intVal = Float.floatToRawIntBits(floatVal); | |
176 | this.hashCode = 0x7FFFFFFF & (type + (int) floatVal); | |
177 | } | |
178 | ||
179 | /** | |
180 | * Sets this item to a double item. | |
181 | * | |
182 | * @param doubleVal | |
183 | * the value of this item. | |
184 | */ | |
185 | void set(final double doubleVal) { | |
186 | this.type = ClassWriter.DOUBLE; | |
187 | this.longVal = Double.doubleToRawLongBits(doubleVal); | |
188 | this.hashCode = 0x7FFFFFFF & (type + (int) doubleVal); | |
189 | } | |
190 | ||
191 | /** | |
192 | * Sets this item to an item that do not hold a primitive value. | |
193 | * | |
194 | * @param type | |
195 | * the type of this item. | |
196 | * @param strVal1 | |
197 | * first part of the value of this item. | |
198 | * @param strVal2 | |
199 | * second part of the value of this item. | |
200 | * @param strVal3 | |
201 | * third part of the value of this item. | |
202 | */ | |
203 | void set(final int type, final String strVal1, final String strVal2, | |
204 | final String strVal3) { | |
205 | this.type = type; | |
206 | this.strVal1 = strVal1; | |
207 | this.strVal2 = strVal2; | |
208 | this.strVal3 = strVal3; | |
209 | switch (type) { | |
210 | case ClassWriter.UTF8: | |
211 | case ClassWriter.STR: | |
212 | case ClassWriter.CLASS: | |
213 | case ClassWriter.MTYPE: | |
214 | case ClassWriter.TYPE_NORMAL: | |
215 | hashCode = 0x7FFFFFFF & (type + strVal1.hashCode()); | |
216 | return; | |
217 | case ClassWriter.NAME_TYPE: { | |
218 | hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() | |
219 | * strVal2.hashCode()); | |
220 | return; | |
221 | } | |
222 | // ClassWriter.FIELD: | |
223 | // ClassWriter.METH: | |
224 | // ClassWriter.IMETH: | |
225 | // ClassWriter.HANDLE_BASE + 1..9 | |
226 | default: | |
227 | hashCode = 0x7FFFFFFF & (type + strVal1.hashCode() | |
228 | * strVal2.hashCode() * strVal3.hashCode()); | |
229 | } | |
230 | } | |
231 | ||
232 | /** | |
233 | * Sets the item to an InvokeDynamic item. | |
234 | * | |
235 | * @param name | |
236 | * invokedynamic's name. | |
237 | * @param desc | |
238 | * invokedynamic's desc. | |
239 | * @param bsmIndex | |
240 | * zero based index into the class attribute BootrapMethods. | |
241 | */ | |
242 | void set(String name, String desc, int bsmIndex) { | |
243 | this.type = ClassWriter.INDY; | |
244 | this.longVal = bsmIndex; | |
245 | this.strVal1 = name; | |
246 | this.strVal2 = desc; | |
247 | this.hashCode = 0x7FFFFFFF & (ClassWriter.INDY + bsmIndex | |
248 | * strVal1.hashCode() * strVal2.hashCode()); | |
249 | } | |
250 | ||
251 | /** | |
252 | * Sets the item to a BootstrapMethod item. | |
253 | * | |
254 | * @param position | |
255 | * position in byte in the class attribute BootrapMethods. | |
256 | * @param hashCode | |
257 | * hashcode of the item. This hashcode is processed from the | |
258 | * hashcode of the bootstrap method and the hashcode of all | |
259 | * bootstrap arguments. | |
260 | */ | |
261 | void set(int position, int hashCode) { | |
262 | this.type = ClassWriter.BSM; | |
263 | this.intVal = position; | |
264 | this.hashCode = hashCode; | |
265 | } | |
266 | ||
267 | /** | |
268 | * Indicates if the given item is equal to this one. <i>This method assumes | |
269 | * that the two items have the same {@link #type}</i>. | |
270 | * | |
271 | * @param i | |
272 | * the item to be compared to this one. Both items must have the | |
273 | * same {@link #type}. | |
274 | * @return <tt>true</tt> if the given item if equal to this one, | |
275 | * <tt>false</tt> otherwise. | |
276 | */ | |
277 | boolean isEqualTo(final Item i) { | |
278 | switch (type) { | |
279 | case ClassWriter.UTF8: | |
280 | case ClassWriter.STR: | |
281 | case ClassWriter.CLASS: | |
282 | case ClassWriter.MTYPE: | |
283 | case ClassWriter.TYPE_NORMAL: | |
284 | return i.strVal1.equals(strVal1); | |
285 | case ClassWriter.TYPE_MERGED: | |
286 | case ClassWriter.LONG: | |
287 | case ClassWriter.DOUBLE: | |
288 | return i.longVal == longVal; | |
289 | case ClassWriter.INT: | |
290 | case ClassWriter.FLOAT: | |
291 | return i.intVal == intVal; | |
292 | case ClassWriter.TYPE_UNINIT: | |
293 | return i.intVal == intVal && i.strVal1.equals(strVal1); | |
294 | case ClassWriter.NAME_TYPE: | |
295 | return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2); | |
296 | case ClassWriter.INDY: { | |
297 | return i.longVal == longVal && i.strVal1.equals(strVal1) | |
298 | && i.strVal2.equals(strVal2); | |
299 | } | |
300 | // case ClassWriter.FIELD: | |
301 | // case ClassWriter.METH: | |
302 | // case ClassWriter.IMETH: | |
303 | // case ClassWriter.HANDLE_BASE + 1..9 | |
304 | default: | |
305 | return i.strVal1.equals(strVal1) && i.strVal2.equals(strVal2) | |
306 | && i.strVal3.equals(strVal3); | |
307 | } | |
308 | } | |
309 | ||
310 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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. A label | |
34 | * designates the <i>instruction</i> that is just after. Note however that there | |
35 | * can be other elements between a label and the instruction it designates (such | |
36 | * as other labels, stack map frames, line numbers, etc.). | |
37 | * | |
38 | * @author Eric Bruneton | |
39 | */ | |
40 | public class Label { | |
41 | ||
42 | /** | |
43 | * Indicates if this label is only used for debug attributes. Such a label | |
44 | * is not the start of a basic block, the target of a jump instruction, or | |
45 | * an exception handler. It can be safely ignored in control flow graph | |
46 | * analysis algorithms (for optimization purposes). | |
47 | */ | |
48 | static final int DEBUG = 1; | |
49 | ||
50 | /** | |
51 | * Indicates if the position of this label is known. | |
52 | */ | |
53 | static final int RESOLVED = 2; | |
54 | ||
55 | /** | |
56 | * Indicates if this label has been updated, after instruction resizing. | |
57 | */ | |
58 | static final int RESIZED = 4; | |
59 | ||
60 | /** | |
61 | * Indicates if this basic block has been pushed in the basic block stack. | |
62 | * See {@link MethodWriter#visitMaxs visitMaxs}. | |
63 | */ | |
64 | static final int PUSHED = 8; | |
65 | ||
66 | /** | |
67 | * Indicates if this label is the target of a jump instruction, or the start | |
68 | * of an exception handler. | |
69 | */ | |
70 | static final int TARGET = 16; | |
71 | ||
72 | /** | |
73 | * Indicates if a stack map frame must be stored for this label. | |
74 | */ | |
75 | static final int STORE = 32; | |
76 | ||
77 | /** | |
78 | * Indicates if this label corresponds to a reachable basic block. | |
79 | */ | |
80 | static final int REACHABLE = 64; | |
81 | ||
82 | /** | |
83 | * Indicates if this basic block ends with a JSR instruction. | |
84 | */ | |
85 | static final int JSR = 128; | |
86 | ||
87 | /** | |
88 | * Indicates if this basic block ends with a RET instruction. | |
89 | */ | |
90 | static final int RET = 256; | |
91 | ||
92 | /** | |
93 | * Indicates if this basic block is the start of a subroutine. | |
94 | */ | |
95 | static final int SUBROUTINE = 512; | |
96 | ||
97 | /** | |
98 | * Indicates if this subroutine basic block has been visited by a | |
99 | * visitSubroutine(null, ...) call. | |
100 | */ | |
101 | static final int VISITED = 1024; | |
102 | ||
103 | /** | |
104 | * Indicates if this subroutine basic block has been visited by a | |
105 | * visitSubroutine(!null, ...) call. | |
106 | */ | |
107 | static final int VISITED2 = 2048; | |
108 | ||
109 | /** | |
110 | * Field used to associate user information to a label. Warning: this field | |
111 | * is used by the ASM tree package. In order to use it with the ASM tree | |
112 | * package you must override the | |
113 | * {@link clojure.asm.tree.MethodNode#getLabelNode} method. | |
114 | */ | |
115 | public Object info; | |
116 | ||
117 | /** | |
118 | * Flags that indicate the status of this label. | |
119 | * | |
120 | * @see #DEBUG | |
121 | * @see #RESOLVED | |
122 | * @see #RESIZED | |
123 | * @see #PUSHED | |
124 | * @see #TARGET | |
125 | * @see #STORE | |
126 | * @see #REACHABLE | |
127 | * @see #JSR | |
128 | * @see #RET | |
129 | */ | |
130 | int status; | |
131 | ||
132 | /** | |
133 | * The line number corresponding to this label, if known. | |
134 | */ | |
135 | int line; | |
136 | ||
137 | /** | |
138 | * The position of this label in the code, if known. | |
139 | */ | |
140 | int position; | |
141 | ||
142 | /** | |
143 | * Number of forward references to this label, times two. | |
144 | */ | |
145 | private int referenceCount; | |
146 | ||
147 | /** | |
148 | * Informations about forward references. Each forward reference is | |
149 | * described by two consecutive integers in this array: the first one is the | |
150 | * position of the first byte of the bytecode instruction that contains the | |
151 | * forward reference, while the second is the position of the first byte of | |
152 | * the forward reference itself. In fact the sign of the first integer | |
153 | * indicates if this reference uses 2 or 4 bytes, and its absolute value | |
154 | * gives the position of the bytecode instruction. This array is also used | |
155 | * as a bitset to store the subroutines to which a basic block belongs. This | |
156 | * information is needed in {@linked MethodWriter#visitMaxs}, after all | |
157 | * forward references have been resolved. Hence the same array can be used | |
158 | * for both purposes without problems. | |
159 | */ | |
160 | private int[] srcAndRefPositions; | |
161 | ||
162 | // ------------------------------------------------------------------------ | |
163 | ||
164 | /* | |
165 | * Fields for the control flow and data flow graph analysis algorithms (used | |
166 | * to compute the maximum stack size or the stack map frames). A control | |
167 | * flow graph contains one node per "basic block", and one edge per "jump" | |
168 | * from one basic block to another. Each node (i.e., each basic block) is | |
169 | * represented by the Label object that corresponds to the first instruction | |
170 | * of this basic block. Each node also stores the list of its successors in | |
171 | * the graph, as a linked list of Edge objects. | |
172 | * | |
173 | * The control flow analysis algorithms used to compute the maximum stack | |
174 | * size or the stack map frames are similar and use two steps. The first | |
175 | * step, during the visit of each instruction, builds information about the | |
176 | * state of the local variables and the operand stack at the end of each | |
177 | * basic block, called the "output frame", <i>relatively</i> to the frame | |
178 | * state at the beginning of the basic block, which is called the "input | |
179 | * frame", and which is <i>unknown</i> during this step. The second step, in | |
180 | * {@link MethodWriter#visitMaxs}, is a fix point algorithm that computes | |
181 | * information about the input frame of each basic block, from the input | |
182 | * state of the first basic block (known from the method signature), and by | |
183 | * the using the previously computed relative output frames. | |
184 | * | |
185 | * The algorithm used to compute the maximum stack size only computes the | |
186 | * relative output and absolute input stack heights, while the algorithm | |
187 | * used to compute stack map frames computes relative output frames and | |
188 | * absolute input frames. | |
189 | */ | |
190 | ||
191 | /** | |
192 | * Start of the output stack relatively to the input stack. The exact | |
193 | * semantics of this field depends on the algorithm that is used. | |
194 | * | |
195 | * When only the maximum stack size is computed, this field is the number of | |
196 | * elements in the input stack. | |
197 | * | |
198 | * When the stack map frames are completely computed, this field is the | |
199 | * offset of the first output stack element relatively to the top of the | |
200 | * input stack. This offset is always negative or null. A null offset means | |
201 | * that the output stack must be appended to the input stack. A -n offset | |
202 | * means that the first n output stack elements must replace the top n input | |
203 | * stack elements, and that the other elements must be appended to the input | |
204 | * stack. | |
205 | */ | |
206 | int inputStackTop; | |
207 | ||
208 | /** | |
209 | * Maximum height reached by the output stack, relatively to the top of the | |
210 | * input stack. This maximum is always positive or null. | |
211 | */ | |
212 | int outputStackMax; | |
213 | ||
214 | /** | |
215 | * Information about the input and output stack map frames of this basic | |
216 | * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} | |
217 | * option is used. | |
218 | */ | |
219 | Frame frame; | |
220 | ||
221 | /** | |
222 | * The successor of this label, in the order they are visited. This linked | |
223 | * list does not include labels used for debug info only. If | |
224 | * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it | |
225 | * does not contain successive labels that denote the same bytecode position | |
226 | * (in this case only the first label appears in this list). | |
227 | */ | |
228 | Label successor; | |
229 | ||
230 | /** | |
231 | * The successors of this node in the control flow graph. These successors | |
232 | * are stored in a linked list of {@link Edge Edge} objects, linked to each | |
233 | * other by their {@link Edge#next} field. | |
234 | */ | |
235 | Edge successors; | |
236 | ||
237 | /** | |
238 | * The next basic block in the basic block stack. This stack is used in the | |
239 | * main loop of the fix point algorithm used in the second step of the | |
240 | * control flow analysis algorithms. It is also used in | |
241 | * {@link #visitSubroutine} to avoid using a recursive method. | |
242 | * | |
243 | * @see MethodWriter#visitMaxs | |
244 | */ | |
245 | Label next; | |
246 | ||
247 | // ------------------------------------------------------------------------ | |
248 | // Constructor | |
249 | // ------------------------------------------------------------------------ | |
250 | ||
251 | /** | |
252 | * Constructs a new label. | |
253 | */ | |
254 | public Label() { | |
255 | } | |
256 | ||
257 | // ------------------------------------------------------------------------ | |
258 | // Methods to compute offsets and to manage forward references | |
259 | // ------------------------------------------------------------------------ | |
260 | ||
261 | /** | |
262 | * Returns the offset corresponding to this label. This offset is computed | |
263 | * from the start of the method's bytecode. <i>This method is intended for | |
264 | * {@link Attribute} sub classes, and is normally not needed by class | |
265 | * generators or adapters.</i> | |
266 | * | |
267 | * @return the offset corresponding to this label. | |
268 | * @throws IllegalStateException | |
269 | * if this label is not resolved yet. | |
270 | */ | |
271 | public int getOffset() { | |
272 | if ((status & RESOLVED) == 0) { | |
273 | throw new IllegalStateException( | |
274 | "Label offset position has not been resolved yet"); | |
275 | } | |
276 | return position; | |
277 | } | |
278 | ||
279 | /** | |
280 | * Puts a reference to this label in the bytecode of a method. If the | |
281 | * position of the label is known, the offset is computed and written | |
282 | * directly. Otherwise, a null offset is written and a new forward reference | |
283 | * is declared for this label. | |
284 | * | |
285 | * @param owner | |
286 | * the code writer that calls this method. | |
287 | * @param out | |
288 | * the bytecode of the method. | |
289 | * @param source | |
290 | * the position of first byte of the bytecode instruction that | |
291 | * contains this label. | |
292 | * @param wideOffset | |
293 | * <tt>true</tt> if the reference must be stored in 4 bytes, or | |
294 | * <tt>false</tt> if it must be stored with 2 bytes. | |
295 | * @throws IllegalArgumentException | |
296 | * if this label has not been created by the given code writer. | |
297 | */ | |
298 | void put(final MethodWriter owner, final ByteVector out, final int source, | |
299 | final boolean wideOffset) { | |
300 | if ((status & RESOLVED) == 0) { | |
301 | if (wideOffset) { | |
302 | addReference(-1 - source, out.length); | |
303 | out.putInt(-1); | |
304 | } else { | |
305 | addReference(source, out.length); | |
306 | out.putShort(-1); | |
307 | } | |
308 | } else { | |
309 | if (wideOffset) { | |
310 | out.putInt(position - source); | |
311 | } else { | |
312 | out.putShort(position - source); | |
313 | } | |
314 | } | |
315 | } | |
316 | ||
317 | /** | |
318 | * Adds a forward reference to this label. This method must be called only | |
319 | * for a true forward reference, i.e. only if this label is not resolved | |
320 | * yet. For backward references, the offset of the reference can be, and | |
321 | * must be, computed and stored directly. | |
322 | * | |
323 | * @param sourcePosition | |
324 | * the position of the referencing instruction. This position | |
325 | * will be used to compute the offset of this forward reference. | |
326 | * @param referencePosition | |
327 | * the position where the offset for this forward reference must | |
328 | * be stored. | |
329 | */ | |
330 | private void addReference(final int sourcePosition, | |
331 | final int referencePosition) { | |
332 | if (srcAndRefPositions == null) { | |
333 | srcAndRefPositions = new int[6]; | |
334 | } | |
335 | if (referenceCount >= srcAndRefPositions.length) { | |
336 | int[] a = new int[srcAndRefPositions.length + 6]; | |
337 | System.arraycopy(srcAndRefPositions, 0, a, 0, | |
338 | srcAndRefPositions.length); | |
339 | srcAndRefPositions = a; | |
340 | } | |
341 | srcAndRefPositions[referenceCount++] = sourcePosition; | |
342 | srcAndRefPositions[referenceCount++] = referencePosition; | |
343 | } | |
344 | ||
345 | /** | |
346 | * Resolves all forward references to this label. This method must be called | |
347 | * when this label is added to the bytecode of the method, i.e. when its | |
348 | * position becomes known. This method fills in the blanks that where left | |
349 | * in the bytecode by each forward reference previously added to this label. | |
350 | * | |
351 | * @param owner | |
352 | * the code writer that calls this method. | |
353 | * @param position | |
354 | * the position of this label in the bytecode. | |
355 | * @param data | |
356 | * the bytecode of the method. | |
357 | * @return <tt>true</tt> if a blank that was left for this label was to | |
358 | * small to store the offset. In such a case the corresponding jump | |
359 | * instruction is replaced with a pseudo instruction (using unused | |
360 | * opcodes) using an unsigned two bytes offset. These pseudo | |
361 | * instructions will need to be replaced with true instructions with | |
362 | * wider offsets (4 bytes instead of 2). This is done in | |
363 | * {@link MethodWriter#resizeInstructions}. | |
364 | * @throws IllegalArgumentException | |
365 | * if this label has already been resolved, or if it has not | |
366 | * been created by the given code writer. | |
367 | */ | |
368 | boolean resolve(final MethodWriter owner, final int position, | |
369 | final byte[] data) { | |
370 | boolean needUpdate = false; | |
371 | this.status |= RESOLVED; | |
372 | this.position = position; | |
373 | int i = 0; | |
374 | while (i < referenceCount) { | |
375 | int source = srcAndRefPositions[i++]; | |
376 | int reference = srcAndRefPositions[i++]; | |
377 | int offset; | |
378 | if (source >= 0) { | |
379 | offset = position - source; | |
380 | if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) { | |
381 | /* | |
382 | * changes the opcode of the jump instruction, in order to | |
383 | * be able to find it later (see resizeInstructions in | |
384 | * MethodWriter). These temporary opcodes are similar to | |
385 | * jump instruction opcodes, except that the 2 bytes offset | |
386 | * is unsigned (and can therefore represent values from 0 to | |
387 | * 65535, which is sufficient since the size of a method is | |
388 | * limited to 65535 bytes). | |
389 | */ | |
390 | int opcode = data[reference - 1] & 0xFF; | |
391 | if (opcode <= Opcodes.JSR) { | |
392 | // changes IFEQ ... JSR to opcodes 202 to 217 | |
393 | data[reference - 1] = (byte) (opcode + 49); | |
394 | } else { | |
395 | // changes IFNULL and IFNONNULL to opcodes 218 and 219 | |
396 | data[reference - 1] = (byte) (opcode + 20); | |
397 | } | |
398 | needUpdate = true; | |
399 | } | |
400 | data[reference++] = (byte) (offset >>> 8); | |
401 | data[reference] = (byte) offset; | |
402 | } else { | |
403 | offset = position + source + 1; | |
404 | data[reference++] = (byte) (offset >>> 24); | |
405 | data[reference++] = (byte) (offset >>> 16); | |
406 | data[reference++] = (byte) (offset >>> 8); | |
407 | data[reference] = (byte) offset; | |
408 | } | |
409 | } | |
410 | return needUpdate; | |
411 | } | |
412 | ||
413 | /** | |
414 | * Returns the first label of the series to which this label belongs. For an | |
415 | * isolated label or for the first label in a series of successive labels, | |
416 | * this method returns the label itself. For other labels it returns the | |
417 | * first label of the series. | |
418 | * | |
419 | * @return the first label of the series to which this label belongs. | |
420 | */ | |
421 | Label getFirst() { | |
422 | return !ClassReader.FRAMES || frame == null ? this : frame.owner; | |
423 | } | |
424 | ||
425 | // ------------------------------------------------------------------------ | |
426 | // Methods related to subroutines | |
427 | // ------------------------------------------------------------------------ | |
428 | ||
429 | /** | |
430 | * Returns true is this basic block belongs to the given subroutine. | |
431 | * | |
432 | * @param id | |
433 | * a subroutine id. | |
434 | * @return true is this basic block belongs to the given subroutine. | |
435 | */ | |
436 | boolean inSubroutine(final long id) { | |
437 | if ((status & Label.VISITED) != 0) { | |
438 | return (srcAndRefPositions[(int) (id >>> 32)] & (int) id) != 0; | |
439 | } | |
440 | return false; | |
441 | } | |
442 | ||
443 | /** | |
444 | * Returns true if this basic block and the given one belong to a common | |
445 | * subroutine. | |
446 | * | |
447 | * @param block | |
448 | * another basic block. | |
449 | * @return true if this basic block and the given one belong to a common | |
450 | * subroutine. | |
451 | */ | |
452 | boolean inSameSubroutine(final Label block) { | |
453 | if ((status & VISITED) == 0 || (block.status & VISITED) == 0) { | |
454 | return false; | |
455 | } | |
456 | for (int i = 0; i < srcAndRefPositions.length; ++i) { | |
457 | if ((srcAndRefPositions[i] & block.srcAndRefPositions[i]) != 0) { | |
458 | return true; | |
459 | } | |
460 | } | |
461 | return false; | |
462 | } | |
463 | ||
464 | /** | |
465 | * Marks this basic block as belonging to the given subroutine. | |
466 | * | |
467 | * @param id | |
468 | * a subroutine id. | |
469 | * @param nbSubroutines | |
470 | * the total number of subroutines in the method. | |
471 | */ | |
472 | void addToSubroutine(final long id, final int nbSubroutines) { | |
473 | if ((status & VISITED) == 0) { | |
474 | status |= VISITED; | |
475 | srcAndRefPositions = new int[(nbSubroutines - 1) / 32 + 1]; | |
476 | } | |
477 | srcAndRefPositions[(int) (id >>> 32)] |= (int) id; | |
478 | } | |
479 | ||
480 | /** | |
481 | * Finds the basic blocks that belong to a given subroutine, and marks these | |
482 | * blocks as belonging to this subroutine. This method follows the control | |
483 | * flow graph to find all the blocks that are reachable from the current | |
484 | * block WITHOUT following any JSR target. | |
485 | * | |
486 | * @param JSR | |
487 | * a JSR block that jumps to this subroutine. If this JSR is not | |
488 | * null it is added to the successor of the RET blocks found in | |
489 | * the subroutine. | |
490 | * @param id | |
491 | * the id of this subroutine. | |
492 | * @param nbSubroutines | |
493 | * the total number of subroutines in the method. | |
494 | */ | |
495 | void visitSubroutine(final Label JSR, final long id, final int nbSubroutines) { | |
496 | // user managed stack of labels, to avoid using a recursive method | |
497 | // (recursivity can lead to stack overflow with very large methods) | |
498 | Label stack = this; | |
499 | while (stack != null) { | |
500 | // removes a label l from the stack | |
501 | Label l = stack; | |
502 | stack = l.next; | |
503 | l.next = null; | |
504 | ||
505 | if (JSR != null) { | |
506 | if ((l.status & VISITED2) != 0) { | |
507 | continue; | |
508 | } | |
509 | l.status |= VISITED2; | |
510 | // adds JSR to the successors of l, if it is a RET block | |
511 | if ((l.status & RET) != 0) { | |
512 | if (!l.inSameSubroutine(JSR)) { | |
513 | Edge e = new Edge(); | |
514 | e.info = l.inputStackTop; | |
515 | e.successor = JSR.successors.successor; | |
516 | e.next = l.successors; | |
517 | l.successors = e; | |
518 | } | |
519 | } | |
520 | } else { | |
521 | // if the l block already belongs to subroutine 'id', continue | |
522 | if (l.inSubroutine(id)) { | |
523 | continue; | |
524 | } | |
525 | // marks the l block as belonging to subroutine 'id' | |
526 | l.addToSubroutine(id, nbSubroutines); | |
527 | } | |
528 | // pushes each successor of l on the stack, except JSR targets | |
529 | Edge e = l.successors; | |
530 | while (e != null) { | |
531 | // if the l block is a JSR block, then 'l.successors.next' leads | |
532 | // to the JSR target (see {@link #visitJumpInsn}) and must | |
533 | // therefore not be followed | |
534 | if ((l.status & Label.JSR) == 0 || e != l.successors.next) { | |
535 | // pushes e.successor on the stack if it not already added | |
536 | if (e.successor.next == null) { | |
537 | e.successor.next = stack; | |
538 | stack = e.successor; | |
539 | } | |
540 | } | |
541 | e = e.next; | |
542 | } | |
543 | } | |
544 | } | |
545 | ||
546 | // ------------------------------------------------------------------------ | |
547 | // Overriden Object methods | |
548 | // ------------------------------------------------------------------------ | |
549 | ||
550 | /** | |
551 | * Returns a string representation of this label. | |
552 | * | |
553 | * @return a string representation of this label. | |
554 | */ | |
555 | @Override | |
556 | public String toString() { | |
557 | return "L" + System.identityHashCode(this); | |
558 | } | |
559 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 class must be called in | |
33 | * 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> | | |
37 | * <tt>visitTryCatchBlock</tt> | <tt>visitLocalVariable</tt> | | |
38 | * <tt>visitLineNumber</tt> )* <tt>visitMaxs</tt> ] <tt>visitEnd</tt>. In | |
39 | * addition, the <tt>visit</tt><i>X</i>Insn</tt> and <tt>visitLabel</tt> methods | |
40 | * must be called in the sequential order of the bytecode instructions of the | |
41 | * visited code, <tt>visitTryCatchBlock</tt> must be called <i>before</i> the | |
42 | * labels passed as arguments have been visited, and the | |
43 | * <tt>visitLocalVariable</tt> and <tt>visitLineNumber</tt> methods must be | |
44 | * called <i>after</i> the labels passed as arguments have been visited. | |
45 | * | |
46 | * @author Eric Bruneton | |
47 | */ | |
48 | public abstract class MethodVisitor { | |
49 | ||
50 | /** | |
51 | * The ASM API version implemented by this visitor. The value of this field | |
52 | * must be one of {@link Opcodes#ASM4}. | |
53 | */ | |
54 | protected final int api; | |
55 | ||
56 | /** | |
57 | * The method visitor to which this visitor must delegate method calls. May | |
58 | * be null. | |
59 | */ | |
60 | protected MethodVisitor mv; | |
61 | ||
62 | /** | |
63 | * Constructs a new {@link MethodVisitor}. | |
64 | * | |
65 | * @param api | |
66 | * the ASM API version implemented by this visitor. Must be one | |
67 | * of {@link Opcodes#ASM4}. | |
68 | */ | |
69 | public MethodVisitor(final int api) { | |
70 | this(api, null); | |
71 | } | |
72 | ||
73 | /** | |
74 | * Constructs a new {@link MethodVisitor}. | |
75 | * | |
76 | * @param api | |
77 | * the ASM API version implemented by this visitor. Must be one | |
78 | * of {@link Opcodes#ASM4}. | |
79 | * @param mv | |
80 | * the method visitor to which this visitor must delegate method | |
81 | * calls. May be null. | |
82 | */ | |
83 | public MethodVisitor(final int api, final MethodVisitor mv) { | |
84 | if (api != Opcodes.ASM4) { | |
85 | throw new IllegalArgumentException(); | |
86 | } | |
87 | this.api = api; | |
88 | this.mv = mv; | |
89 | } | |
90 | ||
91 | // ------------------------------------------------------------------------- | |
92 | // Annotations and non standard attributes | |
93 | // ------------------------------------------------------------------------- | |
94 | ||
95 | /** | |
96 | * Visits the default value of this annotation interface method. | |
97 | * | |
98 | * @return a visitor to the visit the actual default value of this | |
99 | * annotation interface method, or <tt>null</tt> if this visitor is | |
100 | * not interested in visiting this default value. The 'name' | |
101 | * parameters passed to the methods of this annotation visitor are | |
102 | * ignored. Moreover, exacly one visit method must be called on this | |
103 | * annotation visitor, followed by visitEnd. | |
104 | */ | |
105 | public AnnotationVisitor visitAnnotationDefault() { | |
106 | if (mv != null) { | |
107 | return mv.visitAnnotationDefault(); | |
108 | } | |
109 | return null; | |
110 | } | |
111 | ||
112 | /** | |
113 | * Visits an annotation of this method. | |
114 | * | |
115 | * @param desc | |
116 | * the class descriptor of the annotation class. | |
117 | * @param visible | |
118 | * <tt>true</tt> if the annotation is visible at runtime. | |
119 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
120 | * this visitor is not interested in visiting this annotation. | |
121 | */ | |
122 | public AnnotationVisitor visitAnnotation(String desc, boolean visible) { | |
123 | if (mv != null) { | |
124 | return mv.visitAnnotation(desc, visible); | |
125 | } | |
126 | return null; | |
127 | } | |
128 | ||
129 | /** | |
130 | * Visits an annotation of a parameter this method. | |
131 | * | |
132 | * @param parameter | |
133 | * the parameter index. | |
134 | * @param desc | |
135 | * the class descriptor of the annotation class. | |
136 | * @param visible | |
137 | * <tt>true</tt> if the annotation is visible at runtime. | |
138 | * @return a visitor to visit the annotation values, or <tt>null</tt> if | |
139 | * this visitor is not interested in visiting this annotation. | |
140 | */ | |
141 | public AnnotationVisitor visitParameterAnnotation(int parameter, | |
142 | String desc, boolean visible) { | |
143 | if (mv != null) { | |
144 | return mv.visitParameterAnnotation(parameter, desc, visible); | |
145 | } | |
146 | return null; | |
147 | } | |
148 | ||
149 | /** | |
150 | * Visits a non standard attribute of this method. | |
151 | * | |
152 | * @param attr | |
153 | * an attribute. | |
154 | */ | |
155 | public void visitAttribute(Attribute attr) { | |
156 | if (mv != null) { | |
157 | mv.visitAttribute(attr); | |
158 | } | |
159 | } | |
160 | ||
161 | /** | |
162 | * Starts the visit of the method's code, if any (i.e. non abstract method). | |
163 | */ | |
164 | public void visitCode() { | |
165 | if (mv != null) { | |
166 | mv.visitCode(); | |
167 | } | |
168 | } | |
169 | ||
170 | /** | |
171 | * Visits the current state of the local variables and operand stack | |
172 | * elements. This method must(*) be called <i>just before</i> any | |
173 | * instruction <b>i</b> that follows an unconditional branch instruction | |
174 | * such as GOTO or THROW, that is the target of a jump instruction, or that | |
175 | * starts an exception handler block. The visited types must describe the | |
176 | * values of the local variables and of the operand stack elements <i>just | |
177 | * before</i> <b>i</b> is executed.<br> | |
178 | * <br> | |
179 | * (*) this is mandatory only for classes whose version is greater than or | |
180 | * equal to {@link Opcodes#V1_6 V1_6}. <br> | |
181 | * <br> | |
182 | * The frames of a method must be given either in expanded form, or in | |
183 | * compressed form (all frames must use the same format, i.e. you must not | |
184 | * mix expanded and compressed frames within a single method): | |
185 | * <ul> | |
186 | * <li>In expanded form, all frames must have the F_NEW type.</li> | |
187 | * <li>In compressed form, frames are basically "deltas" from the state of | |
188 | * the previous frame: | |
189 | * <ul> | |
190 | * <li>{@link Opcodes#F_SAME} representing frame with exactly the same | |
191 | * locals as the previous frame and with the empty stack.</li> | |
192 | * <li>{@link Opcodes#F_SAME1} representing frame with exactly the same | |
193 | * locals as the previous frame and with single value on the stack ( | |
194 | * <code>nStack</code> is 1 and <code>stack[0]</code> contains value for the | |
195 | * type of the stack item).</li> | |
196 | * <li>{@link Opcodes#F_APPEND} representing frame with current locals are | |
197 | * the same as the locals in the previous frame, except that additional | |
198 | * locals are defined (<code>nLocal</code> is 1, 2 or 3 and | |
199 | * <code>local</code> elements contains values representing added types).</li> | |
200 | * <li>{@link Opcodes#F_CHOP} representing frame with current locals are the | |
201 | * same as the locals in the previous frame, except that the last 1-3 locals | |
202 | * are absent and with the empty stack (<code>nLocals</code> is 1, 2 or 3).</li> | |
203 | * <li>{@link Opcodes#F_FULL} representing complete frame data.</li></li> | |
204 | * </ul> | |
205 | * </ul> <br> | |
206 | * In both cases the first frame, corresponding to the method's parameters | |
207 | * and access flags, is implicit and must not be visited. Also, it is | |
208 | * illegal to visit two or more frames for the same code location (i.e., at | |
209 | * least one instruction must be visited between two calls to visitFrame). | |
210 | * | |
211 | * @param type | |
212 | * the type of this stack map frame. Must be | |
213 | * {@link Opcodes#F_NEW} for expanded frames, or | |
214 | * {@link Opcodes#F_FULL}, {@link Opcodes#F_APPEND}, | |
215 | * {@link Opcodes#F_CHOP}, {@link Opcodes#F_SAME} or | |
216 | * {@link Opcodes#F_APPEND}, {@link Opcodes#F_SAME1} for | |
217 | * compressed frames. | |
218 | * @param nLocal | |
219 | * the number of local variables in the visited frame. | |
220 | * @param local | |
221 | * the local variable types in this frame. This array must not be | |
222 | * modified. Primitive types are represented by | |
223 | * {@link Opcodes#TOP}, {@link Opcodes#INTEGER}, | |
224 | * {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, | |
225 | * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or | |
226 | * {@link Opcodes#UNINITIALIZED_THIS} (long and double are | |
227 | * represented by a single element). Reference types are | |
228 | * represented by String objects (representing internal names), | |
229 | * and uninitialized types by Label objects (this label | |
230 | * designates the NEW instruction that created this uninitialized | |
231 | * value). | |
232 | * @param nStack | |
233 | * the number of operand stack elements in the visited frame. | |
234 | * @param stack | |
235 | * the operand stack types in this frame. This array must not be | |
236 | * modified. Its content has the same format as the "local" | |
237 | * array. | |
238 | * @throws IllegalStateException | |
239 | * if a frame is visited just after another one, without any | |
240 | * instruction between the two (unless this frame is a | |
241 | * Opcodes#F_SAME frame, in which case it is silently ignored). | |
242 | */ | |
243 | public void visitFrame(int type, int nLocal, Object[] local, int nStack, | |
244 | Object[] stack) { | |
245 | if (mv != null) { | |
246 | mv.visitFrame(type, nLocal, local, nStack, stack); | |
247 | } | |
248 | } | |
249 | ||
250 | // ------------------------------------------------------------------------- | |
251 | // Normal instructions | |
252 | // ------------------------------------------------------------------------- | |
253 | ||
254 | /** | |
255 | * Visits a zero operand instruction. | |
256 | * | |
257 | * @param opcode | |
258 | * the opcode of the instruction to be visited. This opcode is | |
259 | * either NOP, ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, | |
260 | * ICONST_2, ICONST_3, ICONST_4, ICONST_5, LCONST_0, LCONST_1, | |
261 | * FCONST_0, FCONST_1, FCONST_2, DCONST_0, DCONST_1, IALOAD, | |
262 | * LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, | |
263 | * IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, | |
264 | * SASTORE, POP, POP2, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, | |
265 | * DUP2_X2, SWAP, IADD, LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, | |
266 | * IMUL, LMUL, FMUL, DMUL, IDIV, LDIV, FDIV, DDIV, IREM, LREM, | |
267 | * FREM, DREM, INEG, LNEG, FNEG, DNEG, ISHL, LSHL, ISHR, LSHR, | |
268 | * IUSHR, LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, I2L, I2F, I2D, | |
269 | * L2I, L2F, L2D, F2I, F2L, F2D, D2I, D2L, D2F, I2B, I2C, I2S, | |
270 | * LCMP, FCMPL, FCMPG, DCMPL, DCMPG, IRETURN, LRETURN, FRETURN, | |
271 | * DRETURN, ARETURN, RETURN, ARRAYLENGTH, ATHROW, MONITORENTER, | |
272 | * or MONITOREXIT. | |
273 | */ | |
274 | public void visitInsn(int opcode) { | |
275 | if (mv != null) { | |
276 | mv.visitInsn(opcode); | |
277 | } | |
278 | } | |
279 | ||
280 | /** | |
281 | * Visits an instruction with a single int operand. | |
282 | * | |
283 | * @param opcode | |
284 | * the opcode of the instruction to be visited. This opcode is | |
285 | * either BIPUSH, SIPUSH or NEWARRAY. | |
286 | * @param operand | |
287 | * the operand of the instruction to be visited.<br> | |
288 | * When opcode is BIPUSH, operand value should be between | |
289 | * Byte.MIN_VALUE and Byte.MAX_VALUE.<br> | |
290 | * When opcode is SIPUSH, operand value should be between | |
291 | * Short.MIN_VALUE and Short.MAX_VALUE.<br> | |
292 | * When opcode is NEWARRAY, operand value should be one of | |
293 | * {@link Opcodes#T_BOOLEAN}, {@link Opcodes#T_CHAR}, | |
294 | * {@link Opcodes#T_FLOAT}, {@link Opcodes#T_DOUBLE}, | |
295 | * {@link Opcodes#T_BYTE}, {@link Opcodes#T_SHORT}, | |
296 | * {@link Opcodes#T_INT} or {@link Opcodes#T_LONG}. | |
297 | */ | |
298 | public void visitIntInsn(int opcode, int operand) { | |
299 | if (mv != null) { | |
300 | mv.visitIntInsn(opcode, operand); | |
301 | } | |
302 | } | |
303 | ||
304 | /** | |
305 | * Visits a local variable instruction. A local variable instruction is an | |
306 | * instruction that loads or stores the value of a local variable. | |
307 | * | |
308 | * @param opcode | |
309 | * the opcode of the local variable instruction to be visited. | |
310 | * This opcode is either ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, | |
311 | * ISTORE, LSTORE, FSTORE, DSTORE, ASTORE or RET. | |
312 | * @param var | |
313 | * the operand of the instruction to be visited. This operand is | |
314 | * the index of a local variable. | |
315 | */ | |
316 | public void visitVarInsn(int opcode, int var) { | |
317 | if (mv != null) { | |
318 | mv.visitVarInsn(opcode, var); | |
319 | } | |
320 | } | |
321 | ||
322 | /** | |
323 | * Visits a type instruction. A type instruction is an instruction that | |
324 | * takes the internal name of a class as parameter. | |
325 | * | |
326 | * @param opcode | |
327 | * the opcode of the type instruction to be visited. This opcode | |
328 | * is either NEW, ANEWARRAY, CHECKCAST or INSTANCEOF. | |
329 | * @param type | |
330 | * the operand of the instruction to be visited. This operand | |
331 | * must be the internal name of an object or array class (see | |
332 | * {@link Type#getInternalName() getInternalName}). | |
333 | */ | |
334 | public void visitTypeInsn(int opcode, String type) { | |
335 | if (mv != null) { | |
336 | mv.visitTypeInsn(opcode, type); | |
337 | } | |
338 | } | |
339 | ||
340 | /** | |
341 | * Visits a field instruction. A field instruction is an instruction that | |
342 | * loads or stores the value of a field of an object. | |
343 | * | |
344 | * @param opcode | |
345 | * the opcode of the type instruction to be visited. This opcode | |
346 | * is either GETSTATIC, PUTSTATIC, GETFIELD or PUTFIELD. | |
347 | * @param owner | |
348 | * the internal name of the field's owner class (see | |
349 | * {@link Type#getInternalName() getInternalName}). | |
350 | * @param name | |
351 | * the field's name. | |
352 | * @param desc | |
353 | * the field's descriptor (see {@link Type Type}). | |
354 | */ | |
355 | public void visitFieldInsn(int opcode, String owner, String name, | |
356 | String desc) { | |
357 | if (mv != null) { | |
358 | mv.visitFieldInsn(opcode, owner, name, desc); | |
359 | } | |
360 | } | |
361 | ||
362 | /** | |
363 | * Visits a method instruction. A method instruction is an instruction that | |
364 | * invokes a method. | |
365 | * | |
366 | * @param opcode | |
367 | * the opcode of the type instruction to be visited. This opcode | |
368 | * is either INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC or | |
369 | * INVOKEINTERFACE. | |
370 | * @param owner | |
371 | * the internal name of the method's owner class (see | |
372 | * {@link Type#getInternalName() getInternalName}). | |
373 | * @param name | |
374 | * the method's name. | |
375 | * @param desc | |
376 | * the method's descriptor (see {@link Type Type}). | |
377 | */ | |
378 | public void visitMethodInsn(int opcode, String owner, String name, | |
379 | String desc) { | |
380 | if (mv != null) { | |
381 | mv.visitMethodInsn(opcode, owner, name, desc); | |
382 | } | |
383 | } | |
384 | ||
385 | /** | |
386 | * Visits an invokedynamic instruction. | |
387 | * | |
388 | * @param name | |
389 | * the method's name. | |
390 | * @param desc | |
391 | * the method's descriptor (see {@link Type Type}). | |
392 | * @param bsm | |
393 | * the bootstrap method. | |
394 | * @param bsmArgs | |
395 | * the bootstrap method constant arguments. Each argument must be | |
396 | * an {@link Integer}, {@link Float}, {@link Long}, | |
397 | * {@link Double}, {@link String}, {@link Type} or {@link Handle} | |
398 | * value. This method is allowed to modify the content of the | |
399 | * array so a caller should expect that this array may change. | |
400 | */ | |
401 | public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, | |
402 | Object... bsmArgs) { | |
403 | if (mv != null) { | |
404 | mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); | |
405 | } | |
406 | } | |
407 | ||
408 | /** | |
409 | * Visits a jump instruction. A jump instruction is an instruction that may | |
410 | * jump to another instruction. | |
411 | * | |
412 | * @param opcode | |
413 | * the opcode of the type instruction to be visited. This opcode | |
414 | * is either IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, IF_ICMPEQ, | |
415 | * IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, | |
416 | * IF_ACMPEQ, IF_ACMPNE, GOTO, JSR, IFNULL or IFNONNULL. | |
417 | * @param label | |
418 | * the operand of the instruction to be visited. This operand is | |
419 | * a label that designates the instruction to which the jump | |
420 | * instruction may jump. | |
421 | */ | |
422 | public void visitJumpInsn(int opcode, Label label) { | |
423 | if (mv != null) { | |
424 | mv.visitJumpInsn(opcode, label); | |
425 | } | |
426 | } | |
427 | ||
428 | /** | |
429 | * Visits a label. A label designates the instruction that will be visited | |
430 | * just after it. | |
431 | * | |
432 | * @param label | |
433 | * a {@link Label Label} object. | |
434 | */ | |
435 | public void visitLabel(Label label) { | |
436 | if (mv != null) { | |
437 | mv.visitLabel(label); | |
438 | } | |
439 | } | |
440 | ||
441 | // ------------------------------------------------------------------------- | |
442 | // Special instructions | |
443 | // ------------------------------------------------------------------------- | |
444 | ||
445 | /** | |
446 | * Visits a LDC instruction. Note that new constant types may be added in | |
447 | * future versions of the Java Virtual Machine. To easily detect new | |
448 | * constant types, implementations of this method should check for | |
449 | * unexpected constant types, like this: | |
450 | * | |
451 | * <pre> | |
452 | * if (cst instanceof Integer) { | |
453 | * // ... | |
454 | * } else if (cst instanceof Float) { | |
455 | * // ... | |
456 | * } else if (cst instanceof Long) { | |
457 | * // ... | |
458 | * } else if (cst instanceof Double) { | |
459 | * // ... | |
460 | * } else if (cst instanceof String) { | |
461 | * // ... | |
462 | * } else if (cst instanceof Type) { | |
463 | * int sort = ((Type) cst).getSort(); | |
464 | * if (sort == Type.OBJECT) { | |
465 | * // ... | |
466 | * } else if (sort == Type.ARRAY) { | |
467 | * // ... | |
468 | * } else if (sort == Type.METHOD) { | |
469 | * // ... | |
470 | * } else { | |
471 | * // throw an exception | |
472 | * } | |
473 | * } else if (cst instanceof Handle) { | |
474 | * // ... | |
475 | * } else { | |
476 | * // throw an exception | |
477 | * } | |
478 | * </pre> | |
479 | * | |
480 | * @param cst | |
481 | * the constant to be loaded on the stack. This parameter must be | |
482 | * a non null {@link Integer}, a {@link Float}, a {@link Long}, a | |
483 | * {@link Double}, a {@link String}, a {@link Type} of OBJECT or | |
484 | * ARRAY sort for <tt>.class</tt> constants, for classes whose | |
485 | * version is 49.0, a {@link Type} of METHOD sort or a | |
486 | * {@link Handle} for MethodType and MethodHandle constants, for | |
487 | * classes whose version is 51.0. | |
488 | */ | |
489 | public void visitLdcInsn(Object cst) { | |
490 | if (mv != null) { | |
491 | mv.visitLdcInsn(cst); | |
492 | } | |
493 | } | |
494 | ||
495 | /** | |
496 | * Visits an IINC instruction. | |
497 | * | |
498 | * @param var | |
499 | * index of the local variable to be incremented. | |
500 | * @param increment | |
501 | * amount to increment the local variable by. | |
502 | */ | |
503 | public void visitIincInsn(int var, int increment) { | |
504 | if (mv != null) { | |
505 | mv.visitIincInsn(var, increment); | |
506 | } | |
507 | } | |
508 | ||
509 | /** | |
510 | * Visits a TABLESWITCH instruction. | |
511 | * | |
512 | * @param min | |
513 | * the minimum key value. | |
514 | * @param max | |
515 | * the maximum key value. | |
516 | * @param dflt | |
517 | * beginning of the default handler block. | |
518 | * @param labels | |
519 | * beginnings of the handler blocks. <tt>labels[i]</tt> is the | |
520 | * beginning of the handler block for the <tt>min + i</tt> key. | |
521 | */ | |
522 | public void visitTableSwitchInsn(int min, int max, Label dflt, | |
523 | Label... labels) { | |
524 | if (mv != null) { | |
525 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
526 | } | |
527 | } | |
528 | ||
529 | /** | |
530 | * Visits a LOOKUPSWITCH instruction. | |
531 | * | |
532 | * @param dflt | |
533 | * beginning of the default handler block. | |
534 | * @param keys | |
535 | * the values of the keys. | |
536 | * @param labels | |
537 | * beginnings of the handler blocks. <tt>labels[i]</tt> is the | |
538 | * beginning of the handler block for the <tt>keys[i]</tt> key. | |
539 | */ | |
540 | public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) { | |
541 | if (mv != null) { | |
542 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
543 | } | |
544 | } | |
545 | ||
546 | /** | |
547 | * Visits a MULTIANEWARRAY instruction. | |
548 | * | |
549 | * @param desc | |
550 | * an array type descriptor (see {@link Type Type}). | |
551 | * @param dims | |
552 | * number of dimensions of the array to allocate. | |
553 | */ | |
554 | public void visitMultiANewArrayInsn(String desc, int dims) { | |
555 | if (mv != null) { | |
556 | mv.visitMultiANewArrayInsn(desc, dims); | |
557 | } | |
558 | } | |
559 | ||
560 | // ------------------------------------------------------------------------- | |
561 | // Exceptions table entries, debug information, max stack and max locals | |
562 | // ------------------------------------------------------------------------- | |
563 | ||
564 | /** | |
565 | * Visits a try catch block. | |
566 | * | |
567 | * @param start | |
568 | * beginning of the exception handler's scope (inclusive). | |
569 | * @param end | |
570 | * end of the exception handler's scope (exclusive). | |
571 | * @param handler | |
572 | * beginning of the exception handler's code. | |
573 | * @param type | |
574 | * internal name of the type of exceptions handled by the | |
575 | * handler, or <tt>null</tt> to catch any exceptions (for | |
576 | * "finally" blocks). | |
577 | * @throws IllegalArgumentException | |
578 | * if one of the labels has already been visited by this visitor | |
579 | * (by the {@link #visitLabel visitLabel} method). | |
580 | */ | |
581 | public void visitTryCatchBlock(Label start, Label end, Label handler, | |
582 | String type) { | |
583 | if (mv != null) { | |
584 | mv.visitTryCatchBlock(start, end, handler, type); | |
585 | } | |
586 | } | |
587 | ||
588 | /** | |
589 | * Visits a local variable declaration. | |
590 | * | |
591 | * @param name | |
592 | * the name of a local variable. | |
593 | * @param desc | |
594 | * the type descriptor of this local variable. | |
595 | * @param signature | |
596 | * the type signature of this local variable. May be | |
597 | * <tt>null</tt> if the local variable type does not use generic | |
598 | * types. | |
599 | * @param start | |
600 | * the first instruction corresponding to the scope of this local | |
601 | * variable (inclusive). | |
602 | * @param end | |
603 | * the last instruction corresponding to the scope of this local | |
604 | * variable (exclusive). | |
605 | * @param index | |
606 | * the local variable's index. | |
607 | * @throws IllegalArgumentException | |
608 | * if one of the labels has not already been visited by this | |
609 | * visitor (by the {@link #visitLabel visitLabel} method). | |
610 | */ | |
611 | public void visitLocalVariable(String name, String desc, String signature, | |
612 | Label start, Label end, int index) { | |
613 | if (mv != null) { | |
614 | mv.visitLocalVariable(name, desc, signature, start, end, index); | |
615 | } | |
616 | } | |
617 | ||
618 | /** | |
619 | * Visits a line number declaration. | |
620 | * | |
621 | * @param line | |
622 | * a line number. This number refers to the source file from | |
623 | * which the class was compiled. | |
624 | * @param start | |
625 | * the first instruction corresponding to this line number. | |
626 | * @throws IllegalArgumentException | |
627 | * if <tt>start</tt> has not already been visited by this | |
628 | * visitor (by the {@link #visitLabel visitLabel} method). | |
629 | */ | |
630 | public void visitLineNumber(int line, Label start) { | |
631 | if (mv != null) { | |
632 | mv.visitLineNumber(line, start); | |
633 | } | |
634 | } | |
635 | ||
636 | /** | |
637 | * Visits the maximum stack size and the maximum number of local variables | |
638 | * of the method. | |
639 | * | |
640 | * @param maxStack | |
641 | * maximum stack size of the method. | |
642 | * @param maxLocals | |
643 | * maximum number of local variables for the method. | |
644 | */ | |
645 | public void visitMaxs(int maxStack, int maxLocals) { | |
646 | if (mv != null) { | |
647 | mv.visitMaxs(maxStack, maxLocals); | |
648 | } | |
649 | } | |
650 | ||
651 | /** | |
652 | * Visits the end of the method. This method, which is the last one to be | |
653 | * called, is used to inform the visitor that all the annotations and | |
654 | * attributes of the method have been visited. | |
655 | */ | |
656 | public void visitEnd() { | |
657 | if (mv != null) { | |
658 | mv.visitEnd(); | |
659 | } | |
660 | } | |
661 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 extends MethodVisitor { | |
40 | ||
41 | /** | |
42 | * Pseudo access flag used to denote constructors. | |
43 | */ | |
44 | static final int ACC_CONSTRUCTOR = 0x80000; | |
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 | static final 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 | static final int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f) | |
57 | ||
58 | /** | |
59 | * Reserved for future use | |
60 | */ | |
61 | static final 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 | static final 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 | static final 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 | static final 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 | static final int APPEND_FRAME = 252; // to 254 // fc-fe | |
88 | ||
89 | /** | |
90 | * Full frame | |
91 | */ | |
92 | static final 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 static final 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 static final int MAXS = 1; | |
110 | ||
111 | /** | |
112 | * Indicates that nothing must be automatically computed. | |
113 | * | |
114 | * @see #compute | |
115 | */ | |
116 | private static final int NOTHING = 2; | |
117 | ||
118 | /** | |
119 | * The class writer to which this method must be added. | |
120 | */ | |
121 | final ClassWriter cw; | |
122 | ||
123 | /** | |
124 | * Access flags of this method. | |
125 | */ | |
126 | private int access; | |
127 | ||
128 | /** | |
129 | * The index of the constant pool item that contains the name of this | |
130 | * method. | |
131 | */ | |
132 | private final int name; | |
133 | ||
134 | /** | |
135 | * The index of the constant pool item that contains the descriptor of this | |
136 | * method. | |
137 | */ | |
138 | private final int desc; | |
139 | ||
140 | /** | |
141 | * The descriptor of this method. | |
142 | */ | |
143 | private final String descriptor; | |
144 | ||
145 | /** | |
146 | * The signature of this method. | |
147 | */ | |
148 | String signature; | |
149 | ||
150 | /** | |
151 | * If not zero, indicates that the code of this method must be copied from | |
152 | * the ClassReader associated to this writer in <code>cw.cr</code>. More | |
153 | * precisely, this field gives the index of the first byte to copied from | |
154 | * <code>cw.cr.b</code>. | |
155 | */ | |
156 | int classReaderOffset; | |
157 | ||
158 | /** | |
159 | * If not zero, indicates that the code of this method must be copied from | |
160 | * the ClassReader associated to this writer in <code>cw.cr</code>. More | |
161 | * precisely, this field gives the number of bytes to copied from | |
162 | * <code>cw.cr.b</code>. | |
163 | */ | |
164 | int classReaderLength; | |
165 | ||
166 | /** | |
167 | * Number of exceptions that can be thrown by this method. | |
168 | */ | |
169 | int exceptionCount; | |
170 | ||
171 | /** | |
172 | * The exceptions that can be thrown by this method. More precisely, this | |
173 | * array contains the indexes of the constant pool items that contain the | |
174 | * internal names of these exception classes. | |
175 | */ | |
176 | int[] exceptions; | |
177 | ||
178 | /** | |
179 | * The annotation default attribute of this method. May be <tt>null</tt>. | |
180 | */ | |
181 | private ByteVector annd; | |
182 | ||
183 | /** | |
184 | * The runtime visible annotations of this method. May be <tt>null</tt>. | |
185 | */ | |
186 | private AnnotationWriter anns; | |
187 | ||
188 | /** | |
189 | * The runtime invisible annotations of this method. May be <tt>null</tt>. | |
190 | */ | |
191 | private AnnotationWriter ianns; | |
192 | ||
193 | /** | |
194 | * The runtime visible parameter annotations of this method. May be | |
195 | * <tt>null</tt>. | |
196 | */ | |
197 | private AnnotationWriter[] panns; | |
198 | ||
199 | /** | |
200 | * The runtime invisible parameter annotations of this method. May be | |
201 | * <tt>null</tt>. | |
202 | */ | |
203 | private AnnotationWriter[] ipanns; | |
204 | ||
205 | /** | |
206 | * The number of synthetic parameters of this method. | |
207 | */ | |
208 | private int synthetics; | |
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 local variables in the current stack map frame. | |
232 | */ | |
233 | private int currentLocals; | |
234 | ||
235 | /** | |
236 | * Number of stack map frames in the StackMapTable attribute. | |
237 | */ | |
238 | private int frameCount; | |
239 | ||
240 | /** | |
241 | * The StackMapTable attribute. | |
242 | */ | |
243 | private ByteVector stackMap; | |
244 | ||
245 | /** | |
246 | * The offset of the last frame that was written in the StackMapTable | |
247 | * attribute. | |
248 | */ | |
249 | private int previousFrameOffset; | |
250 | ||
251 | /** | |
252 | * The last frame that was written in the StackMapTable attribute. | |
253 | * | |
254 | * @see #frame | |
255 | */ | |
256 | private int[] previousFrame; | |
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 | * The number of subroutines in this method. | |
326 | */ | |
327 | private int subroutines; | |
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 final 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 MethodVisitor#visitLabel}, and starting with the first basic | |
355 | * block. | |
356 | */ | |
357 | private Label labels; | |
358 | ||
359 | /** | |
360 | * The previous basic block. | |
361 | */ | |
362 | private Label previousBlock; | |
363 | ||
364 | /** | |
365 | * The current basic block. | |
366 | */ | |
367 | private Label currentBlock; | |
368 | ||
369 | /** | |
370 | * The (relative) stack size after the last visited instruction. This size | |
371 | * is relative to the beginning of the current basic block, i.e., the true | |
372 | * stack size after the last visited instruction is equal to the | |
373 | * {@link Label#inputStackTop beginStackSize} of the current basic block | |
374 | * plus <tt>stackSize</tt>. | |
375 | */ | |
376 | private int stackSize; | |
377 | ||
378 | /** | |
379 | * The (relative) maximum stack size after the last visited instruction. | |
380 | * This size is relative to the beginning of the current basic block, i.e., | |
381 | * the true maximum stack size after the last visited instruction is equal | |
382 | * to the {@link Label#inputStackTop beginStackSize} of the current basic | |
383 | * block plus <tt>stackSize</tt>. | |
384 | */ | |
385 | private int maxStackSize; | |
386 | ||
387 | // ------------------------------------------------------------------------ | |
388 | // Constructor | |
389 | // ------------------------------------------------------------------------ | |
390 | ||
391 | /** | |
392 | * Constructs a new {@link MethodWriter}. | |
393 | * | |
394 | * @param cw | |
395 | * the class writer in which the method must be added. | |
396 | * @param access | |
397 | * the method's access flags (see {@link Opcodes}). | |
398 | * @param name | |
399 | * the method's name. | |
400 | * @param desc | |
401 | * the method's descriptor (see {@link Type}). | |
402 | * @param signature | |
403 | * the method's signature. May be <tt>null</tt>. | |
404 | * @param exceptions | |
405 | * the internal names of the method's exceptions. May be | |
406 | * <tt>null</tt>. | |
407 | * @param computeMaxs | |
408 | * <tt>true</tt> if the maximum stack size and number of local | |
409 | * variables must be automatically computed. | |
410 | * @param computeFrames | |
411 | * <tt>true</tt> if the stack map tables must be recomputed from | |
412 | * scratch. | |
413 | */ | |
414 | MethodWriter(final ClassWriter cw, final int access, final String name, | |
415 | final String desc, final String signature, | |
416 | final String[] exceptions, final boolean computeMaxs, | |
417 | final boolean computeFrames) { | |
418 | super(Opcodes.ASM4); | |
419 | if (cw.firstMethod == null) { | |
420 | cw.firstMethod = this; | |
421 | } else { | |
422 | cw.lastMethod.mv = this; | |
423 | } | |
424 | cw.lastMethod = this; | |
425 | this.cw = cw; | |
426 | this.access = access; | |
427 | if ("<init>".equals(name)) { | |
428 | this.access |= ACC_CONSTRUCTOR; | |
429 | } | |
430 | this.name = cw.newUTF8(name); | |
431 | this.desc = cw.newUTF8(desc); | |
432 | this.descriptor = desc; | |
433 | if (ClassReader.SIGNATURES) { | |
434 | this.signature = signature; | |
435 | } | |
436 | if (exceptions != null && exceptions.length > 0) { | |
437 | exceptionCount = exceptions.length; | |
438 | this.exceptions = new int[exceptionCount]; | |
439 | for (int i = 0; i < exceptionCount; ++i) { | |
440 | this.exceptions[i] = cw.newClass(exceptions[i]); | |
441 | } | |
442 | } | |
443 | this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING); | |
444 | if (computeMaxs || computeFrames) { | |
445 | // updates maxLocals | |
446 | int size = Type.getArgumentsAndReturnSizes(descriptor) >> 2; | |
447 | if ((access & Opcodes.ACC_STATIC) != 0) { | |
448 | --size; | |
449 | } | |
450 | maxLocals = size; | |
451 | currentLocals = 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 abstract class | |
461 | // ------------------------------------------------------------------------ | |
462 | ||
463 | @Override | |
464 | public AnnotationVisitor visitAnnotationDefault() { | |
465 | if (!ClassReader.ANNOTATIONS) { | |
466 | return null; | |
467 | } | |
468 | annd = new ByteVector(); | |
469 | return new AnnotationWriter(cw, false, annd, null, 0); | |
470 | } | |
471 | ||
472 | @Override | |
473 | public AnnotationVisitor visitAnnotation(final String desc, | |
474 | final boolean visible) { | |
475 | if (!ClassReader.ANNOTATIONS) { | |
476 | return null; | |
477 | } | |
478 | ByteVector bv = new ByteVector(); | |
479 | // write type, and reserve space for values count | |
480 | bv.putShort(cw.newUTF8(desc)).putShort(0); | |
481 | AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); | |
482 | if (visible) { | |
483 | aw.next = anns; | |
484 | anns = aw; | |
485 | } else { | |
486 | aw.next = ianns; | |
487 | ianns = aw; | |
488 | } | |
489 | return aw; | |
490 | } | |
491 | ||
492 | @Override | |
493 | public AnnotationVisitor visitParameterAnnotation(final int parameter, | |
494 | final String desc, final boolean visible) { | |
495 | if (!ClassReader.ANNOTATIONS) { | |
496 | return null; | |
497 | } | |
498 | ByteVector bv = new ByteVector(); | |
499 | if ("Ljava/lang/Synthetic;".equals(desc)) { | |
500 | // workaround for a bug in javac with synthetic parameters | |
501 | // see ClassReader.readParameterAnnotations | |
502 | synthetics = Math.max(synthetics, parameter + 1); | |
503 | return new AnnotationWriter(cw, false, bv, null, 0); | |
504 | } | |
505 | // write type, and reserve space for values count | |
506 | bv.putShort(cw.newUTF8(desc)).putShort(0); | |
507 | AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2); | |
508 | if (visible) { | |
509 | if (panns == null) { | |
510 | panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; | |
511 | } | |
512 | aw.next = panns[parameter]; | |
513 | panns[parameter] = aw; | |
514 | } else { | |
515 | if (ipanns == null) { | |
516 | ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length]; | |
517 | } | |
518 | aw.next = ipanns[parameter]; | |
519 | ipanns[parameter] = aw; | |
520 | } | |
521 | return aw; | |
522 | } | |
523 | ||
524 | @Override | |
525 | public void visitAttribute(final Attribute attr) { | |
526 | if (attr.isCodeAttribute()) { | |
527 | attr.next = cattrs; | |
528 | cattrs = attr; | |
529 | } else { | |
530 | attr.next = attrs; | |
531 | attrs = attr; | |
532 | } | |
533 | } | |
534 | ||
535 | @Override | |
536 | public void visitCode() { | |
537 | } | |
538 | ||
539 | @Override | |
540 | public void visitFrame(final int type, final int nLocal, | |
541 | final Object[] local, final int nStack, final Object[] stack) { | |
542 | if (!ClassReader.FRAMES || compute == FRAMES) { | |
543 | return; | |
544 | } | |
545 | ||
546 | if (type == Opcodes.F_NEW) { | |
547 | if (previousFrame == null) { | |
548 | visitImplicitFirstFrame(); | |
549 | } | |
550 | currentLocals = nLocal; | |
551 | int frameIndex = startFrame(code.length, nLocal, nStack); | |
552 | for (int i = 0; i < nLocal; ++i) { | |
553 | if (local[i] instanceof String) { | |
554 | frame[frameIndex++] = Frame.OBJECT | |
555 | | cw.addType((String) local[i]); | |
556 | } else if (local[i] instanceof Integer) { | |
557 | frame[frameIndex++] = ((Integer) local[i]).intValue(); | |
558 | } else { | |
559 | frame[frameIndex++] = Frame.UNINITIALIZED | |
560 | | cw.addUninitializedType("", | |
561 | ((Label) local[i]).position); | |
562 | } | |
563 | } | |
564 | for (int i = 0; i < nStack; ++i) { | |
565 | if (stack[i] instanceof String) { | |
566 | frame[frameIndex++] = Frame.OBJECT | |
567 | | cw.addType((String) stack[i]); | |
568 | } else if (stack[i] instanceof Integer) { | |
569 | frame[frameIndex++] = ((Integer) stack[i]).intValue(); | |
570 | } else { | |
571 | frame[frameIndex++] = Frame.UNINITIALIZED | |
572 | | cw.addUninitializedType("", | |
573 | ((Label) stack[i]).position); | |
574 | } | |
575 | } | |
576 | endFrame(); | |
577 | } else { | |
578 | int delta; | |
579 | if (stackMap == null) { | |
580 | stackMap = new ByteVector(); | |
581 | delta = code.length; | |
582 | } else { | |
583 | delta = code.length - previousFrameOffset - 1; | |
584 | if (delta < 0) { | |
585 | if (type == Opcodes.F_SAME) { | |
586 | return; | |
587 | } else { | |
588 | throw new IllegalStateException(); | |
589 | } | |
590 | } | |
591 | } | |
592 | ||
593 | switch (type) { | |
594 | case Opcodes.F_FULL: | |
595 | currentLocals = nLocal; | |
596 | stackMap.putByte(FULL_FRAME).putShort(delta).putShort(nLocal); | |
597 | for (int i = 0; i < nLocal; ++i) { | |
598 | writeFrameType(local[i]); | |
599 | } | |
600 | stackMap.putShort(nStack); | |
601 | for (int i = 0; i < nStack; ++i) { | |
602 | writeFrameType(stack[i]); | |
603 | } | |
604 | break; | |
605 | case Opcodes.F_APPEND: | |
606 | currentLocals += nLocal; | |
607 | stackMap.putByte(SAME_FRAME_EXTENDED + nLocal).putShort(delta); | |
608 | for (int i = 0; i < nLocal; ++i) { | |
609 | writeFrameType(local[i]); | |
610 | } | |
611 | break; | |
612 | case Opcodes.F_CHOP: | |
613 | currentLocals -= nLocal; | |
614 | stackMap.putByte(SAME_FRAME_EXTENDED - nLocal).putShort(delta); | |
615 | break; | |
616 | case Opcodes.F_SAME: | |
617 | if (delta < 64) { | |
618 | stackMap.putByte(delta); | |
619 | } else { | |
620 | stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); | |
621 | } | |
622 | break; | |
623 | case Opcodes.F_SAME1: | |
624 | if (delta < 64) { | |
625 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); | |
626 | } else { | |
627 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED) | |
628 | .putShort(delta); | |
629 | } | |
630 | writeFrameType(stack[0]); | |
631 | break; | |
632 | } | |
633 | ||
634 | previousFrameOffset = code.length; | |
635 | ++frameCount; | |
636 | } | |
637 | ||
638 | maxStack = Math.max(maxStack, nStack); | |
639 | maxLocals = Math.max(maxLocals, currentLocals); | |
640 | } | |
641 | ||
642 | @Override | |
643 | public void visitInsn(final int opcode) { | |
644 | // adds the instruction to the bytecode of the method | |
645 | code.putByte(opcode); | |
646 | // update currentBlock | |
647 | // Label currentBlock = this.currentBlock; | |
648 | if (currentBlock != null) { | |
649 | if (compute == FRAMES) { | |
650 | currentBlock.frame.execute(opcode, 0, null, null); | |
651 | } else { | |
652 | // updates current and max stack sizes | |
653 | int size = stackSize + Frame.SIZE[opcode]; | |
654 | if (size > maxStackSize) { | |
655 | maxStackSize = size; | |
656 | } | |
657 | stackSize = size; | |
658 | } | |
659 | // if opcode == ATHROW or xRETURN, ends current block (no successor) | |
660 | if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) | |
661 | || opcode == Opcodes.ATHROW) { | |
662 | noSuccessor(); | |
663 | } | |
664 | } | |
665 | } | |
666 | ||
667 | @Override | |
668 | public void visitIntInsn(final int opcode, final int operand) { | |
669 | // Label currentBlock = this.currentBlock; | |
670 | if (currentBlock != null) { | |
671 | if (compute == FRAMES) { | |
672 | currentBlock.frame.execute(opcode, operand, null, null); | |
673 | } else if (opcode != Opcodes.NEWARRAY) { | |
674 | // updates current and max stack sizes only for NEWARRAY | |
675 | // (stack size variation = 0 for BIPUSH or SIPUSH) | |
676 | int size = stackSize + 1; | |
677 | if (size > maxStackSize) { | |
678 | maxStackSize = size; | |
679 | } | |
680 | stackSize = size; | |
681 | } | |
682 | } | |
683 | // adds the instruction to the bytecode of the method | |
684 | if (opcode == Opcodes.SIPUSH) { | |
685 | code.put12(opcode, operand); | |
686 | } else { // BIPUSH or NEWARRAY | |
687 | code.put11(opcode, operand); | |
688 | } | |
689 | } | |
690 | ||
691 | @Override | |
692 | public void visitVarInsn(final int opcode, final int var) { | |
693 | // Label currentBlock = this.currentBlock; | |
694 | if (currentBlock != null) { | |
695 | if (compute == FRAMES) { | |
696 | currentBlock.frame.execute(opcode, var, null, null); | |
697 | } else { | |
698 | // updates current and max stack sizes | |
699 | if (opcode == Opcodes.RET) { | |
700 | // no stack change, but end of current block (no successor) | |
701 | currentBlock.status |= Label.RET; | |
702 | // save 'stackSize' here for future use | |
703 | // (see {@link #findSubroutineSuccessors}) | |
704 | currentBlock.inputStackTop = stackSize; | |
705 | noSuccessor(); | |
706 | } else { // xLOAD or xSTORE | |
707 | int size = stackSize + Frame.SIZE[opcode]; | |
708 | if (size > maxStackSize) { | |
709 | maxStackSize = size; | |
710 | } | |
711 | stackSize = size; | |
712 | } | |
713 | } | |
714 | } | |
715 | if (compute != NOTHING) { | |
716 | // updates max locals | |
717 | int n; | |
718 | if (opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD | |
719 | || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE) { | |
720 | n = var + 2; | |
721 | } else { | |
722 | n = var + 1; | |
723 | } | |
724 | if (n > maxLocals) { | |
725 | maxLocals = n; | |
726 | } | |
727 | } | |
728 | // adds the instruction to the bytecode of the method | |
729 | if (var < 4 && opcode != Opcodes.RET) { | |
730 | int opt; | |
731 | if (opcode < Opcodes.ISTORE) { | |
732 | /* ILOAD_0 */ | |
733 | opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var; | |
734 | } else { | |
735 | /* ISTORE_0 */ | |
736 | opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var; | |
737 | } | |
738 | code.putByte(opt); | |
739 | } else if (var >= 256) { | |
740 | code.putByte(196 /* WIDE */).put12(opcode, var); | |
741 | } else { | |
742 | code.put11(opcode, var); | |
743 | } | |
744 | if (opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0) { | |
745 | visitLabel(new Label()); | |
746 | } | |
747 | } | |
748 | ||
749 | @Override | |
750 | public void visitTypeInsn(final int opcode, final String type) { | |
751 | Item i = cw.newClassItem(type); | |
752 | // Label currentBlock = this.currentBlock; | |
753 | if (currentBlock != null) { | |
754 | if (compute == FRAMES) { | |
755 | currentBlock.frame.execute(opcode, code.length, cw, i); | |
756 | } else if (opcode == Opcodes.NEW) { | |
757 | // updates current and max stack sizes only if opcode == NEW | |
758 | // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF) | |
759 | int size = stackSize + 1; | |
760 | if (size > maxStackSize) { | |
761 | maxStackSize = size; | |
762 | } | |
763 | stackSize = size; | |
764 | } | |
765 | } | |
766 | // adds the instruction to the bytecode of the method | |
767 | code.put12(opcode, i.index); | |
768 | } | |
769 | ||
770 | @Override | |
771 | public void visitFieldInsn(final int opcode, final String owner, | |
772 | final String name, final String desc) { | |
773 | Item i = cw.newFieldItem(owner, name, desc); | |
774 | // Label currentBlock = this.currentBlock; | |
775 | if (currentBlock != null) { | |
776 | if (compute == FRAMES) { | |
777 | currentBlock.frame.execute(opcode, 0, cw, i); | |
778 | } else { | |
779 | int size; | |
780 | // computes the stack size variation | |
781 | char c = desc.charAt(0); | |
782 | switch (opcode) { | |
783 | case Opcodes.GETSTATIC: | |
784 | size = stackSize + (c == 'D' || c == 'J' ? 2 : 1); | |
785 | break; | |
786 | case Opcodes.PUTSTATIC: | |
787 | size = stackSize + (c == 'D' || c == 'J' ? -2 : -1); | |
788 | break; | |
789 | case Opcodes.GETFIELD: | |
790 | size = stackSize + (c == 'D' || c == 'J' ? 1 : 0); | |
791 | break; | |
792 | // case Constants.PUTFIELD: | |
793 | default: | |
794 | size = stackSize + (c == 'D' || c == 'J' ? -3 : -2); | |
795 | break; | |
796 | } | |
797 | // updates current and max stack sizes | |
798 | if (size > maxStackSize) { | |
799 | maxStackSize = size; | |
800 | } | |
801 | stackSize = size; | |
802 | } | |
803 | } | |
804 | // adds the instruction to the bytecode of the method | |
805 | code.put12(opcode, i.index); | |
806 | } | |
807 | ||
808 | @Override | |
809 | public void visitMethodInsn(final int opcode, final String owner, | |
810 | final String name, final String desc) { | |
811 | boolean itf = opcode == Opcodes.INVOKEINTERFACE; | |
812 | Item i = cw.newMethodItem(owner, name, desc, itf); | |
813 | int argSize = i.intVal; | |
814 | // Label currentBlock = this.currentBlock; | |
815 | if (currentBlock != null) { | |
816 | if (compute == FRAMES) { | |
817 | currentBlock.frame.execute(opcode, 0, cw, i); | |
818 | } else { | |
819 | /* | |
820 | * computes the stack size variation. In order not to recompute | |
821 | * several times this variation for the same Item, we use the | |
822 | * intVal field of this item to store this variation, once it | |
823 | * has been computed. More precisely this intVal field stores | |
824 | * the sizes of the arguments and of the return value | |
825 | * corresponding to desc. | |
826 | */ | |
827 | if (argSize == 0) { | |
828 | // the above sizes have not been computed yet, | |
829 | // so we compute them... | |
830 | argSize = Type.getArgumentsAndReturnSizes(desc); | |
831 | // ... and we save them in order | |
832 | // not to recompute them in the future | |
833 | i.intVal = argSize; | |
834 | } | |
835 | int size; | |
836 | if (opcode == Opcodes.INVOKESTATIC) { | |
837 | size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; | |
838 | } else { | |
839 | size = stackSize - (argSize >> 2) + (argSize & 0x03); | |
840 | } | |
841 | // updates current and max stack sizes | |
842 | if (size > maxStackSize) { | |
843 | maxStackSize = size; | |
844 | } | |
845 | stackSize = size; | |
846 | } | |
847 | } | |
848 | // adds the instruction to the bytecode of the method | |
849 | if (itf) { | |
850 | if (argSize == 0) { | |
851 | argSize = Type.getArgumentsAndReturnSizes(desc); | |
852 | i.intVal = argSize; | |
853 | } | |
854 | code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0); | |
855 | } else { | |
856 | code.put12(opcode, i.index); | |
857 | } | |
858 | } | |
859 | ||
860 | @Override | |
861 | public void visitInvokeDynamicInsn(final String name, final String desc, | |
862 | final Handle bsm, final Object... bsmArgs) { | |
863 | Item i = cw.newInvokeDynamicItem(name, desc, bsm, bsmArgs); | |
864 | int argSize = i.intVal; | |
865 | // Label currentBlock = this.currentBlock; | |
866 | if (currentBlock != null) { | |
867 | if (compute == FRAMES) { | |
868 | currentBlock.frame.execute(Opcodes.INVOKEDYNAMIC, 0, cw, i); | |
869 | } else { | |
870 | /* | |
871 | * computes the stack size variation. In order not to recompute | |
872 | * several times this variation for the same Item, we use the | |
873 | * intVal field of this item to store this variation, once it | |
874 | * has been computed. More precisely this intVal field stores | |
875 | * the sizes of the arguments and of the return value | |
876 | * corresponding to desc. | |
877 | */ | |
878 | if (argSize == 0) { | |
879 | // the above sizes have not been computed yet, | |
880 | // so we compute them... | |
881 | argSize = Type.getArgumentsAndReturnSizes(desc); | |
882 | // ... and we save them in order | |
883 | // not to recompute them in the future | |
884 | i.intVal = argSize; | |
885 | } | |
886 | int size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1; | |
887 | ||
888 | // updates current and max stack sizes | |
889 | if (size > maxStackSize) { | |
890 | maxStackSize = size; | |
891 | } | |
892 | stackSize = size; | |
893 | } | |
894 | } | |
895 | // adds the instruction to the bytecode of the method | |
896 | code.put12(Opcodes.INVOKEDYNAMIC, i.index); | |
897 | code.putShort(0); | |
898 | } | |
899 | ||
900 | @Override | |
901 | public void visitJumpInsn(final int opcode, final Label label) { | |
902 | Label nextInsn = null; | |
903 | // Label currentBlock = this.currentBlock; | |
904 | if (currentBlock != null) { | |
905 | if (compute == FRAMES) { | |
906 | currentBlock.frame.execute(opcode, 0, null, null); | |
907 | // 'label' is the target of a jump instruction | |
908 | label.getFirst().status |= Label.TARGET; | |
909 | // adds 'label' as a successor of this basic block | |
910 | addSuccessor(Edge.NORMAL, label); | |
911 | if (opcode != Opcodes.GOTO) { | |
912 | // creates a Label for the next basic block | |
913 | nextInsn = new Label(); | |
914 | } | |
915 | } else { | |
916 | if (opcode == Opcodes.JSR) { | |
917 | if ((label.status & Label.SUBROUTINE) == 0) { | |
918 | label.status |= Label.SUBROUTINE; | |
919 | ++subroutines; | |
920 | } | |
921 | currentBlock.status |= Label.JSR; | |
922 | addSuccessor(stackSize + 1, label); | |
923 | // creates a Label for the next basic block | |
924 | nextInsn = new Label(); | |
925 | /* | |
926 | * note that, by construction in this method, a JSR block | |
927 | * has at least two successors in the control flow graph: | |
928 | * the first one leads the next instruction after the JSR, | |
929 | * while the second one leads to the JSR target. | |
930 | */ | |
931 | } else { | |
932 | // updates current stack size (max stack size unchanged | |
933 | // because stack size variation always negative in this | |
934 | // case) | |
935 | stackSize += Frame.SIZE[opcode]; | |
936 | addSuccessor(stackSize, label); | |
937 | } | |
938 | } | |
939 | } | |
940 | // adds the instruction to the bytecode of the method | |
941 | if ((label.status & Label.RESOLVED) != 0 | |
942 | && label.position - code.length < Short.MIN_VALUE) { | |
943 | /* | |
944 | * case of a backward jump with an offset < -32768. In this case we | |
945 | * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx | |
946 | * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the | |
947 | * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'> | |
948 | * designates the instruction just after the GOTO_W. | |
949 | */ | |
950 | if (opcode == Opcodes.GOTO) { | |
951 | code.putByte(200); // GOTO_W | |
952 | } else if (opcode == Opcodes.JSR) { | |
953 | code.putByte(201); // JSR_W | |
954 | } else { | |
955 | // if the IF instruction is transformed into IFNOT GOTO_W the | |
956 | // next instruction becomes the target of the IFNOT instruction | |
957 | if (nextInsn != null) { | |
958 | nextInsn.status |= Label.TARGET; | |
959 | } | |
960 | code.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 | |
961 | : opcode ^ 1); | |
962 | code.putShort(8); // jump offset | |
963 | code.putByte(200); // GOTO_W | |
964 | } | |
965 | label.put(this, code, code.length - 1, true); | |
966 | } else { | |
967 | /* | |
968 | * case of a backward jump with an offset >= -32768, or of a forward | |
969 | * jump with, of course, an unknown offset. In these cases we store | |
970 | * the offset in 2 bytes (which will be increased in | |
971 | * resizeInstructions, if needed). | |
972 | */ | |
973 | code.putByte(opcode); | |
974 | label.put(this, code, code.length - 1, false); | |
975 | } | |
976 | if (currentBlock != null) { | |
977 | if (nextInsn != null) { | |
978 | // if the jump instruction is not a GOTO, the next instruction | |
979 | // is also a successor of this instruction. Calling visitLabel | |
980 | // adds the label of this next instruction as a successor of the | |
981 | // current block, and starts a new basic block | |
982 | visitLabel(nextInsn); | |
983 | } | |
984 | if (opcode == Opcodes.GOTO) { | |
985 | noSuccessor(); | |
986 | } | |
987 | } | |
988 | } | |
989 | ||
990 | @Override | |
991 | public void visitLabel(final Label label) { | |
992 | // resolves previous forward references to label, if any | |
993 | resize |= label.resolve(this, code.length, code.data); | |
994 | // updates currentBlock | |
995 | if ((label.status & Label.DEBUG) != 0) { | |
996 | return; | |
997 | } | |
998 | if (compute == FRAMES) { | |
999 | if (currentBlock != null) { | |
1000 | if (label.position == currentBlock.position) { | |
1001 | // successive labels, do not start a new basic block | |
1002 | currentBlock.status |= (label.status & Label.TARGET); | |
1003 | label.frame = currentBlock.frame; | |
1004 | return; | |
1005 | } | |
1006 | // ends current block (with one new successor) | |
1007 | addSuccessor(Edge.NORMAL, label); | |
1008 | } | |
1009 | // begins a new current block | |
1010 | currentBlock = label; | |
1011 | if (label.frame == null) { | |
1012 | label.frame = new Frame(); | |
1013 | label.frame.owner = label; | |
1014 | } | |
1015 | // updates the basic block list | |
1016 | if (previousBlock != null) { | |
1017 | if (label.position == previousBlock.position) { | |
1018 | previousBlock.status |= (label.status & Label.TARGET); | |
1019 | label.frame = previousBlock.frame; | |
1020 | currentBlock = previousBlock; | |
1021 | return; | |
1022 | } | |
1023 | previousBlock.successor = label; | |
1024 | } | |
1025 | previousBlock = label; | |
1026 | } else if (compute == MAXS) { | |
1027 | if (currentBlock != null) { | |
1028 | // ends current block (with one new successor) | |
1029 | currentBlock.outputStackMax = maxStackSize; | |
1030 | addSuccessor(stackSize, label); | |
1031 | } | |
1032 | // begins a new current block | |
1033 | currentBlock = label; | |
1034 | // resets the relative current and max stack sizes | |
1035 | stackSize = 0; | |
1036 | maxStackSize = 0; | |
1037 | // updates the basic block list | |
1038 | if (previousBlock != null) { | |
1039 | previousBlock.successor = label; | |
1040 | } | |
1041 | previousBlock = label; | |
1042 | } | |
1043 | } | |
1044 | ||
1045 | @Override | |
1046 | public void visitLdcInsn(final Object cst) { | |
1047 | Item i = cw.newConstItem(cst); | |
1048 | // Label currentBlock = this.currentBlock; | |
1049 | if (currentBlock != null) { | |
1050 | if (compute == FRAMES) { | |
1051 | currentBlock.frame.execute(Opcodes.LDC, 0, cw, i); | |
1052 | } else { | |
1053 | int size; | |
1054 | // computes the stack size variation | |
1055 | if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { | |
1056 | size = stackSize + 2; | |
1057 | } else { | |
1058 | size = stackSize + 1; | |
1059 | } | |
1060 | // updates current and max stack sizes | |
1061 | if (size > maxStackSize) { | |
1062 | maxStackSize = size; | |
1063 | } | |
1064 | stackSize = size; | |
1065 | } | |
1066 | } | |
1067 | // adds the instruction to the bytecode of the method | |
1068 | int index = i.index; | |
1069 | if (i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE) { | |
1070 | code.put12(20 /* LDC2_W */, index); | |
1071 | } else if (index >= 256) { | |
1072 | code.put12(19 /* LDC_W */, index); | |
1073 | } else { | |
1074 | code.put11(Opcodes.LDC, index); | |
1075 | } | |
1076 | } | |
1077 | ||
1078 | @Override | |
1079 | public void visitIincInsn(final int var, final int increment) { | |
1080 | if (currentBlock != null) { | |
1081 | if (compute == FRAMES) { | |
1082 | currentBlock.frame.execute(Opcodes.IINC, var, null, null); | |
1083 | } | |
1084 | } | |
1085 | if (compute != NOTHING) { | |
1086 | // updates max locals | |
1087 | int n = var + 1; | |
1088 | if (n > maxLocals) { | |
1089 | maxLocals = n; | |
1090 | } | |
1091 | } | |
1092 | // adds the instruction to the bytecode of the method | |
1093 | if ((var > 255) || (increment > 127) || (increment < -128)) { | |
1094 | code.putByte(196 /* WIDE */).put12(Opcodes.IINC, var) | |
1095 | .putShort(increment); | |
1096 | } else { | |
1097 | code.putByte(Opcodes.IINC).put11(var, increment); | |
1098 | } | |
1099 | } | |
1100 | ||
1101 | @Override | |
1102 | public void visitTableSwitchInsn(final int min, final int max, | |
1103 | final Label dflt, final Label... labels) { | |
1104 | // adds the instruction to the bytecode of the method | |
1105 | int source = code.length; | |
1106 | code.putByte(Opcodes.TABLESWITCH); | |
1107 | code.putByteArray(null, 0, (4 - code.length % 4) % 4); | |
1108 | dflt.put(this, code, source, true); | |
1109 | code.putInt(min).putInt(max); | |
1110 | for (int i = 0; i < labels.length; ++i) { | |
1111 | labels[i].put(this, code, source, true); | |
1112 | } | |
1113 | // updates currentBlock | |
1114 | visitSwitchInsn(dflt, labels); | |
1115 | } | |
1116 | ||
1117 | @Override | |
1118 | public void visitLookupSwitchInsn(final Label dflt, final int[] keys, | |
1119 | final Label[] labels) { | |
1120 | // adds the instruction to the bytecode of the method | |
1121 | int source = code.length; | |
1122 | code.putByte(Opcodes.LOOKUPSWITCH); | |
1123 | code.putByteArray(null, 0, (4 - code.length % 4) % 4); | |
1124 | dflt.put(this, code, source, true); | |
1125 | code.putInt(labels.length); | |
1126 | for (int i = 0; i < labels.length; ++i) { | |
1127 | code.putInt(keys[i]); | |
1128 | labels[i].put(this, code, source, true); | |
1129 | } | |
1130 | // updates currentBlock | |
1131 | visitSwitchInsn(dflt, labels); | |
1132 | } | |
1133 | ||
1134 | private void visitSwitchInsn(final Label dflt, final Label[] labels) { | |
1135 | // Label currentBlock = this.currentBlock; | |
1136 | if (currentBlock != null) { | |
1137 | if (compute == FRAMES) { | |
1138 | currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null); | |
1139 | // adds current block successors | |
1140 | addSuccessor(Edge.NORMAL, dflt); | |
1141 | dflt.getFirst().status |= Label.TARGET; | |
1142 | for (int i = 0; i < labels.length; ++i) { | |
1143 | addSuccessor(Edge.NORMAL, labels[i]); | |
1144 | labels[i].getFirst().status |= Label.TARGET; | |
1145 | } | |
1146 | } else { | |
1147 | // updates current stack size (max stack size unchanged) | |
1148 | --stackSize; | |
1149 | // adds current block successors | |
1150 | addSuccessor(stackSize, dflt); | |
1151 | for (int i = 0; i < labels.length; ++i) { | |
1152 | addSuccessor(stackSize, labels[i]); | |
1153 | } | |
1154 | } | |
1155 | // ends current block | |
1156 | noSuccessor(); | |
1157 | } | |
1158 | } | |
1159 | ||
1160 | @Override | |
1161 | public void visitMultiANewArrayInsn(final String desc, final int dims) { | |
1162 | Item i = cw.newClassItem(desc); | |
1163 | // Label currentBlock = this.currentBlock; | |
1164 | if (currentBlock != null) { | |
1165 | if (compute == FRAMES) { | |
1166 | currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i); | |
1167 | } else { | |
1168 | // updates current stack size (max stack size unchanged because | |
1169 | // stack size variation always negative or null) | |
1170 | stackSize += 1 - dims; | |
1171 | } | |
1172 | } | |
1173 | // adds the instruction to the bytecode of the method | |
1174 | code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims); | |
1175 | } | |
1176 | ||
1177 | @Override | |
1178 | public void visitTryCatchBlock(final Label start, final Label end, | |
1179 | final Label handler, final String type) { | |
1180 | ++handlerCount; | |
1181 | Handler h = new Handler(); | |
1182 | h.start = start; | |
1183 | h.end = end; | |
1184 | h.handler = handler; | |
1185 | h.desc = type; | |
1186 | h.type = type != null ? cw.newClass(type) : 0; | |
1187 | if (lastHandler == null) { | |
1188 | firstHandler = h; | |
1189 | } else { | |
1190 | lastHandler.next = h; | |
1191 | } | |
1192 | lastHandler = h; | |
1193 | } | |
1194 | ||
1195 | @Override | |
1196 | public void visitLocalVariable(final String name, final String desc, | |
1197 | final String signature, final Label start, final Label end, | |
1198 | final int index) { | |
1199 | if (signature != null) { | |
1200 | if (localVarType == null) { | |
1201 | localVarType = new ByteVector(); | |
1202 | } | |
1203 | ++localVarTypeCount; | |
1204 | localVarType.putShort(start.position) | |
1205 | .putShort(end.position - start.position) | |
1206 | .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(signature)) | |
1207 | .putShort(index); | |
1208 | } | |
1209 | if (localVar == null) { | |
1210 | localVar = new ByteVector(); | |
1211 | } | |
1212 | ++localVarCount; | |
1213 | localVar.putShort(start.position) | |
1214 | .putShort(end.position - start.position) | |
1215 | .putShort(cw.newUTF8(name)).putShort(cw.newUTF8(desc)) | |
1216 | .putShort(index); | |
1217 | if (compute != NOTHING) { | |
1218 | // updates max locals | |
1219 | char c = desc.charAt(0); | |
1220 | int n = index + (c == 'J' || c == 'D' ? 2 : 1); | |
1221 | if (n > maxLocals) { | |
1222 | maxLocals = n; | |
1223 | } | |
1224 | } | |
1225 | } | |
1226 | ||
1227 | @Override | |
1228 | public void visitLineNumber(final int line, final Label start) { | |
1229 | if (lineNumber == null) { | |
1230 | lineNumber = new ByteVector(); | |
1231 | } | |
1232 | ++lineNumberCount; | |
1233 | lineNumber.putShort(start.position); | |
1234 | lineNumber.putShort(line); | |
1235 | } | |
1236 | ||
1237 | @Override | |
1238 | public void visitMaxs(final int maxStack, final int maxLocals) { | |
1239 | if (ClassReader.FRAMES && compute == FRAMES) { | |
1240 | // completes the control flow graph with exception handler blocks | |
1241 | Handler handler = firstHandler; | |
1242 | while (handler != null) { | |
1243 | Label l = handler.start.getFirst(); | |
1244 | Label h = handler.handler.getFirst(); | |
1245 | Label e = handler.end.getFirst(); | |
1246 | // computes the kind of the edges to 'h' | |
1247 | String t = handler.desc == null ? "java/lang/Throwable" | |
1248 | : handler.desc; | |
1249 | int kind = Frame.OBJECT | cw.addType(t); | |
1250 | // h is an exception handler | |
1251 | h.status |= Label.TARGET; | |
1252 | // adds 'h' as a successor of labels between 'start' and 'end' | |
1253 | while (l != e) { | |
1254 | // creates an edge to 'h' | |
1255 | Edge b = new Edge(); | |
1256 | b.info = kind; | |
1257 | b.successor = h; | |
1258 | // adds it to the successors of 'l' | |
1259 | b.next = l.successors; | |
1260 | l.successors = b; | |
1261 | // goes to the next label | |
1262 | l = l.successor; | |
1263 | } | |
1264 | handler = handler.next; | |
1265 | } | |
1266 | ||
1267 | // creates and visits the first (implicit) frame | |
1268 | Frame f = labels.frame; | |
1269 | Type[] args = Type.getArgumentTypes(descriptor); | |
1270 | f.initInputFrame(cw, access, args, this.maxLocals); | |
1271 | visitFrame(f); | |
1272 | ||
1273 | /* | |
1274 | * fix point algorithm: mark the first basic block as 'changed' | |
1275 | * (i.e. put it in the 'changed' list) and, while there are changed | |
1276 | * basic blocks, choose one, mark it as unchanged, and update its | |
1277 | * successors (which can be changed in the process). | |
1278 | */ | |
1279 | int max = 0; | |
1280 | Label changed = labels; | |
1281 | while (changed != null) { | |
1282 | // removes a basic block from the list of changed basic blocks | |
1283 | Label l = changed; | |
1284 | changed = changed.next; | |
1285 | l.next = null; | |
1286 | f = l.frame; | |
1287 | // a reachable jump target must be stored in the stack map | |
1288 | if ((l.status & Label.TARGET) != 0) { | |
1289 | l.status |= Label.STORE; | |
1290 | } | |
1291 | // all visited labels are reachable, by definition | |
1292 | l.status |= Label.REACHABLE; | |
1293 | // updates the (absolute) maximum stack size | |
1294 | int blockMax = f.inputStack.length + l.outputStackMax; | |
1295 | if (blockMax > max) { | |
1296 | max = blockMax; | |
1297 | } | |
1298 | // updates the successors of the current basic block | |
1299 | Edge e = l.successors; | |
1300 | while (e != null) { | |
1301 | Label n = e.successor.getFirst(); | |
1302 | boolean change = f.merge(cw, n.frame, e.info); | |
1303 | if (change && n.next == null) { | |
1304 | // if n has changed and is not already in the 'changed' | |
1305 | // list, adds it to this list | |
1306 | n.next = changed; | |
1307 | changed = n; | |
1308 | } | |
1309 | e = e.next; | |
1310 | } | |
1311 | } | |
1312 | ||
1313 | // visits all the frames that must be stored in the stack map | |
1314 | Label l = labels; | |
1315 | while (l != null) { | |
1316 | f = l.frame; | |
1317 | if ((l.status & Label.STORE) != 0) { | |
1318 | visitFrame(f); | |
1319 | } | |
1320 | if ((l.status & Label.REACHABLE) == 0) { | |
1321 | // finds start and end of dead basic block | |
1322 | Label k = l.successor; | |
1323 | int start = l.position; | |
1324 | int end = (k == null ? code.length : k.position) - 1; | |
1325 | // if non empty basic block | |
1326 | if (end >= start) { | |
1327 | max = Math.max(max, 1); | |
1328 | // replaces instructions with NOP ... NOP ATHROW | |
1329 | for (int i = start; i < end; ++i) { | |
1330 | code.data[i] = Opcodes.NOP; | |
1331 | } | |
1332 | code.data[end] = (byte) Opcodes.ATHROW; | |
1333 | // emits a frame for this unreachable block | |
1334 | int frameIndex = startFrame(start, 0, 1); | |
1335 | frame[frameIndex] = Frame.OBJECT | |
1336 | | cw.addType("java/lang/Throwable"); | |
1337 | endFrame(); | |
1338 | // removes the start-end range from the exception | |
1339 | // handlers | |
1340 | firstHandler = Handler.remove(firstHandler, l, k); | |
1341 | } | |
1342 | } | |
1343 | l = l.successor; | |
1344 | } | |
1345 | ||
1346 | handler = firstHandler; | |
1347 | handlerCount = 0; | |
1348 | while (handler != null) { | |
1349 | handlerCount += 1; | |
1350 | handler = handler.next; | |
1351 | } | |
1352 | ||
1353 | this.maxStack = max; | |
1354 | } else if (compute == MAXS) { | |
1355 | // completes the control flow graph with exception handler blocks | |
1356 | Handler handler = firstHandler; | |
1357 | while (handler != null) { | |
1358 | Label l = handler.start; | |
1359 | Label h = handler.handler; | |
1360 | Label e = handler.end; | |
1361 | // adds 'h' as a successor of labels between 'start' and 'end' | |
1362 | while (l != e) { | |
1363 | // creates an edge to 'h' | |
1364 | Edge b = new Edge(); | |
1365 | b.info = Edge.EXCEPTION; | |
1366 | b.successor = h; | |
1367 | // adds it to the successors of 'l' | |
1368 | if ((l.status & Label.JSR) == 0) { | |
1369 | b.next = l.successors; | |
1370 | l.successors = b; | |
1371 | } else { | |
1372 | // if l is a JSR block, adds b after the first two edges | |
1373 | // to preserve the hypothesis about JSR block successors | |
1374 | // order (see {@link #visitJumpInsn}) | |
1375 | b.next = l.successors.next.next; | |
1376 | l.successors.next.next = b; | |
1377 | } | |
1378 | // goes to the next label | |
1379 | l = l.successor; | |
1380 | } | |
1381 | handler = handler.next; | |
1382 | } | |
1383 | ||
1384 | if (subroutines > 0) { | |
1385 | // completes the control flow graph with the RET successors | |
1386 | /* | |
1387 | * first step: finds the subroutines. This step determines, for | |
1388 | * each basic block, to which subroutine(s) it belongs. | |
1389 | */ | |
1390 | // finds the basic blocks that belong to the "main" subroutine | |
1391 | int id = 0; | |
1392 | labels.visitSubroutine(null, 1, subroutines); | |
1393 | // finds the basic blocks that belong to the real subroutines | |
1394 | Label l = labels; | |
1395 | while (l != null) { | |
1396 | if ((l.status & Label.JSR) != 0) { | |
1397 | // the subroutine is defined by l's TARGET, not by l | |
1398 | Label subroutine = l.successors.next.successor; | |
1399 | // if this subroutine has not been visited yet... | |
1400 | if ((subroutine.status & Label.VISITED) == 0) { | |
1401 | // ...assigns it a new id and finds its basic blocks | |
1402 | id += 1; | |
1403 | subroutine.visitSubroutine(null, (id / 32L) << 32 | |
1404 | | (1L << (id % 32)), subroutines); | |
1405 | } | |
1406 | } | |
1407 | l = l.successor; | |
1408 | } | |
1409 | // second step: finds the successors of RET blocks | |
1410 | l = labels; | |
1411 | while (l != null) { | |
1412 | if ((l.status & Label.JSR) != 0) { | |
1413 | Label L = labels; | |
1414 | while (L != null) { | |
1415 | L.status &= ~Label.VISITED2; | |
1416 | L = L.successor; | |
1417 | } | |
1418 | // the subroutine is defined by l's TARGET, not by l | |
1419 | Label subroutine = l.successors.next.successor; | |
1420 | subroutine.visitSubroutine(l, 0, subroutines); | |
1421 | } | |
1422 | l = l.successor; | |
1423 | } | |
1424 | } | |
1425 | ||
1426 | /* | |
1427 | * control flow analysis algorithm: while the block stack is not | |
1428 | * empty, pop a block from this stack, update the max stack size, | |
1429 | * compute the true (non relative) begin stack size of the | |
1430 | * successors of this block, and push these successors onto the | |
1431 | * stack (unless they have already been pushed onto the stack). | |
1432 | * Note: by hypothesis, the {@link Label#inputStackTop} of the | |
1433 | * blocks in the block stack are the true (non relative) beginning | |
1434 | * stack sizes of these blocks. | |
1435 | */ | |
1436 | int max = 0; | |
1437 | Label stack = labels; | |
1438 | while (stack != null) { | |
1439 | // pops a block from the stack | |
1440 | Label l = stack; | |
1441 | stack = stack.next; | |
1442 | // computes the true (non relative) max stack size of this block | |
1443 | int start = l.inputStackTop; | |
1444 | int blockMax = start + l.outputStackMax; | |
1445 | // updates the global max stack size | |
1446 | if (blockMax > max) { | |
1447 | max = blockMax; | |
1448 | } | |
1449 | // analyzes the successors of the block | |
1450 | Edge b = l.successors; | |
1451 | if ((l.status & Label.JSR) != 0) { | |
1452 | // ignores the first edge of JSR blocks (virtual successor) | |
1453 | b = b.next; | |
1454 | } | |
1455 | while (b != null) { | |
1456 | l = b.successor; | |
1457 | // if this successor has not already been pushed... | |
1458 | if ((l.status & Label.PUSHED) == 0) { | |
1459 | // computes its true beginning stack size... | |
1460 | l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start | |
1461 | + b.info; | |
1462 | // ...and pushes it onto the stack | |
1463 | l.status |= Label.PUSHED; | |
1464 | l.next = stack; | |
1465 | stack = l; | |
1466 | } | |
1467 | b = b.next; | |
1468 | } | |
1469 | } | |
1470 | this.maxStack = Math.max(maxStack, max); | |
1471 | } else { | |
1472 | this.maxStack = maxStack; | |
1473 | this.maxLocals = maxLocals; | |
1474 | } | |
1475 | } | |
1476 | ||
1477 | @Override | |
1478 | public void visitEnd() { | |
1479 | } | |
1480 | ||
1481 | // ------------------------------------------------------------------------ | |
1482 | // Utility methods: control flow analysis algorithm | |
1483 | // ------------------------------------------------------------------------ | |
1484 | ||
1485 | /** | |
1486 | * Adds a successor to the {@link #currentBlock currentBlock} block. | |
1487 | * | |
1488 | * @param info | |
1489 | * information about the control flow edge to be added. | |
1490 | * @param successor | |
1491 | * the successor block to be added to the current block. | |
1492 | */ | |
1493 | private void addSuccessor(final int info, final Label successor) { | |
1494 | // creates and initializes an Edge object... | |
1495 | Edge b = new Edge(); | |
1496 | b.info = info; | |
1497 | b.successor = successor; | |
1498 | // ...and adds it to the successor list of the currentBlock block | |
1499 | b.next = currentBlock.successors; | |
1500 | currentBlock.successors = b; | |
1501 | } | |
1502 | ||
1503 | /** | |
1504 | * Ends the current basic block. This method must be used in the case where | |
1505 | * the current basic block does not have any successor. | |
1506 | */ | |
1507 | private void noSuccessor() { | |
1508 | if (compute == FRAMES) { | |
1509 | Label l = new Label(); | |
1510 | l.frame = new Frame(); | |
1511 | l.frame.owner = l; | |
1512 | l.resolve(this, code.length, code.data); | |
1513 | previousBlock.successor = l; | |
1514 | previousBlock = l; | |
1515 | } else { | |
1516 | currentBlock.outputStackMax = maxStackSize; | |
1517 | } | |
1518 | currentBlock = null; | |
1519 | } | |
1520 | ||
1521 | // ------------------------------------------------------------------------ | |
1522 | // Utility methods: stack map frames | |
1523 | // ------------------------------------------------------------------------ | |
1524 | ||
1525 | /** | |
1526 | * Visits a frame that has been computed from scratch. | |
1527 | * | |
1528 | * @param f | |
1529 | * the frame that must be visited. | |
1530 | */ | |
1531 | private void visitFrame(final Frame f) { | |
1532 | int i, t; | |
1533 | int nTop = 0; | |
1534 | int nLocal = 0; | |
1535 | int nStack = 0; | |
1536 | int[] locals = f.inputLocals; | |
1537 | int[] stacks = f.inputStack; | |
1538 | // computes the number of locals (ignores TOP types that are just after | |
1539 | // a LONG or a DOUBLE, and all trailing TOP types) | |
1540 | for (i = 0; i < locals.length; ++i) { | |
1541 | t = locals[i]; | |
1542 | if (t == Frame.TOP) { | |
1543 | ++nTop; | |
1544 | } else { | |
1545 | nLocal += nTop + 1; | |
1546 | nTop = 0; | |
1547 | } | |
1548 | if (t == Frame.LONG || t == Frame.DOUBLE) { | |
1549 | ++i; | |
1550 | } | |
1551 | } | |
1552 | // computes the stack size (ignores TOP types that are just after | |
1553 | // a LONG or a DOUBLE) | |
1554 | for (i = 0; i < stacks.length; ++i) { | |
1555 | t = stacks[i]; | |
1556 | ++nStack; | |
1557 | if (t == Frame.LONG || t == Frame.DOUBLE) { | |
1558 | ++i; | |
1559 | } | |
1560 | } | |
1561 | // visits the frame and its content | |
1562 | int frameIndex = startFrame(f.owner.position, nLocal, nStack); | |
1563 | for (i = 0; nLocal > 0; ++i, --nLocal) { | |
1564 | t = locals[i]; | |
1565 | frame[frameIndex++] = t; | |
1566 | if (t == Frame.LONG || t == Frame.DOUBLE) { | |
1567 | ++i; | |
1568 | } | |
1569 | } | |
1570 | for (i = 0; i < stacks.length; ++i) { | |
1571 | t = stacks[i]; | |
1572 | frame[frameIndex++] = t; | |
1573 | if (t == Frame.LONG || t == Frame.DOUBLE) { | |
1574 | ++i; | |
1575 | } | |
1576 | } | |
1577 | endFrame(); | |
1578 | } | |
1579 | ||
1580 | /** | |
1581 | * Visit the implicit first frame of this method. | |
1582 | */ | |
1583 | private void visitImplicitFirstFrame() { | |
1584 | // There can be at most descriptor.length() + 1 locals | |
1585 | int frameIndex = startFrame(0, descriptor.length() + 1, 0); | |
1586 | if ((access & Opcodes.ACC_STATIC) == 0) { | |
1587 | if ((access & ACC_CONSTRUCTOR) == 0) { | |
1588 | frame[frameIndex++] = Frame.OBJECT | cw.addType(cw.thisName); | |
1589 | } else { | |
1590 | frame[frameIndex++] = 6; // Opcodes.UNINITIALIZED_THIS; | |
1591 | } | |
1592 | } | |
1593 | int i = 1; | |
1594 | loop: while (true) { | |
1595 | int j = i; | |
1596 | switch (descriptor.charAt(i++)) { | |
1597 | case 'Z': | |
1598 | case 'C': | |
1599 | case 'B': | |
1600 | case 'S': | |
1601 | case 'I': | |
1602 | frame[frameIndex++] = 1; // Opcodes.INTEGER; | |
1603 | break; | |
1604 | case 'F': | |
1605 | frame[frameIndex++] = 2; // Opcodes.FLOAT; | |
1606 | break; | |
1607 | case 'J': | |
1608 | frame[frameIndex++] = 4; // Opcodes.LONG; | |
1609 | break; | |
1610 | case 'D': | |
1611 | frame[frameIndex++] = 3; // Opcodes.DOUBLE; | |
1612 | break; | |
1613 | case '[': | |
1614 | while (descriptor.charAt(i) == '[') { | |
1615 | ++i; | |
1616 | } | |
1617 | if (descriptor.charAt(i) == 'L') { | |
1618 | ++i; | |
1619 | while (descriptor.charAt(i) != ';') { | |
1620 | ++i; | |
1621 | } | |
1622 | } | |
1623 | frame[frameIndex++] = Frame.OBJECT | |
1624 | | cw.addType(descriptor.substring(j, ++i)); | |
1625 | break; | |
1626 | case 'L': | |
1627 | while (descriptor.charAt(i) != ';') { | |
1628 | ++i; | |
1629 | } | |
1630 | frame[frameIndex++] = Frame.OBJECT | |
1631 | | cw.addType(descriptor.substring(j + 1, i++)); | |
1632 | break; | |
1633 | default: | |
1634 | break loop; | |
1635 | } | |
1636 | } | |
1637 | frame[1] = frameIndex - 3; | |
1638 | endFrame(); | |
1639 | } | |
1640 | ||
1641 | /** | |
1642 | * Starts the visit of a stack map frame. | |
1643 | * | |
1644 | * @param offset | |
1645 | * the offset of the instruction to which the frame corresponds. | |
1646 | * @param nLocal | |
1647 | * the number of local variables in the frame. | |
1648 | * @param nStack | |
1649 | * the number of stack elements in the frame. | |
1650 | * @return the index of the next element to be written in this frame. | |
1651 | */ | |
1652 | private int startFrame(final int offset, final int nLocal, final int nStack) { | |
1653 | int n = 3 + nLocal + nStack; | |
1654 | if (frame == null || frame.length < n) { | |
1655 | frame = new int[n]; | |
1656 | } | |
1657 | frame[0] = offset; | |
1658 | frame[1] = nLocal; | |
1659 | frame[2] = nStack; | |
1660 | return 3; | |
1661 | } | |
1662 | ||
1663 | /** | |
1664 | * Checks if the visit of the current frame {@link #frame} is finished, and | |
1665 | * if yes, write it in the StackMapTable attribute. | |
1666 | */ | |
1667 | private void endFrame() { | |
1668 | if (previousFrame != null) { // do not write the first frame | |
1669 | if (stackMap == null) { | |
1670 | stackMap = new ByteVector(); | |
1671 | } | |
1672 | writeFrame(); | |
1673 | ++frameCount; | |
1674 | } | |
1675 | previousFrame = frame; | |
1676 | frame = null; | |
1677 | } | |
1678 | ||
1679 | /** | |
1680 | * Compress and writes the current frame {@link #frame} in the StackMapTable | |
1681 | * attribute. | |
1682 | */ | |
1683 | private void writeFrame() { | |
1684 | int clocalsSize = frame[1]; | |
1685 | int cstackSize = frame[2]; | |
1686 | if ((cw.version & 0xFFFF) < Opcodes.V1_6) { | |
1687 | stackMap.putShort(frame[0]).putShort(clocalsSize); | |
1688 | writeFrameTypes(3, 3 + clocalsSize); | |
1689 | stackMap.putShort(cstackSize); | |
1690 | writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); | |
1691 | return; | |
1692 | } | |
1693 | int localsSize = previousFrame[1]; | |
1694 | int type = FULL_FRAME; | |
1695 | int k = 0; | |
1696 | int delta; | |
1697 | if (frameCount == 0) { | |
1698 | delta = frame[0]; | |
1699 | } else { | |
1700 | delta = frame[0] - previousFrame[0] - 1; | |
1701 | } | |
1702 | if (cstackSize == 0) { | |
1703 | k = clocalsSize - localsSize; | |
1704 | switch (k) { | |
1705 | case -3: | |
1706 | case -2: | |
1707 | case -1: | |
1708 | type = CHOP_FRAME; | |
1709 | localsSize = clocalsSize; | |
1710 | break; | |
1711 | case 0: | |
1712 | type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED; | |
1713 | break; | |
1714 | case 1: | |
1715 | case 2: | |
1716 | case 3: | |
1717 | type = APPEND_FRAME; | |
1718 | break; | |
1719 | } | |
1720 | } else if (clocalsSize == localsSize && cstackSize == 1) { | |
1721 | type = delta < 63 ? SAME_LOCALS_1_STACK_ITEM_FRAME | |
1722 | : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED; | |
1723 | } | |
1724 | if (type != FULL_FRAME) { | |
1725 | // verify if locals are the same | |
1726 | int l = 3; | |
1727 | for (int j = 0; j < localsSize; j++) { | |
1728 | if (frame[l] != previousFrame[l]) { | |
1729 | type = FULL_FRAME; | |
1730 | break; | |
1731 | } | |
1732 | l++; | |
1733 | } | |
1734 | } | |
1735 | switch (type) { | |
1736 | case SAME_FRAME: | |
1737 | stackMap.putByte(delta); | |
1738 | break; | |
1739 | case SAME_LOCALS_1_STACK_ITEM_FRAME: | |
1740 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta); | |
1741 | writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); | |
1742 | break; | |
1743 | case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED: | |
1744 | stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED).putShort( | |
1745 | delta); | |
1746 | writeFrameTypes(3 + clocalsSize, 4 + clocalsSize); | |
1747 | break; | |
1748 | case SAME_FRAME_EXTENDED: | |
1749 | stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta); | |
1750 | break; | |
1751 | case CHOP_FRAME: | |
1752 | stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); | |
1753 | break; | |
1754 | case APPEND_FRAME: | |
1755 | stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta); | |
1756 | writeFrameTypes(3 + localsSize, 3 + clocalsSize); | |
1757 | break; | |
1758 | // case FULL_FRAME: | |
1759 | default: | |
1760 | stackMap.putByte(FULL_FRAME).putShort(delta).putShort(clocalsSize); | |
1761 | writeFrameTypes(3, 3 + clocalsSize); | |
1762 | stackMap.putShort(cstackSize); | |
1763 | writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize); | |
1764 | } | |
1765 | } | |
1766 | ||
1767 | /** | |
1768 | * Writes some types of the current frame {@link #frame} into the | |
1769 | * StackMapTableAttribute. This method converts types from the format used | |
1770 | * in {@link Label} to the format used in StackMapTable attributes. In | |
1771 | * particular, it converts type table indexes to constant pool indexes. | |
1772 | * | |
1773 | * @param start | |
1774 | * index of the first type in {@link #frame} to write. | |
1775 | * @param end | |
1776 | * index of last type in {@link #frame} to write (exclusive). | |
1777 | */ | |
1778 | private void writeFrameTypes(final int start, final int end) { | |
1779 | for (int i = start; i < end; ++i) { | |
1780 | int t = frame[i]; | |
1781 | int d = t & Frame.DIM; | |
1782 | if (d == 0) { | |
1783 | int v = t & Frame.BASE_VALUE; | |
1784 | switch (t & Frame.BASE_KIND) { | |
1785 | case Frame.OBJECT: | |
1786 | stackMap.putByte(7).putShort( | |
1787 | cw.newClass(cw.typeTable[v].strVal1)); | |
1788 | break; | |
1789 | case Frame.UNINITIALIZED: | |
1790 | stackMap.putByte(8).putShort(cw.typeTable[v].intVal); | |
1791 | break; | |
1792 | default: | |
1793 | stackMap.putByte(v); | |
1794 | } | |
1795 | } else { | |
1796 | StringBuffer buf = new StringBuffer(); | |
1797 | d >>= 28; | |
1798 | while (d-- > 0) { | |
1799 | buf.append('['); | |
1800 | } | |
1801 | if ((t & Frame.BASE_KIND) == Frame.OBJECT) { | |
1802 | buf.append('L'); | |
1803 | buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1); | |
1804 | buf.append(';'); | |
1805 | } else { | |
1806 | switch (t & 0xF) { | |
1807 | case 1: | |
1808 | buf.append('I'); | |
1809 | break; | |
1810 | case 2: | |
1811 | buf.append('F'); | |
1812 | break; | |
1813 | case 3: | |
1814 | buf.append('D'); | |
1815 | break; | |
1816 | case 9: | |
1817 | buf.append('Z'); | |
1818 | break; | |
1819 | case 10: | |
1820 | buf.append('B'); | |
1821 | break; | |
1822 | case 11: | |
1823 | buf.append('C'); | |
1824 | break; | |
1825 | case 12: | |
1826 | buf.append('S'); | |
1827 | break; | |
1828 | default: | |
1829 | buf.append('J'); | |
1830 | } | |
1831 | } | |
1832 | stackMap.putByte(7).putShort(cw.newClass(buf.toString())); | |
1833 | } | |
1834 | } | |
1835 | } | |
1836 | ||
1837 | private void writeFrameType(final Object type) { | |
1838 | if (type instanceof String) { | |
1839 | stackMap.putByte(7).putShort(cw.newClass((String) type)); | |
1840 | } else if (type instanceof Integer) { | |
1841 | stackMap.putByte(((Integer) type).intValue()); | |
1842 | } else { | |
1843 | stackMap.putByte(8).putShort(((Label) type).position); | |
1844 | } | |
1845 | } | |
1846 | ||
1847 | // ------------------------------------------------------------------------ | |
1848 | // Utility methods: dump bytecode array | |
1849 | // ------------------------------------------------------------------------ | |
1850 | ||
1851 | /** | |
1852 | * Returns the size of the bytecode of this method. | |
1853 | * | |
1854 | * @return the size of the bytecode of this method. | |
1855 | */ | |
1856 | final int getSize() { | |
1857 | if (classReaderOffset != 0) { | |
1858 | return 6 + classReaderLength; | |
1859 | } | |
1860 | if (resize) { | |
1861 | // replaces the temporary jump opcodes introduced by Label.resolve. | |
1862 | if (ClassReader.RESIZE) { | |
1863 | resizeInstructions(); | |
1864 | } else { | |
1865 | throw new RuntimeException("Method code too large!"); | |
1866 | } | |
1867 | } | |
1868 | int size = 8; | |
1869 | if (code.length > 0) { | |
1870 | if (code.length > 65536) { | |
1871 | throw new RuntimeException("Method code too large!"); | |
1872 | } | |
1873 | cw.newUTF8("Code"); | |
1874 | size += 18 + code.length + 8 * handlerCount; | |
1875 | if (localVar != null) { | |
1876 | cw.newUTF8("LocalVariableTable"); | |
1877 | size += 8 + localVar.length; | |
1878 | } | |
1879 | if (localVarType != null) { | |
1880 | cw.newUTF8("LocalVariableTypeTable"); | |
1881 | size += 8 + localVarType.length; | |
1882 | } | |
1883 | if (lineNumber != null) { | |
1884 | cw.newUTF8("LineNumberTable"); | |
1885 | size += 8 + lineNumber.length; | |
1886 | } | |
1887 | if (stackMap != null) { | |
1888 | boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; | |
1889 | cw.newUTF8(zip ? "StackMapTable" : "StackMap"); | |
1890 | size += 8 + stackMap.length; | |
1891 | } | |
1892 | if (cattrs != null) { | |
1893 | size += cattrs.getSize(cw, code.data, code.length, maxStack, | |
1894 | maxLocals); | |
1895 | } | |
1896 | } | |
1897 | if (exceptionCount > 0) { | |
1898 | cw.newUTF8("Exceptions"); | |
1899 | size += 8 + 2 * exceptionCount; | |
1900 | } | |
1901 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
1902 | if ((cw.version & 0xFFFF) < Opcodes.V1_5 | |
1903 | || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
1904 | cw.newUTF8("Synthetic"); | |
1905 | size += 6; | |
1906 | } | |
1907 | } | |
1908 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
1909 | cw.newUTF8("Deprecated"); | |
1910 | size += 6; | |
1911 | } | |
1912 | if (ClassReader.SIGNATURES && signature != null) { | |
1913 | cw.newUTF8("Signature"); | |
1914 | cw.newUTF8(signature); | |
1915 | size += 8; | |
1916 | } | |
1917 | if (ClassReader.ANNOTATIONS && annd != null) { | |
1918 | cw.newUTF8("AnnotationDefault"); | |
1919 | size += 6 + annd.length; | |
1920 | } | |
1921 | if (ClassReader.ANNOTATIONS && anns != null) { | |
1922 | cw.newUTF8("RuntimeVisibleAnnotations"); | |
1923 | size += 8 + anns.getSize(); | |
1924 | } | |
1925 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
1926 | cw.newUTF8("RuntimeInvisibleAnnotations"); | |
1927 | size += 8 + ianns.getSize(); | |
1928 | } | |
1929 | if (ClassReader.ANNOTATIONS && panns != null) { | |
1930 | cw.newUTF8("RuntimeVisibleParameterAnnotations"); | |
1931 | size += 7 + 2 * (panns.length - synthetics); | |
1932 | for (int i = panns.length - 1; i >= synthetics; --i) { | |
1933 | size += panns[i] == null ? 0 : panns[i].getSize(); | |
1934 | } | |
1935 | } | |
1936 | if (ClassReader.ANNOTATIONS && ipanns != null) { | |
1937 | cw.newUTF8("RuntimeInvisibleParameterAnnotations"); | |
1938 | size += 7 + 2 * (ipanns.length - synthetics); | |
1939 | for (int i = ipanns.length - 1; i >= synthetics; --i) { | |
1940 | size += ipanns[i] == null ? 0 : ipanns[i].getSize(); | |
1941 | } | |
1942 | } | |
1943 | if (attrs != null) { | |
1944 | size += attrs.getSize(cw, null, 0, -1, -1); | |
1945 | } | |
1946 | return size; | |
1947 | } | |
1948 | ||
1949 | /** | |
1950 | * Puts the bytecode of this method in the given byte vector. | |
1951 | * | |
1952 | * @param out | |
1953 | * the byte vector into which the bytecode of this method must be | |
1954 | * copied. | |
1955 | */ | |
1956 | final void put(final ByteVector out) { | |
1957 | final int FACTOR = ClassWriter.TO_ACC_SYNTHETIC; | |
1958 | int mask = ACC_CONSTRUCTOR | Opcodes.ACC_DEPRECATED | |
1959 | | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE | |
1960 | | ((access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) / FACTOR); | |
1961 | out.putShort(access & ~mask).putShort(name).putShort(desc); | |
1962 | if (classReaderOffset != 0) { | |
1963 | out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength); | |
1964 | return; | |
1965 | } | |
1966 | int attributeCount = 0; | |
1967 | if (code.length > 0) { | |
1968 | ++attributeCount; | |
1969 | } | |
1970 | if (exceptionCount > 0) { | |
1971 | ++attributeCount; | |
1972 | } | |
1973 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
1974 | if ((cw.version & 0xFFFF) < Opcodes.V1_5 | |
1975 | || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
1976 | ++attributeCount; | |
1977 | } | |
1978 | } | |
1979 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
1980 | ++attributeCount; | |
1981 | } | |
1982 | if (ClassReader.SIGNATURES && signature != null) { | |
1983 | ++attributeCount; | |
1984 | } | |
1985 | if (ClassReader.ANNOTATIONS && annd != null) { | |
1986 | ++attributeCount; | |
1987 | } | |
1988 | if (ClassReader.ANNOTATIONS && anns != null) { | |
1989 | ++attributeCount; | |
1990 | } | |
1991 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
1992 | ++attributeCount; | |
1993 | } | |
1994 | if (ClassReader.ANNOTATIONS && panns != null) { | |
1995 | ++attributeCount; | |
1996 | } | |
1997 | if (ClassReader.ANNOTATIONS && ipanns != null) { | |
1998 | ++attributeCount; | |
1999 | } | |
2000 | if (attrs != null) { | |
2001 | attributeCount += attrs.getCount(); | |
2002 | } | |
2003 | out.putShort(attributeCount); | |
2004 | if (code.length > 0) { | |
2005 | int size = 12 + code.length + 8 * handlerCount; | |
2006 | if (localVar != null) { | |
2007 | size += 8 + localVar.length; | |
2008 | } | |
2009 | if (localVarType != null) { | |
2010 | size += 8 + localVarType.length; | |
2011 | } | |
2012 | if (lineNumber != null) { | |
2013 | size += 8 + lineNumber.length; | |
2014 | } | |
2015 | if (stackMap != null) { | |
2016 | size += 8 + stackMap.length; | |
2017 | } | |
2018 | if (cattrs != null) { | |
2019 | size += cattrs.getSize(cw, code.data, code.length, maxStack, | |
2020 | maxLocals); | |
2021 | } | |
2022 | out.putShort(cw.newUTF8("Code")).putInt(size); | |
2023 | out.putShort(maxStack).putShort(maxLocals); | |
2024 | out.putInt(code.length).putByteArray(code.data, 0, code.length); | |
2025 | out.putShort(handlerCount); | |
2026 | if (handlerCount > 0) { | |
2027 | Handler h = firstHandler; | |
2028 | while (h != null) { | |
2029 | out.putShort(h.start.position).putShort(h.end.position) | |
2030 | .putShort(h.handler.position).putShort(h.type); | |
2031 | h = h.next; | |
2032 | } | |
2033 | } | |
2034 | attributeCount = 0; | |
2035 | if (localVar != null) { | |
2036 | ++attributeCount; | |
2037 | } | |
2038 | if (localVarType != null) { | |
2039 | ++attributeCount; | |
2040 | } | |
2041 | if (lineNumber != null) { | |
2042 | ++attributeCount; | |
2043 | } | |
2044 | if (stackMap != null) { | |
2045 | ++attributeCount; | |
2046 | } | |
2047 | if (cattrs != null) { | |
2048 | attributeCount += cattrs.getCount(); | |
2049 | } | |
2050 | out.putShort(attributeCount); | |
2051 | if (localVar != null) { | |
2052 | out.putShort(cw.newUTF8("LocalVariableTable")); | |
2053 | out.putInt(localVar.length + 2).putShort(localVarCount); | |
2054 | out.putByteArray(localVar.data, 0, localVar.length); | |
2055 | } | |
2056 | if (localVarType != null) { | |
2057 | out.putShort(cw.newUTF8("LocalVariableTypeTable")); | |
2058 | out.putInt(localVarType.length + 2).putShort(localVarTypeCount); | |
2059 | out.putByteArray(localVarType.data, 0, localVarType.length); | |
2060 | } | |
2061 | if (lineNumber != null) { | |
2062 | out.putShort(cw.newUTF8("LineNumberTable")); | |
2063 | out.putInt(lineNumber.length + 2).putShort(lineNumberCount); | |
2064 | out.putByteArray(lineNumber.data, 0, lineNumber.length); | |
2065 | } | |
2066 | if (stackMap != null) { | |
2067 | boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6; | |
2068 | out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap")); | |
2069 | out.putInt(stackMap.length + 2).putShort(frameCount); | |
2070 | out.putByteArray(stackMap.data, 0, stackMap.length); | |
2071 | } | |
2072 | if (cattrs != null) { | |
2073 | cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out); | |
2074 | } | |
2075 | } | |
2076 | if (exceptionCount > 0) { | |
2077 | out.putShort(cw.newUTF8("Exceptions")).putInt( | |
2078 | 2 * exceptionCount + 2); | |
2079 | out.putShort(exceptionCount); | |
2080 | for (int i = 0; i < exceptionCount; ++i) { | |
2081 | out.putShort(exceptions[i]); | |
2082 | } | |
2083 | } | |
2084 | if ((access & Opcodes.ACC_SYNTHETIC) != 0) { | |
2085 | if ((cw.version & 0xFFFF) < Opcodes.V1_5 | |
2086 | || (access & ClassWriter.ACC_SYNTHETIC_ATTRIBUTE) != 0) { | |
2087 | out.putShort(cw.newUTF8("Synthetic")).putInt(0); | |
2088 | } | |
2089 | } | |
2090 | if ((access & Opcodes.ACC_DEPRECATED) != 0) { | |
2091 | out.putShort(cw.newUTF8("Deprecated")).putInt(0); | |
2092 | } | |
2093 | if (ClassReader.SIGNATURES && signature != null) { | |
2094 | out.putShort(cw.newUTF8("Signature")).putInt(2) | |
2095 | .putShort(cw.newUTF8(signature)); | |
2096 | } | |
2097 | if (ClassReader.ANNOTATIONS && annd != null) { | |
2098 | out.putShort(cw.newUTF8("AnnotationDefault")); | |
2099 | out.putInt(annd.length); | |
2100 | out.putByteArray(annd.data, 0, annd.length); | |
2101 | } | |
2102 | if (ClassReader.ANNOTATIONS && anns != null) { | |
2103 | out.putShort(cw.newUTF8("RuntimeVisibleAnnotations")); | |
2104 | anns.put(out); | |
2105 | } | |
2106 | if (ClassReader.ANNOTATIONS && ianns != null) { | |
2107 | out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations")); | |
2108 | ianns.put(out); | |
2109 | } | |
2110 | if (ClassReader.ANNOTATIONS && panns != null) { | |
2111 | out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations")); | |
2112 | AnnotationWriter.put(panns, synthetics, out); | |
2113 | } | |
2114 | if (ClassReader.ANNOTATIONS && ipanns != null) { | |
2115 | out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations")); | |
2116 | AnnotationWriter.put(ipanns, synthetics, out); | |
2117 | } | |
2118 | if (attrs != null) { | |
2119 | attrs.put(cw, null, 0, -1, -1, out); | |
2120 | } | |
2121 | } | |
2122 | ||
2123 | // ------------------------------------------------------------------------ | |
2124 | // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W) | |
2125 | // ------------------------------------------------------------------------ | |
2126 | ||
2127 | /** | |
2128 | * Resizes and replaces the temporary instructions inserted by | |
2129 | * {@link Label#resolve} for wide forward jumps, while keeping jump offsets | |
2130 | * and instruction addresses consistent. This may require to resize other | |
2131 | * existing instructions, or even to introduce new instructions: for | |
2132 | * example, increasing the size of an instruction by 2 at the middle of a | |
2133 | * method can increases the offset of an IFEQ instruction from 32766 to | |
2134 | * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W | |
2135 | * 32765. This, in turn, may require to increase the size of another jump | |
2136 | * instruction, and so on... All these operations are handled automatically | |
2137 | * by this method. | |
2138 | * <p> | |
2139 | * <i>This method must be called after all the method that is being built | |
2140 | * has been visited</i>. In particular, the {@link Label Label} objects used | |
2141 | * to construct the method are no longer valid after this method has been | |
2142 | * called. | |
2143 | */ | |
2144 | private void resizeInstructions() { | |
2145 | byte[] b = code.data; // bytecode of the method | |
2146 | int u, v, label; // indexes in b | |
2147 | int i, j; // loop indexes | |
2148 | /* | |
2149 | * 1st step: As explained above, resizing an instruction may require to | |
2150 | * resize another one, which may require to resize yet another one, and | |
2151 | * so on. The first step of the algorithm consists in finding all the | |
2152 | * instructions that need to be resized, without modifying the code. | |
2153 | * This is done by the following "fix point" algorithm: | |
2154 | * | |
2155 | * Parse the code to find the jump instructions whose offset will need | |
2156 | * more than 2 bytes to be stored (the future offset is computed from | |
2157 | * the current offset and from the number of bytes that will be inserted | |
2158 | * or removed between the source and target instructions). For each such | |
2159 | * instruction, adds an entry in (a copy of) the indexes and sizes | |
2160 | * arrays (if this has not already been done in a previous iteration!). | |
2161 | * | |
2162 | * If at least one entry has been added during the previous step, go | |
2163 | * back to the beginning, otherwise stop. | |
2164 | * | |
2165 | * In fact the real algorithm is complicated by the fact that the size | |
2166 | * of TABLESWITCH and LOOKUPSWITCH instructions depends on their | |
2167 | * position in the bytecode (because of padding). In order to ensure the | |
2168 | * convergence of the algorithm, the number of bytes to be added or | |
2169 | * removed from these instructions is over estimated during the previous | |
2170 | * loop, and computed exactly only after the loop is finished (this | |
2171 | * requires another pass to parse the bytecode of the method). | |
2172 | */ | |
2173 | int[] allIndexes = new int[0]; // copy of indexes | |
2174 | int[] allSizes = new int[0]; // copy of sizes | |
2175 | boolean[] resize; // instructions to be resized | |
2176 | int newOffset; // future offset of a jump instruction | |
2177 | ||
2178 | resize = new boolean[code.length]; | |
2179 | ||
2180 | // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done | |
2181 | int state = 3; | |
2182 | do { | |
2183 | if (state == 3) { | |
2184 | state = 2; | |
2185 | } | |
2186 | u = 0; | |
2187 | while (u < b.length) { | |
2188 | int opcode = b[u] & 0xFF; // opcode of current instruction | |
2189 | int insert = 0; // bytes to be added after this instruction | |
2190 | ||
2191 | switch (ClassWriter.TYPE[opcode]) { | |
2192 | case ClassWriter.NOARG_INSN: | |
2193 | case ClassWriter.IMPLVAR_INSN: | |
2194 | u += 1; | |
2195 | break; | |
2196 | case ClassWriter.LABEL_INSN: | |
2197 | if (opcode > 201) { | |
2198 | // converts temporary opcodes 202 to 217, 218 and | |
2199 | // 219 to IFEQ ... JSR (inclusive), IFNULL and | |
2200 | // IFNONNULL | |
2201 | opcode = opcode < 218 ? opcode - 49 : opcode - 20; | |
2202 | label = u + readUnsignedShort(b, u + 1); | |
2203 | } else { | |
2204 | label = u + readShort(b, u + 1); | |
2205 | } | |
2206 | newOffset = getNewOffset(allIndexes, allSizes, u, label); | |
2207 | if (newOffset < Short.MIN_VALUE | |
2208 | || newOffset > Short.MAX_VALUE) { | |
2209 | if (!resize[u]) { | |
2210 | if (opcode == Opcodes.GOTO || opcode == Opcodes.JSR) { | |
2211 | // two additional bytes will be required to | |
2212 | // replace this GOTO or JSR instruction with | |
2213 | // a GOTO_W or a JSR_W | |
2214 | insert = 2; | |
2215 | } else { | |
2216 | // five additional bytes will be required to | |
2217 | // replace this IFxxx <l> instruction with | |
2218 | // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx | |
2219 | // is the "opposite" opcode of IFxxx (i.e., | |
2220 | // IFNE for IFEQ) and where <l'> designates | |
2221 | // the instruction just after the GOTO_W. | |
2222 | insert = 5; | |
2223 | } | |
2224 | resize[u] = true; | |
2225 | } | |
2226 | } | |
2227 | u += 3; | |
2228 | break; | |
2229 | case ClassWriter.LABELW_INSN: | |
2230 | u += 5; | |
2231 | break; | |
2232 | case ClassWriter.TABL_INSN: | |
2233 | if (state == 1) { | |
2234 | // true number of bytes to be added (or removed) | |
2235 | // from this instruction = (future number of padding | |
2236 | // bytes - current number of padding byte) - | |
2237 | // previously over estimated variation = | |
2238 | // = ((3 - newOffset%4) - (3 - u%4)) - u%4 | |
2239 | // = (-newOffset%4 + u%4) - u%4 | |
2240 | // = -(newOffset & 3) | |
2241 | newOffset = getNewOffset(allIndexes, allSizes, 0, u); | |
2242 | insert = -(newOffset & 3); | |
2243 | } else if (!resize[u]) { | |
2244 | // over estimation of the number of bytes to be | |
2245 | // added to this instruction = 3 - current number | |
2246 | // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3 | |
2247 | insert = u & 3; | |
2248 | resize[u] = true; | |
2249 | } | |
2250 | // skips instruction | |
2251 | u = u + 4 - (u & 3); | |
2252 | u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12; | |
2253 | break; | |
2254 | case ClassWriter.LOOK_INSN: | |
2255 | if (state == 1) { | |
2256 | // like TABL_INSN | |
2257 | newOffset = getNewOffset(allIndexes, allSizes, 0, u); | |
2258 | insert = -(newOffset & 3); | |
2259 | } else if (!resize[u]) { | |
2260 | // like TABL_INSN | |
2261 | insert = u & 3; | |
2262 | resize[u] = true; | |
2263 | } | |
2264 | // skips instruction | |
2265 | u = u + 4 - (u & 3); | |
2266 | u += 8 * readInt(b, u + 4) + 8; | |
2267 | break; | |
2268 | case ClassWriter.WIDE_INSN: | |
2269 | opcode = b[u + 1] & 0xFF; | |
2270 | if (opcode == Opcodes.IINC) { | |
2271 | u += 6; | |
2272 | } else { | |
2273 | u += 4; | |
2274 | } | |
2275 | break; | |
2276 | case ClassWriter.VAR_INSN: | |
2277 | case ClassWriter.SBYTE_INSN: | |
2278 | case ClassWriter.LDC_INSN: | |
2279 | u += 2; | |
2280 | break; | |
2281 | case ClassWriter.SHORT_INSN: | |
2282 | case ClassWriter.LDCW_INSN: | |
2283 | case ClassWriter.FIELDORMETH_INSN: | |
2284 | case ClassWriter.TYPE_INSN: | |
2285 | case ClassWriter.IINC_INSN: | |
2286 | u += 3; | |
2287 | break; | |
2288 | case ClassWriter.ITFMETH_INSN: | |
2289 | case ClassWriter.INDYMETH_INSN: | |
2290 | u += 5; | |
2291 | break; | |
2292 | // case ClassWriter.MANA_INSN: | |
2293 | default: | |
2294 | u += 4; | |
2295 | break; | |
2296 | } | |
2297 | if (insert != 0) { | |
2298 | // adds a new (u, insert) entry in the allIndexes and | |
2299 | // allSizes arrays | |
2300 | int[] newIndexes = new int[allIndexes.length + 1]; | |
2301 | int[] newSizes = new int[allSizes.length + 1]; | |
2302 | System.arraycopy(allIndexes, 0, newIndexes, 0, | |
2303 | allIndexes.length); | |
2304 | System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length); | |
2305 | newIndexes[allIndexes.length] = u; | |
2306 | newSizes[allSizes.length] = insert; | |
2307 | allIndexes = newIndexes; | |
2308 | allSizes = newSizes; | |
2309 | if (insert > 0) { | |
2310 | state = 3; | |
2311 | } | |
2312 | } | |
2313 | } | |
2314 | if (state < 3) { | |
2315 | --state; | |
2316 | } | |
2317 | } while (state != 0); | |
2318 | ||
2319 | // 2nd step: | |
2320 | // copies the bytecode of the method into a new bytevector, updates the | |
2321 | // offsets, and inserts (or removes) bytes as requested. | |
2322 | ||
2323 | ByteVector newCode = new ByteVector(code.length); | |
2324 | ||
2325 | u = 0; | |
2326 | while (u < code.length) { | |
2327 | int opcode = b[u] & 0xFF; | |
2328 | switch (ClassWriter.TYPE[opcode]) { | |
2329 | case ClassWriter.NOARG_INSN: | |
2330 | case ClassWriter.IMPLVAR_INSN: | |
2331 | newCode.putByte(opcode); | |
2332 | u += 1; | |
2333 | break; | |
2334 | case ClassWriter.LABEL_INSN: | |
2335 | if (opcode > 201) { | |
2336 | // changes temporary opcodes 202 to 217 (inclusive), 218 | |
2337 | // and 219 to IFEQ ... JSR (inclusive), IFNULL and | |
2338 | // IFNONNULL | |
2339 | opcode = opcode < 218 ? opcode - 49 : opcode - 20; | |
2340 | label = u + readUnsignedShort(b, u + 1); | |
2341 | } else { | |
2342 | label = u + readShort(b, u + 1); | |
2343 | } | |
2344 | newOffset = getNewOffset(allIndexes, allSizes, u, label); | |
2345 | if (resize[u]) { | |
2346 | // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx | |
2347 | // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is | |
2348 | // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) | |
2349 | // and where <l'> designates the instruction just after | |
2350 | // the GOTO_W. | |
2351 | if (opcode == Opcodes.GOTO) { | |
2352 | newCode.putByte(200); // GOTO_W | |
2353 | } else if (opcode == Opcodes.JSR) { | |
2354 | newCode.putByte(201); // JSR_W | |
2355 | } else { | |
2356 | newCode.putByte(opcode <= 166 ? ((opcode + 1) ^ 1) - 1 | |
2357 | : opcode ^ 1); | |
2358 | newCode.putShort(8); // jump offset | |
2359 | newCode.putByte(200); // GOTO_W | |
2360 | // newOffset now computed from start of GOTO_W | |
2361 | newOffset -= 3; | |
2362 | } | |
2363 | newCode.putInt(newOffset); | |
2364 | } else { | |
2365 | newCode.putByte(opcode); | |
2366 | newCode.putShort(newOffset); | |
2367 | } | |
2368 | u += 3; | |
2369 | break; | |
2370 | case ClassWriter.LABELW_INSN: | |
2371 | label = u + readInt(b, u + 1); | |
2372 | newOffset = getNewOffset(allIndexes, allSizes, u, label); | |
2373 | newCode.putByte(opcode); | |
2374 | newCode.putInt(newOffset); | |
2375 | u += 5; | |
2376 | break; | |
2377 | case ClassWriter.TABL_INSN: | |
2378 | // skips 0 to 3 padding bytes | |
2379 | v = u; | |
2380 | u = u + 4 - (v & 3); | |
2381 | // reads and copies instruction | |
2382 | newCode.putByte(Opcodes.TABLESWITCH); | |
2383 | newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); | |
2384 | label = v + readInt(b, u); | |
2385 | u += 4; | |
2386 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2387 | newCode.putInt(newOffset); | |
2388 | j = readInt(b, u); | |
2389 | u += 4; | |
2390 | newCode.putInt(j); | |
2391 | j = readInt(b, u) - j + 1; | |
2392 | u += 4; | |
2393 | newCode.putInt(readInt(b, u - 4)); | |
2394 | for (; j > 0; --j) { | |
2395 | label = v + readInt(b, u); | |
2396 | u += 4; | |
2397 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2398 | newCode.putInt(newOffset); | |
2399 | } | |
2400 | break; | |
2401 | case ClassWriter.LOOK_INSN: | |
2402 | // skips 0 to 3 padding bytes | |
2403 | v = u; | |
2404 | u = u + 4 - (v & 3); | |
2405 | // reads and copies instruction | |
2406 | newCode.putByte(Opcodes.LOOKUPSWITCH); | |
2407 | newCode.putByteArray(null, 0, (4 - newCode.length % 4) % 4); | |
2408 | label = v + readInt(b, u); | |
2409 | u += 4; | |
2410 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2411 | newCode.putInt(newOffset); | |
2412 | j = readInt(b, u); | |
2413 | u += 4; | |
2414 | newCode.putInt(j); | |
2415 | for (; j > 0; --j) { | |
2416 | newCode.putInt(readInt(b, u)); | |
2417 | u += 4; | |
2418 | label = v + readInt(b, u); | |
2419 | u += 4; | |
2420 | newOffset = getNewOffset(allIndexes, allSizes, v, label); | |
2421 | newCode.putInt(newOffset); | |
2422 | } | |
2423 | break; | |
2424 | case ClassWriter.WIDE_INSN: | |
2425 | opcode = b[u + 1] & 0xFF; | |
2426 | if (opcode == Opcodes.IINC) { | |
2427 | newCode.putByteArray(b, u, 6); | |
2428 | u += 6; | |
2429 | } else { | |
2430 | newCode.putByteArray(b, u, 4); | |
2431 | u += 4; | |
2432 | } | |
2433 | break; | |
2434 | case ClassWriter.VAR_INSN: | |
2435 | case ClassWriter.SBYTE_INSN: | |
2436 | case ClassWriter.LDC_INSN: | |
2437 | newCode.putByteArray(b, u, 2); | |
2438 | u += 2; | |
2439 | break; | |
2440 | case ClassWriter.SHORT_INSN: | |
2441 | case ClassWriter.LDCW_INSN: | |
2442 | case ClassWriter.FIELDORMETH_INSN: | |
2443 | case ClassWriter.TYPE_INSN: | |
2444 | case ClassWriter.IINC_INSN: | |
2445 | newCode.putByteArray(b, u, 3); | |
2446 | u += 3; | |
2447 | break; | |
2448 | case ClassWriter.ITFMETH_INSN: | |
2449 | case ClassWriter.INDYMETH_INSN: | |
2450 | newCode.putByteArray(b, u, 5); | |
2451 | u += 5; | |
2452 | break; | |
2453 | // case MANA_INSN: | |
2454 | default: | |
2455 | newCode.putByteArray(b, u, 4); | |
2456 | u += 4; | |
2457 | break; | |
2458 | } | |
2459 | } | |
2460 | ||
2461 | // recomputes the stack map frames | |
2462 | if (frameCount > 0) { | |
2463 | if (compute == FRAMES) { | |
2464 | frameCount = 0; | |
2465 | stackMap = null; | |
2466 | previousFrame = null; | |
2467 | frame = null; | |
2468 | Frame f = new Frame(); | |
2469 | f.owner = labels; | |
2470 | Type[] args = Type.getArgumentTypes(descriptor); | |
2471 | f.initInputFrame(cw, access, args, maxLocals); | |
2472 | visitFrame(f); | |
2473 | Label l = labels; | |
2474 | while (l != null) { | |
2475 | /* | |
2476 | * here we need the original label position. getNewOffset | |
2477 | * must therefore never have been called for this label. | |
2478 | */ | |
2479 | u = l.position - 3; | |
2480 | if ((l.status & Label.STORE) != 0 || (u >= 0 && resize[u])) { | |
2481 | getNewOffset(allIndexes, allSizes, l); | |
2482 | // TODO update offsets in UNINITIALIZED values | |
2483 | visitFrame(l.frame); | |
2484 | } | |
2485 | l = l.successor; | |
2486 | } | |
2487 | } else { | |
2488 | /* | |
2489 | * Resizing an existing stack map frame table is really hard. | |
2490 | * Not only the table must be parsed to update the offets, but | |
2491 | * new frames may be needed for jump instructions that were | |
2492 | * inserted by this method. And updating the offsets or | |
2493 | * inserting frames can change the format of the following | |
2494 | * frames, in case of packed frames. In practice the whole table | |
2495 | * must be recomputed. For this the frames are marked as | |
2496 | * potentially invalid. This will cause the whole class to be | |
2497 | * reread and rewritten with the COMPUTE_FRAMES option (see the | |
2498 | * ClassWriter.toByteArray method). This is not very efficient | |
2499 | * but is much easier and requires much less code than any other | |
2500 | * method I can think of. | |
2501 | */ | |
2502 | cw.invalidFrames = true; | |
2503 | } | |
2504 | } | |
2505 | // updates the exception handler block labels | |
2506 | Handler h = firstHandler; | |
2507 | while (h != null) { | |
2508 | getNewOffset(allIndexes, allSizes, h.start); | |
2509 | getNewOffset(allIndexes, allSizes, h.end); | |
2510 | getNewOffset(allIndexes, allSizes, h.handler); | |
2511 | h = h.next; | |
2512 | } | |
2513 | // updates the instructions addresses in the | |
2514 | // local var and line number tables | |
2515 | for (i = 0; i < 2; ++i) { | |
2516 | ByteVector bv = i == 0 ? localVar : localVarType; | |
2517 | if (bv != null) { | |
2518 | b = bv.data; | |
2519 | u = 0; | |
2520 | while (u < bv.length) { | |
2521 | label = readUnsignedShort(b, u); | |
2522 | newOffset = getNewOffset(allIndexes, allSizes, 0, label); | |
2523 | writeShort(b, u, newOffset); | |
2524 | label += readUnsignedShort(b, u + 2); | |
2525 | newOffset = getNewOffset(allIndexes, allSizes, 0, label) | |
2526 | - newOffset; | |
2527 | writeShort(b, u + 2, newOffset); | |
2528 | u += 10; | |
2529 | } | |
2530 | } | |
2531 | } | |
2532 | if (lineNumber != null) { | |
2533 | b = lineNumber.data; | |
2534 | u = 0; | |
2535 | while (u < lineNumber.length) { | |
2536 | writeShort( | |
2537 | b, | |
2538 | u, | |
2539 | getNewOffset(allIndexes, allSizes, 0, | |
2540 | readUnsignedShort(b, u))); | |
2541 | u += 4; | |
2542 | } | |
2543 | } | |
2544 | // updates the labels of the other attributes | |
2545 | Attribute attr = cattrs; | |
2546 | while (attr != null) { | |
2547 | Label[] labels = attr.getLabels(); | |
2548 | if (labels != null) { | |
2549 | for (i = labels.length - 1; i >= 0; --i) { | |
2550 | getNewOffset(allIndexes, allSizes, labels[i]); | |
2551 | } | |
2552 | } | |
2553 | attr = attr.next; | |
2554 | } | |
2555 | ||
2556 | // replaces old bytecodes with new ones | |
2557 | code = newCode; | |
2558 | } | |
2559 | ||
2560 | /** | |
2561 | * Reads an unsigned short value in the given byte array. | |
2562 | * | |
2563 | * @param b | |
2564 | * a byte array. | |
2565 | * @param index | |
2566 | * the start index of the value to be read. | |
2567 | * @return the read value. | |
2568 | */ | |
2569 | static int readUnsignedShort(final byte[] b, final int index) { | |
2570 | return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF); | |
2571 | } | |
2572 | ||
2573 | /** | |
2574 | * Reads a signed short value in the given byte array. | |
2575 | * | |
2576 | * @param b | |
2577 | * a byte array. | |
2578 | * @param index | |
2579 | * the start index of the value to be read. | |
2580 | * @return the read value. | |
2581 | */ | |
2582 | static short readShort(final byte[] b, final int index) { | |
2583 | return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF)); | |
2584 | } | |
2585 | ||
2586 | /** | |
2587 | * Reads a signed int value in the given byte array. | |
2588 | * | |
2589 | * @param b | |
2590 | * a byte array. | |
2591 | * @param index | |
2592 | * the start index of the value to be read. | |
2593 | * @return the read value. | |
2594 | */ | |
2595 | static int readInt(final byte[] b, final int index) { | |
2596 | return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16) | |
2597 | | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF); | |
2598 | } | |
2599 | ||
2600 | /** | |
2601 | * Writes a short value in the given byte array. | |
2602 | * | |
2603 | * @param b | |
2604 | * a byte array. | |
2605 | * @param index | |
2606 | * where the first byte of the short value must be written. | |
2607 | * @param s | |
2608 | * the value to be written in the given byte array. | |
2609 | */ | |
2610 | static void writeShort(final byte[] b, final int index, final int s) { | |
2611 | b[index] = (byte) (s >>> 8); | |
2612 | b[index + 1] = (byte) s; | |
2613 | } | |
2614 | ||
2615 | /** | |
2616 | * Computes the future value of a bytecode offset. | |
2617 | * <p> | |
2618 | * Note: it is possible to have several entries for the same instruction in | |
2619 | * the <tt>indexes</tt> and <tt>sizes</tt>: two entries (index=a,size=b) and | |
2620 | * (index=a,size=b') are equivalent to a single entry (index=a,size=b+b'). | |
2621 | * | |
2622 | * @param indexes | |
2623 | * current positions of the instructions to be resized. Each | |
2624 | * instruction must be designated by the index of its <i>last</i> | |
2625 | * byte, plus one (or, in other words, by the index of the | |
2626 | * <i>first</i> byte of the <i>next</i> instruction). | |
2627 | * @param sizes | |
2628 | * the number of bytes to be <i>added</i> to the above | |
2629 | * instructions. More precisely, for each i < <tt>len</tt>, | |
2630 | * <tt>sizes</tt>[i] bytes will be added at the end of the | |
2631 | * instruction designated by <tt>indexes</tt>[i] or, if | |
2632 | * <tt>sizes</tt>[i] is negative, the <i>last</i> | | |
2633 | * <tt>sizes[i]</tt>| bytes of the instruction will be removed | |
2634 | * (the instruction size <i>must not</i> become negative or | |
2635 | * null). | |
2636 | * @param begin | |
2637 | * index of the first byte of the source instruction. | |
2638 | * @param end | |
2639 | * index of the first byte of the target instruction. | |
2640 | * @return the future value of the given bytecode offset. | |
2641 | */ | |
2642 | static int getNewOffset(final int[] indexes, final int[] sizes, | |
2643 | final int begin, final int end) { | |
2644 | int offset = end - begin; | |
2645 | for (int i = 0; i < indexes.length; ++i) { | |
2646 | if (begin < indexes[i] && indexes[i] <= end) { | |
2647 | // forward jump | |
2648 | offset += sizes[i]; | |
2649 | } else if (end < indexes[i] && indexes[i] <= begin) { | |
2650 | // backward jump | |
2651 | offset -= sizes[i]; | |
2652 | } | |
2653 | } | |
2654 | return offset; | |
2655 | } | |
2656 | ||
2657 | /** | |
2658 | * Updates the offset of the given label. | |
2659 | * | |
2660 | * @param indexes | |
2661 | * current positions of the instructions to be resized. Each | |
2662 | * instruction must be designated by the index of its <i>last</i> | |
2663 | * byte, plus one (or, in other words, by the index of the | |
2664 | * <i>first</i> byte of the <i>next</i> instruction). | |
2665 | * @param sizes | |
2666 | * the number of bytes to be <i>added</i> to the above | |
2667 | * instructions. More precisely, for each i < <tt>len</tt>, | |
2668 | * <tt>sizes</tt>[i] bytes will be added at the end of the | |
2669 | * instruction designated by <tt>indexes</tt>[i] or, if | |
2670 | * <tt>sizes</tt>[i] is negative, the <i>last</i> | | |
2671 | * <tt>sizes[i]</tt>| bytes of the instruction will be removed | |
2672 | * (the instruction size <i>must not</i> become negative or | |
2673 | * null). | |
2674 | * @param label | |
2675 | * the label whose offset must be updated. | |
2676 | */ | |
2677 | static void getNewOffset(final int[] indexes, final int[] sizes, | |
2678 | final Label label) { | |
2679 | if ((label.status & Label.RESIZED) == 0) { | |
2680 | label.position = getNewOffset(indexes, sizes, 0, label.position); | |
2681 | label.status |= Label.RESIZED; | |
2682 | } | |
2683 | } | |
2684 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | // ASM API versions | |
46 | ||
47 | int ASM4 = 4 << 16 | 0 << 8 | 0; | |
48 | ||
49 | // versions | |
50 | ||
51 | int V1_1 = 3 << 16 | 45; | |
52 | int V1_2 = 0 << 16 | 46; | |
53 | int V1_3 = 0 << 16 | 47; | |
54 | int V1_4 = 0 << 16 | 48; | |
55 | int V1_5 = 0 << 16 | 49; | |
56 | int V1_6 = 0 << 16 | 50; | |
57 | int V1_7 = 0 << 16 | 51; | |
58 | ||
59 | // access flags | |
60 | ||
61 | int ACC_PUBLIC = 0x0001; // class, field, method | |
62 | int ACC_PRIVATE = 0x0002; // class, field, method | |
63 | int ACC_PROTECTED = 0x0004; // class, field, method | |
64 | int ACC_STATIC = 0x0008; // field, method | |
65 | int ACC_FINAL = 0x0010; // class, field, method | |
66 | int ACC_SUPER = 0x0020; // class | |
67 | int ACC_SYNCHRONIZED = 0x0020; // method | |
68 | int ACC_VOLATILE = 0x0040; // field | |
69 | int ACC_BRIDGE = 0x0040; // method | |
70 | int ACC_VARARGS = 0x0080; // method | |
71 | int ACC_TRANSIENT = 0x0080; // field | |
72 | int ACC_NATIVE = 0x0100; // method | |
73 | int ACC_INTERFACE = 0x0200; // class | |
74 | int ACC_ABSTRACT = 0x0400; // class, method | |
75 | int ACC_STRICT = 0x0800; // method | |
76 | int ACC_SYNTHETIC = 0x1000; // class, field, method | |
77 | int ACC_ANNOTATION = 0x2000; // class | |
78 | int ACC_ENUM = 0x4000; // class(?) field inner | |
79 | ||
80 | // ASM specific pseudo access flags | |
81 | ||
82 | int ACC_DEPRECATED = 0x20000; // class, field, method | |
83 | ||
84 | // types for NEWARRAY | |
85 | ||
86 | int T_BOOLEAN = 4; | |
87 | int T_CHAR = 5; | |
88 | int T_FLOAT = 6; | |
89 | int T_DOUBLE = 7; | |
90 | int T_BYTE = 8; | |
91 | int T_SHORT = 9; | |
92 | int T_INT = 10; | |
93 | int T_LONG = 11; | |
94 | ||
95 | // tags for Handle | |
96 | ||
97 | int H_GETFIELD = 1; | |
98 | int H_GETSTATIC = 2; | |
99 | int H_PUTFIELD = 3; | |
100 | int H_PUTSTATIC = 4; | |
101 | int H_INVOKEVIRTUAL = 5; | |
102 | int H_INVOKESTATIC = 6; | |
103 | int H_INVOKESPECIAL = 7; | |
104 | int H_NEWINVOKESPECIAL = 8; | |
105 | int H_INVOKEINTERFACE = 9; | |
106 | ||
107 | // stack map frame types | |
108 | ||
109 | /** | |
110 | * Represents an expanded frame. See {@link ClassReader#EXPAND_FRAMES}. | |
111 | */ | |
112 | int F_NEW = -1; | |
113 | ||
114 | /** | |
115 | * Represents a compressed frame with complete frame data. | |
116 | */ | |
117 | int F_FULL = 0; | |
118 | ||
119 | /** | |
120 | * Represents a compressed frame where locals are the same as the locals in | |
121 | * the previous frame, except that additional 1-3 locals are defined, and | |
122 | * with an empty stack. | |
123 | */ | |
124 | int F_APPEND = 1; | |
125 | ||
126 | /** | |
127 | * Represents a compressed frame where locals are the same as the locals in | |
128 | * the previous frame, except that the last 1-3 locals are absent and with | |
129 | * an empty stack. | |
130 | */ | |
131 | int F_CHOP = 2; | |
132 | ||
133 | /** | |
134 | * Represents a compressed frame with exactly the same locals as the | |
135 | * previous frame and with an empty stack. | |
136 | */ | |
137 | int F_SAME = 3; | |
138 | ||
139 | /** | |
140 | * Represents a compressed frame with exactly the same locals as the | |
141 | * previous frame and with a single value on the stack. | |
142 | */ | |
143 | int F_SAME1 = 4; | |
144 | ||
145 | Integer TOP = new Integer(0); | |
146 | Integer INTEGER = new Integer(1); | |
147 | Integer FLOAT = new Integer(2); | |
148 | Integer DOUBLE = new Integer(3); | |
149 | Integer LONG = new Integer(4); | |
150 | Integer NULL = new Integer(5); | |
151 | Integer UNINITIALIZED_THIS = new Integer(6); | |
152 | ||
153 | // opcodes // visit method (- = idem) | |
154 | ||
155 | int NOP = 0; // visitInsn | |
156 | int ACONST_NULL = 1; // - | |
157 | int ICONST_M1 = 2; // - | |
158 | int ICONST_0 = 3; // - | |
159 | int ICONST_1 = 4; // - | |
160 | int ICONST_2 = 5; // - | |
161 | int ICONST_3 = 6; // - | |
162 | int ICONST_4 = 7; // - | |
163 | int ICONST_5 = 8; // - | |
164 | int LCONST_0 = 9; // - | |
165 | int LCONST_1 = 10; // - | |
166 | int FCONST_0 = 11; // - | |
167 | int FCONST_1 = 12; // - | |
168 | int FCONST_2 = 13; // - | |
169 | int DCONST_0 = 14; // - | |
170 | int DCONST_1 = 15; // - | |
171 | int BIPUSH = 16; // visitIntInsn | |
172 | int SIPUSH = 17; // - | |
173 | int LDC = 18; // visitLdcInsn | |
174 | // int LDC_W = 19; // - | |
175 | // int LDC2_W = 20; // - | |
176 | int ILOAD = 21; // visitVarInsn | |
177 | int LLOAD = 22; // - | |
178 | int FLOAD = 23; // - | |
179 | int DLOAD = 24; // - | |
180 | int ALOAD = 25; // - | |
181 | // int ILOAD_0 = 26; // - | |
182 | // int ILOAD_1 = 27; // - | |
183 | // int ILOAD_2 = 28; // - | |
184 | // int ILOAD_3 = 29; // - | |
185 | // int LLOAD_0 = 30; // - | |
186 | // int LLOAD_1 = 31; // - | |
187 | // int LLOAD_2 = 32; // - | |
188 | // int LLOAD_3 = 33; // - | |
189 | // int FLOAD_0 = 34; // - | |
190 | // int FLOAD_1 = 35; // - | |
191 | // int FLOAD_2 = 36; // - | |
192 | // int FLOAD_3 = 37; // - | |
193 | // int DLOAD_0 = 38; // - | |
194 | // int DLOAD_1 = 39; // - | |
195 | // int DLOAD_2 = 40; // - | |
196 | // int DLOAD_3 = 41; // - | |
197 | // int ALOAD_0 = 42; // - | |
198 | // int ALOAD_1 = 43; // - | |
199 | // int ALOAD_2 = 44; // - | |
200 | // int ALOAD_3 = 45; // - | |
201 | int IALOAD = 46; // visitInsn | |
202 | int LALOAD = 47; // - | |
203 | int FALOAD = 48; // - | |
204 | int DALOAD = 49; // - | |
205 | int AALOAD = 50; // - | |
206 | int BALOAD = 51; // - | |
207 | int CALOAD = 52; // - | |
208 | int SALOAD = 53; // - | |
209 | int ISTORE = 54; // visitVarInsn | |
210 | int LSTORE = 55; // - | |
211 | int FSTORE = 56; // - | |
212 | int DSTORE = 57; // - | |
213 | int ASTORE = 58; // - | |
214 | // int ISTORE_0 = 59; // - | |
215 | // int ISTORE_1 = 60; // - | |
216 | // int ISTORE_2 = 61; // - | |
217 | // int ISTORE_3 = 62; // - | |
218 | // int LSTORE_0 = 63; // - | |
219 | // int LSTORE_1 = 64; // - | |
220 | // int LSTORE_2 = 65; // - | |
221 | // int LSTORE_3 = 66; // - | |
222 | // int FSTORE_0 = 67; // - | |
223 | // int FSTORE_1 = 68; // - | |
224 | // int FSTORE_2 = 69; // - | |
225 | // int FSTORE_3 = 70; // - | |
226 | // int DSTORE_0 = 71; // - | |
227 | // int DSTORE_1 = 72; // - | |
228 | // int DSTORE_2 = 73; // - | |
229 | // int DSTORE_3 = 74; // - | |
230 | // int ASTORE_0 = 75; // - | |
231 | // int ASTORE_1 = 76; // - | |
232 | // int ASTORE_2 = 77; // - | |
233 | // int ASTORE_3 = 78; // - | |
234 | int IASTORE = 79; // visitInsn | |
235 | int LASTORE = 80; // - | |
236 | int FASTORE = 81; // - | |
237 | int DASTORE = 82; // - | |
238 | int AASTORE = 83; // - | |
239 | int BASTORE = 84; // - | |
240 | int CASTORE = 85; // - | |
241 | int SASTORE = 86; // - | |
242 | int POP = 87; // - | |
243 | int POP2 = 88; // - | |
244 | int DUP = 89; // - | |
245 | int DUP_X1 = 90; // - | |
246 | int DUP_X2 = 91; // - | |
247 | int DUP2 = 92; // - | |
248 | int DUP2_X1 = 93; // - | |
249 | int DUP2_X2 = 94; // - | |
250 | int SWAP = 95; // - | |
251 | int IADD = 96; // - | |
252 | int LADD = 97; // - | |
253 | int FADD = 98; // - | |
254 | int DADD = 99; // - | |
255 | int ISUB = 100; // - | |
256 | int LSUB = 101; // - | |
257 | int FSUB = 102; // - | |
258 | int DSUB = 103; // - | |
259 | int IMUL = 104; // - | |
260 | int LMUL = 105; // - | |
261 | int FMUL = 106; // - | |
262 | int DMUL = 107; // - | |
263 | int IDIV = 108; // - | |
264 | int LDIV = 109; // - | |
265 | int FDIV = 110; // - | |
266 | int DDIV = 111; // - | |
267 | int IREM = 112; // - | |
268 | int LREM = 113; // - | |
269 | int FREM = 114; // - | |
270 | int DREM = 115; // - | |
271 | int INEG = 116; // - | |
272 | int LNEG = 117; // - | |
273 | int FNEG = 118; // - | |
274 | int DNEG = 119; // - | |
275 | int ISHL = 120; // - | |
276 | int LSHL = 121; // - | |
277 | int ISHR = 122; // - | |
278 | int LSHR = 123; // - | |
279 | int IUSHR = 124; // - | |
280 | int LUSHR = 125; // - | |
281 | int IAND = 126; // - | |
282 | int LAND = 127; // - | |
283 | int IOR = 128; // - | |
284 | int LOR = 129; // - | |
285 | int IXOR = 130; // - | |
286 | int LXOR = 131; // - | |
287 | int IINC = 132; // visitIincInsn | |
288 | int I2L = 133; // visitInsn | |
289 | int I2F = 134; // - | |
290 | int I2D = 135; // - | |
291 | int L2I = 136; // - | |
292 | int L2F = 137; // - | |
293 | int L2D = 138; // - | |
294 | int F2I = 139; // - | |
295 | int F2L = 140; // - | |
296 | int F2D = 141; // - | |
297 | int D2I = 142; // - | |
298 | int D2L = 143; // - | |
299 | int D2F = 144; // - | |
300 | int I2B = 145; // - | |
301 | int I2C = 146; // - | |
302 | int I2S = 147; // - | |
303 | int LCMP = 148; // - | |
304 | int FCMPL = 149; // - | |
305 | int FCMPG = 150; // - | |
306 | int DCMPL = 151; // - | |
307 | int DCMPG = 152; // - | |
308 | int IFEQ = 153; // visitJumpInsn | |
309 | int IFNE = 154; // - | |
310 | int IFLT = 155; // - | |
311 | int IFGE = 156; // - | |
312 | int IFGT = 157; // - | |
313 | int IFLE = 158; // - | |
314 | int IF_ICMPEQ = 159; // - | |
315 | int IF_ICMPNE = 160; // - | |
316 | int IF_ICMPLT = 161; // - | |
317 | int IF_ICMPGE = 162; // - | |
318 | int IF_ICMPGT = 163; // - | |
319 | int IF_ICMPLE = 164; // - | |
320 | int IF_ACMPEQ = 165; // - | |
321 | int IF_ACMPNE = 166; // - | |
322 | int GOTO = 167; // - | |
323 | int JSR = 168; // - | |
324 | int RET = 169; // visitVarInsn | |
325 | int TABLESWITCH = 170; // visiTableSwitchInsn | |
326 | int LOOKUPSWITCH = 171; // visitLookupSwitch | |
327 | int IRETURN = 172; // visitInsn | |
328 | int LRETURN = 173; // - | |
329 | int FRETURN = 174; // - | |
330 | int DRETURN = 175; // - | |
331 | int ARETURN = 176; // - | |
332 | int RETURN = 177; // - | |
333 | int GETSTATIC = 178; // visitFieldInsn | |
334 | int PUTSTATIC = 179; // - | |
335 | int GETFIELD = 180; // - | |
336 | int PUTFIELD = 181; // - | |
337 | int INVOKEVIRTUAL = 182; // visitMethodInsn | |
338 | int INVOKESPECIAL = 183; // - | |
339 | int INVOKESTATIC = 184; // - | |
340 | int INVOKEINTERFACE = 185; // - | |
341 | int INVOKEDYNAMIC = 186; // visitInvokeDynamicInsn | |
342 | int NEW = 187; // visitTypeInsn | |
343 | int NEWARRAY = 188; // visitIntInsn | |
344 | int ANEWARRAY = 189; // visitTypeInsn | |
345 | int ARRAYLENGTH = 190; // visitInsn | |
346 | int ATHROW = 191; // - | |
347 | int CHECKCAST = 192; // visitTypeInsn | |
348 | int INSTANCEOF = 193; // - | |
349 | int MONITORENTER = 194; // visitInsn | |
350 | int MONITOREXIT = 195; // - | |
351 | // int WIDE = 196; // NOT VISITED | |
352 | int MULTIANEWARRAY = 197; // visitMultiANewArrayInsn | |
353 | int IFNULL = 198; // visitJumpInsn | |
354 | int IFNONNULL = 199; // - | |
355 | // int GOTO_W = 200; // - | |
356 | // int JSR_W = 201; // - | |
357 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 field or method type. This class can be used to make it easier to | |
36 | * manipulate type and 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 static final int VOID = 0; | |
47 | ||
48 | /** | |
49 | * The sort of the <tt>boolean</tt> type. See {@link #getSort getSort}. | |
50 | */ | |
51 | public static final int BOOLEAN = 1; | |
52 | ||
53 | /** | |
54 | * The sort of the <tt>char</tt> type. See {@link #getSort getSort}. | |
55 | */ | |
56 | public static final int CHAR = 2; | |
57 | ||
58 | /** | |
59 | * The sort of the <tt>byte</tt> type. See {@link #getSort getSort}. | |
60 | */ | |
61 | public static final int BYTE = 3; | |
62 | ||
63 | /** | |
64 | * The sort of the <tt>short</tt> type. See {@link #getSort getSort}. | |
65 | */ | |
66 | public static final int SHORT = 4; | |
67 | ||
68 | /** | |
69 | * The sort of the <tt>int</tt> type. See {@link #getSort getSort}. | |
70 | */ | |
71 | public static final int INT = 5; | |
72 | ||
73 | /** | |
74 | * The sort of the <tt>float</tt> type. See {@link #getSort getSort}. | |
75 | */ | |
76 | public static final int FLOAT = 6; | |
77 | ||
78 | /** | |
79 | * The sort of the <tt>long</tt> type. See {@link #getSort getSort}. | |
80 | */ | |
81 | public static final int LONG = 7; | |
82 | ||
83 | /** | |
84 | * The sort of the <tt>double</tt> type. See {@link #getSort getSort}. | |
85 | */ | |
86 | public static final int DOUBLE = 8; | |
87 | ||
88 | /** | |
89 | * The sort of array reference types. See {@link #getSort getSort}. | |
90 | */ | |
91 | public static final int ARRAY = 9; | |
92 | ||
93 | /** | |
94 | * The sort of object reference types. See {@link #getSort getSort}. | |
95 | */ | |
96 | public static final int OBJECT = 10; | |
97 | ||
98 | /** | |
99 | * The sort of method types. See {@link #getSort getSort}. | |
100 | */ | |
101 | public static final int METHOD = 11; | |
102 | ||
103 | /** | |
104 | * The <tt>void</tt> type. | |
105 | */ | |
106 | public static final Type VOID_TYPE = new Type(VOID, null, ('V' << 24) | |
107 | | (5 << 16) | (0 << 8) | 0, 1); | |
108 | ||
109 | /** | |
110 | * The <tt>boolean</tt> type. | |
111 | */ | |
112 | public static final Type BOOLEAN_TYPE = new Type(BOOLEAN, null, ('Z' << 24) | |
113 | | (0 << 16) | (5 << 8) | 1, 1); | |
114 | ||
115 | /** | |
116 | * The <tt>char</tt> type. | |
117 | */ | |
118 | public static final Type CHAR_TYPE = new Type(CHAR, null, ('C' << 24) | |
119 | | (0 << 16) | (6 << 8) | 1, 1); | |
120 | ||
121 | /** | |
122 | * The <tt>byte</tt> type. | |
123 | */ | |
124 | public static final Type BYTE_TYPE = new Type(BYTE, null, ('B' << 24) | |
125 | | (0 << 16) | (5 << 8) | 1, 1); | |
126 | ||
127 | /** | |
128 | * The <tt>short</tt> type. | |
129 | */ | |
130 | public static final Type SHORT_TYPE = new Type(SHORT, null, ('S' << 24) | |
131 | | (0 << 16) | (7 << 8) | 1, 1); | |
132 | ||
133 | /** | |
134 | * The <tt>int</tt> type. | |
135 | */ | |
136 | public static final Type INT_TYPE = new Type(INT, null, ('I' << 24) | |
137 | | (0 << 16) | (0 << 8) | 1, 1); | |
138 | ||
139 | /** | |
140 | * The <tt>float</tt> type. | |
141 | */ | |
142 | public static final Type FLOAT_TYPE = new Type(FLOAT, null, ('F' << 24) | |
143 | | (2 << 16) | (2 << 8) | 1, 1); | |
144 | ||
145 | /** | |
146 | * The <tt>long</tt> type. | |
147 | */ | |
148 | public static final Type LONG_TYPE = new Type(LONG, null, ('J' << 24) | |
149 | | (1 << 16) | (1 << 8) | 2, 1); | |
150 | ||
151 | /** | |
152 | * The <tt>double</tt> type. | |
153 | */ | |
154 | public static final Type DOUBLE_TYPE = new Type(DOUBLE, null, ('D' << 24) | |
155 | | (3 << 16) | (3 << 8) | 2, 1); | |
156 | ||
157 | // ------------------------------------------------------------------------ | |
158 | // Fields | |
159 | // ------------------------------------------------------------------------ | |
160 | ||
161 | /** | |
162 | * The sort of this Java type. | |
163 | */ | |
164 | private final int sort; | |
165 | ||
166 | /** | |
167 | * A buffer containing the internal name of this Java type. This field is | |
168 | * only used for reference types. | |
169 | */ | |
170 | private final char[] buf; | |
171 | ||
172 | /** | |
173 | * The offset of the internal name of this Java type in {@link #buf buf} or, | |
174 | * for primitive types, the size, descriptor and getOpcode offsets for this | |
175 | * type (byte 0 contains the size, byte 1 the descriptor, byte 2 the offset | |
176 | * for IALOAD or IASTORE, byte 3 the offset for all other instructions). | |
177 | */ | |
178 | private final int off; | |
179 | ||
180 | /** | |
181 | * The length of the internal name of this Java type. | |
182 | */ | |
183 | private final int len; | |
184 | ||
185 | // ------------------------------------------------------------------------ | |
186 | // Constructors | |
187 | // ------------------------------------------------------------------------ | |
188 | ||
189 | /** | |
190 | * Constructs a reference type. | |
191 | * | |
192 | * @param sort | |
193 | * the sort of the reference type to be constructed. | |
194 | * @param buf | |
195 | * a buffer containing the descriptor of the previous type. | |
196 | * @param off | |
197 | * the offset of this descriptor in the previous buffer. | |
198 | * @param len | |
199 | * the length of this descriptor. | |
200 | */ | |
201 | private Type(final int sort, final char[] buf, final int off, final int len) { | |
202 | this.sort = sort; | |
203 | this.buf = buf; | |
204 | this.off = off; | |
205 | this.len = len; | |
206 | } | |
207 | ||
208 | /** | |
209 | * Returns the Java type corresponding to the given type descriptor. | |
210 | * | |
211 | * @param typeDescriptor | |
212 | * a field or method type descriptor. | |
213 | * @return the Java type corresponding to the given type descriptor. | |
214 | */ | |
215 | public static Type getType(final String typeDescriptor) { | |
216 | return getType(typeDescriptor.toCharArray(), 0); | |
217 | } | |
218 | ||
219 | /** | |
220 | * Returns the Java type corresponding to the given internal name. | |
221 | * | |
222 | * @param internalName | |
223 | * an internal name. | |
224 | * @return the Java type corresponding to the given internal name. | |
225 | */ | |
226 | public static Type getObjectType(final String internalName) { | |
227 | char[] buf = internalName.toCharArray(); | |
228 | return new Type(buf[0] == '[' ? ARRAY : OBJECT, buf, 0, buf.length); | |
229 | } | |
230 | ||
231 | /** | |
232 | * Returns the Java type corresponding to the given method descriptor. | |
233 | * Equivalent to <code>Type.getType(methodDescriptor)</code>. | |
234 | * | |
235 | * @param methodDescriptor | |
236 | * a method descriptor. | |
237 | * @return the Java type corresponding to the given method descriptor. | |
238 | */ | |
239 | public static Type getMethodType(final String methodDescriptor) { | |
240 | return getType(methodDescriptor.toCharArray(), 0); | |
241 | } | |
242 | ||
243 | /** | |
244 | * Returns the Java method type corresponding to the given argument and | |
245 | * return types. | |
246 | * | |
247 | * @param returnType | |
248 | * the return type of the method. | |
249 | * @param argumentTypes | |
250 | * the argument types of the method. | |
251 | * @return the Java type corresponding to the given argument and return | |
252 | * types. | |
253 | */ | |
254 | public static Type getMethodType(final Type returnType, | |
255 | final Type... argumentTypes) { | |
256 | return getType(getMethodDescriptor(returnType, argumentTypes)); | |
257 | } | |
258 | ||
259 | /** | |
260 | * Returns the Java type corresponding to the given class. | |
261 | * | |
262 | * @param c | |
263 | * a class. | |
264 | * @return the Java type corresponding to the given class. | |
265 | */ | |
266 | public static Type getType(final Class<?> c) { | |
267 | if (c.isPrimitive()) { | |
268 | if (c == Integer.TYPE) { | |
269 | return INT_TYPE; | |
270 | } else if (c == Void.TYPE) { | |
271 | return VOID_TYPE; | |
272 | } else if (c == Boolean.TYPE) { | |
273 | return BOOLEAN_TYPE; | |
274 | } else if (c == Byte.TYPE) { | |
275 | return BYTE_TYPE; | |
276 | } else if (c == Character.TYPE) { | |
277 | return CHAR_TYPE; | |
278 | } else if (c == Short.TYPE) { | |
279 | return SHORT_TYPE; | |
280 | } else if (c == Double.TYPE) { | |
281 | return DOUBLE_TYPE; | |
282 | } else if (c == Float.TYPE) { | |
283 | return FLOAT_TYPE; | |
284 | } else /* if (c == Long.TYPE) */{ | |
285 | return LONG_TYPE; | |
286 | } | |
287 | } else { | |
288 | return getType(getDescriptor(c)); | |
289 | } | |
290 | } | |
291 | ||
292 | /** | |
293 | * Returns the Java method type corresponding to the given constructor. | |
294 | * | |
295 | * @param c | |
296 | * a {@link Constructor Constructor} object. | |
297 | * @return the Java method type corresponding to the given constructor. | |
298 | */ | |
299 | public static Type getType(final Constructor<?> c) { | |
300 | return getType(getConstructorDescriptor(c)); | |
301 | } | |
302 | ||
303 | /** | |
304 | * Returns the Java method type corresponding to the given method. | |
305 | * | |
306 | * @param m | |
307 | * a {@link Method Method} object. | |
308 | * @return the Java method type corresponding to the given method. | |
309 | */ | |
310 | public static Type getType(final Method m) { | |
311 | return getType(getMethodDescriptor(m)); | |
312 | } | |
313 | ||
314 | /** | |
315 | * Returns the Java types corresponding to the argument types of the given | |
316 | * method descriptor. | |
317 | * | |
318 | * @param methodDescriptor | |
319 | * a method descriptor. | |
320 | * @return the Java types corresponding to the argument types of the given | |
321 | * method descriptor. | |
322 | */ | |
323 | public static Type[] getArgumentTypes(final String methodDescriptor) { | |
324 | char[] buf = methodDescriptor.toCharArray(); | |
325 | int off = 1; | |
326 | int size = 0; | |
327 | while (true) { | |
328 | char car = buf[off++]; | |
329 | if (car == ')') { | |
330 | break; | |
331 | } else if (car == 'L') { | |
332 | while (buf[off++] != ';') { | |
333 | } | |
334 | ++size; | |
335 | } else if (car != '[') { | |
336 | ++size; | |
337 | } | |
338 | } | |
339 | Type[] args = new Type[size]; | |
340 | off = 1; | |
341 | size = 0; | |
342 | while (buf[off] != ')') { | |
343 | args[size] = getType(buf, off); | |
344 | off += args[size].len + (args[size].sort == OBJECT ? 2 : 0); | |
345 | size += 1; | |
346 | } | |
347 | return args; | |
348 | } | |
349 | ||
350 | /** | |
351 | * Returns the Java types corresponding to the argument types of the given | |
352 | * method. | |
353 | * | |
354 | * @param method | |
355 | * a method. | |
356 | * @return the Java types corresponding to the argument types of the given | |
357 | * method. | |
358 | */ | |
359 | public static Type[] getArgumentTypes(final Method method) { | |
360 | Class<?>[] classes = method.getParameterTypes(); | |
361 | Type[] types = new Type[classes.length]; | |
362 | for (int i = classes.length - 1; i >= 0; --i) { | |
363 | types[i] = getType(classes[i]); | |
364 | } | |
365 | return types; | |
366 | } | |
367 | ||
368 | /** | |
369 | * Returns the Java type corresponding to the return type of the given | |
370 | * method descriptor. | |
371 | * | |
372 | * @param methodDescriptor | |
373 | * a method descriptor. | |
374 | * @return the Java type corresponding to the return type of the given | |
375 | * method descriptor. | |
376 | */ | |
377 | public static Type getReturnType(final String methodDescriptor) { | |
378 | char[] buf = methodDescriptor.toCharArray(); | |
379 | return getType(buf, methodDescriptor.indexOf(')') + 1); | |
380 | } | |
381 | ||
382 | /** | |
383 | * Returns the Java type corresponding to the return type of the given | |
384 | * method. | |
385 | * | |
386 | * @param method | |
387 | * a method. | |
388 | * @return the Java type corresponding to the return type of the given | |
389 | * method. | |
390 | */ | |
391 | public static Type getReturnType(final Method method) { | |
392 | return getType(method.getReturnType()); | |
393 | } | |
394 | ||
395 | /** | |
396 | * Computes the size of the arguments and of the return value of a method. | |
397 | * | |
398 | * @param desc | |
399 | * the descriptor of a method. | |
400 | * @return the size of the arguments of the method (plus one for the | |
401 | * implicit this argument), argSize, and the size of its return | |
402 | * value, retSize, packed into a single int i = | |
403 | * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal to | |
404 | * <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>). | |
405 | */ | |
406 | public static int getArgumentsAndReturnSizes(final String desc) { | |
407 | int n = 1; | |
408 | int c = 1; | |
409 | while (true) { | |
410 | char car = desc.charAt(c++); | |
411 | if (car == ')') { | |
412 | car = desc.charAt(c); | |
413 | return n << 2 | |
414 | | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1)); | |
415 | } else if (car == 'L') { | |
416 | while (desc.charAt(c++) != ';') { | |
417 | } | |
418 | n += 1; | |
419 | } else if (car == '[') { | |
420 | while ((car = desc.charAt(c)) == '[') { | |
421 | ++c; | |
422 | } | |
423 | if (car == 'D' || car == 'J') { | |
424 | n -= 1; | |
425 | } | |
426 | } else if (car == 'D' || car == 'J') { | |
427 | n += 2; | |
428 | } else { | |
429 | n += 1; | |
430 | } | |
431 | } | |
432 | } | |
433 | ||
434 | /** | |
435 | * Returns the Java type corresponding to the given type descriptor. For | |
436 | * method descriptors, buf is supposed to contain nothing more than the | |
437 | * descriptor itself. | |
438 | * | |
439 | * @param buf | |
440 | * a buffer containing a type descriptor. | |
441 | * @param off | |
442 | * the offset of this descriptor in the previous buffer. | |
443 | * @return the Java type corresponding to the given type descriptor. | |
444 | */ | |
445 | private static Type getType(final char[] buf, final int off) { | |
446 | int len; | |
447 | switch (buf[off]) { | |
448 | case 'V': | |
449 | return VOID_TYPE; | |
450 | case 'Z': | |
451 | return BOOLEAN_TYPE; | |
452 | case 'C': | |
453 | return CHAR_TYPE; | |
454 | case 'B': | |
455 | return BYTE_TYPE; | |
456 | case 'S': | |
457 | return SHORT_TYPE; | |
458 | case 'I': | |
459 | return INT_TYPE; | |
460 | case 'F': | |
461 | return FLOAT_TYPE; | |
462 | case 'J': | |
463 | return LONG_TYPE; | |
464 | case 'D': | |
465 | return DOUBLE_TYPE; | |
466 | case '[': | |
467 | len = 1; | |
468 | while (buf[off + len] == '[') { | |
469 | ++len; | |
470 | } | |
471 | if (buf[off + len] == 'L') { | |
472 | ++len; | |
473 | while (buf[off + len] != ';') { | |
474 | ++len; | |
475 | } | |
476 | } | |
477 | return new Type(ARRAY, buf, off, len + 1); | |
478 | case 'L': | |
479 | len = 1; | |
480 | while (buf[off + len] != ';') { | |
481 | ++len; | |
482 | } | |
483 | return new Type(OBJECT, buf, off + 1, len - 1); | |
484 | // case '(': | |
485 | default: | |
486 | return new Type(METHOD, buf, off, buf.length - off); | |
487 | } | |
488 | } | |
489 | ||
490 | // ------------------------------------------------------------------------ | |
491 | // Accessors | |
492 | // ------------------------------------------------------------------------ | |
493 | ||
494 | /** | |
495 | * Returns the sort of this Java type. | |
496 | * | |
497 | * @return {@link #VOID VOID}, {@link #BOOLEAN BOOLEAN}, {@link #CHAR CHAR}, | |
498 | * {@link #BYTE BYTE}, {@link #SHORT SHORT}, {@link #INT INT}, | |
499 | * {@link #FLOAT FLOAT}, {@link #LONG LONG}, {@link #DOUBLE DOUBLE}, | |
500 | * {@link #ARRAY ARRAY}, {@link #OBJECT OBJECT} or {@link #METHOD | |
501 | * METHOD}. | |
502 | */ | |
503 | public int getSort() { | |
504 | return sort; | |
505 | } | |
506 | ||
507 | /** | |
508 | * Returns the number of dimensions of this array type. This method should | |
509 | * only be used for an array type. | |
510 | * | |
511 | * @return the number of dimensions of this array type. | |
512 | */ | |
513 | public int getDimensions() { | |
514 | int i = 1; | |
515 | while (buf[off + i] == '[') { | |
516 | ++i; | |
517 | } | |
518 | return i; | |
519 | } | |
520 | ||
521 | /** | |
522 | * Returns the type of the elements of this array type. This method should | |
523 | * only be used for an array type. | |
524 | * | |
525 | * @return Returns the type of the elements of this array type. | |
526 | */ | |
527 | public Type getElementType() { | |
528 | return getType(buf, off + getDimensions()); | |
529 | } | |
530 | ||
531 | /** | |
532 | * Returns the binary name of the class corresponding to this type. This | |
533 | * method must not be used on method types. | |
534 | * | |
535 | * @return the binary name of the class corresponding to this type. | |
536 | */ | |
537 | public String getClassName() { | |
538 | switch (sort) { | |
539 | case VOID: | |
540 | return "void"; | |
541 | case BOOLEAN: | |
542 | return "boolean"; | |
543 | case CHAR: | |
544 | return "char"; | |
545 | case BYTE: | |
546 | return "byte"; | |
547 | case SHORT: | |
548 | return "short"; | |
549 | case INT: | |
550 | return "int"; | |
551 | case FLOAT: | |
552 | return "float"; | |
553 | case LONG: | |
554 | return "long"; | |
555 | case DOUBLE: | |
556 | return "double"; | |
557 | case ARRAY: | |
558 | StringBuffer b = new StringBuffer(getElementType().getClassName()); | |
559 | for (int i = getDimensions(); i > 0; --i) { | |
560 | b.append("[]"); | |
561 | } | |
562 | return b.toString(); | |
563 | case OBJECT: | |
564 | return new String(buf, off, len).replace('/', '.'); | |
565 | default: | |
566 | return null; | |
567 | } | |
568 | } | |
569 | ||
570 | /** | |
571 | * Returns the internal name of the class corresponding to this object or | |
572 | * array type. The internal name of a class is its fully qualified name (as | |
573 | * returned by Class.getName(), where '.' are replaced by '/'. This method | |
574 | * should only be used for an object or array type. | |
575 | * | |
576 | * @return the internal name of the class corresponding to this object type. | |
577 | */ | |
578 | public String getInternalName() { | |
579 | return new String(buf, off, len); | |
580 | } | |
581 | ||
582 | /** | |
583 | * Returns the argument types of methods of this type. This method should | |
584 | * only be used for method types. | |
585 | * | |
586 | * @return the argument types of methods of this type. | |
587 | */ | |
588 | public Type[] getArgumentTypes() { | |
589 | return getArgumentTypes(getDescriptor()); | |
590 | } | |
591 | ||
592 | /** | |
593 | * Returns the return type of methods of this type. This method should only | |
594 | * be used for method types. | |
595 | * | |
596 | * @return the return type of methods of this type. | |
597 | */ | |
598 | public Type getReturnType() { | |
599 | return getReturnType(getDescriptor()); | |
600 | } | |
601 | ||
602 | /** | |
603 | * Returns the size of the arguments and of the return value of methods of | |
604 | * this type. This method should only be used for method types. | |
605 | * | |
606 | * @return the size of the arguments (plus one for the implicit this | |
607 | * argument), argSize, and the size of the return value, retSize, | |
608 | * packed into a single int i = <tt>(argSize << 2) | retSize</tt> | |
609 | * (argSize is therefore equal to <tt>i >> 2</tt>, and retSize to | |
610 | * <tt>i & 0x03</tt>). | |
611 | */ | |
612 | public int getArgumentsAndReturnSizes() { | |
613 | return getArgumentsAndReturnSizes(getDescriptor()); | |
614 | } | |
615 | ||
616 | // ------------------------------------------------------------------------ | |
617 | // Conversion to type descriptors | |
618 | // ------------------------------------------------------------------------ | |
619 | ||
620 | /** | |
621 | * Returns the descriptor corresponding to this Java type. | |
622 | * | |
623 | * @return the descriptor corresponding to this Java type. | |
624 | */ | |
625 | public String getDescriptor() { | |
626 | StringBuffer buf = new StringBuffer(); | |
627 | getDescriptor(buf); | |
628 | return buf.toString(); | |
629 | } | |
630 | ||
631 | /** | |
632 | * Returns the descriptor corresponding to the given argument and return | |
633 | * types. | |
634 | * | |
635 | * @param returnType | |
636 | * the return type of the method. | |
637 | * @param argumentTypes | |
638 | * the argument types of the method. | |
639 | * @return the descriptor corresponding to the given argument and return | |
640 | * types. | |
641 | */ | |
642 | public static String getMethodDescriptor(final Type returnType, | |
643 | final Type... argumentTypes) { | |
644 | StringBuffer buf = new StringBuffer(); | |
645 | buf.append('('); | |
646 | for (int i = 0; i < argumentTypes.length; ++i) { | |
647 | argumentTypes[i].getDescriptor(buf); | |
648 | } | |
649 | buf.append(')'); | |
650 | returnType.getDescriptor(buf); | |
651 | return buf.toString(); | |
652 | } | |
653 | ||
654 | /** | |
655 | * Appends the descriptor corresponding to this Java type to the given | |
656 | * string buffer. | |
657 | * | |
658 | * @param buf | |
659 | * the string buffer to which the descriptor must be appended. | |
660 | */ | |
661 | private void getDescriptor(final StringBuffer buf) { | |
662 | if (this.buf == null) { | |
663 | // descriptor is in byte 3 of 'off' for primitive types (buf == | |
664 | // null) | |
665 | buf.append((char) ((off & 0xFF000000) >>> 24)); | |
666 | } else if (sort == OBJECT) { | |
667 | buf.append('L'); | |
668 | buf.append(this.buf, off, len); | |
669 | buf.append(';'); | |
670 | } else { // sort == ARRAY || sort == METHOD | |
671 | buf.append(this.buf, off, len); | |
672 | } | |
673 | } | |
674 | ||
675 | // ------------------------------------------------------------------------ | |
676 | // Direct conversion from classes to type descriptors, | |
677 | // without intermediate Type objects | |
678 | // ------------------------------------------------------------------------ | |
679 | ||
680 | /** | |
681 | * Returns the internal name of the given class. The internal name of a | |
682 | * class is its fully qualified name, as returned by Class.getName(), where | |
683 | * '.' are replaced by '/'. | |
684 | * | |
685 | * @param c | |
686 | * an object or array class. | |
687 | * @return the internal name of the given class. | |
688 | */ | |
689 | public static String getInternalName(final Class<?> c) { | |
690 | return c.getName().replace('.', '/'); | |
691 | } | |
692 | ||
693 | /** | |
694 | * Returns the descriptor corresponding to the given Java type. | |
695 | * | |
696 | * @param c | |
697 | * an object class, a primitive class or an array class. | |
698 | * @return the descriptor corresponding to the given class. | |
699 | */ | |
700 | public static String getDescriptor(final Class<?> c) { | |
701 | StringBuffer buf = new StringBuffer(); | |
702 | getDescriptor(buf, c); | |
703 | return buf.toString(); | |
704 | } | |
705 | ||
706 | /** | |
707 | * Returns the descriptor corresponding to the given constructor. | |
708 | * | |
709 | * @param c | |
710 | * a {@link Constructor Constructor} object. | |
711 | * @return the descriptor of the given constructor. | |
712 | */ | |
713 | public static String getConstructorDescriptor(final Constructor<?> c) { | |
714 | Class<?>[] parameters = c.getParameterTypes(); | |
715 | StringBuffer buf = new StringBuffer(); | |
716 | buf.append('('); | |
717 | for (int i = 0; i < parameters.length; ++i) { | |
718 | getDescriptor(buf, parameters[i]); | |
719 | } | |
720 | return buf.append(")V").toString(); | |
721 | } | |
722 | ||
723 | /** | |
724 | * Returns the descriptor corresponding to the given method. | |
725 | * | |
726 | * @param m | |
727 | * a {@link Method Method} object. | |
728 | * @return the descriptor of the given method. | |
729 | */ | |
730 | public static String getMethodDescriptor(final Method m) { | |
731 | Class<?>[] parameters = m.getParameterTypes(); | |
732 | StringBuffer buf = new StringBuffer(); | |
733 | buf.append('('); | |
734 | for (int i = 0; i < parameters.length; ++i) { | |
735 | getDescriptor(buf, parameters[i]); | |
736 | } | |
737 | buf.append(')'); | |
738 | getDescriptor(buf, m.getReturnType()); | |
739 | return buf.toString(); | |
740 | } | |
741 | ||
742 | /** | |
743 | * Appends the descriptor of the given class to the given string buffer. | |
744 | * | |
745 | * @param buf | |
746 | * the string buffer to which the descriptor must be appended. | |
747 | * @param c | |
748 | * the class whose descriptor must be computed. | |
749 | */ | |
750 | private static void getDescriptor(final StringBuffer buf, final Class<?> c) { | |
751 | Class<?> d = c; | |
752 | while (true) { | |
753 | if (d.isPrimitive()) { | |
754 | char car; | |
755 | if (d == Integer.TYPE) { | |
756 | car = 'I'; | |
757 | } else if (d == Void.TYPE) { | |
758 | car = 'V'; | |
759 | } else if (d == Boolean.TYPE) { | |
760 | car = 'Z'; | |
761 | } else if (d == Byte.TYPE) { | |
762 | car = 'B'; | |
763 | } else if (d == Character.TYPE) { | |
764 | car = 'C'; | |
765 | } else if (d == Short.TYPE) { | |
766 | car = 'S'; | |
767 | } else if (d == Double.TYPE) { | |
768 | car = 'D'; | |
769 | } else if (d == Float.TYPE) { | |
770 | car = 'F'; | |
771 | } else /* if (d == Long.TYPE) */{ | |
772 | car = 'J'; | |
773 | } | |
774 | buf.append(car); | |
775 | return; | |
776 | } else if (d.isArray()) { | |
777 | buf.append('['); | |
778 | d = d.getComponentType(); | |
779 | } else { | |
780 | buf.append('L'); | |
781 | String name = d.getName(); | |
782 | int len = name.length(); | |
783 | for (int i = 0; i < len; ++i) { | |
784 | char car = name.charAt(i); | |
785 | buf.append(car == '.' ? '/' : car); | |
786 | } | |
787 | buf.append(';'); | |
788 | return; | |
789 | } | |
790 | } | |
791 | } | |
792 | ||
793 | // ------------------------------------------------------------------------ | |
794 | // Corresponding size and opcodes | |
795 | // ------------------------------------------------------------------------ | |
796 | ||
797 | /** | |
798 | * Returns the size of values of this type. This method must not be used for | |
799 | * method types. | |
800 | * | |
801 | * @return the size of values of this type, i.e., 2 for <tt>long</tt> and | |
802 | * <tt>double</tt>, 0 for <tt>void</tt> and 1 otherwise. | |
803 | */ | |
804 | public int getSize() { | |
805 | // the size is in byte 0 of 'off' for primitive types (buf == null) | |
806 | return buf == null ? (off & 0xFF) : 1; | |
807 | } | |
808 | ||
809 | /** | |
810 | * Returns a JVM instruction opcode adapted to this Java type. This method | |
811 | * must not be used for method types. | |
812 | * | |
813 | * @param opcode | |
814 | * a JVM instruction opcode. This opcode must be one of ILOAD, | |
815 | * ISTORE, IALOAD, IASTORE, IADD, ISUB, IMUL, IDIV, IREM, INEG, | |
816 | * ISHL, ISHR, IUSHR, IAND, IOR, IXOR and IRETURN. | |
817 | * @return an opcode that is similar to the given opcode, but adapted to | |
818 | * this Java type. For example, if this type is <tt>float</tt> and | |
819 | * <tt>opcode</tt> is IRETURN, this method returns FRETURN. | |
820 | */ | |
821 | public int getOpcode(final int opcode) { | |
822 | if (opcode == Opcodes.IALOAD || opcode == Opcodes.IASTORE) { | |
823 | // the offset for IALOAD or IASTORE is in byte 1 of 'off' for | |
824 | // primitive types (buf == null) | |
825 | return opcode + (buf == null ? (off & 0xFF00) >> 8 : 4); | |
826 | } else { | |
827 | // the offset for other instructions is in byte 2 of 'off' for | |
828 | // primitive types (buf == null) | |
829 | return opcode + (buf == null ? (off & 0xFF0000) >> 16 : 4); | |
830 | } | |
831 | } | |
832 | ||
833 | // ------------------------------------------------------------------------ | |
834 | // Equals, hashCode and toString | |
835 | // ------------------------------------------------------------------------ | |
836 | ||
837 | /** | |
838 | * Tests if the given object is equal to this type. | |
839 | * | |
840 | * @param o | |
841 | * the object to be compared to this type. | |
842 | * @return <tt>true</tt> if the given object is equal to this type. | |
843 | */ | |
844 | @Override | |
845 | public boolean equals(final Object o) { | |
846 | if (this == o) { | |
847 | return true; | |
848 | } | |
849 | if (!(o instanceof Type)) { | |
850 | return false; | |
851 | } | |
852 | Type t = (Type) o; | |
853 | if (sort != t.sort) { | |
854 | return false; | |
855 | } | |
856 | if (sort >= ARRAY) { | |
857 | if (len != t.len) { | |
858 | return false; | |
859 | } | |
860 | for (int i = off, j = t.off, end = i + len; i < end; i++, j++) { | |
861 | if (buf[i] != t.buf[j]) { | |
862 | return false; | |
863 | } | |
864 | } | |
865 | } | |
866 | return true; | |
867 | } | |
868 | ||
869 | /** | |
870 | * Returns a hash code value for this type. | |
871 | * | |
872 | * @return a hash code value for this type. | |
873 | */ | |
874 | @Override | |
875 | public int hashCode() { | |
876 | int hc = 13 * sort; | |
877 | if (sort >= ARRAY) { | |
878 | for (int i = off, end = i + len; i < end; i++) { | |
879 | hc = 17 * (hc + buf[i]); | |
880 | } | |
881 | } | |
882 | return hc; | |
883 | } | |
884 | ||
885 | /** | |
886 | * Returns a string representation of this type. | |
887 | * | |
888 | * @return the descriptor of this type. | |
889 | */ | |
890 | @Override | |
891 | public String toString() { | |
892 | return getDescriptor(); | |
893 | } | |
894 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.Handle; | |
37 | import clojure.asm.Label; | |
38 | import clojure.asm.MethodVisitor; | |
39 | import clojure.asm.Opcodes; | |
40 | import clojure.asm.Type; | |
41 | ||
42 | /** | |
43 | * A {@link clojure.asm.MethodVisitor} to insert before, after and around | |
44 | * advices in methods and constructors. | |
45 | * <p> | |
46 | * The behavior for constructors is like this: | |
47 | * <ol> | |
48 | * | |
49 | * <li>as long as the INVOKESPECIAL for the object initialization has not been | |
50 | * reached, every bytecode instruction is dispatched in the ctor code visitor</li> | |
51 | * | |
52 | * <li>when this one is reached, it is only added in the ctor code visitor and a | |
53 | * JP invoke is added</li> | |
54 | * | |
55 | * <li>after that, only the other code visitor receives the instructions</li> | |
56 | * | |
57 | * </ol> | |
58 | * | |
59 | * @author Eugene Kuleshov | |
60 | * @author Eric Bruneton | |
61 | */ | |
62 | public abstract class AdviceAdapter extends GeneratorAdapter implements Opcodes { | |
63 | ||
64 | private static final Object THIS = new Object(); | |
65 | ||
66 | private static final Object OTHER = new Object(); | |
67 | ||
68 | protected int methodAccess; | |
69 | ||
70 | protected String methodDesc; | |
71 | ||
72 | private boolean constructor; | |
73 | ||
74 | private boolean superInitialized; | |
75 | ||
76 | private List<Object> stackFrame; | |
77 | ||
78 | private Map<Label, List<Object>> branches; | |
79 | ||
80 | /** | |
81 | * Creates a new {@link AdviceAdapter}. | |
82 | * | |
83 | * @param api | |
84 | * the ASM API version implemented by this visitor. Must be one | |
85 | * of {@link Opcodes#ASM4}. | |
86 | * @param mv | |
87 | * the method visitor to which this adapter delegates calls. | |
88 | * @param access | |
89 | * the method's access flags (see {@link Opcodes}). | |
90 | * @param name | |
91 | * the method's name. | |
92 | * @param desc | |
93 | * the method's descriptor (see {@link Type Type}). | |
94 | */ | |
95 | protected AdviceAdapter(final int api, final MethodVisitor mv, | |
96 | final int access, final String name, final String desc) { | |
97 | super(api, mv, access, name, desc); | |
98 | methodAccess = access; | |
99 | methodDesc = desc; | |
100 | constructor = "<init>".equals(name); | |
101 | } | |
102 | ||
103 | @Override | |
104 | public void visitCode() { | |
105 | mv.visitCode(); | |
106 | if (constructor) { | |
107 | stackFrame = new ArrayList<Object>(); | |
108 | branches = new HashMap<Label, List<Object>>(); | |
109 | } else { | |
110 | superInitialized = true; | |
111 | onMethodEnter(); | |
112 | } | |
113 | } | |
114 | ||
115 | @Override | |
116 | public void visitLabel(final Label label) { | |
117 | mv.visitLabel(label); | |
118 | if (constructor && branches != null) { | |
119 | List<Object> frame = branches.get(label); | |
120 | if (frame != null) { | |
121 | stackFrame = frame; | |
122 | branches.remove(label); | |
123 | } | |
124 | } | |
125 | } | |
126 | ||
127 | @Override | |
128 | public void visitInsn(final int opcode) { | |
129 | if (constructor) { | |
130 | int s; | |
131 | switch (opcode) { | |
132 | case RETURN: // empty stack | |
133 | onMethodExit(opcode); | |
134 | break; | |
135 | case IRETURN: // 1 before n/a after | |
136 | case FRETURN: // 1 before n/a after | |
137 | case ARETURN: // 1 before n/a after | |
138 | case ATHROW: // 1 before n/a after | |
139 | popValue(); | |
140 | onMethodExit(opcode); | |
141 | break; | |
142 | case LRETURN: // 2 before n/a after | |
143 | case DRETURN: // 2 before n/a after | |
144 | popValue(); | |
145 | popValue(); | |
146 | onMethodExit(opcode); | |
147 | break; | |
148 | case NOP: | |
149 | case LALOAD: // remove 2 add 2 | |
150 | case DALOAD: // remove 2 add 2 | |
151 | case LNEG: | |
152 | case DNEG: | |
153 | case FNEG: | |
154 | case INEG: | |
155 | case L2D: | |
156 | case D2L: | |
157 | case F2I: | |
158 | case I2B: | |
159 | case I2C: | |
160 | case I2S: | |
161 | case I2F: | |
162 | case ARRAYLENGTH: | |
163 | break; | |
164 | case ACONST_NULL: | |
165 | case ICONST_M1: | |
166 | case ICONST_0: | |
167 | case ICONST_1: | |
168 | case ICONST_2: | |
169 | case ICONST_3: | |
170 | case ICONST_4: | |
171 | case ICONST_5: | |
172 | case FCONST_0: | |
173 | case FCONST_1: | |
174 | case FCONST_2: | |
175 | case F2L: // 1 before 2 after | |
176 | case F2D: | |
177 | case I2L: | |
178 | case I2D: | |
179 | pushValue(OTHER); | |
180 | break; | |
181 | case LCONST_0: | |
182 | case LCONST_1: | |
183 | case DCONST_0: | |
184 | case DCONST_1: | |
185 | pushValue(OTHER); | |
186 | pushValue(OTHER); | |
187 | break; | |
188 | case IALOAD: // remove 2 add 1 | |
189 | case FALOAD: // remove 2 add 1 | |
190 | case AALOAD: // remove 2 add 1 | |
191 | case BALOAD: // remove 2 add 1 | |
192 | case CALOAD: // remove 2 add 1 | |
193 | case SALOAD: // remove 2 add 1 | |
194 | case POP: | |
195 | case IADD: | |
196 | case FADD: | |
197 | case ISUB: | |
198 | case LSHL: // 3 before 2 after | |
199 | case LSHR: // 3 before 2 after | |
200 | case LUSHR: // 3 before 2 after | |
201 | case L2I: // 2 before 1 after | |
202 | case L2F: // 2 before 1 after | |
203 | case D2I: // 2 before 1 after | |
204 | case D2F: // 2 before 1 after | |
205 | case FSUB: | |
206 | case FMUL: | |
207 | case FDIV: | |
208 | case FREM: | |
209 | case FCMPL: // 2 before 1 after | |
210 | case FCMPG: // 2 before 1 after | |
211 | case IMUL: | |
212 | case IDIV: | |
213 | case IREM: | |
214 | case ISHL: | |
215 | case ISHR: | |
216 | case IUSHR: | |
217 | case IAND: | |
218 | case IOR: | |
219 | case IXOR: | |
220 | case MONITORENTER: | |
221 | case MONITOREXIT: | |
222 | popValue(); | |
223 | break; | |
224 | case POP2: | |
225 | case LSUB: | |
226 | case LMUL: | |
227 | case LDIV: | |
228 | case LREM: | |
229 | case LADD: | |
230 | case LAND: | |
231 | case LOR: | |
232 | case LXOR: | |
233 | case DADD: | |
234 | case DMUL: | |
235 | case DSUB: | |
236 | case DDIV: | |
237 | case DREM: | |
238 | popValue(); | |
239 | popValue(); | |
240 | break; | |
241 | case IASTORE: | |
242 | case FASTORE: | |
243 | case AASTORE: | |
244 | case BASTORE: | |
245 | case CASTORE: | |
246 | case SASTORE: | |
247 | case LCMP: // 4 before 1 after | |
248 | case DCMPL: | |
249 | case DCMPG: | |
250 | popValue(); | |
251 | popValue(); | |
252 | popValue(); | |
253 | break; | |
254 | case LASTORE: | |
255 | case DASTORE: | |
256 | popValue(); | |
257 | popValue(); | |
258 | popValue(); | |
259 | popValue(); | |
260 | break; | |
261 | case DUP: | |
262 | pushValue(peekValue()); | |
263 | break; | |
264 | case DUP_X1: | |
265 | s = stackFrame.size(); | |
266 | stackFrame.add(s - 2, stackFrame.get(s - 1)); | |
267 | break; | |
268 | case DUP_X2: | |
269 | s = stackFrame.size(); | |
270 | stackFrame.add(s - 3, stackFrame.get(s - 1)); | |
271 | break; | |
272 | case DUP2: | |
273 | s = stackFrame.size(); | |
274 | stackFrame.add(s - 2, stackFrame.get(s - 1)); | |
275 | stackFrame.add(s - 2, stackFrame.get(s - 1)); | |
276 | break; | |
277 | case DUP2_X1: | |
278 | s = stackFrame.size(); | |
279 | stackFrame.add(s - 3, stackFrame.get(s - 1)); | |
280 | stackFrame.add(s - 3, stackFrame.get(s - 1)); | |
281 | break; | |
282 | case DUP2_X2: | |
283 | s = stackFrame.size(); | |
284 | stackFrame.add(s - 4, stackFrame.get(s - 1)); | |
285 | stackFrame.add(s - 4, stackFrame.get(s - 1)); | |
286 | break; | |
287 | case SWAP: | |
288 | s = stackFrame.size(); | |
289 | stackFrame.add(s - 2, stackFrame.get(s - 1)); | |
290 | stackFrame.remove(s); | |
291 | break; | |
292 | } | |
293 | } else { | |
294 | switch (opcode) { | |
295 | case RETURN: | |
296 | case IRETURN: | |
297 | case FRETURN: | |
298 | case ARETURN: | |
299 | case LRETURN: | |
300 | case DRETURN: | |
301 | case ATHROW: | |
302 | onMethodExit(opcode); | |
303 | break; | |
304 | } | |
305 | } | |
306 | mv.visitInsn(opcode); | |
307 | } | |
308 | ||
309 | @Override | |
310 | public void visitVarInsn(final int opcode, final int var) { | |
311 | super.visitVarInsn(opcode, var); | |
312 | if (constructor) { | |
313 | switch (opcode) { | |
314 | case ILOAD: | |
315 | case FLOAD: | |
316 | pushValue(OTHER); | |
317 | break; | |
318 | case LLOAD: | |
319 | case DLOAD: | |
320 | pushValue(OTHER); | |
321 | pushValue(OTHER); | |
322 | break; | |
323 | case ALOAD: | |
324 | pushValue(var == 0 ? THIS : OTHER); | |
325 | break; | |
326 | case ASTORE: | |
327 | case ISTORE: | |
328 | case FSTORE: | |
329 | popValue(); | |
330 | break; | |
331 | case LSTORE: | |
332 | case DSTORE: | |
333 | popValue(); | |
334 | popValue(); | |
335 | break; | |
336 | } | |
337 | } | |
338 | } | |
339 | ||
340 | @Override | |
341 | public void visitFieldInsn(final int opcode, final String owner, | |
342 | final String name, final String desc) { | |
343 | mv.visitFieldInsn(opcode, owner, name, desc); | |
344 | if (constructor) { | |
345 | char c = desc.charAt(0); | |
346 | boolean longOrDouble = c == 'J' || c == 'D'; | |
347 | switch (opcode) { | |
348 | case GETSTATIC: | |
349 | pushValue(OTHER); | |
350 | if (longOrDouble) { | |
351 | pushValue(OTHER); | |
352 | } | |
353 | break; | |
354 | case PUTSTATIC: | |
355 | popValue(); | |
356 | if (longOrDouble) { | |
357 | popValue(); | |
358 | } | |
359 | break; | |
360 | case PUTFIELD: | |
361 | popValue(); | |
362 | if (longOrDouble) { | |
363 | popValue(); | |
364 | popValue(); | |
365 | } | |
366 | break; | |
367 | // case GETFIELD: | |
368 | default: | |
369 | if (longOrDouble) { | |
370 | pushValue(OTHER); | |
371 | } | |
372 | } | |
373 | } | |
374 | } | |
375 | ||
376 | @Override | |
377 | public void visitIntInsn(final int opcode, final int operand) { | |
378 | mv.visitIntInsn(opcode, operand); | |
379 | if (constructor && opcode != NEWARRAY) { | |
380 | pushValue(OTHER); | |
381 | } | |
382 | } | |
383 | ||
384 | @Override | |
385 | public void visitLdcInsn(final Object cst) { | |
386 | mv.visitLdcInsn(cst); | |
387 | if (constructor) { | |
388 | pushValue(OTHER); | |
389 | if (cst instanceof Double || cst instanceof Long) { | |
390 | pushValue(OTHER); | |
391 | } | |
392 | } | |
393 | } | |
394 | ||
395 | @Override | |
396 | public void visitMultiANewArrayInsn(final String desc, final int dims) { | |
397 | mv.visitMultiANewArrayInsn(desc, dims); | |
398 | if (constructor) { | |
399 | for (int i = 0; i < dims; i++) { | |
400 | popValue(); | |
401 | } | |
402 | pushValue(OTHER); | |
403 | } | |
404 | } | |
405 | ||
406 | @Override | |
407 | public void visitTypeInsn(final int opcode, final String type) { | |
408 | mv.visitTypeInsn(opcode, type); | |
409 | // ANEWARRAY, CHECKCAST or INSTANCEOF don't change stack | |
410 | if (constructor && opcode == NEW) { | |
411 | pushValue(OTHER); | |
412 | } | |
413 | } | |
414 | ||
415 | @Override | |
416 | public void visitMethodInsn(final int opcode, final String owner, | |
417 | final String name, final String desc) { | |
418 | mv.visitMethodInsn(opcode, owner, name, desc); | |
419 | if (constructor) { | |
420 | Type[] types = Type.getArgumentTypes(desc); | |
421 | for (int i = 0; i < types.length; i++) { | |
422 | popValue(); | |
423 | if (types[i].getSize() == 2) { | |
424 | popValue(); | |
425 | } | |
426 | } | |
427 | switch (opcode) { | |
428 | // case INVOKESTATIC: | |
429 | // break; | |
430 | case INVOKEINTERFACE: | |
431 | case INVOKEVIRTUAL: | |
432 | popValue(); // objectref | |
433 | break; | |
434 | case INVOKESPECIAL: | |
435 | Object type = popValue(); // objectref | |
436 | if (type == THIS && !superInitialized) { | |
437 | onMethodEnter(); | |
438 | superInitialized = true; | |
439 | // once super has been initialized it is no longer | |
440 | // necessary to keep track of stack state | |
441 | constructor = false; | |
442 | } | |
443 | break; | |
444 | } | |
445 | ||
446 | Type returnType = Type.getReturnType(desc); | |
447 | if (returnType != Type.VOID_TYPE) { | |
448 | pushValue(OTHER); | |
449 | if (returnType.getSize() == 2) { | |
450 | pushValue(OTHER); | |
451 | } | |
452 | } | |
453 | } | |
454 | } | |
455 | ||
456 | @Override | |
457 | public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, | |
458 | Object... bsmArgs) { | |
459 | mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); | |
460 | if (constructor) { | |
461 | Type[] types = Type.getArgumentTypes(desc); | |
462 | for (int i = 0; i < types.length; i++) { | |
463 | popValue(); | |
464 | if (types[i].getSize() == 2) { | |
465 | popValue(); | |
466 | } | |
467 | } | |
468 | ||
469 | Type returnType = Type.getReturnType(desc); | |
470 | if (returnType != Type.VOID_TYPE) { | |
471 | pushValue(OTHER); | |
472 | if (returnType.getSize() == 2) { | |
473 | pushValue(OTHER); | |
474 | } | |
475 | } | |
476 | } | |
477 | } | |
478 | ||
479 | @Override | |
480 | public void visitJumpInsn(final int opcode, final Label label) { | |
481 | mv.visitJumpInsn(opcode, label); | |
482 | if (constructor) { | |
483 | switch (opcode) { | |
484 | case IFEQ: | |
485 | case IFNE: | |
486 | case IFLT: | |
487 | case IFGE: | |
488 | case IFGT: | |
489 | case IFLE: | |
490 | case IFNULL: | |
491 | case IFNONNULL: | |
492 | popValue(); | |
493 | break; | |
494 | case IF_ICMPEQ: | |
495 | case IF_ICMPNE: | |
496 | case IF_ICMPLT: | |
497 | case IF_ICMPGE: | |
498 | case IF_ICMPGT: | |
499 | case IF_ICMPLE: | |
500 | case IF_ACMPEQ: | |
501 | case IF_ACMPNE: | |
502 | popValue(); | |
503 | popValue(); | |
504 | break; | |
505 | case JSR: | |
506 | pushValue(OTHER); | |
507 | break; | |
508 | } | |
509 | addBranch(label); | |
510 | } | |
511 | } | |
512 | ||
513 | @Override | |
514 | public void visitLookupSwitchInsn(final Label dflt, final int[] keys, | |
515 | final Label[] labels) { | |
516 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
517 | if (constructor) { | |
518 | popValue(); | |
519 | addBranches(dflt, labels); | |
520 | } | |
521 | } | |
522 | ||
523 | @Override | |
524 | public void visitTableSwitchInsn(final int min, final int max, | |
525 | final Label dflt, final Label... labels) { | |
526 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
527 | if (constructor) { | |
528 | popValue(); | |
529 | addBranches(dflt, labels); | |
530 | } | |
531 | } | |
532 | ||
533 | @Override | |
534 | public void visitTryCatchBlock(Label start, Label end, Label handler, | |
535 | String type) { | |
536 | super.visitTryCatchBlock(start, end, handler, type); | |
537 | if (constructor && !branches.containsKey(handler)) { | |
538 | List<Object> stackFrame = new ArrayList<Object>(); | |
539 | stackFrame.add(OTHER); | |
540 | branches.put(handler, stackFrame); | |
541 | } | |
542 | } | |
543 | ||
544 | private void addBranches(final Label dflt, final Label[] labels) { | |
545 | addBranch(dflt); | |
546 | for (int i = 0; i < labels.length; i++) { | |
547 | addBranch(labels[i]); | |
548 | } | |
549 | } | |
550 | ||
551 | private void addBranch(final Label label) { | |
552 | if (branches.containsKey(label)) { | |
553 | return; | |
554 | } | |
555 | branches.put(label, new ArrayList<Object>(stackFrame)); | |
556 | } | |
557 | ||
558 | private Object popValue() { | |
559 | return stackFrame.remove(stackFrame.size() - 1); | |
560 | } | |
561 | ||
562 | private Object peekValue() { | |
563 | return stackFrame.get(stackFrame.size() - 1); | |
564 | } | |
565 | ||
566 | private void pushValue(final Object o) { | |
567 | stackFrame.add(o); | |
568 | } | |
569 | ||
570 | /** | |
571 | * Called at the beginning of the method or after super class class call in | |
572 | * the constructor. <br> | |
573 | * <br> | |
574 | * | |
575 | * <i>Custom code can use or change all the local variables, but should not | |
576 | * change state of the stack.</i> | |
577 | */ | |
578 | protected void onMethodEnter() { | |
579 | } | |
580 | ||
581 | /** | |
582 | * Called before explicit exit from the method using either return or throw. | |
583 | * Top element on the stack contains the return value or exception instance. | |
584 | * For example: | |
585 | * | |
586 | * <pre> | |
587 | * public void onMethodExit(int opcode) { | |
588 | * if(opcode==RETURN) { | |
589 | * visitInsn(ACONST_NULL); | |
590 | * } else if(opcode==ARETURN || opcode==ATHROW) { | |
591 | * dup(); | |
592 | * } else { | |
593 | * if(opcode==LRETURN || opcode==DRETURN) { | |
594 | * dup2(); | |
595 | * } else { | |
596 | * dup(); | |
597 | * } | |
598 | * box(Type.getReturnType(this.methodDesc)); | |
599 | * } | |
600 | * visitIntInsn(SIPUSH, opcode); | |
601 | * visitMethodInsn(INVOKESTATIC, owner, "onExit", "(Ljava/lang/Object;I)V"); | |
602 | * } | |
603 | * | |
604 | * // an actual call back method | |
605 | * public static void onExit(Object param, int opcode) { | |
606 | * ... | |
607 | * </pre> | |
608 | * | |
609 | * <br> | |
610 | * <br> | |
611 | * | |
612 | * <i>Custom code can use or change all the local variables, but should not | |
613 | * change state of the stack.</i> | |
614 | * | |
615 | * @param opcode | |
616 | * one of the RETURN, IRETURN, FRETURN, ARETURN, LRETURN, DRETURN | |
617 | * or ATHROW | |
618 | * | |
619 | */ | |
620 | protected void onMethodExit(int opcode) { | |
621 | } | |
622 | ||
623 | // TODO onException, onMethodCall | |
624 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.Handle; | |
37 | import clojure.asm.Label; | |
38 | import clojure.asm.MethodVisitor; | |
39 | import clojure.asm.Opcodes; | |
40 | import clojure.asm.Type; | |
41 | ||
42 | /** | |
43 | * A {@link MethodVisitor} 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 | |
47 | * visit<i>X</i> instruction delegates to the next visitor in the chain, if any, | |
48 | * and then simulates the effect of this instruction on the stack map frame, | |
49 | * represented by {@link #locals} and {@link #stack}. The next visitor in the | |
50 | * chain can get the state of the stack map frame <i>before</i> each instruction | |
51 | * by reading the value of these fields in its visit<i>X</i> methods (this | |
52 | * requires a reference to the AnalyzerAdapter that is before it in the chain). | |
53 | * If this adapter is used with a class that does not contain stack map table | |
54 | * attributes (i.e., pre Java 6 classes) then this adapter may not be able to | |
55 | * compute the stack map frame for each instruction. In this case no exception | |
56 | * is thrown but the {@link #locals} and {@link #stack} fields will be null for | |
57 | * these instructions. | |
58 | * | |
59 | * @author Eric Bruneton | |
60 | */ | |
61 | public class AnalyzerAdapter extends MethodVisitor { | |
62 | ||
63 | /** | |
64 | * <code>List</code> of the local variable slots for current execution | |
65 | * frame. Primitive types are represented by {@link Opcodes#TOP}, | |
66 | * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, | |
67 | * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or | |
68 | * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by | |
69 | * two elements, the second one being TOP). Reference types are represented | |
70 | * by String objects (representing internal names), and uninitialized types | |
71 | * by Label objects (this label designates the NEW instruction that created | |
72 | * this uninitialized value). This field is <tt>null</tt> for unreachable | |
73 | * instructions. | |
74 | */ | |
75 | public List<Object> locals; | |
76 | ||
77 | /** | |
78 | * <code>List</code> of the operand stack slots for current execution frame. | |
79 | * Primitive types are represented by {@link Opcodes#TOP}, | |
80 | * {@link Opcodes#INTEGER}, {@link Opcodes#FLOAT}, {@link Opcodes#LONG}, | |
81 | * {@link Opcodes#DOUBLE},{@link Opcodes#NULL} or | |
82 | * {@link Opcodes#UNINITIALIZED_THIS} (long and double are represented by | |
83 | * two elements, the second one being TOP). Reference types are represented | |
84 | * by String objects (representing internal names), and uninitialized types | |
85 | * by Label objects (this label designates the NEW instruction that created | |
86 | * this uninitialized value). This field is <tt>null</tt> for unreachable | |
87 | * instructions. | |
88 | */ | |
89 | public List<Object> stack; | |
90 | ||
91 | /** | |
92 | * The labels that designate the next instruction to be visited. May be | |
93 | * <tt>null</tt>. | |
94 | */ | |
95 | private List<Label> labels; | |
96 | ||
97 | /** | |
98 | * Information about uninitialized types in the current execution frame. | |
99 | * This map associates internal names to Label objects. Each label | |
100 | * designates a NEW instruction that created the currently uninitialized | |
101 | * types, and the associated internal name represents the NEW operand, i.e. | |
102 | * the final, initialized type value. | |
103 | */ | |
104 | public Map<Object, Object> uninitializedTypes; | |
105 | ||
106 | /** | |
107 | * The maximum stack size of this method. | |
108 | */ | |
109 | private int maxStack; | |
110 | ||
111 | /** | |
112 | * The maximum number of local variables of this method. | |
113 | */ | |
114 | private int maxLocals; | |
115 | ||
116 | /** | |
117 | * The owner's class name. | |
118 | */ | |
119 | private String owner; | |
120 | ||
121 | /** | |
122 | * Creates a new {@link AnalyzerAdapter}. <i>Subclasses must not use this | |
123 | * constructor</i>. Instead, they must use the | |
124 | * {@link #AnalyzerAdapter(int, String, int, String, String, MethodVisitor)} | |
125 | * version. | |
126 | * | |
127 | * @param owner | |
128 | * the owner's class name. | |
129 | * @param access | |
130 | * the method's access flags (see {@link Opcodes}). | |
131 | * @param name | |
132 | * the method's name. | |
133 | * @param desc | |
134 | * the method's descriptor (see {@link Type Type}). | |
135 | * @param mv | |
136 | * the method visitor to which this adapter delegates calls. May | |
137 | * be <tt>null</tt>. | |
138 | */ | |
139 | public AnalyzerAdapter(final String owner, final int access, | |
140 | final String name, final String desc, final MethodVisitor mv) { | |
141 | this(Opcodes.ASM4, owner, access, name, desc, mv); | |
142 | } | |
143 | ||
144 | /** | |
145 | * Creates a new {@link AnalyzerAdapter}. | |
146 | * | |
147 | * @param api | |
148 | * the ASM API version implemented by this visitor. Must be one | |
149 | * of {@link Opcodes#ASM4}. | |
150 | * @param owner | |
151 | * the owner's class name. | |
152 | * @param access | |
153 | * the method's access flags (see {@link Opcodes}). | |
154 | * @param name | |
155 | * the method's name. | |
156 | * @param desc | |
157 | * the method's descriptor (see {@link Type Type}). | |
158 | * @param mv | |
159 | * the method visitor to which this adapter delegates calls. May | |
160 | * be <tt>null</tt>. | |
161 | */ | |
162 | protected AnalyzerAdapter(final int api, final String owner, | |
163 | final int access, final String name, final String desc, | |
164 | final MethodVisitor mv) { | |
165 | super(api, mv); | |
166 | this.owner = owner; | |
167 | locals = new ArrayList<Object>(); | |
168 | stack = new ArrayList<Object>(); | |
169 | uninitializedTypes = new HashMap<Object, Object>(); | |
170 | ||
171 | if ((access & Opcodes.ACC_STATIC) == 0) { | |
172 | if ("<init>".equals(name)) { | |
173 | locals.add(Opcodes.UNINITIALIZED_THIS); | |
174 | } else { | |
175 | locals.add(owner); | |
176 | } | |
177 | } | |
178 | Type[] types = Type.getArgumentTypes(desc); | |
179 | for (int i = 0; i < types.length; ++i) { | |
180 | Type type = types[i]; | |
181 | switch (type.getSort()) { | |
182 | case Type.BOOLEAN: | |
183 | case Type.CHAR: | |
184 | case Type.BYTE: | |
185 | case Type.SHORT: | |
186 | case Type.INT: | |
187 | locals.add(Opcodes.INTEGER); | |
188 | break; | |
189 | case Type.FLOAT: | |
190 | locals.add(Opcodes.FLOAT); | |
191 | break; | |
192 | case Type.LONG: | |
193 | locals.add(Opcodes.LONG); | |
194 | locals.add(Opcodes.TOP); | |
195 | break; | |
196 | case Type.DOUBLE: | |
197 | locals.add(Opcodes.DOUBLE); | |
198 | locals.add(Opcodes.TOP); | |
199 | break; | |
200 | case Type.ARRAY: | |
201 | locals.add(types[i].getDescriptor()); | |
202 | break; | |
203 | // case Type.OBJECT: | |
204 | default: | |
205 | locals.add(types[i].getInternalName()); | |
206 | } | |
207 | } | |
208 | } | |
209 | ||
210 | @Override | |
211 | public void visitFrame(final int type, final int nLocal, | |
212 | final Object[] local, final int nStack, final Object[] stack) { | |
213 | if (type != Opcodes.F_NEW) { // uncompressed frame | |
214 | throw new IllegalStateException( | |
215 | "ClassReader.accept() should be called with EXPAND_FRAMES flag"); | |
216 | } | |
217 | ||
218 | if (mv != null) { | |
219 | mv.visitFrame(type, nLocal, local, nStack, stack); | |
220 | } | |
221 | ||
222 | if (this.locals != null) { | |
223 | this.locals.clear(); | |
224 | this.stack.clear(); | |
225 | } else { | |
226 | this.locals = new ArrayList<Object>(); | |
227 | this.stack = new ArrayList<Object>(); | |
228 | } | |
229 | visitFrameTypes(nLocal, local, this.locals); | |
230 | visitFrameTypes(nStack, stack, this.stack); | |
231 | maxStack = Math.max(maxStack, this.stack.size()); | |
232 | } | |
233 | ||
234 | private static void visitFrameTypes(final int n, final Object[] types, | |
235 | final List<Object> result) { | |
236 | for (int i = 0; i < n; ++i) { | |
237 | Object type = types[i]; | |
238 | result.add(type); | |
239 | if (type == Opcodes.LONG || type == Opcodes.DOUBLE) { | |
240 | result.add(Opcodes.TOP); | |
241 | } | |
242 | } | |
243 | } | |
244 | ||
245 | @Override | |
246 | public void visitInsn(final int opcode) { | |
247 | if (mv != null) { | |
248 | mv.visitInsn(opcode); | |
249 | } | |
250 | execute(opcode, 0, null); | |
251 | if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN) | |
252 | || opcode == Opcodes.ATHROW) { | |
253 | this.locals = null; | |
254 | this.stack = null; | |
255 | } | |
256 | } | |
257 | ||
258 | @Override | |
259 | public void visitIntInsn(final int opcode, final int operand) { | |
260 | if (mv != null) { | |
261 | mv.visitIntInsn(opcode, operand); | |
262 | } | |
263 | execute(opcode, operand, null); | |
264 | } | |
265 | ||
266 | @Override | |
267 | public void visitVarInsn(final int opcode, final int var) { | |
268 | if (mv != null) { | |
269 | mv.visitVarInsn(opcode, var); | |
270 | } | |
271 | execute(opcode, var, null); | |
272 | } | |
273 | ||
274 | @Override | |
275 | public void visitTypeInsn(final int opcode, final String type) { | |
276 | if (opcode == Opcodes.NEW) { | |
277 | if (labels == null) { | |
278 | Label l = new Label(); | |
279 | labels = new ArrayList<Label>(3); | |
280 | labels.add(l); | |
281 | if (mv != null) { | |
282 | mv.visitLabel(l); | |
283 | } | |
284 | } | |
285 | for (int i = 0; i < labels.size(); ++i) { | |
286 | uninitializedTypes.put(labels.get(i), type); | |
287 | } | |
288 | } | |
289 | if (mv != null) { | |
290 | mv.visitTypeInsn(opcode, type); | |
291 | } | |
292 | execute(opcode, 0, type); | |
293 | } | |
294 | ||
295 | @Override | |
296 | public void visitFieldInsn(final int opcode, final String owner, | |
297 | final String name, final String desc) { | |
298 | if (mv != null) { | |
299 | mv.visitFieldInsn(opcode, owner, name, desc); | |
300 | } | |
301 | execute(opcode, 0, desc); | |
302 | } | |
303 | ||
304 | @Override | |
305 | public void visitMethodInsn(final int opcode, final String owner, | |
306 | final String name, final String desc) { | |
307 | if (mv != null) { | |
308 | mv.visitMethodInsn(opcode, owner, name, desc); | |
309 | } | |
310 | if (this.locals == null) { | |
311 | labels = null; | |
312 | return; | |
313 | } | |
314 | pop(desc); | |
315 | if (opcode != Opcodes.INVOKESTATIC) { | |
316 | Object t = pop(); | |
317 | if (opcode == Opcodes.INVOKESPECIAL && name.charAt(0) == '<') { | |
318 | Object u; | |
319 | if (t == Opcodes.UNINITIALIZED_THIS) { | |
320 | u = this.owner; | |
321 | } else { | |
322 | u = uninitializedTypes.get(t); | |
323 | } | |
324 | for (int i = 0; i < locals.size(); ++i) { | |
325 | if (locals.get(i) == t) { | |
326 | locals.set(i, u); | |
327 | } | |
328 | } | |
329 | for (int i = 0; i < stack.size(); ++i) { | |
330 | if (stack.get(i) == t) { | |
331 | stack.set(i, u); | |
332 | } | |
333 | } | |
334 | } | |
335 | } | |
336 | pushDesc(desc); | |
337 | labels = null; | |
338 | } | |
339 | ||
340 | @Override | |
341 | public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, | |
342 | Object... bsmArgs) { | |
343 | if (mv != null) { | |
344 | mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); | |
345 | } | |
346 | if (this.locals == null) { | |
347 | labels = null; | |
348 | return; | |
349 | } | |
350 | pop(desc); | |
351 | pushDesc(desc); | |
352 | labels = null; | |
353 | } | |
354 | ||
355 | @Override | |
356 | public void visitJumpInsn(final int opcode, final Label label) { | |
357 | if (mv != null) { | |
358 | mv.visitJumpInsn(opcode, label); | |
359 | } | |
360 | execute(opcode, 0, null); | |
361 | if (opcode == Opcodes.GOTO) { | |
362 | this.locals = null; | |
363 | this.stack = null; | |
364 | } | |
365 | } | |
366 | ||
367 | @Override | |
368 | public void visitLabel(final Label label) { | |
369 | if (mv != null) { | |
370 | mv.visitLabel(label); | |
371 | } | |
372 | if (labels == null) { | |
373 | labels = new ArrayList<Label>(3); | |
374 | } | |
375 | labels.add(label); | |
376 | } | |
377 | ||
378 | @Override | |
379 | public void visitLdcInsn(final Object cst) { | |
380 | if (mv != null) { | |
381 | mv.visitLdcInsn(cst); | |
382 | } | |
383 | if (this.locals == null) { | |
384 | labels = null; | |
385 | return; | |
386 | } | |
387 | if (cst instanceof Integer) { | |
388 | push(Opcodes.INTEGER); | |
389 | } else if (cst instanceof Long) { | |
390 | push(Opcodes.LONG); | |
391 | push(Opcodes.TOP); | |
392 | } else if (cst instanceof Float) { | |
393 | push(Opcodes.FLOAT); | |
394 | } else if (cst instanceof Double) { | |
395 | push(Opcodes.DOUBLE); | |
396 | push(Opcodes.TOP); | |
397 | } else if (cst instanceof String) { | |
398 | push("java/lang/String"); | |
399 | } else if (cst instanceof Type) { | |
400 | int sort = ((Type) cst).getSort(); | |
401 | if (sort == Type.OBJECT || sort == Type.ARRAY) { | |
402 | push("java/lang/Class"); | |
403 | } else if (sort == Type.METHOD) { | |
404 | push("java/lang/invoke/MethodType"); | |
405 | } else { | |
406 | throw new IllegalArgumentException(); | |
407 | } | |
408 | } else if (cst instanceof Handle) { | |
409 | push("java/lang/invoke/MethodHandle"); | |
410 | } else { | |
411 | throw new IllegalArgumentException(); | |
412 | } | |
413 | labels = null; | |
414 | } | |
415 | ||
416 | @Override | |
417 | public void visitIincInsn(final int var, final int increment) { | |
418 | if (mv != null) { | |
419 | mv.visitIincInsn(var, increment); | |
420 | } | |
421 | execute(Opcodes.IINC, var, null); | |
422 | } | |
423 | ||
424 | @Override | |
425 | public void visitTableSwitchInsn(final int min, final int max, | |
426 | final Label dflt, final Label... labels) { | |
427 | if (mv != null) { | |
428 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
429 | } | |
430 | execute(Opcodes.TABLESWITCH, 0, null); | |
431 | this.locals = null; | |
432 | this.stack = null; | |
433 | } | |
434 | ||
435 | @Override | |
436 | public void visitLookupSwitchInsn(final Label dflt, final int[] keys, | |
437 | final Label[] labels) { | |
438 | if (mv != null) { | |
439 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
440 | } | |
441 | execute(Opcodes.LOOKUPSWITCH, 0, null); | |
442 | this.locals = null; | |
443 | this.stack = null; | |
444 | } | |
445 | ||
446 | @Override | |
447 | public void visitMultiANewArrayInsn(final String desc, final int dims) { | |
448 | if (mv != null) { | |
449 | mv.visitMultiANewArrayInsn(desc, dims); | |
450 | } | |
451 | execute(Opcodes.MULTIANEWARRAY, dims, desc); | |
452 | } | |
453 | ||
454 | @Override | |
455 | public void visitMaxs(final int maxStack, final int maxLocals) { | |
456 | if (mv != null) { | |
457 | this.maxStack = Math.max(this.maxStack, maxStack); | |
458 | this.maxLocals = Math.max(this.maxLocals, maxLocals); | |
459 | mv.visitMaxs(this.maxStack, this.maxLocals); | |
460 | } | |
461 | } | |
462 | ||
463 | // ------------------------------------------------------------------------ | |
464 | ||
465 | private Object get(final int local) { | |
466 | maxLocals = Math.max(maxLocals, local); | |
467 | return local < locals.size() ? locals.get(local) : Opcodes.TOP; | |
468 | } | |
469 | ||
470 | private void set(final int local, final Object type) { | |
471 | maxLocals = Math.max(maxLocals, local); | |
472 | while (local >= locals.size()) { | |
473 | locals.add(Opcodes.TOP); | |
474 | } | |
475 | locals.set(local, type); | |
476 | } | |
477 | ||
478 | private void push(final Object type) { | |
479 | stack.add(type); | |
480 | maxStack = Math.max(maxStack, stack.size()); | |
481 | } | |
482 | ||
483 | private void pushDesc(final String desc) { | |
484 | int index = desc.charAt(0) == '(' ? desc.indexOf(')') + 1 : 0; | |
485 | switch (desc.charAt(index)) { | |
486 | case 'V': | |
487 | return; | |
488 | case 'Z': | |
489 | case 'C': | |
490 | case 'B': | |
491 | case 'S': | |
492 | case 'I': | |
493 | push(Opcodes.INTEGER); | |
494 | return; | |
495 | case 'F': | |
496 | push(Opcodes.FLOAT); | |
497 | return; | |
498 | case 'J': | |
499 | push(Opcodes.LONG); | |
500 | push(Opcodes.TOP); | |
501 | return; | |
502 | case 'D': | |
503 | push(Opcodes.DOUBLE); | |
504 | push(Opcodes.TOP); | |
505 | return; | |
506 | case '[': | |
507 | if (index == 0) { | |
508 | push(desc); | |
509 | } else { | |
510 | push(desc.substring(index, desc.length())); | |
511 | } | |
512 | break; | |
513 | // case 'L': | |
514 | default: | |
515 | if (index == 0) { | |
516 | push(desc.substring(1, desc.length() - 1)); | |
517 | } else { | |
518 | push(desc.substring(index + 1, desc.length() - 1)); | |
519 | } | |
520 | } | |
521 | } | |
522 | ||
523 | private Object pop() { | |
524 | return stack.remove(stack.size() - 1); | |
525 | } | |
526 | ||
527 | private void pop(final int n) { | |
528 | int size = stack.size(); | |
529 | int end = size - n; | |
530 | for (int i = size - 1; i >= end; --i) { | |
531 | stack.remove(i); | |
532 | } | |
533 | } | |
534 | ||
535 | private void pop(final String desc) { | |
536 | char c = desc.charAt(0); | |
537 | if (c == '(') { | |
538 | int n = 0; | |
539 | Type[] types = Type.getArgumentTypes(desc); | |
540 | for (int i = 0; i < types.length; ++i) { | |
541 | n += types[i].getSize(); | |
542 | } | |
543 | pop(n); | |
544 | } else if (c == 'J' || c == 'D') { | |
545 | pop(2); | |
546 | } else { | |
547 | pop(1); | |
548 | } | |
549 | } | |
550 | ||
551 | private void execute(final int opcode, final int iarg, final String sarg) { | |
552 | if (this.locals == null) { | |
553 | labels = null; | |
554 | return; | |
555 | } | |
556 | Object t1, t2, t3, t4; | |
557 | switch (opcode) { | |
558 | case Opcodes.NOP: | |
559 | case Opcodes.INEG: | |
560 | case Opcodes.LNEG: | |
561 | case Opcodes.FNEG: | |
562 | case Opcodes.DNEG: | |
563 | case Opcodes.I2B: | |
564 | case Opcodes.I2C: | |
565 | case Opcodes.I2S: | |
566 | case Opcodes.GOTO: | |
567 | case Opcodes.RETURN: | |
568 | break; | |
569 | case Opcodes.ACONST_NULL: | |
570 | push(Opcodes.NULL); | |
571 | break; | |
572 | case Opcodes.ICONST_M1: | |
573 | case Opcodes.ICONST_0: | |
574 | case Opcodes.ICONST_1: | |
575 | case Opcodes.ICONST_2: | |
576 | case Opcodes.ICONST_3: | |
577 | case Opcodes.ICONST_4: | |
578 | case Opcodes.ICONST_5: | |
579 | case Opcodes.BIPUSH: | |
580 | case Opcodes.SIPUSH: | |
581 | push(Opcodes.INTEGER); | |
582 | break; | |
583 | case Opcodes.LCONST_0: | |
584 | case Opcodes.LCONST_1: | |
585 | push(Opcodes.LONG); | |
586 | push(Opcodes.TOP); | |
587 | break; | |
588 | case Opcodes.FCONST_0: | |
589 | case Opcodes.FCONST_1: | |
590 | case Opcodes.FCONST_2: | |
591 | push(Opcodes.FLOAT); | |
592 | break; | |
593 | case Opcodes.DCONST_0: | |
594 | case Opcodes.DCONST_1: | |
595 | push(Opcodes.DOUBLE); | |
596 | push(Opcodes.TOP); | |
597 | break; | |
598 | case Opcodes.ILOAD: | |
599 | case Opcodes.FLOAD: | |
600 | case Opcodes.ALOAD: | |
601 | push(get(iarg)); | |
602 | break; | |
603 | case Opcodes.LLOAD: | |
604 | case Opcodes.DLOAD: | |
605 | push(get(iarg)); | |
606 | push(Opcodes.TOP); | |
607 | break; | |
608 | case Opcodes.IALOAD: | |
609 | case Opcodes.BALOAD: | |
610 | case Opcodes.CALOAD: | |
611 | case Opcodes.SALOAD: | |
612 | pop(2); | |
613 | push(Opcodes.INTEGER); | |
614 | break; | |
615 | case Opcodes.LALOAD: | |
616 | case Opcodes.D2L: | |
617 | pop(2); | |
618 | push(Opcodes.LONG); | |
619 | push(Opcodes.TOP); | |
620 | break; | |
621 | case Opcodes.FALOAD: | |
622 | pop(2); | |
623 | push(Opcodes.FLOAT); | |
624 | break; | |
625 | case Opcodes.DALOAD: | |
626 | case Opcodes.L2D: | |
627 | pop(2); | |
628 | push(Opcodes.DOUBLE); | |
629 | push(Opcodes.TOP); | |
630 | break; | |
631 | case Opcodes.AALOAD: | |
632 | pop(1); | |
633 | t1 = pop(); | |
634 | if (t1 instanceof String) { | |
635 | pushDesc(((String) t1).substring(1)); | |
636 | } else { | |
637 | push("java/lang/Object"); | |
638 | } | |
639 | break; | |
640 | case Opcodes.ISTORE: | |
641 | case Opcodes.FSTORE: | |
642 | case Opcodes.ASTORE: | |
643 | t1 = pop(); | |
644 | set(iarg, t1); | |
645 | if (iarg > 0) { | |
646 | t2 = get(iarg - 1); | |
647 | if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) { | |
648 | set(iarg - 1, Opcodes.TOP); | |
649 | } | |
650 | } | |
651 | break; | |
652 | case Opcodes.LSTORE: | |
653 | case Opcodes.DSTORE: | |
654 | pop(1); | |
655 | t1 = pop(); | |
656 | set(iarg, t1); | |
657 | set(iarg + 1, Opcodes.TOP); | |
658 | if (iarg > 0) { | |
659 | t2 = get(iarg - 1); | |
660 | if (t2 == Opcodes.LONG || t2 == Opcodes.DOUBLE) { | |
661 | set(iarg - 1, Opcodes.TOP); | |
662 | } | |
663 | } | |
664 | break; | |
665 | case Opcodes.IASTORE: | |
666 | case Opcodes.BASTORE: | |
667 | case Opcodes.CASTORE: | |
668 | case Opcodes.SASTORE: | |
669 | case Opcodes.FASTORE: | |
670 | case Opcodes.AASTORE: | |
671 | pop(3); | |
672 | break; | |
673 | case Opcodes.LASTORE: | |
674 | case Opcodes.DASTORE: | |
675 | pop(4); | |
676 | break; | |
677 | case Opcodes.POP: | |
678 | case Opcodes.IFEQ: | |
679 | case Opcodes.IFNE: | |
680 | case Opcodes.IFLT: | |
681 | case Opcodes.IFGE: | |
682 | case Opcodes.IFGT: | |
683 | case Opcodes.IFLE: | |
684 | case Opcodes.IRETURN: | |
685 | case Opcodes.FRETURN: | |
686 | case Opcodes.ARETURN: | |
687 | case Opcodes.TABLESWITCH: | |
688 | case Opcodes.LOOKUPSWITCH: | |
689 | case Opcodes.ATHROW: | |
690 | case Opcodes.MONITORENTER: | |
691 | case Opcodes.MONITOREXIT: | |
692 | case Opcodes.IFNULL: | |
693 | case Opcodes.IFNONNULL: | |
694 | pop(1); | |
695 | break; | |
696 | case Opcodes.POP2: | |
697 | case Opcodes.IF_ICMPEQ: | |
698 | case Opcodes.IF_ICMPNE: | |
699 | case Opcodes.IF_ICMPLT: | |
700 | case Opcodes.IF_ICMPGE: | |
701 | case Opcodes.IF_ICMPGT: | |
702 | case Opcodes.IF_ICMPLE: | |
703 | case Opcodes.IF_ACMPEQ: | |
704 | case Opcodes.IF_ACMPNE: | |
705 | case Opcodes.LRETURN: | |
706 | case Opcodes.DRETURN: | |
707 | pop(2); | |
708 | break; | |
709 | case Opcodes.DUP: | |
710 | t1 = pop(); | |
711 | push(t1); | |
712 | push(t1); | |
713 | break; | |
714 | case Opcodes.DUP_X1: | |
715 | t1 = pop(); | |
716 | t2 = pop(); | |
717 | push(t1); | |
718 | push(t2); | |
719 | push(t1); | |
720 | break; | |
721 | case Opcodes.DUP_X2: | |
722 | t1 = pop(); | |
723 | t2 = pop(); | |
724 | t3 = pop(); | |
725 | push(t1); | |
726 | push(t3); | |
727 | push(t2); | |
728 | push(t1); | |
729 | break; | |
730 | case Opcodes.DUP2: | |
731 | t1 = pop(); | |
732 | t2 = pop(); | |
733 | push(t2); | |
734 | push(t1); | |
735 | push(t2); | |
736 | push(t1); | |
737 | break; | |
738 | case Opcodes.DUP2_X1: | |
739 | t1 = pop(); | |
740 | t2 = pop(); | |
741 | t3 = pop(); | |
742 | push(t2); | |
743 | push(t1); | |
744 | push(t3); | |
745 | push(t2); | |
746 | push(t1); | |
747 | break; | |
748 | case Opcodes.DUP2_X2: | |
749 | t1 = pop(); | |
750 | t2 = pop(); | |
751 | t3 = pop(); | |
752 | t4 = pop(); | |
753 | push(t2); | |
754 | push(t1); | |
755 | push(t4); | |
756 | push(t3); | |
757 | push(t2); | |
758 | push(t1); | |
759 | break; | |
760 | case Opcodes.SWAP: | |
761 | t1 = pop(); | |
762 | t2 = pop(); | |
763 | push(t1); | |
764 | push(t2); | |
765 | break; | |
766 | case Opcodes.IADD: | |
767 | case Opcodes.ISUB: | |
768 | case Opcodes.IMUL: | |
769 | case Opcodes.IDIV: | |
770 | case Opcodes.IREM: | |
771 | case Opcodes.IAND: | |
772 | case Opcodes.IOR: | |
773 | case Opcodes.IXOR: | |
774 | case Opcodes.ISHL: | |
775 | case Opcodes.ISHR: | |
776 | case Opcodes.IUSHR: | |
777 | case Opcodes.L2I: | |
778 | case Opcodes.D2I: | |
779 | case Opcodes.FCMPL: | |
780 | case Opcodes.FCMPG: | |
781 | pop(2); | |
782 | push(Opcodes.INTEGER); | |
783 | break; | |
784 | case Opcodes.LADD: | |
785 | case Opcodes.LSUB: | |
786 | case Opcodes.LMUL: | |
787 | case Opcodes.LDIV: | |
788 | case Opcodes.LREM: | |
789 | case Opcodes.LAND: | |
790 | case Opcodes.LOR: | |
791 | case Opcodes.LXOR: | |
792 | pop(4); | |
793 | push(Opcodes.LONG); | |
794 | push(Opcodes.TOP); | |
795 | break; | |
796 | case Opcodes.FADD: | |
797 | case Opcodes.FSUB: | |
798 | case Opcodes.FMUL: | |
799 | case Opcodes.FDIV: | |
800 | case Opcodes.FREM: | |
801 | case Opcodes.L2F: | |
802 | case Opcodes.D2F: | |
803 | pop(2); | |
804 | push(Opcodes.FLOAT); | |
805 | break; | |
806 | case Opcodes.DADD: | |
807 | case Opcodes.DSUB: | |
808 | case Opcodes.DMUL: | |
809 | case Opcodes.DDIV: | |
810 | case Opcodes.DREM: | |
811 | pop(4); | |
812 | push(Opcodes.DOUBLE); | |
813 | push(Opcodes.TOP); | |
814 | break; | |
815 | case Opcodes.LSHL: | |
816 | case Opcodes.LSHR: | |
817 | case Opcodes.LUSHR: | |
818 | pop(3); | |
819 | push(Opcodes.LONG); | |
820 | push(Opcodes.TOP); | |
821 | break; | |
822 | case Opcodes.IINC: | |
823 | set(iarg, Opcodes.INTEGER); | |
824 | break; | |
825 | case Opcodes.I2L: | |
826 | case Opcodes.F2L: | |
827 | pop(1); | |
828 | push(Opcodes.LONG); | |
829 | push(Opcodes.TOP); | |
830 | break; | |
831 | case Opcodes.I2F: | |
832 | pop(1); | |
833 | push(Opcodes.FLOAT); | |
834 | break; | |
835 | case Opcodes.I2D: | |
836 | case Opcodes.F2D: | |
837 | pop(1); | |
838 | push(Opcodes.DOUBLE); | |
839 | push(Opcodes.TOP); | |
840 | break; | |
841 | case Opcodes.F2I: | |
842 | case Opcodes.ARRAYLENGTH: | |
843 | case Opcodes.INSTANCEOF: | |
844 | pop(1); | |
845 | push(Opcodes.INTEGER); | |
846 | break; | |
847 | case Opcodes.LCMP: | |
848 | case Opcodes.DCMPL: | |
849 | case Opcodes.DCMPG: | |
850 | pop(4); | |
851 | push(Opcodes.INTEGER); | |
852 | break; | |
853 | case Opcodes.JSR: | |
854 | case Opcodes.RET: | |
855 | throw new RuntimeException("JSR/RET are not supported"); | |
856 | case Opcodes.GETSTATIC: | |
857 | pushDesc(sarg); | |
858 | break; | |
859 | case Opcodes.PUTSTATIC: | |
860 | pop(sarg); | |
861 | break; | |
862 | case Opcodes.GETFIELD: | |
863 | pop(1); | |
864 | pushDesc(sarg); | |
865 | break; | |
866 | case Opcodes.PUTFIELD: | |
867 | pop(sarg); | |
868 | pop(); | |
869 | break; | |
870 | case Opcodes.NEW: | |
871 | push(labels.get(0)); | |
872 | break; | |
873 | case Opcodes.NEWARRAY: | |
874 | pop(); | |
875 | switch (iarg) { | |
876 | case Opcodes.T_BOOLEAN: | |
877 | pushDesc("[Z"); | |
878 | break; | |
879 | case Opcodes.T_CHAR: | |
880 | pushDesc("[C"); | |
881 | break; | |
882 | case Opcodes.T_BYTE: | |
883 | pushDesc("[B"); | |
884 | break; | |
885 | case Opcodes.T_SHORT: | |
886 | pushDesc("[S"); | |
887 | break; | |
888 | case Opcodes.T_INT: | |
889 | pushDesc("[I"); | |
890 | break; | |
891 | case Opcodes.T_FLOAT: | |
892 | pushDesc("[F"); | |
893 | break; | |
894 | case Opcodes.T_DOUBLE: | |
895 | pushDesc("[D"); | |
896 | break; | |
897 | // case Opcodes.T_LONG: | |
898 | default: | |
899 | pushDesc("[J"); | |
900 | break; | |
901 | } | |
902 | break; | |
903 | case Opcodes.ANEWARRAY: | |
904 | pop(); | |
905 | pushDesc("[" + Type.getObjectType(sarg)); | |
906 | break; | |
907 | case Opcodes.CHECKCAST: | |
908 | pop(); | |
909 | pushDesc(Type.getObjectType(sarg).getDescriptor()); | |
910 | break; | |
911 | // case Opcodes.MULTIANEWARRAY: | |
912 | default: | |
913 | pop(iarg); | |
914 | pushDesc(sarg); | |
915 | break; | |
916 | } | |
917 | labels = null; | |
918 | } | |
919 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.Handle; | |
32 | import clojure.asm.Label; | |
33 | import clojure.asm.MethodVisitor; | |
34 | import clojure.asm.Opcodes; | |
35 | ||
36 | /** | |
37 | * A {@link MethodVisitor} that can be used to approximate method size. | |
38 | * | |
39 | * @author Eugene Kuleshov | |
40 | */ | |
41 | public class CodeSizeEvaluator extends MethodVisitor implements Opcodes { | |
42 | ||
43 | private int minSize; | |
44 | ||
45 | private int maxSize; | |
46 | ||
47 | public CodeSizeEvaluator(final MethodVisitor mv) { | |
48 | this(Opcodes.ASM4, mv); | |
49 | } | |
50 | ||
51 | protected CodeSizeEvaluator(final int api, final MethodVisitor mv) { | |
52 | super(api, mv); | |
53 | } | |
54 | ||
55 | public int getMinSize() { | |
56 | return this.minSize; | |
57 | } | |
58 | ||
59 | public int getMaxSize() { | |
60 | return this.maxSize; | |
61 | } | |
62 | ||
63 | @Override | |
64 | public void visitInsn(final int opcode) { | |
65 | minSize += 1; | |
66 | maxSize += 1; | |
67 | if (mv != null) { | |
68 | mv.visitInsn(opcode); | |
69 | } | |
70 | } | |
71 | ||
72 | @Override | |
73 | public void visitIntInsn(final int opcode, final int operand) { | |
74 | if (opcode == SIPUSH) { | |
75 | minSize += 3; | |
76 | maxSize += 3; | |
77 | } else { | |
78 | minSize += 2; | |
79 | maxSize += 2; | |
80 | } | |
81 | if (mv != null) { | |
82 | mv.visitIntInsn(opcode, operand); | |
83 | } | |
84 | } | |
85 | ||
86 | @Override | |
87 | public void visitVarInsn(final int opcode, final int var) { | |
88 | if (var < 4 && opcode != RET) { | |
89 | minSize += 1; | |
90 | maxSize += 1; | |
91 | } else if (var >= 256) { | |
92 | minSize += 4; | |
93 | maxSize += 4; | |
94 | } else { | |
95 | minSize += 2; | |
96 | maxSize += 2; | |
97 | } | |
98 | if (mv != null) { | |
99 | mv.visitVarInsn(opcode, var); | |
100 | } | |
101 | } | |
102 | ||
103 | @Override | |
104 | public void visitTypeInsn(final int opcode, final String type) { | |
105 | minSize += 3; | |
106 | maxSize += 3; | |
107 | if (mv != null) { | |
108 | mv.visitTypeInsn(opcode, type); | |
109 | } | |
110 | } | |
111 | ||
112 | @Override | |
113 | public void visitFieldInsn(final int opcode, final String owner, | |
114 | final String name, final String desc) { | |
115 | minSize += 3; | |
116 | maxSize += 3; | |
117 | if (mv != null) { | |
118 | mv.visitFieldInsn(opcode, owner, name, desc); | |
119 | } | |
120 | } | |
121 | ||
122 | @Override | |
123 | public void visitMethodInsn(final int opcode, final String owner, | |
124 | final String name, final String desc) { | |
125 | if (opcode == INVOKEINTERFACE) { | |
126 | minSize += 5; | |
127 | maxSize += 5; | |
128 | } else { | |
129 | minSize += 3; | |
130 | maxSize += 3; | |
131 | } | |
132 | if (mv != null) { | |
133 | mv.visitMethodInsn(opcode, owner, name, desc); | |
134 | } | |
135 | } | |
136 | ||
137 | @Override | |
138 | public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, | |
139 | Object... bsmArgs) { | |
140 | minSize += 5; | |
141 | maxSize += 5; | |
142 | if (mv != null) { | |
143 | mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); | |
144 | } | |
145 | } | |
146 | ||
147 | @Override | |
148 | public void visitJumpInsn(final int opcode, final Label label) { | |
149 | minSize += 3; | |
150 | if (opcode == GOTO || opcode == JSR) { | |
151 | maxSize += 5; | |
152 | } else { | |
153 | maxSize += 8; | |
154 | } | |
155 | if (mv != null) { | |
156 | mv.visitJumpInsn(opcode, label); | |
157 | } | |
158 | } | |
159 | ||
160 | @Override | |
161 | public void visitLdcInsn(final Object cst) { | |
162 | if (cst instanceof Long || cst instanceof Double) { | |
163 | minSize += 3; | |
164 | maxSize += 3; | |
165 | } else { | |
166 | minSize += 2; | |
167 | maxSize += 3; | |
168 | } | |
169 | if (mv != null) { | |
170 | mv.visitLdcInsn(cst); | |
171 | } | |
172 | } | |
173 | ||
174 | @Override | |
175 | public void visitIincInsn(final int var, final int increment) { | |
176 | if (var > 255 || increment > 127 || increment < -128) { | |
177 | minSize += 6; | |
178 | maxSize += 6; | |
179 | } else { | |
180 | minSize += 3; | |
181 | maxSize += 3; | |
182 | } | |
183 | if (mv != null) { | |
184 | mv.visitIincInsn(var, increment); | |
185 | } | |
186 | } | |
187 | ||
188 | @Override | |
189 | public void visitTableSwitchInsn(final int min, final int max, | |
190 | final Label dflt, final Label... labels) { | |
191 | minSize += 13 + labels.length * 4; | |
192 | maxSize += 16 + labels.length * 4; | |
193 | if (mv != null) { | |
194 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
195 | } | |
196 | } | |
197 | ||
198 | @Override | |
199 | public void visitLookupSwitchInsn(final Label dflt, final int[] keys, | |
200 | final Label[] labels) { | |
201 | minSize += 9 + keys.length * 8; | |
202 | maxSize += 12 + keys.length * 8; | |
203 | if (mv != null) { | |
204 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
205 | } | |
206 | } | |
207 | ||
208 | @Override | |
209 | public void visitMultiANewArrayInsn(final String desc, final int dims) { | |
210 | minSize += 4; | |
211 | maxSize += 4; | |
212 | if (mv != null) { | |
213 | mv.visitMultiANewArrayInsn(desc, dims); | |
214 | } | |
215 | } | |
216 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.Handle; | |
37 | import clojure.asm.Label; | |
38 | import clojure.asm.MethodVisitor; | |
39 | import clojure.asm.Opcodes; | |
40 | import clojure.asm.Type; | |
41 | ||
42 | /** | |
43 | * A {@link clojure.asm.MethodVisitor} with convenient methods to generate | |
44 | * code. For example, using this adapter, the class below | |
45 | * | |
46 | * <pre> | |
47 | * public class Example { | |
48 | * public static void main(String[] args) { | |
49 | * System.out.println("Hello world!"); | |
50 | * } | |
51 | * } | |
52 | * </pre> | |
53 | * | |
54 | * can be generated as follows: | |
55 | * | |
56 | * <pre> | |
57 | * ClassWriter cw = new ClassWriter(true); | |
58 | * cw.visit(V1_1, ACC_PUBLIC, "Example", null, "java/lang/Object", null); | |
59 | * | |
60 | * Method m = Method.getMethod("void <init> ()"); | |
61 | * GeneratorAdapter mg = new GeneratorAdapter(ACC_PUBLIC, m, null, null, cw); | |
62 | * mg.loadThis(); | |
63 | * mg.invokeConstructor(Type.getType(Object.class), m); | |
64 | * mg.returnValue(); | |
65 | * mg.endMethod(); | |
66 | * | |
67 | * m = Method.getMethod("void main (String[])"); | |
68 | * mg = new GeneratorAdapter(ACC_PUBLIC + ACC_STATIC, m, null, null, cw); | |
69 | * mg.getStatic(Type.getType(System.class), "out", Type.getType(PrintStream.class)); | |
70 | * mg.push("Hello world!"); | |
71 | * mg.invokeVirtual(Type.getType(PrintStream.class), | |
72 | * Method.getMethod("void println (String)")); | |
73 | * mg.returnValue(); | |
74 | * mg.endMethod(); | |
75 | * | |
76 | * cw.visitEnd(); | |
77 | * </pre> | |
78 | * | |
79 | * @author Juozas Baliuka | |
80 | * @author Chris Nokleberg | |
81 | * @author Eric Bruneton | |
82 | * @author Prashant Deva | |
83 | */ | |
84 | public class GeneratorAdapter extends LocalVariablesSorter { | |
85 | ||
86 | private static final String CLDESC = "Ljava/lang/Class;"; | |
87 | ||
88 | private static final Type BYTE_TYPE = Type.getObjectType("java/lang/Byte"); | |
89 | ||
90 | private static final Type BOOLEAN_TYPE = Type | |
91 | .getObjectType("java/lang/Boolean"); | |
92 | ||
93 | private static final Type SHORT_TYPE = Type | |
94 | .getObjectType("java/lang/Short"); | |
95 | ||
96 | private static final Type CHARACTER_TYPE = Type | |
97 | .getObjectType("java/lang/Character"); | |
98 | ||
99 | private static final Type INTEGER_TYPE = Type | |
100 | .getObjectType("java/lang/Integer"); | |
101 | ||
102 | private static final Type FLOAT_TYPE = Type | |
103 | .getObjectType("java/lang/Float"); | |
104 | ||
105 | private static final Type LONG_TYPE = Type.getObjectType("java/lang/Long"); | |
106 | ||
107 | private static final Type DOUBLE_TYPE = Type | |
108 | .getObjectType("java/lang/Double"); | |
109 | ||
110 | private static final Type NUMBER_TYPE = Type | |
111 | .getObjectType("java/lang/Number"); | |
112 | ||
113 | private static final Type OBJECT_TYPE = Type | |
114 | .getObjectType("java/lang/Object"); | |
115 | ||
116 | private static final Method BOOLEAN_VALUE = Method | |
117 | .getMethod("boolean booleanValue()"); | |
118 | ||
119 | private static final Method CHAR_VALUE = Method | |
120 | .getMethod("char charValue()"); | |
121 | ||
122 | private static final Method INT_VALUE = Method.getMethod("int intValue()"); | |
123 | ||
124 | private static final Method FLOAT_VALUE = Method | |
125 | .getMethod("float floatValue()"); | |
126 | ||
127 | private static final Method LONG_VALUE = Method | |
128 | .getMethod("long longValue()"); | |
129 | ||
130 | private static final Method DOUBLE_VALUE = Method | |
131 | .getMethod("double doubleValue()"); | |
132 | ||
133 | /** | |
134 | * Constant for the {@link #math math} method. | |
135 | */ | |
136 | public static final int ADD = Opcodes.IADD; | |
137 | ||
138 | /** | |
139 | * Constant for the {@link #math math} method. | |
140 | */ | |
141 | public static final int SUB = Opcodes.ISUB; | |
142 | ||
143 | /** | |
144 | * Constant for the {@link #math math} method. | |
145 | */ | |
146 | public static final int MUL = Opcodes.IMUL; | |
147 | ||
148 | /** | |
149 | * Constant for the {@link #math math} method. | |
150 | */ | |
151 | public static final int DIV = Opcodes.IDIV; | |
152 | ||
153 | /** | |
154 | * Constant for the {@link #math math} method. | |
155 | */ | |
156 | public static final int REM = Opcodes.IREM; | |
157 | ||
158 | /** | |
159 | * Constant for the {@link #math math} method. | |
160 | */ | |
161 | public static final int NEG = Opcodes.INEG; | |
162 | ||
163 | /** | |
164 | * Constant for the {@link #math math} method. | |
165 | */ | |
166 | public static final int SHL = Opcodes.ISHL; | |
167 | ||
168 | /** | |
169 | * Constant for the {@link #math math} method. | |
170 | */ | |
171 | public static final int SHR = Opcodes.ISHR; | |
172 | ||
173 | /** | |
174 | * Constant for the {@link #math math} method. | |
175 | */ | |
176 | public static final int USHR = Opcodes.IUSHR; | |
177 | ||
178 | /** | |
179 | * Constant for the {@link #math math} method. | |
180 | */ | |
181 | public static final int AND = Opcodes.IAND; | |
182 | ||
183 | /** | |
184 | * Constant for the {@link #math math} method. | |
185 | */ | |
186 | public static final int OR = Opcodes.IOR; | |
187 | ||
188 | /** | |
189 | * Constant for the {@link #math math} method. | |
190 | */ | |
191 | public static final int XOR = Opcodes.IXOR; | |
192 | ||
193 | /** | |
194 | * Constant for the {@link #ifCmp ifCmp} method. | |
195 | */ | |
196 | public static final int EQ = Opcodes.IFEQ; | |
197 | ||
198 | /** | |
199 | * Constant for the {@link #ifCmp ifCmp} method. | |
200 | */ | |
201 | public static final int NE = Opcodes.IFNE; | |
202 | ||
203 | /** | |
204 | * Constant for the {@link #ifCmp ifCmp} method. | |
205 | */ | |
206 | public static final int LT = Opcodes.IFLT; | |
207 | ||
208 | /** | |
209 | * Constant for the {@link #ifCmp ifCmp} method. | |
210 | */ | |
211 | public static final int GE = Opcodes.IFGE; | |
212 | ||
213 | /** | |
214 | * Constant for the {@link #ifCmp ifCmp} method. | |
215 | */ | |
216 | public static final int GT = Opcodes.IFGT; | |
217 | ||
218 | /** | |
219 | * Constant for the {@link #ifCmp ifCmp} method. | |
220 | */ | |
221 | public static final int LE = Opcodes.IFLE; | |
222 | ||
223 | /** | |
224 | * Access flags of the method visited by this adapter. | |
225 | */ | |
226 | private final int access; | |
227 | ||
228 | /** | |
229 | * Return type of the method visited by this adapter. | |
230 | */ | |
231 | private final Type returnType; | |
232 | ||
233 | /** | |
234 | * Argument types of the method visited by this adapter. | |
235 | */ | |
236 | private final Type[] argumentTypes; | |
237 | ||
238 | /** | |
239 | * Types of the local variables of the method visited by this adapter. | |
240 | */ | |
241 | private final List<Type> localTypes = new ArrayList<Type>(); | |
242 | ||
243 | /** | |
244 | * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this | |
245 | * constructor</i>. Instead, they must use the | |
246 | * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} | |
247 | * version. | |
248 | * | |
249 | * @param mv | |
250 | * the method visitor to which this adapter delegates calls. | |
251 | * @param access | |
252 | * the method's access flags (see {@link Opcodes}). | |
253 | * @param name | |
254 | * the method's name. | |
255 | * @param desc | |
256 | * the method's descriptor (see {@link Type Type}). | |
257 | */ | |
258 | public GeneratorAdapter(final MethodVisitor mv, final int access, | |
259 | final String name, final String desc) { | |
260 | this(Opcodes.ASM4, mv, access, name, desc); | |
261 | } | |
262 | ||
263 | /** | |
264 | * Creates a new {@link GeneratorAdapter}. | |
265 | * | |
266 | * @param api | |
267 | * the ASM API version implemented by this visitor. Must be one | |
268 | * of {@link Opcodes#ASM4}. | |
269 | * @param mv | |
270 | * the method visitor to which this adapter delegates calls. | |
271 | * @param access | |
272 | * the method's access flags (see {@link Opcodes}). | |
273 | * @param name | |
274 | * the method's name. | |
275 | * @param desc | |
276 | * the method's descriptor (see {@link Type Type}). | |
277 | */ | |
278 | protected GeneratorAdapter(final int api, final MethodVisitor mv, | |
279 | final int access, final String name, final String desc) { | |
280 | super(api, access, desc, mv); | |
281 | this.access = access; | |
282 | this.returnType = Type.getReturnType(desc); | |
283 | this.argumentTypes = Type.getArgumentTypes(desc); | |
284 | } | |
285 | ||
286 | /** | |
287 | * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this | |
288 | * constructor</i>. Instead, they must use the | |
289 | * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} | |
290 | * version. | |
291 | * | |
292 | * @param access | |
293 | * access flags of the adapted method. | |
294 | * @param method | |
295 | * the adapted method. | |
296 | * @param mv | |
297 | * the method visitor to which this adapter delegates calls. | |
298 | */ | |
299 | public GeneratorAdapter(final int access, final Method method, | |
300 | final MethodVisitor mv) { | |
301 | this(mv, access, null, method.getDescriptor()); | |
302 | } | |
303 | ||
304 | /** | |
305 | * Creates a new {@link GeneratorAdapter}. <i>Subclasses must not use this | |
306 | * constructor</i>. Instead, they must use the | |
307 | * {@link #GeneratorAdapter(int, MethodVisitor, int, String, String)} | |
308 | * version. | |
309 | * | |
310 | * @param access | |
311 | * access flags of the adapted method. | |
312 | * @param method | |
313 | * the adapted method. | |
314 | * @param signature | |
315 | * the signature of the adapted method (may be <tt>null</tt>). | |
316 | * @param exceptions | |
317 | * the exceptions thrown by the adapted method (may be | |
318 | * <tt>null</tt>). | |
319 | * @param cv | |
320 | * the class visitor to which this adapter delegates calls. | |
321 | */ | |
322 | public GeneratorAdapter(final int access, final Method method, | |
323 | final String signature, final Type[] exceptions, | |
324 | final ClassVisitor cv) { | |
325 | this(access, method, cv | |
326 | .visitMethod(access, method.getName(), method.getDescriptor(), | |
327 | signature, getInternalNames(exceptions))); | |
328 | } | |
329 | ||
330 | /** | |
331 | * Returns the internal names of the given types. | |
332 | * | |
333 | * @param types | |
334 | * a set of types. | |
335 | * @return the internal names of the given types. | |
336 | */ | |
337 | private static String[] getInternalNames(final Type[] types) { | |
338 | if (types == null) { | |
339 | return null; | |
340 | } | |
341 | String[] names = new String[types.length]; | |
342 | for (int i = 0; i < names.length; ++i) { | |
343 | names[i] = types[i].getInternalName(); | |
344 | } | |
345 | return names; | |
346 | } | |
347 | ||
348 | // ------------------------------------------------------------------------ | |
349 | // Instructions to push constants on the stack | |
350 | // ------------------------------------------------------------------------ | |
351 | ||
352 | /** | |
353 | * Generates the instruction to push the given value on the stack. | |
354 | * | |
355 | * @param value | |
356 | * the value to be pushed on the stack. | |
357 | */ | |
358 | public void push(final boolean value) { | |
359 | push(value ? 1 : 0); | |
360 | } | |
361 | ||
362 | /** | |
363 | * Generates the instruction to push the given value on the stack. | |
364 | * | |
365 | * @param value | |
366 | * the value to be pushed on the stack. | |
367 | */ | |
368 | public void push(final int value) { | |
369 | if (value >= -1 && value <= 5) { | |
370 | mv.visitInsn(Opcodes.ICONST_0 + value); | |
371 | } else if (value >= Byte.MIN_VALUE && value <= Byte.MAX_VALUE) { | |
372 | mv.visitIntInsn(Opcodes.BIPUSH, value); | |
373 | } else if (value >= Short.MIN_VALUE && value <= Short.MAX_VALUE) { | |
374 | mv.visitIntInsn(Opcodes.SIPUSH, value); | |
375 | } else { | |
376 | mv.visitLdcInsn(new Integer(value)); | |
377 | } | |
378 | } | |
379 | ||
380 | /** | |
381 | * Generates the instruction to push the given value on the stack. | |
382 | * | |
383 | * @param value | |
384 | * the value to be pushed on the stack. | |
385 | */ | |
386 | public void push(final long value) { | |
387 | if (value == 0L || value == 1L) { | |
388 | mv.visitInsn(Opcodes.LCONST_0 + (int) value); | |
389 | } else { | |
390 | mv.visitLdcInsn(new Long(value)); | |
391 | } | |
392 | } | |
393 | ||
394 | /** | |
395 | * Generates the instruction to push the given value on the stack. | |
396 | * | |
397 | * @param value | |
398 | * the value to be pushed on the stack. | |
399 | */ | |
400 | public void push(final float value) { | |
401 | int bits = Float.floatToIntBits(value); | |
402 | if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2 | |
403 | mv.visitInsn(Opcodes.FCONST_0 + (int) value); | |
404 | } else { | |
405 | mv.visitLdcInsn(new Float(value)); | |
406 | } | |
407 | } | |
408 | ||
409 | /** | |
410 | * Generates the instruction to push the given value on the stack. | |
411 | * | |
412 | * @param value | |
413 | * the value to be pushed on the stack. | |
414 | */ | |
415 | public void push(final double value) { | |
416 | long bits = Double.doubleToLongBits(value); | |
417 | if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d | |
418 | mv.visitInsn(Opcodes.DCONST_0 + (int) value); | |
419 | } else { | |
420 | mv.visitLdcInsn(new Double(value)); | |
421 | } | |
422 | } | |
423 | ||
424 | /** | |
425 | * Generates the instruction to push the given value on the stack. | |
426 | * | |
427 | * @param value | |
428 | * the value to be pushed on the stack. May be <tt>null</tt>. | |
429 | */ | |
430 | public void push(final String value) { | |
431 | if (value == null) { | |
432 | mv.visitInsn(Opcodes.ACONST_NULL); | |
433 | } else { | |
434 | mv.visitLdcInsn(value); | |
435 | } | |
436 | } | |
437 | ||
438 | /** | |
439 | * Generates the instruction to push the given value on the stack. | |
440 | * | |
441 | * @param value | |
442 | * the value to be pushed on the stack. | |
443 | */ | |
444 | public void push(final Type value) { | |
445 | if (value == null) { | |
446 | mv.visitInsn(Opcodes.ACONST_NULL); | |
447 | } else { | |
448 | switch (value.getSort()) { | |
449 | case Type.BOOLEAN: | |
450 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", | |
451 | "TYPE", CLDESC); | |
452 | break; | |
453 | case Type.CHAR: | |
454 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", | |
455 | "TYPE", CLDESC); | |
456 | break; | |
457 | case Type.BYTE: | |
458 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", | |
459 | CLDESC); | |
460 | break; | |
461 | case Type.SHORT: | |
462 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", | |
463 | CLDESC); | |
464 | break; | |
465 | case Type.INT: | |
466 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", | |
467 | "TYPE", CLDESC); | |
468 | break; | |
469 | case Type.FLOAT: | |
470 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", | |
471 | CLDESC); | |
472 | break; | |
473 | case Type.LONG: | |
474 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", | |
475 | CLDESC); | |
476 | break; | |
477 | case Type.DOUBLE: | |
478 | mv.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", | |
479 | "TYPE", CLDESC); | |
480 | break; | |
481 | default: | |
482 | mv.visitLdcInsn(value); | |
483 | } | |
484 | } | |
485 | } | |
486 | ||
487 | /** | |
488 | * Generates the instruction to push a handle on the stack. | |
489 | * | |
490 | * @param handle | |
491 | * the handle to be pushed on the stack. | |
492 | */ | |
493 | public void push(final Handle handle) { | |
494 | mv.visitLdcInsn(handle); | |
495 | } | |
496 | ||
497 | // ------------------------------------------------------------------------ | |
498 | // Instructions to load and store method arguments | |
499 | // ------------------------------------------------------------------------ | |
500 | ||
501 | /** | |
502 | * Returns the index of the given method argument in the frame's local | |
503 | * variables array. | |
504 | * | |
505 | * @param arg | |
506 | * the index of a method argument. | |
507 | * @return the index of the given method argument in the frame's local | |
508 | * variables array. | |
509 | */ | |
510 | private int getArgIndex(final int arg) { | |
511 | int index = (access & Opcodes.ACC_STATIC) == 0 ? 1 : 0; | |
512 | for (int i = 0; i < arg; i++) { | |
513 | index += argumentTypes[i].getSize(); | |
514 | } | |
515 | return index; | |
516 | } | |
517 | ||
518 | /** | |
519 | * Generates the instruction to push a local variable on the stack. | |
520 | * | |
521 | * @param type | |
522 | * the type of the local variable to be loaded. | |
523 | * @param index | |
524 | * an index in the frame's local variables array. | |
525 | */ | |
526 | private void loadInsn(final Type type, final int index) { | |
527 | mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), index); | |
528 | } | |
529 | ||
530 | /** | |
531 | * Generates the instruction to store the top stack value in a local | |
532 | * variable. | |
533 | * | |
534 | * @param type | |
535 | * the type of the local variable to be stored. | |
536 | * @param index | |
537 | * an index in the frame's local variables array. | |
538 | */ | |
539 | private void storeInsn(final Type type, final int index) { | |
540 | mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), index); | |
541 | } | |
542 | ||
543 | /** | |
544 | * Generates the instruction to load 'this' on the stack. | |
545 | */ | |
546 | public void loadThis() { | |
547 | if ((access & Opcodes.ACC_STATIC) != 0) { | |
548 | throw new IllegalStateException( | |
549 | "no 'this' pointer within static method"); | |
550 | } | |
551 | mv.visitVarInsn(Opcodes.ALOAD, 0); | |
552 | } | |
553 | ||
554 | /** | |
555 | * Generates the instruction to load the given method argument on the stack. | |
556 | * | |
557 | * @param arg | |
558 | * the index of a method argument. | |
559 | */ | |
560 | public void loadArg(final int arg) { | |
561 | loadInsn(argumentTypes[arg], getArgIndex(arg)); | |
562 | } | |
563 | ||
564 | /** | |
565 | * Generates the instructions to load the given method arguments on the | |
566 | * stack. | |
567 | * | |
568 | * @param arg | |
569 | * the index of the first method argument to be loaded. | |
570 | * @param count | |
571 | * the number of method arguments to be loaded. | |
572 | */ | |
573 | public void loadArgs(final int arg, final int count) { | |
574 | int index = getArgIndex(arg); | |
575 | for (int i = 0; i < count; ++i) { | |
576 | Type t = argumentTypes[arg + i]; | |
577 | loadInsn(t, index); | |
578 | index += t.getSize(); | |
579 | } | |
580 | } | |
581 | ||
582 | /** | |
583 | * Generates the instructions to load all the method arguments on the stack. | |
584 | */ | |
585 | public void loadArgs() { | |
586 | loadArgs(0, argumentTypes.length); | |
587 | } | |
588 | ||
589 | /** | |
590 | * Generates the instructions to load all the method arguments on the stack, | |
591 | * as a single object array. | |
592 | */ | |
593 | public void loadArgArray() { | |
594 | push(argumentTypes.length); | |
595 | newArray(OBJECT_TYPE); | |
596 | for (int i = 0; i < argumentTypes.length; i++) { | |
597 | dup(); | |
598 | push(i); | |
599 | loadArg(i); | |
600 | box(argumentTypes[i]); | |
601 | arrayStore(OBJECT_TYPE); | |
602 | } | |
603 | } | |
604 | ||
605 | /** | |
606 | * Generates the instruction to store the top stack value in the given | |
607 | * method argument. | |
608 | * | |
609 | * @param arg | |
610 | * the index of a method argument. | |
611 | */ | |
612 | public void storeArg(final int arg) { | |
613 | storeInsn(argumentTypes[arg], getArgIndex(arg)); | |
614 | } | |
615 | ||
616 | // ------------------------------------------------------------------------ | |
617 | // Instructions to load and store local variables | |
618 | // ------------------------------------------------------------------------ | |
619 | ||
620 | /** | |
621 | * Returns the type of the given local variable. | |
622 | * | |
623 | * @param local | |
624 | * a local variable identifier, as returned by | |
625 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
626 | * @return the type of the given local variable. | |
627 | */ | |
628 | public Type getLocalType(final int local) { | |
629 | return localTypes.get(local - firstLocal); | |
630 | } | |
631 | ||
632 | @Override | |
633 | protected void setLocalType(final int local, final Type type) { | |
634 | int index = local - firstLocal; | |
635 | while (localTypes.size() < index + 1) { | |
636 | localTypes.add(null); | |
637 | } | |
638 | localTypes.set(index, type); | |
639 | } | |
640 | ||
641 | /** | |
642 | * Generates the instruction to load the given local variable on the stack. | |
643 | * | |
644 | * @param local | |
645 | * a local variable identifier, as returned by | |
646 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
647 | */ | |
648 | public void loadLocal(final int local) { | |
649 | loadInsn(getLocalType(local), local); | |
650 | } | |
651 | ||
652 | /** | |
653 | * Generates the instruction to load the given local variable on the stack. | |
654 | * | |
655 | * @param local | |
656 | * a local variable identifier, as returned by | |
657 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
658 | * @param type | |
659 | * the type of this local variable. | |
660 | */ | |
661 | public void loadLocal(final int local, final Type type) { | |
662 | setLocalType(local, type); | |
663 | loadInsn(type, local); | |
664 | } | |
665 | ||
666 | /** | |
667 | * Generates the instruction to store the top stack value in the given local | |
668 | * variable. | |
669 | * | |
670 | * @param local | |
671 | * a local variable identifier, as returned by | |
672 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
673 | */ | |
674 | public void storeLocal(final int local) { | |
675 | storeInsn(getLocalType(local), local); | |
676 | } | |
677 | ||
678 | /** | |
679 | * Generates the instruction to store the top stack value in the given local | |
680 | * variable. | |
681 | * | |
682 | * @param local | |
683 | * a local variable identifier, as returned by | |
684 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
685 | * @param type | |
686 | * the type of this local variable. | |
687 | */ | |
688 | public void storeLocal(final int local, final Type type) { | |
689 | setLocalType(local, type); | |
690 | storeInsn(type, local); | |
691 | } | |
692 | ||
693 | /** | |
694 | * Generates the instruction to load an element from an array. | |
695 | * | |
696 | * @param type | |
697 | * the type of the array element to be loaded. | |
698 | */ | |
699 | public void arrayLoad(final Type type) { | |
700 | mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); | |
701 | } | |
702 | ||
703 | /** | |
704 | * Generates the instruction to store an element in an array. | |
705 | * | |
706 | * @param type | |
707 | * the type of the array element to be stored. | |
708 | */ | |
709 | public void arrayStore(final Type type) { | |
710 | mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); | |
711 | } | |
712 | ||
713 | // ------------------------------------------------------------------------ | |
714 | // Instructions to manage the stack | |
715 | // ------------------------------------------------------------------------ | |
716 | ||
717 | /** | |
718 | * Generates a POP instruction. | |
719 | */ | |
720 | public void pop() { | |
721 | mv.visitInsn(Opcodes.POP); | |
722 | } | |
723 | ||
724 | /** | |
725 | * Generates a POP2 instruction. | |
726 | */ | |
727 | public void pop2() { | |
728 | mv.visitInsn(Opcodes.POP2); | |
729 | } | |
730 | ||
731 | /** | |
732 | * Generates a DUP instruction. | |
733 | */ | |
734 | public void dup() { | |
735 | mv.visitInsn(Opcodes.DUP); | |
736 | } | |
737 | ||
738 | /** | |
739 | * Generates a DUP2 instruction. | |
740 | */ | |
741 | public void dup2() { | |
742 | mv.visitInsn(Opcodes.DUP2); | |
743 | } | |
744 | ||
745 | /** | |
746 | * Generates a DUP_X1 instruction. | |
747 | */ | |
748 | public void dupX1() { | |
749 | mv.visitInsn(Opcodes.DUP_X1); | |
750 | } | |
751 | ||
752 | /** | |
753 | * Generates a DUP_X2 instruction. | |
754 | */ | |
755 | public void dupX2() { | |
756 | mv.visitInsn(Opcodes.DUP_X2); | |
757 | } | |
758 | ||
759 | /** | |
760 | * Generates a DUP2_X1 instruction. | |
761 | */ | |
762 | public void dup2X1() { | |
763 | mv.visitInsn(Opcodes.DUP2_X1); | |
764 | } | |
765 | ||
766 | /** | |
767 | * Generates a DUP2_X2 instruction. | |
768 | */ | |
769 | public void dup2X2() { | |
770 | mv.visitInsn(Opcodes.DUP2_X2); | |
771 | } | |
772 | ||
773 | /** | |
774 | * Generates a SWAP instruction. | |
775 | */ | |
776 | public void swap() { | |
777 | mv.visitInsn(Opcodes.SWAP); | |
778 | } | |
779 | ||
780 | /** | |
781 | * Generates the instructions to swap the top two stack values. | |
782 | * | |
783 | * @param prev | |
784 | * type of the top - 1 stack value. | |
785 | * @param type | |
786 | * type of the top stack value. | |
787 | */ | |
788 | public void swap(final Type prev, final Type type) { | |
789 | if (type.getSize() == 1) { | |
790 | if (prev.getSize() == 1) { | |
791 | swap(); // same as dupX1(), pop(); | |
792 | } else { | |
793 | dupX2(); | |
794 | pop(); | |
795 | } | |
796 | } else { | |
797 | if (prev.getSize() == 1) { | |
798 | dup2X1(); | |
799 | pop2(); | |
800 | } else { | |
801 | dup2X2(); | |
802 | pop2(); | |
803 | } | |
804 | } | |
805 | } | |
806 | ||
807 | // ------------------------------------------------------------------------ | |
808 | // Instructions to do mathematical and logical operations | |
809 | // ------------------------------------------------------------------------ | |
810 | ||
811 | /** | |
812 | * Generates the instruction to do the specified mathematical or logical | |
813 | * operation. | |
814 | * | |
815 | * @param op | |
816 | * a mathematical or logical operation. Must be one of ADD, SUB, | |
817 | * MUL, DIV, REM, NEG, SHL, SHR, USHR, AND, OR, XOR. | |
818 | * @param type | |
819 | * the type of the operand(s) for this operation. | |
820 | */ | |
821 | public void math(final int op, final Type type) { | |
822 | mv.visitInsn(type.getOpcode(op)); | |
823 | } | |
824 | ||
825 | /** | |
826 | * Generates the instructions to compute the bitwise negation of the top | |
827 | * stack value. | |
828 | */ | |
829 | public void not() { | |
830 | mv.visitInsn(Opcodes.ICONST_1); | |
831 | mv.visitInsn(Opcodes.IXOR); | |
832 | } | |
833 | ||
834 | /** | |
835 | * Generates the instruction to increment the given local variable. | |
836 | * | |
837 | * @param local | |
838 | * the local variable to be incremented. | |
839 | * @param amount | |
840 | * the amount by which the local variable must be incremented. | |
841 | */ | |
842 | public void iinc(final int local, final int amount) { | |
843 | mv.visitIincInsn(local, amount); | |
844 | } | |
845 | ||
846 | /** | |
847 | * Generates the instructions to cast a numerical value from one type to | |
848 | * another. | |
849 | * | |
850 | * @param from | |
851 | * the type of the top stack value | |
852 | * @param to | |
853 | * the type into which this value must be cast. | |
854 | */ | |
855 | public void cast(final Type from, final Type to) { | |
856 | if (from != to) { | |
857 | if (from == Type.DOUBLE_TYPE) { | |
858 | if (to == Type.FLOAT_TYPE) { | |
859 | mv.visitInsn(Opcodes.D2F); | |
860 | } else if (to == Type.LONG_TYPE) { | |
861 | mv.visitInsn(Opcodes.D2L); | |
862 | } else { | |
863 | mv.visitInsn(Opcodes.D2I); | |
864 | cast(Type.INT_TYPE, to); | |
865 | } | |
866 | } else if (from == Type.FLOAT_TYPE) { | |
867 | if (to == Type.DOUBLE_TYPE) { | |
868 | mv.visitInsn(Opcodes.F2D); | |
869 | } else if (to == Type.LONG_TYPE) { | |
870 | mv.visitInsn(Opcodes.F2L); | |
871 | } else { | |
872 | mv.visitInsn(Opcodes.F2I); | |
873 | cast(Type.INT_TYPE, to); | |
874 | } | |
875 | } else if (from == Type.LONG_TYPE) { | |
876 | if (to == Type.DOUBLE_TYPE) { | |
877 | mv.visitInsn(Opcodes.L2D); | |
878 | } else if (to == Type.FLOAT_TYPE) { | |
879 | mv.visitInsn(Opcodes.L2F); | |
880 | } else { | |
881 | mv.visitInsn(Opcodes.L2I); | |
882 | cast(Type.INT_TYPE, to); | |
883 | } | |
884 | } else { | |
885 | if (to == Type.BYTE_TYPE) { | |
886 | mv.visitInsn(Opcodes.I2B); | |
887 | } else if (to == Type.CHAR_TYPE) { | |
888 | mv.visitInsn(Opcodes.I2C); | |
889 | } else if (to == Type.DOUBLE_TYPE) { | |
890 | mv.visitInsn(Opcodes.I2D); | |
891 | } else if (to == Type.FLOAT_TYPE) { | |
892 | mv.visitInsn(Opcodes.I2F); | |
893 | } else if (to == Type.LONG_TYPE) { | |
894 | mv.visitInsn(Opcodes.I2L); | |
895 | } else if (to == Type.SHORT_TYPE) { | |
896 | mv.visitInsn(Opcodes.I2S); | |
897 | } | |
898 | } | |
899 | } | |
900 | } | |
901 | ||
902 | // ------------------------------------------------------------------------ | |
903 | // Instructions to do boxing and unboxing operations | |
904 | // ------------------------------------------------------------------------ | |
905 | ||
906 | private static Type getBoxedType(final Type type) { | |
907 | switch (type.getSort()) { | |
908 | case Type.BYTE: | |
909 | return BYTE_TYPE; | |
910 | case Type.BOOLEAN: | |
911 | return BOOLEAN_TYPE; | |
912 | case Type.SHORT: | |
913 | return SHORT_TYPE; | |
914 | case Type.CHAR: | |
915 | return CHARACTER_TYPE; | |
916 | case Type.INT: | |
917 | return INTEGER_TYPE; | |
918 | case Type.FLOAT: | |
919 | return FLOAT_TYPE; | |
920 | case Type.LONG: | |
921 | return LONG_TYPE; | |
922 | case Type.DOUBLE: | |
923 | return DOUBLE_TYPE; | |
924 | } | |
925 | return type; | |
926 | } | |
927 | ||
928 | /** | |
929 | * Generates the instructions to box the top stack value. This value is | |
930 | * replaced by its boxed equivalent on top of the stack. | |
931 | * | |
932 | * @param type | |
933 | * the type of the top stack value. | |
934 | */ | |
935 | public void box(final Type type) { | |
936 | if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { | |
937 | return; | |
938 | } | |
939 | if (type == Type.VOID_TYPE) { | |
940 | push((String) null); | |
941 | } else { | |
942 | Type boxed = getBoxedType(type); | |
943 | newInstance(boxed); | |
944 | if (type.getSize() == 2) { | |
945 | // Pp -> Ppo -> oPpo -> ooPpo -> ooPp -> o | |
946 | dupX2(); | |
947 | dupX2(); | |
948 | pop(); | |
949 | } else { | |
950 | // p -> po -> opo -> oop -> o | |
951 | dupX1(); | |
952 | swap(); | |
953 | } | |
954 | invokeConstructor(boxed, new Method("<init>", Type.VOID_TYPE, | |
955 | new Type[] { type })); | |
956 | } | |
957 | } | |
958 | ||
959 | /** | |
960 | * Generates the instructions to box the top stack value using Java 5's | |
961 | * valueOf() method. This value is replaced by its boxed equivalent on top | |
962 | * of the stack. | |
963 | * | |
964 | * @param type | |
965 | * the type of the top stack value. | |
966 | */ | |
967 | public void valueOf(final Type type) { | |
968 | if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { | |
969 | return; | |
970 | } | |
971 | if (type == Type.VOID_TYPE) { | |
972 | push((String) null); | |
973 | } else { | |
974 | Type boxed = getBoxedType(type); | |
975 | invokeStatic(boxed, new Method("valueOf", boxed, | |
976 | new Type[] { type })); | |
977 | } | |
978 | } | |
979 | ||
980 | /** | |
981 | * Generates the instructions to unbox the top stack value. This value is | |
982 | * replaced by its unboxed equivalent on top of the stack. | |
983 | * | |
984 | * @param type | |
985 | * the type of the top stack value. | |
986 | */ | |
987 | public void unbox(final Type type) { | |
988 | Type t = NUMBER_TYPE; | |
989 | Method sig = null; | |
990 | switch (type.getSort()) { | |
991 | case Type.VOID: | |
992 | return; | |
993 | case Type.CHAR: | |
994 | t = CHARACTER_TYPE; | |
995 | sig = CHAR_VALUE; | |
996 | break; | |
997 | case Type.BOOLEAN: | |
998 | t = BOOLEAN_TYPE; | |
999 | sig = BOOLEAN_VALUE; | |
1000 | break; | |
1001 | case Type.DOUBLE: | |
1002 | sig = DOUBLE_VALUE; | |
1003 | break; | |
1004 | case Type.FLOAT: | |
1005 | sig = FLOAT_VALUE; | |
1006 | break; | |
1007 | case Type.LONG: | |
1008 | sig = LONG_VALUE; | |
1009 | break; | |
1010 | case Type.INT: | |
1011 | case Type.SHORT: | |
1012 | case Type.BYTE: | |
1013 | sig = INT_VALUE; | |
1014 | } | |
1015 | if (sig == null) { | |
1016 | checkCast(type); | |
1017 | } else { | |
1018 | checkCast(t); | |
1019 | invokeVirtual(t, sig); | |
1020 | } | |
1021 | } | |
1022 | ||
1023 | // ------------------------------------------------------------------------ | |
1024 | // Instructions to jump to other instructions | |
1025 | // ------------------------------------------------------------------------ | |
1026 | ||
1027 | /** | |
1028 | * Creates a new {@link Label}. | |
1029 | * | |
1030 | * @return a new {@link Label}. | |
1031 | */ | |
1032 | public Label newLabel() { | |
1033 | return new Label(); | |
1034 | } | |
1035 | ||
1036 | /** | |
1037 | * Marks the current code position with the given label. | |
1038 | * | |
1039 | * @param label | |
1040 | * a label. | |
1041 | */ | |
1042 | public void mark(final Label label) { | |
1043 | mv.visitLabel(label); | |
1044 | } | |
1045 | ||
1046 | /** | |
1047 | * Marks the current code position with a new label. | |
1048 | * | |
1049 | * @return the label that was created to mark the current code position. | |
1050 | */ | |
1051 | public Label mark() { | |
1052 | Label label = new Label(); | |
1053 | mv.visitLabel(label); | |
1054 | return label; | |
1055 | } | |
1056 | ||
1057 | /** | |
1058 | * Generates the instructions to jump to a label based on the comparison of | |
1059 | * the top two stack values. | |
1060 | * | |
1061 | * @param type | |
1062 | * the type of the top two stack values. | |
1063 | * @param mode | |
1064 | * how these values must be compared. One of EQ, NE, LT, GE, GT, | |
1065 | * LE. | |
1066 | * @param label | |
1067 | * where to jump if the comparison result is <tt>true</tt>. | |
1068 | */ | |
1069 | public void ifCmp(final Type type, final int mode, final Label label) { | |
1070 | switch (type.getSort()) { | |
1071 | case Type.LONG: | |
1072 | mv.visitInsn(Opcodes.LCMP); | |
1073 | break; | |
1074 | case Type.DOUBLE: | |
1075 | mv.visitInsn(mode == GE || mode == GT ? Opcodes.DCMPL | |
1076 | : Opcodes.DCMPG); | |
1077 | break; | |
1078 | case Type.FLOAT: | |
1079 | mv.visitInsn(mode == GE || mode == GT ? Opcodes.FCMPL | |
1080 | : Opcodes.FCMPG); | |
1081 | break; | |
1082 | case Type.ARRAY: | |
1083 | case Type.OBJECT: | |
1084 | switch (mode) { | |
1085 | case EQ: | |
1086 | mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); | |
1087 | return; | |
1088 | case NE: | |
1089 | mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); | |
1090 | return; | |
1091 | } | |
1092 | throw new IllegalArgumentException("Bad comparison for type " | |
1093 | + type); | |
1094 | default: | |
1095 | int intOp = -1; | |
1096 | switch (mode) { | |
1097 | case EQ: | |
1098 | intOp = Opcodes.IF_ICMPEQ; | |
1099 | break; | |
1100 | case NE: | |
1101 | intOp = Opcodes.IF_ICMPNE; | |
1102 | break; | |
1103 | case GE: | |
1104 | intOp = Opcodes.IF_ICMPGE; | |
1105 | break; | |
1106 | case LT: | |
1107 | intOp = Opcodes.IF_ICMPLT; | |
1108 | break; | |
1109 | case LE: | |
1110 | intOp = Opcodes.IF_ICMPLE; | |
1111 | break; | |
1112 | case GT: | |
1113 | intOp = Opcodes.IF_ICMPGT; | |
1114 | break; | |
1115 | } | |
1116 | mv.visitJumpInsn(intOp, label); | |
1117 | return; | |
1118 | } | |
1119 | mv.visitJumpInsn(mode, label); | |
1120 | } | |
1121 | ||
1122 | /** | |
1123 | * Generates the instructions to jump to a label based on the comparison of | |
1124 | * the top two integer stack values. | |
1125 | * | |
1126 | * @param mode | |
1127 | * how these values must be compared. One of EQ, NE, LT, GE, GT, | |
1128 | * LE. | |
1129 | * @param label | |
1130 | * where to jump if the comparison result is <tt>true</tt>. | |
1131 | */ | |
1132 | public void ifICmp(final int mode, final Label label) { | |
1133 | ifCmp(Type.INT_TYPE, mode, label); | |
1134 | } | |
1135 | ||
1136 | /** | |
1137 | * Generates the instructions to jump to a label based on the comparison of | |
1138 | * the top integer stack value with zero. | |
1139 | * | |
1140 | * @param mode | |
1141 | * how these values must be compared. One of EQ, NE, LT, GE, GT, | |
1142 | * LE. | |
1143 | * @param label | |
1144 | * where to jump if the comparison result is <tt>true</tt>. | |
1145 | */ | |
1146 | public void ifZCmp(final int mode, final Label label) { | |
1147 | mv.visitJumpInsn(mode, label); | |
1148 | } | |
1149 | ||
1150 | /** | |
1151 | * Generates the instruction to jump to the given label if the top stack | |
1152 | * value is null. | |
1153 | * | |
1154 | * @param label | |
1155 | * where to jump if the condition is <tt>true</tt>. | |
1156 | */ | |
1157 | public void ifNull(final Label label) { | |
1158 | mv.visitJumpInsn(Opcodes.IFNULL, label); | |
1159 | } | |
1160 | ||
1161 | /** | |
1162 | * Generates the instruction to jump to the given label if the top stack | |
1163 | * value is not null. | |
1164 | * | |
1165 | * @param label | |
1166 | * where to jump if the condition is <tt>true</tt>. | |
1167 | */ | |
1168 | public void ifNonNull(final Label label) { | |
1169 | mv.visitJumpInsn(Opcodes.IFNONNULL, label); | |
1170 | } | |
1171 | ||
1172 | /** | |
1173 | * Generates the instruction to jump to the given label. | |
1174 | * | |
1175 | * @param label | |
1176 | * where to jump if the condition is <tt>true</tt>. | |
1177 | */ | |
1178 | public void goTo(final Label label) { | |
1179 | mv.visitJumpInsn(Opcodes.GOTO, label); | |
1180 | } | |
1181 | ||
1182 | /** | |
1183 | * Generates a RET instruction. | |
1184 | * | |
1185 | * @param local | |
1186 | * a local variable identifier, as returned by | |
1187 | * {@link LocalVariablesSorter#newLocal(Type) newLocal()}. | |
1188 | */ | |
1189 | public void ret(final int local) { | |
1190 | mv.visitVarInsn(Opcodes.RET, local); | |
1191 | } | |
1192 | ||
1193 | /** | |
1194 | * Generates the instructions for a switch statement. | |
1195 | * | |
1196 | * @param keys | |
1197 | * the switch case keys. | |
1198 | * @param generator | |
1199 | * a generator to generate the code for the switch cases. | |
1200 | */ | |
1201 | public void tableSwitch(final int[] keys, | |
1202 | final TableSwitchGenerator generator) { | |
1203 | float density; | |
1204 | if (keys.length == 0) { | |
1205 | density = 0; | |
1206 | } else { | |
1207 | density = (float) keys.length | |
1208 | / (keys[keys.length - 1] - keys[0] + 1); | |
1209 | } | |
1210 | tableSwitch(keys, generator, density >= 0.5f); | |
1211 | } | |
1212 | ||
1213 | /** | |
1214 | * Generates the instructions for a switch statement. | |
1215 | * | |
1216 | * @param keys | |
1217 | * the switch case keys. | |
1218 | * @param generator | |
1219 | * a generator to generate the code for the switch cases. | |
1220 | * @param useTable | |
1221 | * <tt>true</tt> to use a TABLESWITCH instruction, or | |
1222 | * <tt>false</tt> to use a LOOKUPSWITCH instruction. | |
1223 | */ | |
1224 | public void tableSwitch(final int[] keys, | |
1225 | final TableSwitchGenerator generator, final boolean useTable) { | |
1226 | for (int i = 1; i < keys.length; ++i) { | |
1227 | if (keys[i] < keys[i - 1]) { | |
1228 | throw new IllegalArgumentException( | |
1229 | "keys must be sorted ascending"); | |
1230 | } | |
1231 | } | |
1232 | Label def = newLabel(); | |
1233 | Label end = newLabel(); | |
1234 | if (keys.length > 0) { | |
1235 | int len = keys.length; | |
1236 | int min = keys[0]; | |
1237 | int max = keys[len - 1]; | |
1238 | int range = max - min + 1; | |
1239 | if (useTable) { | |
1240 | Label[] labels = new Label[range]; | |
1241 | Arrays.fill(labels, def); | |
1242 | for (int i = 0; i < len; ++i) { | |
1243 | labels[keys[i] - min] = newLabel(); | |
1244 | } | |
1245 | mv.visitTableSwitchInsn(min, max, def, labels); | |
1246 | for (int i = 0; i < range; ++i) { | |
1247 | Label label = labels[i]; | |
1248 | if (label != def) { | |
1249 | mark(label); | |
1250 | generator.generateCase(i + min, end); | |
1251 | } | |
1252 | } | |
1253 | } else { | |
1254 | Label[] labels = new Label[len]; | |
1255 | for (int i = 0; i < len; ++i) { | |
1256 | labels[i] = newLabel(); | |
1257 | } | |
1258 | mv.visitLookupSwitchInsn(def, keys, labels); | |
1259 | for (int i = 0; i < len; ++i) { | |
1260 | mark(labels[i]); | |
1261 | generator.generateCase(keys[i], end); | |
1262 | } | |
1263 | } | |
1264 | } | |
1265 | mark(def); | |
1266 | generator.generateDefault(); | |
1267 | mark(end); | |
1268 | } | |
1269 | ||
1270 | /** | |
1271 | * Generates the instruction to return the top stack value to the caller. | |
1272 | */ | |
1273 | public void returnValue() { | |
1274 | mv.visitInsn(returnType.getOpcode(Opcodes.IRETURN)); | |
1275 | } | |
1276 | ||
1277 | // ------------------------------------------------------------------------ | |
1278 | // Instructions to load and store fields | |
1279 | // ------------------------------------------------------------------------ | |
1280 | ||
1281 | /** | |
1282 | * Generates a get field or set field instruction. | |
1283 | * | |
1284 | * @param opcode | |
1285 | * the instruction's opcode. | |
1286 | * @param ownerType | |
1287 | * the class in which the field is defined. | |
1288 | * @param name | |
1289 | * the name of the field. | |
1290 | * @param fieldType | |
1291 | * the type of the field. | |
1292 | */ | |
1293 | private void fieldInsn(final int opcode, final Type ownerType, | |
1294 | final String name, final Type fieldType) { | |
1295 | mv.visitFieldInsn(opcode, ownerType.getInternalName(), name, | |
1296 | fieldType.getDescriptor()); | |
1297 | } | |
1298 | ||
1299 | /** | |
1300 | * Generates the instruction to push the value of a static field on the | |
1301 | * stack. | |
1302 | * | |
1303 | * @param owner | |
1304 | * the class in which the field is defined. | |
1305 | * @param name | |
1306 | * the name of the field. | |
1307 | * @param type | |
1308 | * the type of the field. | |
1309 | */ | |
1310 | public void getStatic(final Type owner, final String name, final Type type) { | |
1311 | fieldInsn(Opcodes.GETSTATIC, owner, name, type); | |
1312 | } | |
1313 | ||
1314 | /** | |
1315 | * Generates the instruction to store the top stack value in a static field. | |
1316 | * | |
1317 | * @param owner | |
1318 | * the class in which the field is defined. | |
1319 | * @param name | |
1320 | * the name of the field. | |
1321 | * @param type | |
1322 | * the type of the field. | |
1323 | */ | |
1324 | public void putStatic(final Type owner, final String name, final Type type) { | |
1325 | fieldInsn(Opcodes.PUTSTATIC, owner, name, type); | |
1326 | } | |
1327 | ||
1328 | /** | |
1329 | * Generates the instruction to push the value of a non static field on the | |
1330 | * stack. | |
1331 | * | |
1332 | * @param owner | |
1333 | * the class in which the field is defined. | |
1334 | * @param name | |
1335 | * the name of the field. | |
1336 | * @param type | |
1337 | * the type of the field. | |
1338 | */ | |
1339 | public void getField(final Type owner, final String name, final Type type) { | |
1340 | fieldInsn(Opcodes.GETFIELD, owner, name, type); | |
1341 | } | |
1342 | ||
1343 | /** | |
1344 | * Generates the instruction to store the top stack value in a non static | |
1345 | * field. | |
1346 | * | |
1347 | * @param owner | |
1348 | * the class in which the field is defined. | |
1349 | * @param name | |
1350 | * the name of the field. | |
1351 | * @param type | |
1352 | * the type of the field. | |
1353 | */ | |
1354 | public void putField(final Type owner, final String name, final Type type) { | |
1355 | fieldInsn(Opcodes.PUTFIELD, owner, name, type); | |
1356 | } | |
1357 | ||
1358 | // ------------------------------------------------------------------------ | |
1359 | // Instructions to invoke methods | |
1360 | // ------------------------------------------------------------------------ | |
1361 | ||
1362 | /** | |
1363 | * Generates an invoke method instruction. | |
1364 | * | |
1365 | * @param opcode | |
1366 | * the instruction's opcode. | |
1367 | * @param type | |
1368 | * the class in which the method is defined. | |
1369 | * @param method | |
1370 | * the method to be invoked. | |
1371 | */ | |
1372 | private void invokeInsn(final int opcode, final Type type, | |
1373 | final Method method) { | |
1374 | String owner = type.getSort() == Type.ARRAY ? type.getDescriptor() | |
1375 | : type.getInternalName(); | |
1376 | mv.visitMethodInsn(opcode, owner, method.getName(), | |
1377 | method.getDescriptor()); | |
1378 | } | |
1379 | ||
1380 | /** | |
1381 | * Generates the instruction to invoke a normal method. | |
1382 | * | |
1383 | * @param owner | |
1384 | * the class in which the method is defined. | |
1385 | * @param method | |
1386 | * the method to be invoked. | |
1387 | */ | |
1388 | public void invokeVirtual(final Type owner, final Method method) { | |
1389 | invokeInsn(Opcodes.INVOKEVIRTUAL, owner, method); | |
1390 | } | |
1391 | ||
1392 | /** | |
1393 | * Generates the instruction to invoke a constructor. | |
1394 | * | |
1395 | * @param type | |
1396 | * the class in which the constructor is defined. | |
1397 | * @param method | |
1398 | * the constructor to be invoked. | |
1399 | */ | |
1400 | public void invokeConstructor(final Type type, final Method method) { | |
1401 | invokeInsn(Opcodes.INVOKESPECIAL, type, method); | |
1402 | } | |
1403 | ||
1404 | /** | |
1405 | * Generates the instruction to invoke a static method. | |
1406 | * | |
1407 | * @param owner | |
1408 | * the class in which the method is defined. | |
1409 | * @param method | |
1410 | * the method to be invoked. | |
1411 | */ | |
1412 | public void invokeStatic(final Type owner, final Method method) { | |
1413 | invokeInsn(Opcodes.INVOKESTATIC, owner, method); | |
1414 | } | |
1415 | ||
1416 | /** | |
1417 | * Generates the instruction to invoke an interface method. | |
1418 | * | |
1419 | * @param owner | |
1420 | * the class in which the method is defined. | |
1421 | * @param method | |
1422 | * the method to be invoked. | |
1423 | */ | |
1424 | public void invokeInterface(final Type owner, final Method method) { | |
1425 | invokeInsn(Opcodes.INVOKEINTERFACE, owner, method); | |
1426 | } | |
1427 | ||
1428 | /** | |
1429 | * Generates an invokedynamic instruction. | |
1430 | * | |
1431 | * @param name | |
1432 | * the method's name. | |
1433 | * @param desc | |
1434 | * the method's descriptor (see {@link Type Type}). | |
1435 | * @param bsm | |
1436 | * the bootstrap method. | |
1437 | * @param bsmArgs | |
1438 | * the bootstrap method constant arguments. Each argument must be | |
1439 | * an {@link Integer}, {@link Float}, {@link Long}, | |
1440 | * {@link Double}, {@link String}, {@link Type} or {@link Handle} | |
1441 | * value. This method is allowed to modify the content of the | |
1442 | * array so a caller should expect that this array may change. | |
1443 | */ | |
1444 | public void invokeDynamic(String name, String desc, Handle bsm, | |
1445 | Object... bsmArgs) { | |
1446 | mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); | |
1447 | } | |
1448 | ||
1449 | // ------------------------------------------------------------------------ | |
1450 | // Instructions to create objects and arrays | |
1451 | // ------------------------------------------------------------------------ | |
1452 | ||
1453 | /** | |
1454 | * Generates a type dependent instruction. | |
1455 | * | |
1456 | * @param opcode | |
1457 | * the instruction's opcode. | |
1458 | * @param type | |
1459 | * the instruction's operand. | |
1460 | */ | |
1461 | private void typeInsn(final int opcode, final Type type) { | |
1462 | mv.visitTypeInsn(opcode, type.getInternalName()); | |
1463 | } | |
1464 | ||
1465 | /** | |
1466 | * Generates the instruction to create a new object. | |
1467 | * | |
1468 | * @param type | |
1469 | * the class of the object to be created. | |
1470 | */ | |
1471 | public void newInstance(final Type type) { | |
1472 | typeInsn(Opcodes.NEW, type); | |
1473 | } | |
1474 | ||
1475 | /** | |
1476 | * Generates the instruction to create a new array. | |
1477 | * | |
1478 | * @param type | |
1479 | * the type of the array elements. | |
1480 | */ | |
1481 | public void newArray(final Type type) { | |
1482 | int typ; | |
1483 | switch (type.getSort()) { | |
1484 | case Type.BOOLEAN: | |
1485 | typ = Opcodes.T_BOOLEAN; | |
1486 | break; | |
1487 | case Type.CHAR: | |
1488 | typ = Opcodes.T_CHAR; | |
1489 | break; | |
1490 | case Type.BYTE: | |
1491 | typ = Opcodes.T_BYTE; | |
1492 | break; | |
1493 | case Type.SHORT: | |
1494 | typ = Opcodes.T_SHORT; | |
1495 | break; | |
1496 | case Type.INT: | |
1497 | typ = Opcodes.T_INT; | |
1498 | break; | |
1499 | case Type.FLOAT: | |
1500 | typ = Opcodes.T_FLOAT; | |
1501 | break; | |
1502 | case Type.LONG: | |
1503 | typ = Opcodes.T_LONG; | |
1504 | break; | |
1505 | case Type.DOUBLE: | |
1506 | typ = Opcodes.T_DOUBLE; | |
1507 | break; | |
1508 | default: | |
1509 | typeInsn(Opcodes.ANEWARRAY, type); | |
1510 | return; | |
1511 | } | |
1512 | mv.visitIntInsn(Opcodes.NEWARRAY, typ); | |
1513 | } | |
1514 | ||
1515 | // ------------------------------------------------------------------------ | |
1516 | // Miscelaneous instructions | |
1517 | // ------------------------------------------------------------------------ | |
1518 | ||
1519 | /** | |
1520 | * Generates the instruction to compute the length of an array. | |
1521 | */ | |
1522 | public void arrayLength() { | |
1523 | mv.visitInsn(Opcodes.ARRAYLENGTH); | |
1524 | } | |
1525 | ||
1526 | /** | |
1527 | * Generates the instruction to throw an exception. | |
1528 | */ | |
1529 | public void throwException() { | |
1530 | mv.visitInsn(Opcodes.ATHROW); | |
1531 | } | |
1532 | ||
1533 | /** | |
1534 | * Generates the instructions to create and throw an exception. The | |
1535 | * exception class must have a constructor with a single String argument. | |
1536 | * | |
1537 | * @param type | |
1538 | * the class of the exception to be thrown. | |
1539 | * @param msg | |
1540 | * the detailed message of the exception. | |
1541 | */ | |
1542 | public void throwException(final Type type, final String msg) { | |
1543 | newInstance(type); | |
1544 | dup(); | |
1545 | push(msg); | |
1546 | invokeConstructor(type, Method.getMethod("void <init> (String)")); | |
1547 | throwException(); | |
1548 | } | |
1549 | ||
1550 | /** | |
1551 | * Generates the instruction to check that the top stack value is of the | |
1552 | * given type. | |
1553 | * | |
1554 | * @param type | |
1555 | * a class or interface type. | |
1556 | */ | |
1557 | public void checkCast(final Type type) { | |
1558 | if (!type.equals(OBJECT_TYPE)) { | |
1559 | typeInsn(Opcodes.CHECKCAST, type); | |
1560 | } | |
1561 | } | |
1562 | ||
1563 | /** | |
1564 | * Generates the instruction to test if the top stack value is of the given | |
1565 | * type. | |
1566 | * | |
1567 | * @param type | |
1568 | * a class or interface type. | |
1569 | */ | |
1570 | public void instanceOf(final Type type) { | |
1571 | typeInsn(Opcodes.INSTANCEOF, type); | |
1572 | } | |
1573 | ||
1574 | /** | |
1575 | * Generates the instruction to get the monitor of the top stack value. | |
1576 | */ | |
1577 | public void monitorEnter() { | |
1578 | mv.visitInsn(Opcodes.MONITORENTER); | |
1579 | } | |
1580 | ||
1581 | /** | |
1582 | * Generates the instruction to release the monitor of the top stack value. | |
1583 | */ | |
1584 | public void monitorExit() { | |
1585 | mv.visitInsn(Opcodes.MONITOREXIT); | |
1586 | } | |
1587 | ||
1588 | // ------------------------------------------------------------------------ | |
1589 | // Non instructions | |
1590 | // ------------------------------------------------------------------------ | |
1591 | ||
1592 | /** | |
1593 | * Marks the end of the visited method. | |
1594 | */ | |
1595 | public void endMethod() { | |
1596 | if ((access & Opcodes.ACC_ABSTRACT) == 0) { | |
1597 | mv.visitMaxs(0, 0); | |
1598 | } | |
1599 | mv.visitEnd(); | |
1600 | } | |
1601 | ||
1602 | /** | |
1603 | * Marks the start of an exception handler. | |
1604 | * | |
1605 | * @param start | |
1606 | * beginning of the exception handler's scope (inclusive). | |
1607 | * @param end | |
1608 | * end of the exception handler's scope (exclusive). | |
1609 | * @param exception | |
1610 | * internal name of the type of exceptions handled by the | |
1611 | * handler. | |
1612 | */ | |
1613 | public void catchException(final Label start, final Label end, | |
1614 | final Type exception) { | |
1615 | if (exception == null) { | |
1616 | mv.visitTryCatchBlock(start, end, mark(), null); | |
1617 | } else { | |
1618 | mv.visitTryCatchBlock(start, end, mark(), | |
1619 | exception.getInternalName()); | |
1620 | } | |
1621 | } | |
1622 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | ||
30 | package clojure.asm.commons; | |
31 | ||
32 | import clojure.asm.Handle; | |
33 | import clojure.asm.Label; | |
34 | import clojure.asm.MethodVisitor; | |
35 | import clojure.asm.Opcodes; | |
36 | import clojure.asm.Type; | |
37 | ||
38 | /** | |
39 | * A {@link MethodVisitor} providing a more detailed API to generate and | |
40 | * transform instructions. | |
41 | * | |
42 | * @author Eric Bruneton | |
43 | */ | |
44 | public class InstructionAdapter extends MethodVisitor { | |
45 | ||
46 | public final static Type OBJECT_TYPE = Type.getType("Ljava/lang/Object;"); | |
47 | ||
48 | /** | |
49 | * Creates a new {@link InstructionAdapter}. <i>Subclasses must not use this | |
50 | * constructor</i>. Instead, they must use the | |
51 | * {@link #InstructionAdapter(int, MethodVisitor)} version. | |
52 | * | |
53 | * @param mv | |
54 | * the method visitor to which this adapter delegates calls. | |
55 | */ | |
56 | public InstructionAdapter(final MethodVisitor mv) { | |
57 | this(Opcodes.ASM4, mv); | |
58 | } | |
59 | ||
60 | /** | |
61 | * Creates a new {@link InstructionAdapter}. | |
62 | * | |
63 | * @param api | |
64 | * the ASM API version implemented by this visitor. Must be one | |
65 | * of {@link Opcodes#ASM4}. | |
66 | * @param mv | |
67 | * the method visitor to which this adapter delegates calls. | |
68 | */ | |
69 | protected InstructionAdapter(final int api, final MethodVisitor mv) { | |
70 | super(api, mv); | |
71 | } | |
72 | ||
73 | @Override | |
74 | public void visitInsn(final int opcode) { | |
75 | switch (opcode) { | |
76 | case Opcodes.NOP: | |
77 | nop(); | |
78 | break; | |
79 | case Opcodes.ACONST_NULL: | |
80 | aconst(null); | |
81 | break; | |
82 | case Opcodes.ICONST_M1: | |
83 | case Opcodes.ICONST_0: | |
84 | case Opcodes.ICONST_1: | |
85 | case Opcodes.ICONST_2: | |
86 | case Opcodes.ICONST_3: | |
87 | case Opcodes.ICONST_4: | |
88 | case Opcodes.ICONST_5: | |
89 | iconst(opcode - Opcodes.ICONST_0); | |
90 | break; | |
91 | case Opcodes.LCONST_0: | |
92 | case Opcodes.LCONST_1: | |
93 | lconst(opcode - Opcodes.LCONST_0); | |
94 | break; | |
95 | case Opcodes.FCONST_0: | |
96 | case Opcodes.FCONST_1: | |
97 | case Opcodes.FCONST_2: | |
98 | fconst(opcode - Opcodes.FCONST_0); | |
99 | break; | |
100 | case Opcodes.DCONST_0: | |
101 | case Opcodes.DCONST_1: | |
102 | dconst(opcode - Opcodes.DCONST_0); | |
103 | break; | |
104 | case Opcodes.IALOAD: | |
105 | aload(Type.INT_TYPE); | |
106 | break; | |
107 | case Opcodes.LALOAD: | |
108 | aload(Type.LONG_TYPE); | |
109 | break; | |
110 | case Opcodes.FALOAD: | |
111 | aload(Type.FLOAT_TYPE); | |
112 | break; | |
113 | case Opcodes.DALOAD: | |
114 | aload(Type.DOUBLE_TYPE); | |
115 | break; | |
116 | case Opcodes.AALOAD: | |
117 | aload(OBJECT_TYPE); | |
118 | break; | |
119 | case Opcodes.BALOAD: | |
120 | aload(Type.BYTE_TYPE); | |
121 | break; | |
122 | case Opcodes.CALOAD: | |
123 | aload(Type.CHAR_TYPE); | |
124 | break; | |
125 | case Opcodes.SALOAD: | |
126 | aload(Type.SHORT_TYPE); | |
127 | break; | |
128 | case Opcodes.IASTORE: | |
129 | astore(Type.INT_TYPE); | |
130 | break; | |
131 | case Opcodes.LASTORE: | |
132 | astore(Type.LONG_TYPE); | |
133 | break; | |
134 | case Opcodes.FASTORE: | |
135 | astore(Type.FLOAT_TYPE); | |
136 | break; | |
137 | case Opcodes.DASTORE: | |
138 | astore(Type.DOUBLE_TYPE); | |
139 | break; | |
140 | case Opcodes.AASTORE: | |
141 | astore(OBJECT_TYPE); | |
142 | break; | |
143 | case Opcodes.BASTORE: | |
144 | astore(Type.BYTE_TYPE); | |
145 | break; | |
146 | case Opcodes.CASTORE: | |
147 | astore(Type.CHAR_TYPE); | |
148 | break; | |
149 | case Opcodes.SASTORE: | |
150 | astore(Type.SHORT_TYPE); | |
151 | break; | |
152 | case Opcodes.POP: | |
153 | pop(); | |
154 | break; | |
155 | case Opcodes.POP2: | |
156 | pop2(); | |
157 | break; | |
158 | case Opcodes.DUP: | |
159 | dup(); | |
160 | break; | |
161 | case Opcodes.DUP_X1: | |
162 | dupX1(); | |
163 | break; | |
164 | case Opcodes.DUP_X2: | |
165 | dupX2(); | |
166 | break; | |
167 | case Opcodes.DUP2: | |
168 | dup2(); | |
169 | break; | |
170 | case Opcodes.DUP2_X1: | |
171 | dup2X1(); | |
172 | break; | |
173 | case Opcodes.DUP2_X2: | |
174 | dup2X2(); | |
175 | break; | |
176 | case Opcodes.SWAP: | |
177 | swap(); | |
178 | break; | |
179 | case Opcodes.IADD: | |
180 | add(Type.INT_TYPE); | |
181 | break; | |
182 | case Opcodes.LADD: | |
183 | add(Type.LONG_TYPE); | |
184 | break; | |
185 | case Opcodes.FADD: | |
186 | add(Type.FLOAT_TYPE); | |
187 | break; | |
188 | case Opcodes.DADD: | |
189 | add(Type.DOUBLE_TYPE); | |
190 | break; | |
191 | case Opcodes.ISUB: | |
192 | sub(Type.INT_TYPE); | |
193 | break; | |
194 | case Opcodes.LSUB: | |
195 | sub(Type.LONG_TYPE); | |
196 | break; | |
197 | case Opcodes.FSUB: | |
198 | sub(Type.FLOAT_TYPE); | |
199 | break; | |
200 | case Opcodes.DSUB: | |
201 | sub(Type.DOUBLE_TYPE); | |
202 | break; | |
203 | case Opcodes.IMUL: | |
204 | mul(Type.INT_TYPE); | |
205 | break; | |
206 | case Opcodes.LMUL: | |
207 | mul(Type.LONG_TYPE); | |
208 | break; | |
209 | case Opcodes.FMUL: | |
210 | mul(Type.FLOAT_TYPE); | |
211 | break; | |
212 | case Opcodes.DMUL: | |
213 | mul(Type.DOUBLE_TYPE); | |
214 | break; | |
215 | case Opcodes.IDIV: | |
216 | div(Type.INT_TYPE); | |
217 | break; | |
218 | case Opcodes.LDIV: | |
219 | div(Type.LONG_TYPE); | |
220 | break; | |
221 | case Opcodes.FDIV: | |
222 | div(Type.FLOAT_TYPE); | |
223 | break; | |
224 | case Opcodes.DDIV: | |
225 | div(Type.DOUBLE_TYPE); | |
226 | break; | |
227 | case Opcodes.IREM: | |
228 | rem(Type.INT_TYPE); | |
229 | break; | |
230 | case Opcodes.LREM: | |
231 | rem(Type.LONG_TYPE); | |
232 | break; | |
233 | case Opcodes.FREM: | |
234 | rem(Type.FLOAT_TYPE); | |
235 | break; | |
236 | case Opcodes.DREM: | |
237 | rem(Type.DOUBLE_TYPE); | |
238 | break; | |
239 | case Opcodes.INEG: | |
240 | neg(Type.INT_TYPE); | |
241 | break; | |
242 | case Opcodes.LNEG: | |
243 | neg(Type.LONG_TYPE); | |
244 | break; | |
245 | case Opcodes.FNEG: | |
246 | neg(Type.FLOAT_TYPE); | |
247 | break; | |
248 | case Opcodes.DNEG: | |
249 | neg(Type.DOUBLE_TYPE); | |
250 | break; | |
251 | case Opcodes.ISHL: | |
252 | shl(Type.INT_TYPE); | |
253 | break; | |
254 | case Opcodes.LSHL: | |
255 | shl(Type.LONG_TYPE); | |
256 | break; | |
257 | case Opcodes.ISHR: | |
258 | shr(Type.INT_TYPE); | |
259 | break; | |
260 | case Opcodes.LSHR: | |
261 | shr(Type.LONG_TYPE); | |
262 | break; | |
263 | case Opcodes.IUSHR: | |
264 | ushr(Type.INT_TYPE); | |
265 | break; | |
266 | case Opcodes.LUSHR: | |
267 | ushr(Type.LONG_TYPE); | |
268 | break; | |
269 | case Opcodes.IAND: | |
270 | and(Type.INT_TYPE); | |
271 | break; | |
272 | case Opcodes.LAND: | |
273 | and(Type.LONG_TYPE); | |
274 | break; | |
275 | case Opcodes.IOR: | |
276 | or(Type.INT_TYPE); | |
277 | break; | |
278 | case Opcodes.LOR: | |
279 | or(Type.LONG_TYPE); | |
280 | break; | |
281 | case Opcodes.IXOR: | |
282 | xor(Type.INT_TYPE); | |
283 | break; | |
284 | case Opcodes.LXOR: | |
285 | xor(Type.LONG_TYPE); | |
286 | break; | |
287 | case Opcodes.I2L: | |
288 | cast(Type.INT_TYPE, Type.LONG_TYPE); | |
289 | break; | |
290 | case Opcodes.I2F: | |
291 | cast(Type.INT_TYPE, Type.FLOAT_TYPE); | |
292 | break; | |
293 | case Opcodes.I2D: | |
294 | cast(Type.INT_TYPE, Type.DOUBLE_TYPE); | |
295 | break; | |
296 | case Opcodes.L2I: | |
297 | cast(Type.LONG_TYPE, Type.INT_TYPE); | |
298 | break; | |
299 | case Opcodes.L2F: | |
300 | cast(Type.LONG_TYPE, Type.FLOAT_TYPE); | |
301 | break; | |
302 | case Opcodes.L2D: | |
303 | cast(Type.LONG_TYPE, Type.DOUBLE_TYPE); | |
304 | break; | |
305 | case Opcodes.F2I: | |
306 | cast(Type.FLOAT_TYPE, Type.INT_TYPE); | |
307 | break; | |
308 | case Opcodes.F2L: | |
309 | cast(Type.FLOAT_TYPE, Type.LONG_TYPE); | |
310 | break; | |
311 | case Opcodes.F2D: | |
312 | cast(Type.FLOAT_TYPE, Type.DOUBLE_TYPE); | |
313 | break; | |
314 | case Opcodes.D2I: | |
315 | cast(Type.DOUBLE_TYPE, Type.INT_TYPE); | |
316 | break; | |
317 | case Opcodes.D2L: | |
318 | cast(Type.DOUBLE_TYPE, Type.LONG_TYPE); | |
319 | break; | |
320 | case Opcodes.D2F: | |
321 | cast(Type.DOUBLE_TYPE, Type.FLOAT_TYPE); | |
322 | break; | |
323 | case Opcodes.I2B: | |
324 | cast(Type.INT_TYPE, Type.BYTE_TYPE); | |
325 | break; | |
326 | case Opcodes.I2C: | |
327 | cast(Type.INT_TYPE, Type.CHAR_TYPE); | |
328 | break; | |
329 | case Opcodes.I2S: | |
330 | cast(Type.INT_TYPE, Type.SHORT_TYPE); | |
331 | break; | |
332 | case Opcodes.LCMP: | |
333 | lcmp(); | |
334 | break; | |
335 | case Opcodes.FCMPL: | |
336 | cmpl(Type.FLOAT_TYPE); | |
337 | break; | |
338 | case Opcodes.FCMPG: | |
339 | cmpg(Type.FLOAT_TYPE); | |
340 | break; | |
341 | case Opcodes.DCMPL: | |
342 | cmpl(Type.DOUBLE_TYPE); | |
343 | break; | |
344 | case Opcodes.DCMPG: | |
345 | cmpg(Type.DOUBLE_TYPE); | |
346 | break; | |
347 | case Opcodes.IRETURN: | |
348 | areturn(Type.INT_TYPE); | |
349 | break; | |
350 | case Opcodes.LRETURN: | |
351 | areturn(Type.LONG_TYPE); | |
352 | break; | |
353 | case Opcodes.FRETURN: | |
354 | areturn(Type.FLOAT_TYPE); | |
355 | break; | |
356 | case Opcodes.DRETURN: | |
357 | areturn(Type.DOUBLE_TYPE); | |
358 | break; | |
359 | case Opcodes.ARETURN: | |
360 | areturn(OBJECT_TYPE); | |
361 | break; | |
362 | case Opcodes.RETURN: | |
363 | areturn(Type.VOID_TYPE); | |
364 | break; | |
365 | case Opcodes.ARRAYLENGTH: | |
366 | arraylength(); | |
367 | break; | |
368 | case Opcodes.ATHROW: | |
369 | athrow(); | |
370 | break; | |
371 | case Opcodes.MONITORENTER: | |
372 | monitorenter(); | |
373 | break; | |
374 | case Opcodes.MONITOREXIT: | |
375 | monitorexit(); | |
376 | break; | |
377 | default: | |
378 | throw new IllegalArgumentException(); | |
379 | } | |
380 | } | |
381 | ||
382 | @Override | |
383 | public void visitIntInsn(final int opcode, final int operand) { | |
384 | switch (opcode) { | |
385 | case Opcodes.BIPUSH: | |
386 | iconst(operand); | |
387 | break; | |
388 | case Opcodes.SIPUSH: | |
389 | iconst(operand); | |
390 | break; | |
391 | case Opcodes.NEWARRAY: | |
392 | switch (operand) { | |
393 | case Opcodes.T_BOOLEAN: | |
394 | newarray(Type.BOOLEAN_TYPE); | |
395 | break; | |
396 | case Opcodes.T_CHAR: | |
397 | newarray(Type.CHAR_TYPE); | |
398 | break; | |
399 | case Opcodes.T_BYTE: | |
400 | newarray(Type.BYTE_TYPE); | |
401 | break; | |
402 | case Opcodes.T_SHORT: | |
403 | newarray(Type.SHORT_TYPE); | |
404 | break; | |
405 | case Opcodes.T_INT: | |
406 | newarray(Type.INT_TYPE); | |
407 | break; | |
408 | case Opcodes.T_FLOAT: | |
409 | newarray(Type.FLOAT_TYPE); | |
410 | break; | |
411 | case Opcodes.T_LONG: | |
412 | newarray(Type.LONG_TYPE); | |
413 | break; | |
414 | case Opcodes.T_DOUBLE: | |
415 | newarray(Type.DOUBLE_TYPE); | |
416 | break; | |
417 | default: | |
418 | throw new IllegalArgumentException(); | |
419 | } | |
420 | break; | |
421 | default: | |
422 | throw new IllegalArgumentException(); | |
423 | } | |
424 | } | |
425 | ||
426 | @Override | |
427 | public void visitVarInsn(final int opcode, final int var) { | |
428 | switch (opcode) { | |
429 | case Opcodes.ILOAD: | |
430 | load(var, Type.INT_TYPE); | |
431 | break; | |
432 | case Opcodes.LLOAD: | |
433 | load(var, Type.LONG_TYPE); | |
434 | break; | |
435 | case Opcodes.FLOAD: | |
436 | load(var, Type.FLOAT_TYPE); | |
437 | break; | |
438 | case Opcodes.DLOAD: | |
439 | load(var, Type.DOUBLE_TYPE); | |
440 | break; | |
441 | case Opcodes.ALOAD: | |
442 | load(var, OBJECT_TYPE); | |
443 | break; | |
444 | case Opcodes.ISTORE: | |
445 | store(var, Type.INT_TYPE); | |
446 | break; | |
447 | case Opcodes.LSTORE: | |
448 | store(var, Type.LONG_TYPE); | |
449 | break; | |
450 | case Opcodes.FSTORE: | |
451 | store(var, Type.FLOAT_TYPE); | |
452 | break; | |
453 | case Opcodes.DSTORE: | |
454 | store(var, Type.DOUBLE_TYPE); | |
455 | break; | |
456 | case Opcodes.ASTORE: | |
457 | store(var, OBJECT_TYPE); | |
458 | break; | |
459 | case Opcodes.RET: | |
460 | ret(var); | |
461 | break; | |
462 | default: | |
463 | throw new IllegalArgumentException(); | |
464 | } | |
465 | } | |
466 | ||
467 | @Override | |
468 | public void visitTypeInsn(final int opcode, final String type) { | |
469 | Type t = Type.getObjectType(type); | |
470 | switch (opcode) { | |
471 | case Opcodes.NEW: | |
472 | anew(t); | |
473 | break; | |
474 | case Opcodes.ANEWARRAY: | |
475 | newarray(t); | |
476 | break; | |
477 | case Opcodes.CHECKCAST: | |
478 | checkcast(t); | |
479 | break; | |
480 | case Opcodes.INSTANCEOF: | |
481 | instanceOf(t); | |
482 | break; | |
483 | default: | |
484 | throw new IllegalArgumentException(); | |
485 | } | |
486 | } | |
487 | ||
488 | @Override | |
489 | public void visitFieldInsn(final int opcode, final String owner, | |
490 | final String name, final String desc) { | |
491 | switch (opcode) { | |
492 | case Opcodes.GETSTATIC: | |
493 | getstatic(owner, name, desc); | |
494 | break; | |
495 | case Opcodes.PUTSTATIC: | |
496 | putstatic(owner, name, desc); | |
497 | break; | |
498 | case Opcodes.GETFIELD: | |
499 | getfield(owner, name, desc); | |
500 | break; | |
501 | case Opcodes.PUTFIELD: | |
502 | putfield(owner, name, desc); | |
503 | break; | |
504 | default: | |
505 | throw new IllegalArgumentException(); | |
506 | } | |
507 | } | |
508 | ||
509 | @Override | |
510 | public void visitMethodInsn(final int opcode, final String owner, | |
511 | final String name, final String desc) { | |
512 | switch (opcode) { | |
513 | case Opcodes.INVOKESPECIAL: | |
514 | invokespecial(owner, name, desc); | |
515 | break; | |
516 | case Opcodes.INVOKEVIRTUAL: | |
517 | invokevirtual(owner, name, desc); | |
518 | break; | |
519 | case Opcodes.INVOKESTATIC: | |
520 | invokestatic(owner, name, desc); | |
521 | break; | |
522 | case Opcodes.INVOKEINTERFACE: | |
523 | invokeinterface(owner, name, desc); | |
524 | break; | |
525 | default: | |
526 | throw new IllegalArgumentException(); | |
527 | } | |
528 | } | |
529 | ||
530 | @Override | |
531 | public void visitInvokeDynamicInsn(String name, String desc, Handle bsm, | |
532 | Object... bsmArgs) { | |
533 | invokedynamic(name, desc, bsm, bsmArgs); | |
534 | } | |
535 | ||
536 | @Override | |
537 | public void visitJumpInsn(final int opcode, final Label label) { | |
538 | switch (opcode) { | |
539 | case Opcodes.IFEQ: | |
540 | ifeq(label); | |
541 | break; | |
542 | case Opcodes.IFNE: | |
543 | ifne(label); | |
544 | break; | |
545 | case Opcodes.IFLT: | |
546 | iflt(label); | |
547 | break; | |
548 | case Opcodes.IFGE: | |
549 | ifge(label); | |
550 | break; | |
551 | case Opcodes.IFGT: | |
552 | ifgt(label); | |
553 | break; | |
554 | case Opcodes.IFLE: | |
555 | ifle(label); | |
556 | break; | |
557 | case Opcodes.IF_ICMPEQ: | |
558 | ificmpeq(label); | |
559 | break; | |
560 | case Opcodes.IF_ICMPNE: | |
561 | ificmpne(label); | |
562 | break; | |
563 | case Opcodes.IF_ICMPLT: | |
564 | ificmplt(label); | |
565 | break; | |
566 | case Opcodes.IF_ICMPGE: | |
567 | ificmpge(label); | |
568 | break; | |
569 | case Opcodes.IF_ICMPGT: | |
570 | ificmpgt(label); | |
571 | break; | |
572 | case Opcodes.IF_ICMPLE: | |
573 | ificmple(label); | |
574 | break; | |
575 | case Opcodes.IF_ACMPEQ: | |
576 | ifacmpeq(label); | |
577 | break; | |
578 | case Opcodes.IF_ACMPNE: | |
579 | ifacmpne(label); | |
580 | break; | |
581 | case Opcodes.GOTO: | |
582 | goTo(label); | |
583 | break; | |
584 | case Opcodes.JSR: | |
585 | jsr(label); | |
586 | break; | |
587 | case Opcodes.IFNULL: | |
588 | ifnull(label); | |
589 | break; | |
590 | case Opcodes.IFNONNULL: | |
591 | ifnonnull(label); | |
592 | break; | |
593 | default: | |
594 | throw new IllegalArgumentException(); | |
595 | } | |
596 | } | |
597 | ||
598 | @Override | |
599 | public void visitLabel(final Label label) { | |
600 | mark(label); | |
601 | } | |
602 | ||
603 | @Override | |
604 | public void visitLdcInsn(final Object cst) { | |
605 | if (cst instanceof Integer) { | |
606 | int val = ((Integer) cst).intValue(); | |
607 | iconst(val); | |
608 | } else if (cst instanceof Byte) { | |
609 | int val = ((Byte) cst).intValue(); | |
610 | iconst(val); | |
611 | } else if (cst instanceof Character) { | |
612 | int val = ((Character) cst).charValue(); | |
613 | iconst(val); | |
614 | } else if (cst instanceof Short) { | |
615 | int val = ((Short) cst).intValue(); | |
616 | iconst(val); | |
617 | } else if (cst instanceof Boolean) { | |
618 | int val = ((Boolean) cst).booleanValue() ? 1 : 0; | |
619 | iconst(val); | |
620 | } else if (cst instanceof Float) { | |
621 | float val = ((Float) cst).floatValue(); | |
622 | fconst(val); | |
623 | } else if (cst instanceof Long) { | |
624 | long val = ((Long) cst).longValue(); | |
625 | lconst(val); | |
626 | } else if (cst instanceof Double) { | |
627 | double val = ((Double) cst).doubleValue(); | |
628 | dconst(val); | |
629 | } else if (cst instanceof String) { | |
630 | aconst(cst); | |
631 | } else if (cst instanceof Type) { | |
632 | tconst((Type) cst); | |
633 | } else if (cst instanceof Handle) { | |
634 | hconst((Handle) cst); | |
635 | } else { | |
636 | throw new IllegalArgumentException(); | |
637 | } | |
638 | } | |
639 | ||
640 | @Override | |
641 | public void visitIincInsn(final int var, final int increment) { | |
642 | iinc(var, increment); | |
643 | } | |
644 | ||
645 | @Override | |
646 | public void visitTableSwitchInsn(final int min, final int max, | |
647 | final Label dflt, final Label... labels) { | |
648 | tableswitch(min, max, dflt, labels); | |
649 | } | |
650 | ||
651 | @Override | |
652 | public void visitLookupSwitchInsn(final Label dflt, final int[] keys, | |
653 | final Label[] labels) { | |
654 | lookupswitch(dflt, keys, labels); | |
655 | } | |
656 | ||
657 | @Override | |
658 | public void visitMultiANewArrayInsn(final String desc, final int dims) { | |
659 | multianewarray(desc, dims); | |
660 | } | |
661 | ||
662 | // ----------------------------------------------------------------------- | |
663 | ||
664 | public void nop() { | |
665 | mv.visitInsn(Opcodes.NOP); | |
666 | } | |
667 | ||
668 | public void aconst(final Object cst) { | |
669 | if (cst == null) { | |
670 | mv.visitInsn(Opcodes.ACONST_NULL); | |
671 | } else { | |
672 | mv.visitLdcInsn(cst); | |
673 | } | |
674 | } | |
675 | ||
676 | public void iconst(final int cst) { | |
677 | if (cst >= -1 && cst <= 5) { | |
678 | mv.visitInsn(Opcodes.ICONST_0 + cst); | |
679 | } else if (cst >= Byte.MIN_VALUE && cst <= Byte.MAX_VALUE) { | |
680 | mv.visitIntInsn(Opcodes.BIPUSH, cst); | |
681 | } else if (cst >= Short.MIN_VALUE && cst <= Short.MAX_VALUE) { | |
682 | mv.visitIntInsn(Opcodes.SIPUSH, cst); | |
683 | } else { | |
684 | mv.visitLdcInsn(new Integer(cst)); | |
685 | } | |
686 | } | |
687 | ||
688 | public void lconst(final long cst) { | |
689 | if (cst == 0L || cst == 1L) { | |
690 | mv.visitInsn(Opcodes.LCONST_0 + (int) cst); | |
691 | } else { | |
692 | mv.visitLdcInsn(new Long(cst)); | |
693 | } | |
694 | } | |
695 | ||
696 | public void fconst(final float cst) { | |
697 | int bits = Float.floatToIntBits(cst); | |
698 | if (bits == 0L || bits == 0x3f800000 || bits == 0x40000000) { // 0..2 | |
699 | mv.visitInsn(Opcodes.FCONST_0 + (int) cst); | |
700 | } else { | |
701 | mv.visitLdcInsn(new Float(cst)); | |
702 | } | |
703 | } | |
704 | ||
705 | public void dconst(final double cst) { | |
706 | long bits = Double.doubleToLongBits(cst); | |
707 | if (bits == 0L || bits == 0x3ff0000000000000L) { // +0.0d and 1.0d | |
708 | mv.visitInsn(Opcodes.DCONST_0 + (int) cst); | |
709 | } else { | |
710 | mv.visitLdcInsn(new Double(cst)); | |
711 | } | |
712 | } | |
713 | ||
714 | public void tconst(final Type type) { | |
715 | mv.visitLdcInsn(type); | |
716 | } | |
717 | ||
718 | public void hconst(final Handle handle) { | |
719 | mv.visitLdcInsn(handle); | |
720 | } | |
721 | ||
722 | public void load(final int var, final Type type) { | |
723 | mv.visitVarInsn(type.getOpcode(Opcodes.ILOAD), var); | |
724 | } | |
725 | ||
726 | public void aload(final Type type) { | |
727 | mv.visitInsn(type.getOpcode(Opcodes.IALOAD)); | |
728 | } | |
729 | ||
730 | public void store(final int var, final Type type) { | |
731 | mv.visitVarInsn(type.getOpcode(Opcodes.ISTORE), var); | |
732 | } | |
733 | ||
734 | public void astore(final Type type) { | |
735 | mv.visitInsn(type.getOpcode(Opcodes.IASTORE)); | |
736 | } | |
737 | ||
738 | public void pop() { | |
739 | mv.visitInsn(Opcodes.POP); | |
740 | } | |
741 | ||
742 | public void pop2() { | |
743 | mv.visitInsn(Opcodes.POP2); | |
744 | } | |
745 | ||
746 | public void dup() { | |
747 | mv.visitInsn(Opcodes.DUP); | |
748 | } | |
749 | ||
750 | public void dup2() { | |
751 | mv.visitInsn(Opcodes.DUP2); | |
752 | } | |
753 | ||
754 | public void dupX1() { | |
755 | mv.visitInsn(Opcodes.DUP_X1); | |
756 | } | |
757 | ||
758 | public void dupX2() { | |
759 | mv.visitInsn(Opcodes.DUP_X2); | |
760 | } | |
761 | ||
762 | public void dup2X1() { | |
763 | mv.visitInsn(Opcodes.DUP2_X1); | |
764 | } | |
765 | ||
766 | public void dup2X2() { | |
767 | mv.visitInsn(Opcodes.DUP2_X2); | |
768 | } | |
769 | ||
770 | public void swap() { | |
771 | mv.visitInsn(Opcodes.SWAP); | |
772 | } | |
773 | ||
774 | public void add(final Type type) { | |
775 | mv.visitInsn(type.getOpcode(Opcodes.IADD)); | |
776 | } | |
777 | ||
778 | public void sub(final Type type) { | |
779 | mv.visitInsn(type.getOpcode(Opcodes.ISUB)); | |
780 | } | |
781 | ||
782 | public void mul(final Type type) { | |
783 | mv.visitInsn(type.getOpcode(Opcodes.IMUL)); | |
784 | } | |
785 | ||
786 | public void div(final Type type) { | |
787 | mv.visitInsn(type.getOpcode(Opcodes.IDIV)); | |
788 | } | |
789 | ||
790 | public void rem(final Type type) { | |
791 | mv.visitInsn(type.getOpcode(Opcodes.IREM)); | |
792 | } | |
793 | ||
794 | public void neg(final Type type) { | |
795 | mv.visitInsn(type.getOpcode(Opcodes.INEG)); | |
796 | } | |
797 | ||
798 | public void shl(final Type type) { | |
799 | mv.visitInsn(type.getOpcode(Opcodes.ISHL)); | |
800 | } | |
801 | ||
802 | public void shr(final Type type) { | |
803 | mv.visitInsn(type.getOpcode(Opcodes.ISHR)); | |
804 | } | |
805 | ||
806 | public void ushr(final Type type) { | |
807 | mv.visitInsn(type.getOpcode(Opcodes.IUSHR)); | |
808 | } | |
809 | ||
810 | public void and(final Type type) { | |
811 | mv.visitInsn(type.getOpcode(Opcodes.IAND)); | |
812 | } | |
813 | ||
814 | public void or(final Type type) { | |
815 | mv.visitInsn(type.getOpcode(Opcodes.IOR)); | |
816 | } | |
817 | ||
818 | public void xor(final Type type) { | |
819 | mv.visitInsn(type.getOpcode(Opcodes.IXOR)); | |
820 | } | |
821 | ||
822 | public void iinc(final int var, final int increment) { | |
823 | mv.visitIincInsn(var, increment); | |
824 | } | |
825 | ||
826 | public void cast(final Type from, final Type to) { | |
827 | if (from != to) { | |
828 | if (from == Type.DOUBLE_TYPE) { | |
829 | if (to == Type.FLOAT_TYPE) { | |
830 | mv.visitInsn(Opcodes.D2F); | |
831 | } else if (to == Type.LONG_TYPE) { | |
832 | mv.visitInsn(Opcodes.D2L); | |
833 | } else { | |
834 | mv.visitInsn(Opcodes.D2I); | |
835 | cast(Type.INT_TYPE, to); | |
836 | } | |
837 | } else if (from == Type.FLOAT_TYPE) { | |
838 | if (to == Type.DOUBLE_TYPE) { | |
839 | mv.visitInsn(Opcodes.F2D); | |
840 | } else if (to == Type.LONG_TYPE) { | |
841 | mv.visitInsn(Opcodes.F2L); | |
842 | } else { | |
843 | mv.visitInsn(Opcodes.F2I); | |
844 | cast(Type.INT_TYPE, to); | |
845 | } | |
846 | } else if (from == Type.LONG_TYPE) { | |
847 | if (to == Type.DOUBLE_TYPE) { | |
848 | mv.visitInsn(Opcodes.L2D); | |
849 | } else if (to == Type.FLOAT_TYPE) { | |
850 | mv.visitInsn(Opcodes.L2F); | |
851 | } else { | |
852 | mv.visitInsn(Opcodes.L2I); | |
853 | cast(Type.INT_TYPE, to); | |
854 | } | |
855 | } else { | |
856 | if (to == Type.BYTE_TYPE) { | |
857 | mv.visitInsn(Opcodes.I2B); | |
858 | } else if (to == Type.CHAR_TYPE) { | |
859 | mv.visitInsn(Opcodes.I2C); | |
860 | } else if (to == Type.DOUBLE_TYPE) { | |
861 | mv.visitInsn(Opcodes.I2D); | |
862 | } else if (to == Type.FLOAT_TYPE) { | |
863 | mv.visitInsn(Opcodes.I2F); | |
864 | } else if (to == Type.LONG_TYPE) { | |
865 | mv.visitInsn(Opcodes.I2L); | |
866 | } else if (to == Type.SHORT_TYPE) { | |
867 | mv.visitInsn(Opcodes.I2S); | |
868 | } | |
869 | } | |
870 | } | |
871 | } | |
872 | ||
873 | public void lcmp() { | |
874 | mv.visitInsn(Opcodes.LCMP); | |
875 | } | |
876 | ||
877 | public void cmpl(final Type type) { | |
878 | mv.visitInsn(type == Type.FLOAT_TYPE ? Opcodes.FCMPL : Opcodes.DCMPL); | |
879 | } | |
880 | ||
881 | public void cmpg(final Type type) { | |
882 | mv.visitInsn(type == Type.FLOAT_TYPE ? Opcodes.FCMPG : Opcodes.DCMPG); | |
883 | } | |
884 | ||
885 | public void ifeq(final Label label) { | |
886 | mv.visitJumpInsn(Opcodes.IFEQ, label); | |
887 | } | |
888 | ||
889 | public void ifne(final Label label) { | |
890 | mv.visitJumpInsn(Opcodes.IFNE, label); | |
891 | } | |
892 | ||
893 | public void iflt(final Label label) { | |
894 | mv.visitJumpInsn(Opcodes.IFLT, label); | |
895 | } | |
896 | ||
897 | public void ifge(final Label label) { | |
898 | mv.visitJumpInsn(Opcodes.IFGE, label); | |
899 | } | |
900 | ||
901 | public void ifgt(final Label label) { | |
902 | mv.visitJumpInsn(Opcodes.IFGT, label); | |
903 | } | |
904 | ||
905 | public void ifle(final Label label) { | |
906 | mv.visitJumpInsn(Opcodes.IFLE, label); | |
907 | } | |
908 | ||
909 | public void ificmpeq(final Label label) { | |
910 | mv.visitJumpInsn(Opcodes.IF_ICMPEQ, label); | |
911 | } | |
912 | ||
913 | public void ificmpne(final Label label) { | |
914 | mv.visitJumpInsn(Opcodes.IF_ICMPNE, label); | |
915 | } | |
916 | ||
917 | public void ificmplt(final Label label) { | |
918 | mv.visitJumpInsn(Opcodes.IF_ICMPLT, label); | |
919 | } | |
920 | ||
921 | public void ificmpge(final Label label) { | |
922 | mv.visitJumpInsn(Opcodes.IF_ICMPGE, label); | |
923 | } | |
924 | ||
925 | public void ificmpgt(final Label label) { | |
926 | mv.visitJumpInsn(Opcodes.IF_ICMPGT, label); | |
927 | } | |
928 | ||
929 | public void ificmple(final Label label) { | |
930 | mv.visitJumpInsn(Opcodes.IF_ICMPLE, label); | |
931 | } | |
932 | ||
933 | public void ifacmpeq(final Label label) { | |
934 | mv.visitJumpInsn(Opcodes.IF_ACMPEQ, label); | |
935 | } | |
936 | ||
937 | public void ifacmpne(final Label label) { | |
938 | mv.visitJumpInsn(Opcodes.IF_ACMPNE, label); | |
939 | } | |
940 | ||
941 | public void goTo(final Label label) { | |
942 | mv.visitJumpInsn(Opcodes.GOTO, label); | |
943 | } | |
944 | ||
945 | public void jsr(final Label label) { | |
946 | mv.visitJumpInsn(Opcodes.JSR, label); | |
947 | } | |
948 | ||
949 | public void ret(final int var) { | |
950 | mv.visitVarInsn(Opcodes.RET, var); | |
951 | } | |
952 | ||
953 | public void tableswitch(final int min, final int max, final Label dflt, | |
954 | final Label... labels) { | |
955 | mv.visitTableSwitchInsn(min, max, dflt, labels); | |
956 | } | |
957 | ||
958 | public void lookupswitch(final Label dflt, final int[] keys, | |
959 | final Label[] labels) { | |
960 | mv.visitLookupSwitchInsn(dflt, keys, labels); | |
961 | } | |
962 | ||
963 | public void areturn(final Type t) { | |
964 | mv.visitInsn(t.getOpcode(Opcodes.IRETURN)); | |
965 | } | |
966 | ||
967 | public void getstatic(final String owner, final String name, | |
968 | final String desc) { | |
969 | mv.visitFieldInsn(Opcodes.GETSTATIC, owner, name, desc); | |
970 | } | |
971 | ||
972 | public void putstatic(final String owner, final String name, | |
973 | final String desc) { | |
974 | mv.visitFieldInsn(Opcodes.PUTSTATIC, owner, name, desc); | |
975 | } | |
976 | ||
977 | public void getfield(final String owner, final String name, | |
978 | final String desc) { | |
979 | mv.visitFieldInsn(Opcodes.GETFIELD, owner, name, desc); | |
980 | } | |
981 | ||
982 | public void putfield(final String owner, final String name, | |
983 | final String desc) { | |
984 | mv.visitFieldInsn(Opcodes.PUTFIELD, owner, name, desc); | |
985 | } | |
986 | ||
987 | public void invokevirtual(final String owner, final String name, | |
988 | final String desc) { | |
989 | mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, owner, name, desc); | |
990 | } | |
991 | ||
992 | public void invokespecial(final String owner, final String name, | |
993 | final String desc) { | |
994 | mv.visitMethodInsn(Opcodes.INVOKESPECIAL, owner, name, desc); | |
995 | } | |
996 | ||
997 | public void invokestatic(final String owner, final String name, | |
998 | final String desc) { | |
999 | mv.visitMethodInsn(Opcodes.INVOKESTATIC, owner, name, desc); | |
1000 | } | |
1001 | ||
1002 | public void invokeinterface(final String owner, final String name, | |
1003 | final String desc) { | |
1004 | mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, owner, name, desc); | |
1005 | } | |
1006 | ||
1007 | public void invokedynamic(String name, String desc, Handle bsm, | |
1008 | Object[] bsmArgs) { | |
1009 | mv.visitInvokeDynamicInsn(name, desc, bsm, bsmArgs); | |
1010 | } | |
1011 | ||
1012 | public void anew(final Type type) { | |
1013 | mv.visitTypeInsn(Opcodes.NEW, type.getInternalName()); | |
1014 | } | |
1015 | ||
1016 | public void newarray(final Type type) { | |
1017 | int typ; | |
1018 | switch (type.getSort()) { | |
1019 | case Type.BOOLEAN: | |
1020 | typ = Opcodes.T_BOOLEAN; | |
1021 | break; | |
1022 | case Type.CHAR: | |
1023 | typ = Opcodes.T_CHAR; | |
1024 | break; | |
1025 | case Type.BYTE: | |
1026 | typ = Opcodes.T_BYTE; | |
1027 | break; | |
1028 | case Type.SHORT: | |
1029 | typ = Opcodes.T_SHORT; | |
1030 | break; | |
1031 | case Type.INT: | |
1032 | typ = Opcodes.T_INT; | |
1033 | break; | |
1034 | case Type.FLOAT: | |
1035 | typ = Opcodes.T_FLOAT; | |
1036 | break; | |
1037 | case Type.LONG: | |
1038 | typ = Opcodes.T_LONG; | |
1039 | break; | |
1040 | case Type.DOUBLE: | |
1041 | typ = Opcodes.T_DOUBLE; | |
1042 | break; | |
1043 | default: | |
1044 | mv.visitTypeInsn(Opcodes.ANEWARRAY, type.getInternalName()); | |
1045 | return; | |
1046 | } | |
1047 | mv.visitIntInsn(Opcodes.NEWARRAY, typ); | |
1048 | } | |
1049 | ||
1050 | public void arraylength() { | |
1051 | mv.visitInsn(Opcodes.ARRAYLENGTH); | |
1052 | } | |
1053 | ||
1054 | public void athrow() { | |
1055 | mv.visitInsn(Opcodes.ATHROW); | |
1056 | } | |
1057 | ||
1058 | public void checkcast(final Type type) { | |
1059 | mv.visitTypeInsn(Opcodes.CHECKCAST, type.getInternalName()); | |
1060 | } | |
1061 | ||
1062 | public void instanceOf(final Type type) { | |
1063 | mv.visitTypeInsn(Opcodes.INSTANCEOF, type.getInternalName()); | |
1064 | } | |
1065 | ||
1066 | public void monitorenter() { | |
1067 | mv.visitInsn(Opcodes.MONITORENTER); | |
1068 | } | |
1069 | ||
1070 | public void monitorexit() { | |
1071 | mv.visitInsn(Opcodes.MONITOREXIT); | |
1072 | } | |
1073 | ||
1074 | public void multianewarray(final String desc, final int dims) { | |
1075 | mv.visitMultiANewArrayInsn(desc, dims); | |
1076 | } | |
1077 | ||
1078 | public void ifnull(final Label label) { | |
1079 | mv.visitJumpInsn(Opcodes.IFNULL, label); | |
1080 | } | |
1081 | ||
1082 | public void ifnonnull(final Label label) { | |
1083 | mv.visitJumpInsn(Opcodes.IFNONNULL, label); | |
1084 | } | |
1085 | ||
1086 | public void mark(final Label label) { | |
1087 | mv.visitLabel(label); | |
1088 | } | |
1089 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.MethodVisitor; | |
33 | import clojure.asm.Opcodes; | |
34 | import clojure.asm.Type; | |
35 | ||
36 | /** | |
37 | * A {@link MethodVisitor} that renumbers local variables in their order of | |
38 | * appearance. This adapter allows one to easily add new local variables to a | |
39 | * method. It may be used by inheriting from this class, but the preferred way | |
40 | * of using it is via delegation: the next visitor in the chain can indeed add | |
41 | * new locals when needed by calling {@link #newLocal} on this adapter (this | |
42 | * requires a reference back to this {@link LocalVariablesSorter}). | |
43 | * | |
44 | * @author Chris Nokleberg | |
45 | * @author Eugene Kuleshov | |
46 | * @author Eric Bruneton | |
47 | */ | |
48 | public class LocalVariablesSorter extends MethodVisitor { | |
49 | ||
50 | private static final Type OBJECT_TYPE = Type | |
51 | .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}. <i>Subclasses must not use | |
82 | * this constructor</i>. Instead, they must use the | |
83 | * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version. | |
84 | * | |
85 | * @param access | |
86 | * access flags of the adapted method. | |
87 | * @param desc | |
88 | * the method's descriptor (see {@link Type Type}). | |
89 | * @param mv | |
90 | * the method visitor to which this adapter delegates calls. | |
91 | */ | |
92 | public LocalVariablesSorter(final int access, final String desc, | |
93 | final MethodVisitor mv) { | |
94 | this(Opcodes.ASM4, access, desc, mv); | |
95 | } | |
96 | ||
97 | /** | |
98 | * Creates a new {@link LocalVariablesSorter}. | |
99 | * | |
100 | * @param api | |
101 | * the ASM API version implemented by this visitor. Must be one | |
102 | * of {@link Opcodes#ASM4}. | |
103 | * @param access | |
104 | * access flags of the adapted method. | |
105 | * @param desc | |
106 | * the method's descriptor (see {@link Type Type}). | |
107 | * @param mv | |
108 | * the method visitor to which this adapter delegates calls. | |
109 | */ | |
110 | protected LocalVariablesSorter(final int api, final int access, | |
111 | final String desc, final MethodVisitor mv) { | |
112 | super(api, mv); | |
113 | Type[] args = Type.getArgumentTypes(desc); | |
114 | nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; | |
115 | for (int i = 0; i < args.length; i++) { | |
116 | nextLocal += args[i].getSize(); | |
117 | } | |
118 | firstLocal = nextLocal; | |
119 | } | |
120 | ||
121 | @Override | |
122 | public void visitVarInsn(final int opcode, final int var) { | |
123 | Type type; | |
124 | switch (opcode) { | |
125 | case Opcodes.LLOAD: | |
126 | case Opcodes.LSTORE: | |
127 | type = Type.LONG_TYPE; | |
128 | break; | |
129 | ||
130 | case Opcodes.DLOAD: | |
131 | case Opcodes.DSTORE: | |
132 | type = Type.DOUBLE_TYPE; | |
133 | break; | |
134 | ||
135 | case Opcodes.FLOAD: | |
136 | case Opcodes.FSTORE: | |
137 | type = Type.FLOAT_TYPE; | |
138 | break; | |
139 | ||
140 | case Opcodes.ILOAD: | |
141 | case Opcodes.ISTORE: | |
142 | type = Type.INT_TYPE; | |
143 | break; | |
144 | ||
145 | default: | |
146 | // case Opcodes.ALOAD: | |
147 | // case Opcodes.ASTORE: | |
148 | // case RET: | |
149 | type = OBJECT_TYPE; | |
150 | break; | |
151 | } | |
152 | mv.visitVarInsn(opcode, remap(var, type)); | |
153 | } | |
154 | ||
155 | @Override | |
156 | public void visitIincInsn(final int var, final int increment) { | |
157 | mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); | |
158 | } | |
159 | ||
160 | @Override | |
161 | public void visitMaxs(final int maxStack, final int maxLocals) { | |
162 | mv.visitMaxs(maxStack, nextLocal); | |
163 | } | |
164 | ||
165 | @Override | |
166 | public void visitLocalVariable(final String name, final String desc, | |
167 | final String signature, final Label start, final Label end, | |
168 | final int index) { | |
169 | int newIndex = remap(index, Type.getType(desc)); | |
170 | mv.visitLocalVariable(name, desc, signature, start, end, newIndex); | |
171 | } | |
172 | ||
173 | @Override | |
174 | public void visitFrame(final int type, final int nLocal, | |
175 | final Object[] local, final int nStack, final Object[] stack) { | |
176 | if (type != Opcodes.F_NEW) { // uncompressed frame | |
177 | throw new IllegalStateException( | |
178 | "ClassReader.accept() should be called with EXPAND_FRAMES flag"); | |
179 | } | |
180 | ||
181 | if (!changed) { // optimization for the case where mapping = identity | |
182 | mv.visitFrame(type, nLocal, local, nStack, stack); | |
183 | return; | |
184 | } | |
185 | ||
186 | // creates a copy of newLocals | |
187 | Object[] oldLocals = new Object[newLocals.length]; | |
188 | System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); | |
189 | ||
190 | updateNewLocals(newLocals); | |
191 | ||
192 | // copies types from 'local' to 'newLocals' | |
193 | // 'newLocals' already contains the variables added with 'newLocal' | |
194 | ||
195 | int index = 0; // old local variable index | |
196 | int number = 0; // old local variable number | |
197 | for (; number < nLocal; ++number) { | |
198 | Object t = local[number]; | |
199 | int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; | |
200 | if (t != Opcodes.TOP) { | |
201 | Type typ = OBJECT_TYPE; | |
202 | if (t == Opcodes.INTEGER) { | |
203 | typ = Type.INT_TYPE; | |
204 | } else if (t == Opcodes.FLOAT) { | |
205 | typ = Type.FLOAT_TYPE; | |
206 | } else if (t == Opcodes.LONG) { | |
207 | typ = Type.LONG_TYPE; | |
208 | } else if (t == Opcodes.DOUBLE) { | |
209 | typ = Type.DOUBLE_TYPE; | |
210 | } else if (t instanceof String) { | |
211 | typ = Type.getObjectType((String) t); | |
212 | } | |
213 | setFrameLocal(remap(index, typ), t); | |
214 | } | |
215 | index += size; | |
216 | } | |
217 | ||
218 | // removes TOP after long and double types as well as trailing TOPs | |
219 | ||
220 | index = 0; | |
221 | number = 0; | |
222 | for (int i = 0; index < newLocals.length; ++i) { | |
223 | Object t = newLocals[index++]; | |
224 | if (t != null && t != Opcodes.TOP) { | |
225 | newLocals[i] = t; | |
226 | number = i + 1; | |
227 | if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { | |
228 | index += 1; | |
229 | } | |
230 | } else { | |
231 | newLocals[i] = Opcodes.TOP; | |
232 | } | |
233 | } | |
234 | ||
235 | // visits remapped frame | |
236 | mv.visitFrame(type, number, newLocals, nStack, stack); | |
237 | ||
238 | // restores original value of 'newLocals' | |
239 | newLocals = oldLocals; | |
240 | } | |
241 | ||
242 | // ------------- | |
243 | ||
244 | /** | |
245 | * Creates a new local variable of the given type. | |
246 | * | |
247 | * @param type | |
248 | * the type of the local variable to be created. | |
249 | * @return the identifier of the newly created local variable. | |
250 | */ | |
251 | public int newLocal(final Type type) { | |
252 | Object t; | |
253 | switch (type.getSort()) { | |
254 | case Type.BOOLEAN: | |
255 | case Type.CHAR: | |
256 | case Type.BYTE: | |
257 | case Type.SHORT: | |
258 | case Type.INT: | |
259 | t = Opcodes.INTEGER; | |
260 | break; | |
261 | case Type.FLOAT: | |
262 | t = Opcodes.FLOAT; | |
263 | break; | |
264 | case Type.LONG: | |
265 | t = Opcodes.LONG; | |
266 | break; | |
267 | case Type.DOUBLE: | |
268 | t = Opcodes.DOUBLE; | |
269 | break; | |
270 | case Type.ARRAY: | |
271 | t = type.getDescriptor(); | |
272 | break; | |
273 | // case Type.OBJECT: | |
274 | default: | |
275 | t = type.getInternalName(); | |
276 | break; | |
277 | } | |
278 | int local = newLocalMapping(type); | |
279 | setLocalType(local, type); | |
280 | setFrameLocal(local, t); | |
281 | return local; | |
282 | } | |
283 | ||
284 | /** | |
285 | * Notifies subclasses that a new stack map frame is being visited. The | |
286 | * array argument contains the stack map frame types corresponding to the | |
287 | * local variables added with {@link #newLocal}. This method can update | |
288 | * these types in place for the stack map frame being visited. The default | |
289 | * implementation of this method does nothing, i.e. a local variable added | |
290 | * with {@link #newLocal} will have the same type in all stack map frames. | |
291 | * But this behavior is not always the desired one, for instance if a local | |
292 | * variable is added in the middle of a try/catch block: the frame for the | |
293 | * exception handler should have a TOP type for this new local. | |
294 | * | |
295 | * @param newLocals | |
296 | * the stack map frame types corresponding to the local variables | |
297 | * added with {@link #newLocal} (and null for the others). The | |
298 | * format of this array is the same as in | |
299 | * {@link MethodVisitor#visitFrame}, except that long and double | |
300 | * types use two slots. The types for the current stack map frame | |
301 | * must be updated in place in this array. | |
302 | */ | |
303 | protected void updateNewLocals(Object[] newLocals) { | |
304 | } | |
305 | ||
306 | /** | |
307 | * Notifies subclasses that a local variable has been added or remapped. The | |
308 | * default implementation of this method does nothing. | |
309 | * | |
310 | * @param local | |
311 | * a local variable identifier, as returned by {@link #newLocal | |
312 | * newLocal()}. | |
313 | * @param type | |
314 | * the type of the value being stored in the local variable. | |
315 | */ | |
316 | protected void setLocalType(final int local, final Type type) { | |
317 | } | |
318 | ||
319 | private void setFrameLocal(final int local, final Object type) { | |
320 | int l = newLocals.length; | |
321 | if (local >= l) { | |
322 | Object[] a = new Object[Math.max(2 * l, local + 1)]; | |
323 | System.arraycopy(newLocals, 0, a, 0, l); | |
324 | newLocals = a; | |
325 | } | |
326 | newLocals[local] = type; | |
327 | } | |
328 | ||
329 | private int remap(final int var, final Type type) { | |
330 | if (var + type.getSize() <= firstLocal) { | |
331 | return var; | |
332 | } | |
333 | int key = 2 * var + type.getSize() - 1; | |
334 | int size = mapping.length; | |
335 | if (key >= size) { | |
336 | int[] newMapping = new int[Math.max(2 * size, key + 1)]; | |
337 | System.arraycopy(mapping, 0, newMapping, 0, size); | |
338 | mapping = newMapping; | |
339 | } | |
340 | int value = mapping[key]; | |
341 | if (value == 0) { | |
342 | value = newLocalMapping(type); | |
343 | setLocalType(value, type); | |
344 | mapping[key] = value + 1; | |
345 | } else { | |
346 | value--; | |
347 | } | |
348 | if (value != var) { | |
349 | changed = true; | |
350 | } | |
351 | return value; | |
352 | } | |
353 | ||
354 | protected int newLocalMapping(final Type type) { | |
355 | int local = nextLocal; | |
356 | nextLocal += type.getSize(); | |
357 | return local; | |
358 | } | |
359 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 static final Map<String, String> DESCRIPTORS; | |
59 | ||
60 | static { | |
61 | DESCRIPTORS = new HashMap<String, String>(); | |
62 | DESCRIPTORS.put("void", "V"); | |
63 | DESCRIPTORS.put("byte", "B"); | |
64 | DESCRIPTORS.put("char", "C"); | |
65 | DESCRIPTORS.put("double", "D"); | |
66 | DESCRIPTORS.put("float", "F"); | |
67 | DESCRIPTORS.put("int", "I"); | |
68 | DESCRIPTORS.put("long", "J"); | |
69 | DESCRIPTORS.put("short", "S"); | |
70 | DESCRIPTORS.put("boolean", "Z"); | |
71 | } | |
72 | ||
73 | /** | |
74 | * Creates a new {@link Method}. | |
75 | * | |
76 | * @param name | |
77 | * the method's name. | |
78 | * @param desc | |
79 | * the method's descriptor. | |
80 | */ | |
81 | public Method(final String name, final String desc) { | |
82 | this.name = name; | |
83 | this.desc = desc; | |
84 | } | |
85 | ||
86 | /** | |
87 | * Creates a new {@link Method}. | |
88 | * | |
89 | * @param name | |
90 | * the method's name. | |
91 | * @param returnType | |
92 | * the method's return type. | |
93 | * @param argumentTypes | |
94 | * the method's argument types. | |
95 | */ | |
96 | public Method(final String name, final Type returnType, | |
97 | final Type[] argumentTypes) { | |
98 | this(name, Type.getMethodDescriptor(returnType, argumentTypes)); | |
99 | } | |
100 | ||
101 | /** | |
102 | * Creates a new {@link Method}. | |
103 | * | |
104 | * @param m | |
105 | * a java.lang.reflect method descriptor | |
106 | * @return a {@link Method} corresponding to the given Java method | |
107 | * declaration. | |
108 | */ | |
109 | public static Method getMethod(java.lang.reflect.Method m) { | |
110 | return new Method(m.getName(), Type.getMethodDescriptor(m)); | |
111 | } | |
112 | ||
113 | /** | |
114 | * Creates a new {@link Method}. | |
115 | * | |
116 | * @param c | |
117 | * a java.lang.reflect constructor descriptor | |
118 | * @return a {@link Method} corresponding to the given Java constructor | |
119 | * declaration. | |
120 | */ | |
121 | public static Method getMethod(java.lang.reflect.Constructor<?> c) { | |
122 | return new Method("<init>", Type.getConstructorDescriptor(c)); | |
123 | } | |
124 | ||
125 | /** | |
126 | * Returns a {@link Method} corresponding to the given Java method | |
127 | * declaration. | |
128 | * | |
129 | * @param method | |
130 | * a Java method declaration, without argument names, of the form | |
131 | * "returnType name (argumentType1, ... argumentTypeN)", where | |
132 | * the types are in plain Java (e.g. "int", "float", | |
133 | * "java.util.List", ...). Classes of the java.lang package can | |
134 | * be specified by their unqualified name; all other classes | |
135 | * names must be fully qualified. | |
136 | * @return a {@link Method} corresponding to the given Java method | |
137 | * declaration. | |
138 | * @throws IllegalArgumentException | |
139 | * if <code>method</code> could not get parsed. | |
140 | */ | |
141 | public static Method getMethod(final String method) | |
142 | throws IllegalArgumentException { | |
143 | return getMethod(method, false); | |
144 | } | |
145 | ||
146 | /** | |
147 | * Returns a {@link Method} corresponding to the given Java method | |
148 | * declaration. | |
149 | * | |
150 | * @param method | |
151 | * a Java method declaration, without argument names, of the form | |
152 | * "returnType name (argumentType1, ... argumentTypeN)", where | |
153 | * the types are in plain Java (e.g. "int", "float", | |
154 | * "java.util.List", ...). Classes of the java.lang package may | |
155 | * be specified by their unqualified name, depending on the | |
156 | * defaultPackage argument; all other classes names must be fully | |
157 | * qualified. | |
158 | * @param defaultPackage | |
159 | * true if unqualified class names belong to the default package, | |
160 | * or false if they correspond to java.lang classes. For instance | |
161 | * "Object" means "Object" if this option is true, or | |
162 | * "java.lang.Object" otherwise. | |
163 | * @return a {@link Method} corresponding to the given Java method | |
164 | * declaration. | |
165 | * @throws IllegalArgumentException | |
166 | * if <code>method</code> could not get parsed. | |
167 | */ | |
168 | public static Method getMethod(final String method, | |
169 | final boolean defaultPackage) throws IllegalArgumentException { | |
170 | int space = method.indexOf(' '); | |
171 | int start = method.indexOf('(', space) + 1; | |
172 | int end = method.indexOf(')', start); | |
173 | if (space == -1 || start == -1 || end == -1) { | |
174 | throw new IllegalArgumentException(); | |
175 | } | |
176 | String returnType = method.substring(0, space); | |
177 | String methodName = method.substring(space + 1, start - 1).trim(); | |
178 | StringBuffer sb = new StringBuffer(); | |
179 | sb.append('('); | |
180 | int p; | |
181 | do { | |
182 | String s; | |
183 | p = method.indexOf(',', start); | |
184 | if (p == -1) { | |
185 | s = map(method.substring(start, end).trim(), defaultPackage); | |
186 | } else { | |
187 | s = map(method.substring(start, p).trim(), defaultPackage); | |
188 | start = p + 1; | |
189 | } | |
190 | sb.append(s); | |
191 | } while (p != -1); | |
192 | sb.append(')'); | |
193 | sb.append(map(returnType, defaultPackage)); | |
194 | return new Method(methodName, sb.toString()); | |
195 | } | |
196 | ||
197 | private static String map(final String type, final boolean defaultPackage) { | |
198 | if ("".equals(type)) { | |
199 | return type; | |
200 | } | |
201 | ||
202 | StringBuffer sb = new StringBuffer(); | |
203 | int index = 0; | |
204 | while ((index = type.indexOf("[]", index) + 1) > 0) { | |
205 | sb.append('['); | |
206 | } | |
207 | ||
208 | String t = type.substring(0, type.length() - sb.length() * 2); | |
209 | String desc = DESCRIPTORS.get(t); | |
210 | if (desc != null) { | |
211 | sb.append(desc); | |
212 | } else { | |
213 | sb.append('L'); | |
214 | if (t.indexOf('.') < 0) { | |
215 | if (!defaultPackage) { | |
216 | sb.append("java/lang/"); | |
217 | } | |
218 | sb.append(t); | |
219 | } else { | |
220 | sb.append(t.replace('.', '/')); | |
221 | } | |
222 | sb.append(';'); | |
223 | } | |
224 | return sb.toString(); | |
225 | } | |
226 | ||
227 | /** | |
228 | * Returns the name of the method described by this object. | |
229 | * | |
230 | * @return the name of the method described by this object. | |
231 | */ | |
232 | public String getName() { | |
233 | return name; | |
234 | } | |
235 | ||
236 | /** | |
237 | * Returns the descriptor of the method described by this object. | |
238 | * | |
239 | * @return the descriptor of the method described by this object. | |
240 | */ | |
241 | public String getDescriptor() { | |
242 | return desc; | |
243 | } | |
244 | ||
245 | /** | |
246 | * Returns the return type of the method described by this object. | |
247 | * | |
248 | * @return the return type of the method described by this object. | |
249 | */ | |
250 | public Type getReturnType() { | |
251 | return Type.getReturnType(desc); | |
252 | } | |
253 | ||
254 | /** | |
255 | * Returns the argument types of the method described by this object. | |
256 | * | |
257 | * @return the argument types of the method described by this object. | |
258 | */ | |
259 | public Type[] getArgumentTypes() { | |
260 | return Type.getArgumentTypes(desc); | |
261 | } | |
262 | ||
263 | @Override | |
264 | public String toString() { | |
265 | return name + desc; | |
266 | } | |
267 | ||
268 | @Override | |
269 | public boolean equals(final Object o) { | |
270 | if (!(o instanceof Method)) { | |
271 | return false; | |
272 | } | |
273 | Method other = (Method) o; | |
274 | return name.equals(other.name) && desc.equals(other.desc); | |
275 | } | |
276 | ||
277 | @Override | |
278 | public int hashCode() { | |
279 | return name.hashCode() ^ desc.hashCode(); | |
280 | } | |
281 | }⏎ |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.DataOutput; | |
33 | import java.io.DataOutputStream; | |
34 | import java.io.IOException; | |
35 | import java.security.MessageDigest; | |
36 | import java.util.ArrayList; | |
37 | import java.util.Arrays; | |
38 | import java.util.Collection; | |
39 | ||
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 ClassVisitor} that adds a serial version unique identifier to a | |
47 | * class if missing. Here is typical usage of this class: | |
48 | * | |
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 | * | |
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 | * | |
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 | * | |
69 | * The sequence of items in the stream is as follows: | |
70 | * | |
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 | * | |
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 | * | |
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 ClassVisitor { | |
113 | ||
114 | /** | |
115 | * Flag that indicates if we need to compute SVUID. | |
116 | */ | |
117 | private boolean computeSVUID; | |
118 | ||
119 | /** | |
120 | * Set to true if the class already has SVUID. | |
121 | */ | |
122 | private boolean hasSVUID; | |
123 | ||
124 | /** | |
125 | * Classes access flags. | |
126 | */ | |
127 | private int access; | |
128 | ||
129 | /** | |
130 | * Internal name of the class | |
131 | */ | |
132 | private String name; | |
133 | ||
134 | /** | |
135 | * Interfaces implemented by the class. | |
136 | */ | |
137 | private String[] interfaces; | |
138 | ||
139 | /** | |
140 | * Collection of fields. (except private static and private transient | |
141 | * fields) | |
142 | */ | |
143 | private Collection<Item> svuidFields; | |
144 | ||
145 | /** | |
146 | * Set to true if the class has static initializer. | |
147 | */ | |
148 | private boolean hasStaticInitializer; | |
149 | ||
150 | /** | |
151 | * Collection of non-private constructors. | |
152 | */ | |
153 | private Collection<Item> svuidConstructors; | |
154 | ||
155 | /** | |
156 | * Collection of non-private methods. | |
157 | */ | |
158 | private Collection<Item> svuidMethods; | |
159 | ||
160 | /** | |
161 | * Creates a new {@link SerialVersionUIDAdder}. <i>Subclasses must not use | |
162 | * this constructor</i>. Instead, they must use the | |
163 | * {@link #SerialVersionUIDAdder(int, ClassVisitor)} version. | |
164 | * | |
165 | * @param cv | |
166 | * a {@link ClassVisitor} to which this visitor will delegate | |
167 | * calls. | |
168 | */ | |
169 | public SerialVersionUIDAdder(final ClassVisitor cv) { | |
170 | this(Opcodes.ASM4, cv); | |
171 | } | |
172 | ||
173 | /** | |
174 | * Creates a new {@link SerialVersionUIDAdder}. | |
175 | * | |
176 | * @param api | |
177 | * the ASM API version implemented by this visitor. Must be one | |
178 | * of {@link Opcodes#ASM4}. | |
179 | * @param cv | |
180 | * a {@link ClassVisitor} to which this visitor will delegate | |
181 | * calls. | |
182 | */ | |
183 | protected SerialVersionUIDAdder(final int api, final ClassVisitor cv) { | |
184 | super(api, cv); | |
185 | svuidFields = new ArrayList<Item>(); | |
186 | svuidConstructors = new ArrayList<Item>(); | |
187 | svuidMethods = new ArrayList<Item>(); | |
188 | } | |
189 | ||
190 | // ------------------------------------------------------------------------ | |
191 | // Overriden methods | |
192 | // ------------------------------------------------------------------------ | |
193 | ||
194 | /* | |
195 | * Visit class header and get class name, access , and interfaces | |
196 | * information (step 1,2, and 3) for SVUID computation. | |
197 | */ | |
198 | @Override | |
199 | public void visit(final int version, final int access, final String name, | |
200 | final String signature, final String superName, | |
201 | final String[] interfaces) { | |
202 | computeSVUID = (access & Opcodes.ACC_INTERFACE) == 0; | |
203 | ||
204 | if (computeSVUID) { | |
205 | this.name = name; | |
206 | this.access = access; | |
207 | this.interfaces = interfaces; | |
208 | } | |
209 | ||
210 | super.visit(version, access, name, signature, superName, interfaces); | |
211 | } | |
212 | ||
213 | /* | |
214 | * Visit the methods and get constructor and method information (step 5 and | |
215 | * 7). Also determine if there is a class initializer (step 6). | |
216 | */ | |
217 | @Override | |
218 | public MethodVisitor visitMethod(final int access, final String name, | |
219 | final String desc, final String signature, final String[] exceptions) { | |
220 | if (computeSVUID) { | |
221 | if ("<clinit>".equals(name)) { | |
222 | hasStaticInitializer = true; | |
223 | } | |
224 | /* | |
225 | * Remembers non private constructors and methods for SVUID | |
226 | * computation For constructor and method modifiers, only the | |
227 | * ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, ACC_FINAL, | |
228 | * ACC_SYNCHRONIZED, ACC_NATIVE, ACC_ABSTRACT and ACC_STRICT flags | |
229 | * are used. | |
230 | */ | |
231 | int mods = access | |
232 | & (Opcodes.ACC_PUBLIC | Opcodes.ACC_PRIVATE | |
233 | | Opcodes.ACC_PROTECTED | Opcodes.ACC_STATIC | |
234 | | Opcodes.ACC_FINAL | Opcodes.ACC_SYNCHRONIZED | |
235 | | Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT | Opcodes.ACC_STRICT); | |
236 | ||
237 | // all non private methods | |
238 | if ((access & Opcodes.ACC_PRIVATE) == 0) { | |
239 | if ("<init>".equals(name)) { | |
240 | svuidConstructors.add(new Item(name, mods, desc)); | |
241 | } else if (!"<clinit>".equals(name)) { | |
242 | svuidMethods.add(new Item(name, mods, desc)); | |
243 | } | |
244 | } | |
245 | } | |
246 | ||
247 | return super.visitMethod(access, name, desc, signature, exceptions); | |
248 | } | |
249 | ||
250 | /* | |
251 | * Gets class field information for step 4 of the algorithm. Also determines | |
252 | * if the class already has a SVUID. | |
253 | */ | |
254 | @Override | |
255 | public FieldVisitor visitField(final int access, final String name, | |
256 | final String desc, final String signature, final Object value) { | |
257 | if (computeSVUID) { | |
258 | if ("serialVersionUID".equals(name)) { | |
259 | // since the class already has SVUID, we won't be computing it. | |
260 | computeSVUID = false; | |
261 | hasSVUID = true; | |
262 | } | |
263 | /* | |
264 | * Remember field for SVUID computation For field modifiers, only | |
265 | * the ACC_PUBLIC, ACC_PRIVATE, ACC_PROTECTED, ACC_STATIC, | |
266 | * ACC_FINAL, ACC_VOLATILE, and ACC_TRANSIENT flags are used when | |
267 | * computing serialVersionUID values. | |
268 | */ | |
269 | if ((access & Opcodes.ACC_PRIVATE) == 0 | |
270 | || (access & (Opcodes.ACC_STATIC | Opcodes.ACC_TRANSIENT)) == 0) { | |
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 | svuidFields.add(new Item(name, mods, desc)); | |
276 | } | |
277 | } | |
278 | ||
279 | return super.visitField(access, name, desc, signature, value); | |
280 | } | |
281 | ||
282 | /** | |
283 | * Handle a bizarre special case. Nested classes (static classes declared | |
284 | * inside another class) that are protected have their access bit set to | |
285 | * public in their class files to deal with some odd reflection situation. | |
286 | * Our SVUID computation must do as the JVM does and ignore access bits in | |
287 | * the class file in favor of the access bits InnerClass attribute. | |
288 | */ | |
289 | @Override | |
290 | public void visitInnerClass(final String aname, final String outerName, | |
291 | final String innerName, final int attr_access) { | |
292 | if ((name != null) && name.equals(aname)) { | |
293 | this.access = attr_access; | |
294 | } | |
295 | super.visitInnerClass(aname, outerName, innerName, attr_access); | |
296 | } | |
297 | ||
298 | /* | |
299 | * Add the SVUID if class doesn't have one | |
300 | */ | |
301 | @Override | |
302 | public void visitEnd() { | |
303 | // compute SVUID and add it to the class | |
304 | if (computeSVUID && !hasSVUID) { | |
305 | try { | |
306 | addSVUID(computeSVUID()); | |
307 | } catch (Throwable e) { | |
308 | throw new RuntimeException("Error while computing SVUID for " | |
309 | + name, e); | |
310 | } | |
311 | } | |
312 | ||
313 | super.visitEnd(); | |
314 | } | |
315 | ||
316 | // ------------------------------------------------------------------------ | |
317 | // Utility methods | |
318 | // ------------------------------------------------------------------------ | |
319 | ||
320 | /** | |
321 | * Returns true if the class already has a SVUID field. The result of this | |
322 | * method is only valid when visitEnd is or has been called. | |
323 | * | |
324 | * @return true if the class already has a SVUID field. | |
325 | */ | |
326 | public boolean hasSVUID() { | |
327 | return hasSVUID; | |
328 | } | |
329 | ||
330 | protected void addSVUID(long svuid) { | |
331 | FieldVisitor fv = super.visitField(Opcodes.ACC_FINAL | |
332 | + Opcodes.ACC_STATIC, "serialVersionUID", "J", null, new Long( | |
333 | svuid)); | |
334 | if (fv != null) { | |
335 | fv.visitEnd(); | |
336 | } | |
337 | } | |
338 | ||
339 | /** | |
340 | * Computes and returns the value of SVUID. | |
341 | * | |
342 | * @return Returns the serial version UID | |
343 | * @throws IOException | |
344 | * if an I/O error occurs | |
345 | */ | |
346 | protected long computeSVUID() throws IOException { | |
347 | ByteArrayOutputStream bos; | |
348 | DataOutputStream dos = null; | |
349 | long svuid = 0; | |
350 | ||
351 | try { | |
352 | bos = new ByteArrayOutputStream(); | |
353 | dos = new DataOutputStream(bos); | |
354 | ||
355 | /* | |
356 | * 1. The class name written using UTF encoding. | |
357 | */ | |
358 | dos.writeUTF(name.replace('/', '.')); | |
359 | ||
360 | /* | |
361 | * 2. The class modifiers written as a 32-bit integer. | |
362 | */ | |
363 | dos.writeInt(access | |
364 | & (Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | |
365 | | Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT)); | |
366 | ||
367 | /* | |
368 | * 3. The name of each interface sorted by name written using UTF | |
369 | * encoding. | |
370 | */ | |
371 | Arrays.sort(interfaces); | |
372 | for (int i = 0; i < interfaces.length; i++) { | |
373 | dos.writeUTF(interfaces[i].replace('/', '.')); | |
374 | } | |
375 | ||
376 | /* | |
377 | * 4. For each field of the class sorted by field name (except | |
378 | * private static and private transient fields): | |
379 | * | |
380 | * 1. The name of the field in UTF encoding. 2. The modifiers of the | |
381 | * field written as a 32-bit integer. 3. The descriptor of the field | |
382 | * in UTF encoding | |
383 | * | |
384 | * Note that field signatures are not dot separated. Method and | |
385 | * constructor signatures are dot separated. Go figure... | |
386 | */ | |
387 | writeItems(svuidFields, dos, false); | |
388 | ||
389 | /* | |
390 | * 5. If a class initializer exists, write out the following: 1. The | |
391 | * name of the method, <clinit>, in UTF encoding. 2. The modifier of | |
392 | * the method, java.lang.reflect.Modifier.STATIC, written as a | |
393 | * 32-bit integer. 3. The descriptor of the method, ()V, in UTF | |
394 | * encoding. | |
395 | */ | |
396 | if (hasStaticInitializer) { | |
397 | dos.writeUTF("<clinit>"); | |
398 | dos.writeInt(Opcodes.ACC_STATIC); | |
399 | dos.writeUTF("()V"); | |
400 | } // if.. | |
401 | ||
402 | /* | |
403 | * 6. For each non-private constructor sorted by method name and | |
404 | * signature: 1. The name of the method, <init>, in UTF encoding. 2. | |
405 | * The modifiers of the method written as a 32-bit integer. 3. The | |
406 | * descriptor of the method in UTF encoding. | |
407 | */ | |
408 | writeItems(svuidConstructors, dos, true); | |
409 | ||
410 | /* | |
411 | * 7. For each non-private method sorted by method name and | |
412 | * signature: 1. The name of the method in UTF encoding. 2. The | |
413 | * modifiers of the method written as a 32-bit integer. 3. The | |
414 | * descriptor of the method in UTF encoding. | |
415 | */ | |
416 | writeItems(svuidMethods, dos, true); | |
417 | ||
418 | dos.flush(); | |
419 | ||
420 | /* | |
421 | * 8. The SHA-1 algorithm is executed on the stream of bytes | |
422 | * produced by DataOutputStream and produces five 32-bit values | |
423 | * sha[0..4]. | |
424 | */ | |
425 | byte[] hashBytes = computeSHAdigest(bos.toByteArray()); | |
426 | ||
427 | /* | |
428 | * 9. The hash value is assembled from the first and second 32-bit | |
429 | * values of the SHA-1 message digest. If the result of the message | |
430 | * digest, the five 32-bit words H0 H1 H2 H3 H4, is in an array of | |
431 | * five int values named sha, the hash value would be computed as | |
432 | * follows: | |
433 | * | |
434 | * long hash = ((sha[0] >>> 24) & 0xFF) | ((sha[0] >>> 16) & 0xFF) | |
435 | * << 8 | ((sha[0] >>> 8) & 0xFF) << 16 | ((sha[0] >>> 0) & 0xFF) << | |
436 | * 24 | ((sha[1] >>> 24) & 0xFF) << 32 | ((sha[1] >>> 16) & 0xFF) << | |
437 | * 40 | ((sha[1] >>> 8) & 0xFF) << 48 | ((sha[1] >>> 0) & 0xFF) << | |
438 | * 56; | |
439 | */ | |
440 | for (int i = Math.min(hashBytes.length, 8) - 1; i >= 0; i--) { | |
441 | svuid = (svuid << 8) | (hashBytes[i] & 0xFF); | |
442 | } | |
443 | } finally { | |
444 | // close the stream (if open) | |
445 | if (dos != null) { | |
446 | dos.close(); | |
447 | } | |
448 | } | |
449 | ||
450 | return svuid; | |
451 | } | |
452 | ||
453 | /** | |
454 | * Returns the SHA-1 message digest of the given value. | |
455 | * | |
456 | * @param value | |
457 | * the value whose SHA message digest must be computed. | |
458 | * @return the SHA-1 message digest of the given value. | |
459 | */ | |
460 | protected byte[] computeSHAdigest(final byte[] value) { | |
461 | try { | |
462 | return MessageDigest.getInstance("SHA").digest(value); | |
463 | } catch (Exception e) { | |
464 | throw new UnsupportedOperationException(e.toString()); | |
465 | } | |
466 | } | |
467 | ||
468 | /** | |
469 | * Sorts the items in the collection and writes it to the data output stream | |
470 | * | |
471 | * @param itemCollection | |
472 | * collection of items | |
473 | * @param dos | |
474 | * a <code>DataOutputStream</code> value | |
475 | * @param dotted | |
476 | * a <code>boolean</code> value | |
477 | * @exception IOException | |
478 | * if an error occurs | |
479 | */ | |
480 | private static void writeItems(final Collection<Item> itemCollection, | |
481 | final DataOutput dos, final boolean dotted) throws IOException { | |
482 | int size = itemCollection.size(); | |
483 | Item[] items = itemCollection.toArray(new Item[size]); | |
484 | Arrays.sort(items); | |
485 | for (int i = 0; i < size; i++) { | |
486 | dos.writeUTF(items[i].name); | |
487 | dos.writeInt(items[i].access); | |
488 | dos.writeUTF(dotted ? items[i].desc.replace('/', '.') | |
489 | : items[i].desc); | |
490 | } | |
491 | } | |
492 | ||
493 | // ------------------------------------------------------------------------ | |
494 | // Inner classes | |
495 | // ------------------------------------------------------------------------ | |
496 | ||
497 | private static class Item implements Comparable<Item> { | |
498 | ||
499 | final String name; | |
500 | ||
501 | final int access; | |
502 | ||
503 | final String desc; | |
504 | ||
505 | Item(final String name, final int access, final String desc) { | |
506 | this.name = name; | |
507 | this.access = access; | |
508 | this.desc = desc; | |
509 | } | |
510 | ||
511 | public int compareTo(final Item other) { | |
512 | int retVal = name.compareTo(other.name); | |
513 | if (retVal == 0) { | |
514 | retVal = desc.compareTo(other.desc); | |
515 | } | |
516 | return retVal; | |
517 | } | |
518 | ||
519 | @Override | |
520 | public boolean equals(final Object o) { | |
521 | if (o instanceof Item) { | |
522 | return compareTo((Item) o) == 0; | |
523 | } | |
524 | return false; | |
525 | } | |
526 | ||
527 | @Override | |
528 | public int hashCode() { | |
529 | return (name + desc).hashCode(); | |
530 | } | |
531 | } | |
532 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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.ClassVisitor; | |
32 | import clojure.asm.MethodVisitor; | |
33 | import clojure.asm.Opcodes; | |
34 | ||
35 | /** | |
36 | * A {@link ClassVisitor} that merges clinit methods into a single one. | |
37 | * | |
38 | * @author Eric Bruneton | |
39 | */ | |
40 | public class StaticInitMerger extends ClassVisitor { | |
41 | ||
42 | private String name; | |
43 | ||
44 | private MethodVisitor clinit; | |
45 | ||
46 | private final String prefix; | |
47 | ||
48 | private int counter; | |
49 | ||
50 | public StaticInitMerger(final String prefix, final ClassVisitor cv) { | |
51 | this(Opcodes.ASM4, prefix, cv); | |
52 | } | |
53 | ||
54 | protected StaticInitMerger(final int api, final String prefix, | |
55 | final ClassVisitor cv) { | |
56 | super(api, cv); | |
57 | this.prefix = prefix; | |
58 | } | |
59 | ||
60 | @Override | |
61 | public void visit(final int version, final int access, final String name, | |
62 | final String signature, final String superName, | |
63 | final String[] interfaces) { | |
64 | cv.visit(version, access, name, signature, superName, interfaces); | |
65 | this.name = name; | |
66 | } | |
67 | ||
68 | @Override | |
69 | public MethodVisitor visitMethod(final int access, final String name, | |
70 | final String desc, final String signature, final String[] exceptions) { | |
71 | MethodVisitor mv; | |
72 | if ("<clinit>".equals(name)) { | |
73 | int a = Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC; | |
74 | String n = prefix + counter++; | |
75 | mv = cv.visitMethod(a, n, desc, signature, exceptions); | |
76 | ||
77 | if (clinit == null) { | |
78 | clinit = cv.visitMethod(a, name, desc, null, null); | |
79 | } | |
80 | clinit.visitMethodInsn(Opcodes.INVOKESTATIC, this.name, n, desc); | |
81 | } else { | |
82 | mv = cv.visitMethod(access, name, desc, signature, exceptions); | |
83 | } | |
84 | return mv; | |
85 | } | |
86 | ||
87 | @Override | |
88 | public void visitEnd() { | |
89 | if (clinit != null) { | |
90 | clinit.visitInsn(Opcodes.RETURN); | |
91 | clinit.visitMaxs(0, 0); | |
92 | } | |
93 | cv.visitEnd(); | |
94 | } | |
95 | } |
0 | /*** | |
1 | * ASM: a very small and fast Java bytecode manipulation framework | |
2 | * Copyright (c) 2000-2011 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 | |
46 | * the switch case key. | |
47 | * @param end | |
48 | * a label that corresponds to the end of the switch statement. | |
49 | */ | |
50 | void generateCase(int key, Label end); | |
51 | ||
52 | /** | |
53 | * Generates the code for the default switch case. | |
54 | */ | |
55 | void generateDefault(); | |
56 | } |
0 | <html> | |
1 | <!-- | |
2 | * ASM: a very small and fast Java bytecode manipulation framework | |
3 | * Copyright (c) 2000-2011 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-2011 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}, | |
37 | {@link clojure.asm.MethodVisitor MethodVisitor} and | |
38 | {@link clojure.asm.AnnotationVisitor AnnotationVisitor} abstract classes, | |
39 | which allow one to visit the fields, methods and annotations of a class, | |
40 | including the bytecode instructions of each method. | |
41 | ||
42 | <p> | |
43 | In addition to these main abstract classes, ASM provides a {@link | |
44 | clojure.asm.ClassReader ClassReader} class, that can parse an | |
45 | existing class and make a given visitor visit it. ASM also provides | |
46 | a {@link clojure.asm.ClassWriter ClassWriter} class, which is | |
47 | a visitor that generates Java class files. | |
48 | ||
49 | <p> | |
50 | In order to generate a class from scratch, only the {@link | |
51 | clojure.asm.ClassWriter ClassWriter} class is necessary. Indeed, | |
52 | in order to generate a class, one must just call its visit<i>Xxx</i> | |
53 | methods with the appropriate arguments to generate the desired fields | |
54 | and methods. See the "helloworld" example in the ASM distribution for | |
55 | more details about class generation. | |
56 | ||
57 | <p> | |
58 | In order to modify existing classes, one must use a {@link | |
59 | clojure.asm.ClassReader ClassReader} class to analyze | |
60 | the original class, a class modifier, and a {@link clojure.asm.ClassWriter | |
61 | ClassWriter} to construct the modified class. The class modifier | |
62 | is just a {@link clojure.asm.ClassVisitor ClassVisitor} | |
63 | that delegates most of the work to another {@link clojure.asm.ClassVisitor | |
64 | ClassVisitor}, but that sometimes changes some parameter values, | |
65 | or call additional methods, in order to implement the desired | |
66 | modification process. In order to make it easier to implement such | |
67 | class modifiers, the {@link clojure.asm.ClassVisitor | |
68 | ClassVisitor} and {@link clojure.asm.MethodVisitor MethodVisitor} | |
69 | classes delegate by default all the method calls they receive to an | |
70 | optional visitor. 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 45KB, 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> |