rlm@10
|
1 /***
|
rlm@10
|
2 * ASM: a very small and fast Java bytecode manipulation framework
|
rlm@10
|
3 * Copyright (c) 2000-2005 INRIA, France Telecom
|
rlm@10
|
4 * All rights reserved.
|
rlm@10
|
5 *
|
rlm@10
|
6 * Redistribution and use in source and binary forms, with or without
|
rlm@10
|
7 * modification, are permitted provided that the following conditions
|
rlm@10
|
8 * are met:
|
rlm@10
|
9 * 1. Redistributions of source code must retain the above copyright
|
rlm@10
|
10 * notice, this list of conditions and the following disclaimer.
|
rlm@10
|
11 * 2. Redistributions in binary form must reproduce the above copyright
|
rlm@10
|
12 * notice, this list of conditions and the following disclaimer in the
|
rlm@10
|
13 * documentation and/or other materials provided with the distribution.
|
rlm@10
|
14 * 3. Neither the name of the copyright holders nor the names of its
|
rlm@10
|
15 * contributors may be used to endorse or promote products derived from
|
rlm@10
|
16 * this software without specific prior written permission.
|
rlm@10
|
17 *
|
rlm@10
|
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
rlm@10
|
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
rlm@10
|
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
rlm@10
|
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
rlm@10
|
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
rlm@10
|
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
rlm@10
|
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
rlm@10
|
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
rlm@10
|
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
rlm@10
|
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
rlm@10
|
28 * THE POSSIBILITY OF SUCH DAMAGE.
|
rlm@10
|
29 */
|
rlm@10
|
30 package clojure.asm;
|
rlm@10
|
31
|
rlm@10
|
32 /**
|
rlm@10
|
33 * A {@link MethodVisitor} that generates methods in bytecode form. Each visit
|
rlm@10
|
34 * method of this class appends the bytecode corresponding to the visited
|
rlm@10
|
35 * instruction to a byte vector, in the order these methods are called.
|
rlm@10
|
36 *
|
rlm@10
|
37 * @author Eric Bruneton
|
rlm@10
|
38 * @author Eugene Kuleshov
|
rlm@10
|
39 */
|
rlm@10
|
40 class MethodWriter implements MethodVisitor{
|
rlm@10
|
41
|
rlm@10
|
42 /**
|
rlm@10
|
43 * Pseudo access flag used to denote constructors.
|
rlm@10
|
44 */
|
rlm@10
|
45 final static int ACC_CONSTRUCTOR = 262144;
|
rlm@10
|
46
|
rlm@10
|
47 /**
|
rlm@10
|
48 * Frame has exactly the same locals as the previous stack map frame and
|
rlm@10
|
49 * number of stack items is zero.
|
rlm@10
|
50 */
|
rlm@10
|
51 final static int SAME_FRAME = 0; // to 63 (0-3f)
|
rlm@10
|
52
|
rlm@10
|
53 /**
|
rlm@10
|
54 * Frame has exactly the same locals as the previous stack map frame and
|
rlm@10
|
55 * number of stack items is 1
|
rlm@10
|
56 */
|
rlm@10
|
57 final static int SAME_LOCALS_1_STACK_ITEM_FRAME = 64; // to 127 (40-7f)
|
rlm@10
|
58
|
rlm@10
|
59 /**
|
rlm@10
|
60 * Reserved for future use
|
rlm@10
|
61 */
|
rlm@10
|
62 final static int RESERVED = 128;
|
rlm@10
|
63
|
rlm@10
|
64 /**
|
rlm@10
|
65 * Frame has exactly the same locals as the previous stack map frame and
|
rlm@10
|
66 * number of stack items is 1. Offset is bigger then 63;
|
rlm@10
|
67 */
|
rlm@10
|
68 final static int SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED = 247; // f7
|
rlm@10
|
69
|
rlm@10
|
70 /**
|
rlm@10
|
71 * Frame where current locals are the same as the locals in the previous
|
rlm@10
|
72 * frame, except that the k last locals are absent. The value of k is given
|
rlm@10
|
73 * by the formula 251-frame_type.
|
rlm@10
|
74 */
|
rlm@10
|
75 final static int CHOP_FRAME = 248; // to 250 (f8-fA)
|
rlm@10
|
76
|
rlm@10
|
77 /**
|
rlm@10
|
78 * Frame has exactly the same locals as the previous stack map frame and
|
rlm@10
|
79 * number of stack items is zero. Offset is bigger then 63;
|
rlm@10
|
80 */
|
rlm@10
|
81 final static int SAME_FRAME_EXTENDED = 251; // fb
|
rlm@10
|
82
|
rlm@10
|
83 /**
|
rlm@10
|
84 * Frame where current locals are the same as the locals in the previous
|
rlm@10
|
85 * frame, except that k additional locals are defined. The value of k is
|
rlm@10
|
86 * given by the formula frame_type-251.
|
rlm@10
|
87 */
|
rlm@10
|
88 final static int APPEND_FRAME = 252; // to 254 // fc-fe
|
rlm@10
|
89
|
rlm@10
|
90 /**
|
rlm@10
|
91 * Full frame
|
rlm@10
|
92 */
|
rlm@10
|
93 final static int FULL_FRAME = 255; // ff
|
rlm@10
|
94
|
rlm@10
|
95 /**
|
rlm@10
|
96 * Indicates that the stack map frames must be recomputed from scratch. In
|
rlm@10
|
97 * this case the maximum stack size and number of local variables is also
|
rlm@10
|
98 * recomputed from scratch.
|
rlm@10
|
99 *
|
rlm@10
|
100 * @see #compute
|
rlm@10
|
101 */
|
rlm@10
|
102 private final static int FRAMES = 0;
|
rlm@10
|
103
|
rlm@10
|
104 /**
|
rlm@10
|
105 * Indicates that the maximum stack size and number of local variables must
|
rlm@10
|
106 * be automatically computed.
|
rlm@10
|
107 *
|
rlm@10
|
108 * @see #compute
|
rlm@10
|
109 */
|
rlm@10
|
110 private final static int MAXS = 1;
|
rlm@10
|
111
|
rlm@10
|
112 /**
|
rlm@10
|
113 * Indicates that nothing must be automatically computed.
|
rlm@10
|
114 *
|
rlm@10
|
115 * @see #compute
|
rlm@10
|
116 */
|
rlm@10
|
117 private final static int NOTHING = 2;
|
rlm@10
|
118
|
rlm@10
|
119 /**
|
rlm@10
|
120 * Next method writer (see {@link ClassWriter#firstMethod firstMethod}).
|
rlm@10
|
121 */
|
rlm@10
|
122 MethodWriter next;
|
rlm@10
|
123
|
rlm@10
|
124 /**
|
rlm@10
|
125 * The class writer to which this method must be added.
|
rlm@10
|
126 */
|
rlm@10
|
127 ClassWriter cw;
|
rlm@10
|
128
|
rlm@10
|
129 /**
|
rlm@10
|
130 * Access flags of this method.
|
rlm@10
|
131 */
|
rlm@10
|
132 private int access;
|
rlm@10
|
133
|
rlm@10
|
134 /**
|
rlm@10
|
135 * The index of the constant pool item that contains the name of this
|
rlm@10
|
136 * method.
|
rlm@10
|
137 */
|
rlm@10
|
138 private int name;
|
rlm@10
|
139
|
rlm@10
|
140 /**
|
rlm@10
|
141 * The index of the constant pool item that contains the descriptor of this
|
rlm@10
|
142 * method.
|
rlm@10
|
143 */
|
rlm@10
|
144 private int desc;
|
rlm@10
|
145
|
rlm@10
|
146 /**
|
rlm@10
|
147 * The descriptor of this method.
|
rlm@10
|
148 */
|
rlm@10
|
149 private String descriptor;
|
rlm@10
|
150
|
rlm@10
|
151 /**
|
rlm@10
|
152 * The signature of this method.
|
rlm@10
|
153 */
|
rlm@10
|
154 String signature;
|
rlm@10
|
155
|
rlm@10
|
156 /**
|
rlm@10
|
157 * If not zero, indicates that the code of this method must be copied from
|
rlm@10
|
158 * the ClassReader associated to this writer in <code>cw.cr</code>. More
|
rlm@10
|
159 * precisely, this field gives the index of the first byte to copied from
|
rlm@10
|
160 * <code>cw.cr.b</code>.
|
rlm@10
|
161 */
|
rlm@10
|
162 int classReaderOffset;
|
rlm@10
|
163
|
rlm@10
|
164 /**
|
rlm@10
|
165 * If not zero, indicates that the code of this method must be copied from
|
rlm@10
|
166 * the ClassReader associated to this writer in <code>cw.cr</code>. More
|
rlm@10
|
167 * precisely, this field gives the number of bytes to copied from
|
rlm@10
|
168 * <code>cw.cr.b</code>.
|
rlm@10
|
169 */
|
rlm@10
|
170 int classReaderLength;
|
rlm@10
|
171
|
rlm@10
|
172 /**
|
rlm@10
|
173 * Number of exceptions that can be thrown by this method.
|
rlm@10
|
174 */
|
rlm@10
|
175 int exceptionCount;
|
rlm@10
|
176
|
rlm@10
|
177 /**
|
rlm@10
|
178 * The exceptions that can be thrown by this method. More precisely, this
|
rlm@10
|
179 * array contains the indexes of the constant pool items that contain the
|
rlm@10
|
180 * internal names of these exception classes.
|
rlm@10
|
181 */
|
rlm@10
|
182 int[] exceptions;
|
rlm@10
|
183
|
rlm@10
|
184 /**
|
rlm@10
|
185 * The annotation default attribute of this method. May be <tt>null</tt>.
|
rlm@10
|
186 */
|
rlm@10
|
187 private ByteVector annd;
|
rlm@10
|
188
|
rlm@10
|
189 /**
|
rlm@10
|
190 * The runtime visible annotations of this method. May be <tt>null</tt>.
|
rlm@10
|
191 */
|
rlm@10
|
192 private AnnotationWriter anns;
|
rlm@10
|
193
|
rlm@10
|
194 /**
|
rlm@10
|
195 * The runtime invisible annotations of this method. May be <tt>null</tt>.
|
rlm@10
|
196 */
|
rlm@10
|
197 private AnnotationWriter ianns;
|
rlm@10
|
198
|
rlm@10
|
199 /**
|
rlm@10
|
200 * The runtime visible parameter annotations of this method. May be
|
rlm@10
|
201 * <tt>null</tt>.
|
rlm@10
|
202 */
|
rlm@10
|
203 private AnnotationWriter[] panns;
|
rlm@10
|
204
|
rlm@10
|
205 /**
|
rlm@10
|
206 * The runtime invisible parameter annotations of this method. May be
|
rlm@10
|
207 * <tt>null</tt>.
|
rlm@10
|
208 */
|
rlm@10
|
209 private AnnotationWriter[] ipanns;
|
rlm@10
|
210
|
rlm@10
|
211 /**
|
rlm@10
|
212 * The non standard attributes of the method.
|
rlm@10
|
213 */
|
rlm@10
|
214 private Attribute attrs;
|
rlm@10
|
215
|
rlm@10
|
216 /**
|
rlm@10
|
217 * The bytecode of this method.
|
rlm@10
|
218 */
|
rlm@10
|
219 private ByteVector code = new ByteVector();
|
rlm@10
|
220
|
rlm@10
|
221 /**
|
rlm@10
|
222 * Maximum stack size of this method.
|
rlm@10
|
223 */
|
rlm@10
|
224 private int maxStack;
|
rlm@10
|
225
|
rlm@10
|
226 /**
|
rlm@10
|
227 * Maximum number of local variables for this method.
|
rlm@10
|
228 */
|
rlm@10
|
229 private int maxLocals;
|
rlm@10
|
230
|
rlm@10
|
231 /**
|
rlm@10
|
232 * Number of stack map frames in the StackMapTable attribute.
|
rlm@10
|
233 */
|
rlm@10
|
234 private int frameCount;
|
rlm@10
|
235
|
rlm@10
|
236 /**
|
rlm@10
|
237 * The StackMapTable attribute.
|
rlm@10
|
238 */
|
rlm@10
|
239 private ByteVector stackMap;
|
rlm@10
|
240
|
rlm@10
|
241 /**
|
rlm@10
|
242 * The offset of the last frame that was written in the StackMapTable
|
rlm@10
|
243 * attribute.
|
rlm@10
|
244 */
|
rlm@10
|
245 private int previousFrameOffset;
|
rlm@10
|
246
|
rlm@10
|
247 /**
|
rlm@10
|
248 * The last frame that was written in the StackMapTable attribute.
|
rlm@10
|
249 *
|
rlm@10
|
250 * @see #frame
|
rlm@10
|
251 */
|
rlm@10
|
252 private int[] previousFrame;
|
rlm@10
|
253
|
rlm@10
|
254 /**
|
rlm@10
|
255 * Index of the next element to be added in {@link #frame}.
|
rlm@10
|
256 */
|
rlm@10
|
257 private int frameIndex;
|
rlm@10
|
258
|
rlm@10
|
259 /**
|
rlm@10
|
260 * The current stack map frame. The first element contains the offset of the
|
rlm@10
|
261 * instruction to which the frame corresponds, the second element is the
|
rlm@10
|
262 * number of locals and the third one is the number of stack elements. The
|
rlm@10
|
263 * local variables start at index 3 and are followed by the operand stack
|
rlm@10
|
264 * values. In summary frame[0] = offset, frame[1] = nLocal, frame[2] =
|
rlm@10
|
265 * nStack, frame[3] = nLocal. All types are encoded as integers, with the
|
rlm@10
|
266 * same format as the one used in {@link Label}, but limited to BASE types.
|
rlm@10
|
267 */
|
rlm@10
|
268 private int[] frame;
|
rlm@10
|
269
|
rlm@10
|
270 /**
|
rlm@10
|
271 * Number of elements in the exception handler list.
|
rlm@10
|
272 */
|
rlm@10
|
273 private int handlerCount;
|
rlm@10
|
274
|
rlm@10
|
275 /**
|
rlm@10
|
276 * The first element in the exception handler list.
|
rlm@10
|
277 */
|
rlm@10
|
278 private Handler firstHandler;
|
rlm@10
|
279
|
rlm@10
|
280 /**
|
rlm@10
|
281 * The last element in the exception handler list.
|
rlm@10
|
282 */
|
rlm@10
|
283 private Handler lastHandler;
|
rlm@10
|
284
|
rlm@10
|
285 /**
|
rlm@10
|
286 * Number of entries in the LocalVariableTable attribute.
|
rlm@10
|
287 */
|
rlm@10
|
288 private int localVarCount;
|
rlm@10
|
289
|
rlm@10
|
290 /**
|
rlm@10
|
291 * The LocalVariableTable attribute.
|
rlm@10
|
292 */
|
rlm@10
|
293 private ByteVector localVar;
|
rlm@10
|
294
|
rlm@10
|
295 /**
|
rlm@10
|
296 * Number of entries in the LocalVariableTypeTable attribute.
|
rlm@10
|
297 */
|
rlm@10
|
298 private int localVarTypeCount;
|
rlm@10
|
299
|
rlm@10
|
300 /**
|
rlm@10
|
301 * The LocalVariableTypeTable attribute.
|
rlm@10
|
302 */
|
rlm@10
|
303 private ByteVector localVarType;
|
rlm@10
|
304
|
rlm@10
|
305 /**
|
rlm@10
|
306 * Number of entries in the LineNumberTable attribute.
|
rlm@10
|
307 */
|
rlm@10
|
308 private int lineNumberCount;
|
rlm@10
|
309
|
rlm@10
|
310 /**
|
rlm@10
|
311 * The LineNumberTable attribute.
|
rlm@10
|
312 */
|
rlm@10
|
313 private ByteVector lineNumber;
|
rlm@10
|
314
|
rlm@10
|
315 /**
|
rlm@10
|
316 * The non standard attributes of the method's code.
|
rlm@10
|
317 */
|
rlm@10
|
318 private Attribute cattrs;
|
rlm@10
|
319
|
rlm@10
|
320 /**
|
rlm@10
|
321 * Indicates if some jump instructions are too small and need to be resized.
|
rlm@10
|
322 */
|
rlm@10
|
323 private boolean resize;
|
rlm@10
|
324
|
rlm@10
|
325 /**
|
rlm@10
|
326 * Indicates if the instructions contain at least one JSR instruction.
|
rlm@10
|
327 */
|
rlm@10
|
328 private boolean jsr;
|
rlm@10
|
329
|
rlm@10
|
330 // ------------------------------------------------------------------------
|
rlm@10
|
331
|
rlm@10
|
332 /*
|
rlm@10
|
333 * Fields for the control flow graph analysis algorithm (used to compute the
|
rlm@10
|
334 * maximum stack size). A control flow graph contains one node per "basic
|
rlm@10
|
335 * block", and one edge per "jump" from one basic block to another. Each
|
rlm@10
|
336 * node (i.e., each basic block) is represented by the Label object that
|
rlm@10
|
337 * corresponds to the first instruction of this basic block. Each node also
|
rlm@10
|
338 * stores the list of its successors in the graph, as a linked list of Edge
|
rlm@10
|
339 * objects.
|
rlm@10
|
340 */
|
rlm@10
|
341
|
rlm@10
|
342 /**
|
rlm@10
|
343 * Indicates what must be automatically computed.
|
rlm@10
|
344 *
|
rlm@10
|
345 * @see FRAMES
|
rlm@10
|
346 * @see MAXS
|
rlm@10
|
347 * @see NOTHING
|
rlm@10
|
348 */
|
rlm@10
|
349 private int compute;
|
rlm@10
|
350
|
rlm@10
|
351 /**
|
rlm@10
|
352 * A list of labels. This list is the list of basic blocks in the method,
|
rlm@10
|
353 * i.e. a list of Label objects linked to each other by their
|
rlm@10
|
354 * {@link Label#successor} field, in the order they are visited by
|
rlm@10
|
355 * {@link visitLabel}, and starting with the first basic block.
|
rlm@10
|
356 */
|
rlm@10
|
357 private Label labels;
|
rlm@10
|
358
|
rlm@10
|
359 /**
|
rlm@10
|
360 * The previous basic block.
|
rlm@10
|
361 */
|
rlm@10
|
362 private Label previousBlock;
|
rlm@10
|
363
|
rlm@10
|
364 /**
|
rlm@10
|
365 * The current basic block.
|
rlm@10
|
366 */
|
rlm@10
|
367 private Label currentBlock;
|
rlm@10
|
368
|
rlm@10
|
369 /**
|
rlm@10
|
370 * The (relative) stack size after the last visited instruction. This size
|
rlm@10
|
371 * is relative to the beginning of the current basic block, i.e., the true
|
rlm@10
|
372 * stack size after the last visited instruction is equal to the
|
rlm@10
|
373 * {@link Label#inputStackTop beginStackSize} of the current basic block
|
rlm@10
|
374 * plus <tt>stackSize</tt>.
|
rlm@10
|
375 */
|
rlm@10
|
376 private int stackSize;
|
rlm@10
|
377
|
rlm@10
|
378 /**
|
rlm@10
|
379 * The (relative) maximum stack size after the last visited instruction.
|
rlm@10
|
380 * This size is relative to the beginning of the current basic block, i.e.,
|
rlm@10
|
381 * the true maximum stack size after the last visited instruction is equal
|
rlm@10
|
382 * to the {@link Label#inputStackTop beginStackSize} of the current basic
|
rlm@10
|
383 * block plus <tt>stackSize</tt>.
|
rlm@10
|
384 */
|
rlm@10
|
385 private int maxStackSize;
|
rlm@10
|
386
|
rlm@10
|
387 // ------------------------------------------------------------------------
|
rlm@10
|
388 // Constructor
|
rlm@10
|
389 // ------------------------------------------------------------------------
|
rlm@10
|
390
|
rlm@10
|
391 /**
|
rlm@10
|
392 * Constructs a new {@link MethodWriter}.
|
rlm@10
|
393 *
|
rlm@10
|
394 * @param cw the class writer in which the method must be added.
|
rlm@10
|
395 * @param access the method's access flags (see {@link Opcodes}).
|
rlm@10
|
396 * @param name the method's name.
|
rlm@10
|
397 * @param desc the method's descriptor (see {@link Type}).
|
rlm@10
|
398 * @param signature the method's signature. May be <tt>null</tt>.
|
rlm@10
|
399 * @param exceptions the internal names of the method's exceptions. May be
|
rlm@10
|
400 * <tt>null</tt>.
|
rlm@10
|
401 * @param computeMaxs <tt>true</tt> if the maximum stack size and number
|
rlm@10
|
402 * of local variables must be automatically computed.
|
rlm@10
|
403 * @param computeFrames <tt>true</tt> if the stack map tables must be
|
rlm@10
|
404 * recomputed from scratch.
|
rlm@10
|
405 */
|
rlm@10
|
406 MethodWriter(
|
rlm@10
|
407 final ClassWriter cw,
|
rlm@10
|
408 final int access,
|
rlm@10
|
409 final String name,
|
rlm@10
|
410 final String desc,
|
rlm@10
|
411 final String signature,
|
rlm@10
|
412 final String[] exceptions,
|
rlm@10
|
413 final boolean computeMaxs,
|
rlm@10
|
414 final boolean computeFrames){
|
rlm@10
|
415 if(cw.firstMethod == null)
|
rlm@10
|
416 {
|
rlm@10
|
417 cw.firstMethod = this;
|
rlm@10
|
418 }
|
rlm@10
|
419 else
|
rlm@10
|
420 {
|
rlm@10
|
421 cw.lastMethod.next = this;
|
rlm@10
|
422 }
|
rlm@10
|
423 cw.lastMethod = this;
|
rlm@10
|
424 this.cw = cw;
|
rlm@10
|
425 this.access = access;
|
rlm@10
|
426 this.name = cw.newUTF8(name);
|
rlm@10
|
427 this.desc = cw.newUTF8(desc);
|
rlm@10
|
428 this.descriptor = desc;
|
rlm@10
|
429 this.signature = signature;
|
rlm@10
|
430 if(exceptions != null && exceptions.length > 0)
|
rlm@10
|
431 {
|
rlm@10
|
432 exceptionCount = exceptions.length;
|
rlm@10
|
433 this.exceptions = new int[exceptionCount];
|
rlm@10
|
434 for(int i = 0; i < exceptionCount; ++i)
|
rlm@10
|
435 {
|
rlm@10
|
436 this.exceptions[i] = cw.newClass(exceptions[i]);
|
rlm@10
|
437 }
|
rlm@10
|
438 }
|
rlm@10
|
439 this.compute = computeFrames ? FRAMES : (computeMaxs ? MAXS : NOTHING);
|
rlm@10
|
440 if(computeMaxs || computeFrames)
|
rlm@10
|
441 {
|
rlm@10
|
442 if(computeFrames && name.equals("<init>"))
|
rlm@10
|
443 {
|
rlm@10
|
444 this.access |= ACC_CONSTRUCTOR;
|
rlm@10
|
445 }
|
rlm@10
|
446 // updates maxLocals
|
rlm@10
|
447 int size = getArgumentsAndReturnSizes(descriptor) >> 2;
|
rlm@10
|
448 if((access & Opcodes.ACC_STATIC) != 0)
|
rlm@10
|
449 {
|
rlm@10
|
450 --size;
|
rlm@10
|
451 }
|
rlm@10
|
452 maxLocals = size;
|
rlm@10
|
453 // creates and visits the label for the first basic block
|
rlm@10
|
454 labels = new Label();
|
rlm@10
|
455 labels.status |= Label.PUSHED;
|
rlm@10
|
456 visitLabel(labels);
|
rlm@10
|
457 }
|
rlm@10
|
458 }
|
rlm@10
|
459
|
rlm@10
|
460 // ------------------------------------------------------------------------
|
rlm@10
|
461 // Implementation of the MethodVisitor interface
|
rlm@10
|
462 // ------------------------------------------------------------------------
|
rlm@10
|
463
|
rlm@10
|
464 public AnnotationVisitor visitAnnotationDefault(){
|
rlm@10
|
465 annd = new ByteVector();
|
rlm@10
|
466 return new AnnotationWriter(cw, false, annd, null, 0);
|
rlm@10
|
467 }
|
rlm@10
|
468
|
rlm@10
|
469 public AnnotationVisitor visitAnnotation(
|
rlm@10
|
470 final String desc,
|
rlm@10
|
471 final boolean visible){
|
rlm@10
|
472 ByteVector bv = new ByteVector();
|
rlm@10
|
473 // write type, and reserve space for values count
|
rlm@10
|
474 bv.putShort(cw.newUTF8(desc)).putShort(0);
|
rlm@10
|
475 AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
|
rlm@10
|
476 if(visible)
|
rlm@10
|
477 {
|
rlm@10
|
478 aw.next = anns;
|
rlm@10
|
479 anns = aw;
|
rlm@10
|
480 }
|
rlm@10
|
481 else
|
rlm@10
|
482 {
|
rlm@10
|
483 aw.next = ianns;
|
rlm@10
|
484 ianns = aw;
|
rlm@10
|
485 }
|
rlm@10
|
486 return aw;
|
rlm@10
|
487 }
|
rlm@10
|
488
|
rlm@10
|
489 public AnnotationVisitor visitParameterAnnotation(
|
rlm@10
|
490 final int parameter,
|
rlm@10
|
491 final String desc,
|
rlm@10
|
492 final boolean visible){
|
rlm@10
|
493 ByteVector bv = new ByteVector();
|
rlm@10
|
494 // write type, and reserve space for values count
|
rlm@10
|
495 bv.putShort(cw.newUTF8(desc)).putShort(0);
|
rlm@10
|
496 AnnotationWriter aw = new AnnotationWriter(cw, true, bv, bv, 2);
|
rlm@10
|
497 if(visible)
|
rlm@10
|
498 {
|
rlm@10
|
499 if(panns == null)
|
rlm@10
|
500 {
|
rlm@10
|
501 panns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
|
rlm@10
|
502 }
|
rlm@10
|
503 aw.next = panns[parameter];
|
rlm@10
|
504 panns[parameter] = aw;
|
rlm@10
|
505 }
|
rlm@10
|
506 else
|
rlm@10
|
507 {
|
rlm@10
|
508 if(ipanns == null)
|
rlm@10
|
509 {
|
rlm@10
|
510 ipanns = new AnnotationWriter[Type.getArgumentTypes(descriptor).length];
|
rlm@10
|
511 }
|
rlm@10
|
512 aw.next = ipanns[parameter];
|
rlm@10
|
513 ipanns[parameter] = aw;
|
rlm@10
|
514 }
|
rlm@10
|
515 return aw;
|
rlm@10
|
516 }
|
rlm@10
|
517
|
rlm@10
|
518 public void visitAttribute(final Attribute attr){
|
rlm@10
|
519 if(attr.isCodeAttribute())
|
rlm@10
|
520 {
|
rlm@10
|
521 attr.next = cattrs;
|
rlm@10
|
522 cattrs = attr;
|
rlm@10
|
523 }
|
rlm@10
|
524 else
|
rlm@10
|
525 {
|
rlm@10
|
526 attr.next = attrs;
|
rlm@10
|
527 attrs = attr;
|
rlm@10
|
528 }
|
rlm@10
|
529 }
|
rlm@10
|
530
|
rlm@10
|
531 public void visitCode(){
|
rlm@10
|
532 }
|
rlm@10
|
533
|
rlm@10
|
534 public void visitFrame(
|
rlm@10
|
535 final int type,
|
rlm@10
|
536 final int nLocal,
|
rlm@10
|
537 final Object[] local,
|
rlm@10
|
538 final int nStack,
|
rlm@10
|
539 final Object[] stack){
|
rlm@10
|
540 if(compute == FRAMES)
|
rlm@10
|
541 {
|
rlm@10
|
542 return;
|
rlm@10
|
543 }
|
rlm@10
|
544
|
rlm@10
|
545 if(type == Opcodes.F_NEW)
|
rlm@10
|
546 {
|
rlm@10
|
547 startFrame(code.length, nLocal, nStack);
|
rlm@10
|
548 for(int i = 0; i < nLocal; ++i)
|
rlm@10
|
549 {
|
rlm@10
|
550 if(local[i] instanceof String)
|
rlm@10
|
551 {
|
rlm@10
|
552 frame[frameIndex++] = Frame.OBJECT
|
rlm@10
|
553 | cw.addType((String) local[i]);
|
rlm@10
|
554 }
|
rlm@10
|
555 else if(local[i] instanceof Integer)
|
rlm@10
|
556 {
|
rlm@10
|
557 frame[frameIndex++] = ((Integer) local[i]).intValue();
|
rlm@10
|
558 }
|
rlm@10
|
559 else
|
rlm@10
|
560 {
|
rlm@10
|
561 frame[frameIndex++] = Frame.UNINITIALIZED
|
rlm@10
|
562 | cw.addUninitializedType("",
|
rlm@10
|
563 ((Label) local[i]).position);
|
rlm@10
|
564 }
|
rlm@10
|
565 }
|
rlm@10
|
566 for(int i = 0; i < nStack; ++i)
|
rlm@10
|
567 {
|
rlm@10
|
568 if(stack[i] instanceof String)
|
rlm@10
|
569 {
|
rlm@10
|
570 frame[frameIndex++] = Frame.OBJECT
|
rlm@10
|
571 | cw.addType((String) stack[i]);
|
rlm@10
|
572 }
|
rlm@10
|
573 else if(stack[i] instanceof Integer)
|
rlm@10
|
574 {
|
rlm@10
|
575 frame[frameIndex++] = ((Integer) stack[i]).intValue();
|
rlm@10
|
576 }
|
rlm@10
|
577 else
|
rlm@10
|
578 {
|
rlm@10
|
579 frame[frameIndex++] = Frame.UNINITIALIZED
|
rlm@10
|
580 | cw.addUninitializedType("",
|
rlm@10
|
581 ((Label) stack[i]).position);
|
rlm@10
|
582 }
|
rlm@10
|
583 }
|
rlm@10
|
584 endFrame();
|
rlm@10
|
585 }
|
rlm@10
|
586 else
|
rlm@10
|
587 {
|
rlm@10
|
588 int delta;
|
rlm@10
|
589 if(stackMap == null)
|
rlm@10
|
590 {
|
rlm@10
|
591 stackMap = new ByteVector();
|
rlm@10
|
592 delta = code.length;
|
rlm@10
|
593 }
|
rlm@10
|
594 else
|
rlm@10
|
595 {
|
rlm@10
|
596 delta = code.length - previousFrameOffset - 1;
|
rlm@10
|
597 }
|
rlm@10
|
598
|
rlm@10
|
599 switch(type)
|
rlm@10
|
600 {
|
rlm@10
|
601 case Opcodes.F_FULL:
|
rlm@10
|
602 stackMap.putByte(FULL_FRAME)
|
rlm@10
|
603 .putShort(delta)
|
rlm@10
|
604 .putShort(nLocal);
|
rlm@10
|
605 for(int i = 0; i < nLocal; ++i)
|
rlm@10
|
606 {
|
rlm@10
|
607 writeFrameType(local[i]);
|
rlm@10
|
608 }
|
rlm@10
|
609 stackMap.putShort(nStack);
|
rlm@10
|
610 for(int i = 0; i < nStack; ++i)
|
rlm@10
|
611 {
|
rlm@10
|
612 writeFrameType(stack[i]);
|
rlm@10
|
613 }
|
rlm@10
|
614 break;
|
rlm@10
|
615 case Opcodes.F_APPEND:
|
rlm@10
|
616 stackMap.putByte(SAME_FRAME_EXTENDED + nLocal)
|
rlm@10
|
617 .putShort(delta);
|
rlm@10
|
618 for(int i = 0; i < nLocal; ++i)
|
rlm@10
|
619 {
|
rlm@10
|
620 writeFrameType(local[i]);
|
rlm@10
|
621 }
|
rlm@10
|
622 break;
|
rlm@10
|
623 case Opcodes.F_CHOP:
|
rlm@10
|
624 stackMap.putByte(SAME_FRAME_EXTENDED - nLocal)
|
rlm@10
|
625 .putShort(delta);
|
rlm@10
|
626 break;
|
rlm@10
|
627 case Opcodes.F_SAME:
|
rlm@10
|
628 if(delta < 64)
|
rlm@10
|
629 {
|
rlm@10
|
630 stackMap.putByte(delta);
|
rlm@10
|
631 }
|
rlm@10
|
632 else
|
rlm@10
|
633 {
|
rlm@10
|
634 stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
|
rlm@10
|
635 }
|
rlm@10
|
636 break;
|
rlm@10
|
637 case Opcodes.F_SAME1:
|
rlm@10
|
638 if(delta < 64)
|
rlm@10
|
639 {
|
rlm@10
|
640 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
|
rlm@10
|
641 }
|
rlm@10
|
642 else
|
rlm@10
|
643 {
|
rlm@10
|
644 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
|
rlm@10
|
645 .putShort(delta);
|
rlm@10
|
646 }
|
rlm@10
|
647 writeFrameType(stack[0]);
|
rlm@10
|
648 break;
|
rlm@10
|
649 }
|
rlm@10
|
650
|
rlm@10
|
651 previousFrameOffset = code.length;
|
rlm@10
|
652 ++frameCount;
|
rlm@10
|
653 }
|
rlm@10
|
654 }
|
rlm@10
|
655
|
rlm@10
|
656 public void visitInsn(final int opcode){
|
rlm@10
|
657 // adds the instruction to the bytecode of the method
|
rlm@10
|
658 code.putByte(opcode);
|
rlm@10
|
659 // update currentBlock
|
rlm@10
|
660 // Label currentBlock = this.currentBlock;
|
rlm@10
|
661 if(currentBlock != null)
|
rlm@10
|
662 {
|
rlm@10
|
663 if(compute == FRAMES)
|
rlm@10
|
664 {
|
rlm@10
|
665 currentBlock.frame.execute(opcode, 0, null, null);
|
rlm@10
|
666 }
|
rlm@10
|
667 else
|
rlm@10
|
668 {
|
rlm@10
|
669 // updates current and max stack sizes
|
rlm@10
|
670 int size = stackSize + Frame.SIZE[opcode];
|
rlm@10
|
671 if(size > maxStackSize)
|
rlm@10
|
672 {
|
rlm@10
|
673 maxStackSize = size;
|
rlm@10
|
674 }
|
rlm@10
|
675 stackSize = size;
|
rlm@10
|
676 }
|
rlm@10
|
677 // if opcode == ATHROW or xRETURN, ends current block (no successor)
|
rlm@10
|
678 if((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)
|
rlm@10
|
679 || opcode == Opcodes.ATHROW)
|
rlm@10
|
680 {
|
rlm@10
|
681 noSuccessor();
|
rlm@10
|
682 }
|
rlm@10
|
683 }
|
rlm@10
|
684 }
|
rlm@10
|
685
|
rlm@10
|
686 public void visitIntInsn(final int opcode, final int operand){
|
rlm@10
|
687 // Label currentBlock = this.currentBlock;
|
rlm@10
|
688 if(currentBlock != null)
|
rlm@10
|
689 {
|
rlm@10
|
690 if(compute == FRAMES)
|
rlm@10
|
691 {
|
rlm@10
|
692 currentBlock.frame.execute(opcode, operand, null, null);
|
rlm@10
|
693 }
|
rlm@10
|
694 else if(opcode != Opcodes.NEWARRAY)
|
rlm@10
|
695 {
|
rlm@10
|
696 // updates current and max stack sizes only for NEWARRAY
|
rlm@10
|
697 // (stack size variation = 0 for BIPUSH or SIPUSH)
|
rlm@10
|
698 int size = stackSize + 1;
|
rlm@10
|
699 if(size > maxStackSize)
|
rlm@10
|
700 {
|
rlm@10
|
701 maxStackSize = size;
|
rlm@10
|
702 }
|
rlm@10
|
703 stackSize = size;
|
rlm@10
|
704 }
|
rlm@10
|
705 }
|
rlm@10
|
706 // adds the instruction to the bytecode of the method
|
rlm@10
|
707 if(opcode == Opcodes.SIPUSH)
|
rlm@10
|
708 {
|
rlm@10
|
709 code.put12(opcode, operand);
|
rlm@10
|
710 }
|
rlm@10
|
711 else
|
rlm@10
|
712 { // BIPUSH or NEWARRAY
|
rlm@10
|
713 code.put11(opcode, operand);
|
rlm@10
|
714 }
|
rlm@10
|
715 }
|
rlm@10
|
716
|
rlm@10
|
717 public void visitVarInsn(final int opcode, final int var){
|
rlm@10
|
718 // Label currentBlock = this.currentBlock;
|
rlm@10
|
719 if(currentBlock != null)
|
rlm@10
|
720 {
|
rlm@10
|
721 if(compute == FRAMES)
|
rlm@10
|
722 {
|
rlm@10
|
723 currentBlock.frame.execute(opcode, var, null, null);
|
rlm@10
|
724 }
|
rlm@10
|
725 else
|
rlm@10
|
726 {
|
rlm@10
|
727 // updates current and max stack sizes
|
rlm@10
|
728 if(opcode == Opcodes.RET)
|
rlm@10
|
729 {
|
rlm@10
|
730 // no stack change, but end of current block (no successor)
|
rlm@10
|
731 currentBlock.status |= Label.RET;
|
rlm@10
|
732 // save 'stackSize' here for future use
|
rlm@10
|
733 // (see {@link #findSubroutineSuccessors})
|
rlm@10
|
734 currentBlock.inputStackTop = stackSize;
|
rlm@10
|
735 noSuccessor();
|
rlm@10
|
736 }
|
rlm@10
|
737 else
|
rlm@10
|
738 { // xLOAD or xSTORE
|
rlm@10
|
739 int size = stackSize + Frame.SIZE[opcode];
|
rlm@10
|
740 if(size > maxStackSize)
|
rlm@10
|
741 {
|
rlm@10
|
742 maxStackSize = size;
|
rlm@10
|
743 }
|
rlm@10
|
744 stackSize = size;
|
rlm@10
|
745 }
|
rlm@10
|
746 }
|
rlm@10
|
747 }
|
rlm@10
|
748 if(compute != NOTHING)
|
rlm@10
|
749 {
|
rlm@10
|
750 // updates max locals
|
rlm@10
|
751 int n;
|
rlm@10
|
752 if(opcode == Opcodes.LLOAD || opcode == Opcodes.DLOAD
|
rlm@10
|
753 || opcode == Opcodes.LSTORE || opcode == Opcodes.DSTORE)
|
rlm@10
|
754 {
|
rlm@10
|
755 n = var + 2;
|
rlm@10
|
756 }
|
rlm@10
|
757 else
|
rlm@10
|
758 {
|
rlm@10
|
759 n = var + 1;
|
rlm@10
|
760 }
|
rlm@10
|
761 if(n > maxLocals)
|
rlm@10
|
762 {
|
rlm@10
|
763 maxLocals = n;
|
rlm@10
|
764 }
|
rlm@10
|
765 }
|
rlm@10
|
766 // adds the instruction to the bytecode of the method
|
rlm@10
|
767 if(var < 4 && opcode != Opcodes.RET)
|
rlm@10
|
768 {
|
rlm@10
|
769 int opt;
|
rlm@10
|
770 if(opcode < Opcodes.ISTORE)
|
rlm@10
|
771 {
|
rlm@10
|
772 /* ILOAD_0 */
|
rlm@10
|
773 opt = 26 + ((opcode - Opcodes.ILOAD) << 2) + var;
|
rlm@10
|
774 }
|
rlm@10
|
775 else
|
rlm@10
|
776 {
|
rlm@10
|
777 /* ISTORE_0 */
|
rlm@10
|
778 opt = 59 + ((opcode - Opcodes.ISTORE) << 2) + var;
|
rlm@10
|
779 }
|
rlm@10
|
780 code.putByte(opt);
|
rlm@10
|
781 }
|
rlm@10
|
782 else if(var >= 256)
|
rlm@10
|
783 {
|
rlm@10
|
784 code.putByte(196 /* WIDE */).put12(opcode, var);
|
rlm@10
|
785 }
|
rlm@10
|
786 else
|
rlm@10
|
787 {
|
rlm@10
|
788 code.put11(opcode, var);
|
rlm@10
|
789 }
|
rlm@10
|
790 if(opcode >= Opcodes.ISTORE && compute == FRAMES && handlerCount > 0)
|
rlm@10
|
791 {
|
rlm@10
|
792 visitLabel(new Label());
|
rlm@10
|
793 }
|
rlm@10
|
794 }
|
rlm@10
|
795
|
rlm@10
|
796 public void visitTypeInsn(final int opcode, final String desc){
|
rlm@10
|
797 Item i = cw.newClassItem(desc);
|
rlm@10
|
798 // Label currentBlock = this.currentBlock;
|
rlm@10
|
799 if(currentBlock != null)
|
rlm@10
|
800 {
|
rlm@10
|
801 if(compute == FRAMES)
|
rlm@10
|
802 {
|
rlm@10
|
803 currentBlock.frame.execute(opcode, code.length, cw, i);
|
rlm@10
|
804 }
|
rlm@10
|
805 else if(opcode == Opcodes.NEW)
|
rlm@10
|
806 {
|
rlm@10
|
807 // updates current and max stack sizes only if opcode == NEW
|
rlm@10
|
808 // (no stack change for ANEWARRAY, CHECKCAST, INSTANCEOF)
|
rlm@10
|
809 int size = stackSize + 1;
|
rlm@10
|
810 if(size > maxStackSize)
|
rlm@10
|
811 {
|
rlm@10
|
812 maxStackSize = size;
|
rlm@10
|
813 }
|
rlm@10
|
814 stackSize = size;
|
rlm@10
|
815 }
|
rlm@10
|
816 }
|
rlm@10
|
817 // adds the instruction to the bytecode of the method
|
rlm@10
|
818 code.put12(opcode, i.index);
|
rlm@10
|
819 }
|
rlm@10
|
820
|
rlm@10
|
821 public void visitFieldInsn(
|
rlm@10
|
822 final int opcode,
|
rlm@10
|
823 final String owner,
|
rlm@10
|
824 final String name,
|
rlm@10
|
825 final String desc){
|
rlm@10
|
826 Item i = cw.newFieldItem(owner, name, desc);
|
rlm@10
|
827 // Label currentBlock = this.currentBlock;
|
rlm@10
|
828 if(currentBlock != null)
|
rlm@10
|
829 {
|
rlm@10
|
830 if(compute == FRAMES)
|
rlm@10
|
831 {
|
rlm@10
|
832 currentBlock.frame.execute(opcode, 0, cw, i);
|
rlm@10
|
833 }
|
rlm@10
|
834 else
|
rlm@10
|
835 {
|
rlm@10
|
836 int size;
|
rlm@10
|
837 // computes the stack size variation
|
rlm@10
|
838 char c = desc.charAt(0);
|
rlm@10
|
839 switch(opcode)
|
rlm@10
|
840 {
|
rlm@10
|
841 case Opcodes.GETSTATIC:
|
rlm@10
|
842 size = stackSize + (c == 'D' || c == 'J' ? 2 : 1);
|
rlm@10
|
843 break;
|
rlm@10
|
844 case Opcodes.PUTSTATIC:
|
rlm@10
|
845 size = stackSize + (c == 'D' || c == 'J' ? -2 : -1);
|
rlm@10
|
846 break;
|
rlm@10
|
847 case Opcodes.GETFIELD:
|
rlm@10
|
848 size = stackSize + (c == 'D' || c == 'J' ? 1 : 0);
|
rlm@10
|
849 break;
|
rlm@10
|
850 // case Constants.PUTFIELD:
|
rlm@10
|
851 default:
|
rlm@10
|
852 size = stackSize + (c == 'D' || c == 'J' ? -3 : -2);
|
rlm@10
|
853 break;
|
rlm@10
|
854 }
|
rlm@10
|
855 // updates current and max stack sizes
|
rlm@10
|
856 if(size > maxStackSize)
|
rlm@10
|
857 {
|
rlm@10
|
858 maxStackSize = size;
|
rlm@10
|
859 }
|
rlm@10
|
860 stackSize = size;
|
rlm@10
|
861 }
|
rlm@10
|
862 }
|
rlm@10
|
863 // adds the instruction to the bytecode of the method
|
rlm@10
|
864 code.put12(opcode, i.index);
|
rlm@10
|
865 }
|
rlm@10
|
866
|
rlm@10
|
867 public void visitMethodInsn(
|
rlm@10
|
868 final int opcode,
|
rlm@10
|
869 final String owner,
|
rlm@10
|
870 final String name,
|
rlm@10
|
871 final String desc){
|
rlm@10
|
872 boolean itf = opcode == Opcodes.INVOKEINTERFACE;
|
rlm@10
|
873 Item i = cw.newMethodItem(owner, name, desc, itf);
|
rlm@10
|
874 int argSize = i.intVal;
|
rlm@10
|
875 // Label currentBlock = this.currentBlock;
|
rlm@10
|
876 if(currentBlock != null)
|
rlm@10
|
877 {
|
rlm@10
|
878 if(compute == FRAMES)
|
rlm@10
|
879 {
|
rlm@10
|
880 currentBlock.frame.execute(opcode, 0, cw, i);
|
rlm@10
|
881 }
|
rlm@10
|
882 else
|
rlm@10
|
883 {
|
rlm@10
|
884 /*
|
rlm@10
|
885 * computes the stack size variation. In order not to recompute
|
rlm@10
|
886 * several times this variation for the same Item, we use the
|
rlm@10
|
887 * intVal field of this item to store this variation, once it
|
rlm@10
|
888 * has been computed. More precisely this intVal field stores
|
rlm@10
|
889 * the sizes of the arguments and of the return value
|
rlm@10
|
890 * corresponding to desc.
|
rlm@10
|
891 */
|
rlm@10
|
892 if(argSize == 0)
|
rlm@10
|
893 {
|
rlm@10
|
894 // the above sizes have not been computed yet,
|
rlm@10
|
895 // so we compute them...
|
rlm@10
|
896 argSize = getArgumentsAndReturnSizes(desc);
|
rlm@10
|
897 // ... and we save them in order
|
rlm@10
|
898 // not to recompute them in the future
|
rlm@10
|
899 i.intVal = argSize;
|
rlm@10
|
900 }
|
rlm@10
|
901 int size;
|
rlm@10
|
902 if(opcode == Opcodes.INVOKESTATIC)
|
rlm@10
|
903 {
|
rlm@10
|
904 size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
|
rlm@10
|
905 }
|
rlm@10
|
906 else
|
rlm@10
|
907 {
|
rlm@10
|
908 size = stackSize - (argSize >> 2) + (argSize & 0x03);
|
rlm@10
|
909 }
|
rlm@10
|
910 // updates current and max stack sizes
|
rlm@10
|
911 if(size > maxStackSize)
|
rlm@10
|
912 {
|
rlm@10
|
913 maxStackSize = size;
|
rlm@10
|
914 }
|
rlm@10
|
915 stackSize = size;
|
rlm@10
|
916 }
|
rlm@10
|
917 }
|
rlm@10
|
918 // adds the instruction to the bytecode of the method
|
rlm@10
|
919 if(itf)
|
rlm@10
|
920 {
|
rlm@10
|
921 if(argSize == 0)
|
rlm@10
|
922 {
|
rlm@10
|
923 argSize = getArgumentsAndReturnSizes(desc);
|
rlm@10
|
924 i.intVal = argSize;
|
rlm@10
|
925 }
|
rlm@10
|
926 code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
|
rlm@10
|
927 }
|
rlm@10
|
928 else
|
rlm@10
|
929 {
|
rlm@10
|
930 code.put12(opcode, i.index);
|
rlm@10
|
931 }
|
rlm@10
|
932 }
|
rlm@10
|
933
|
rlm@10
|
934 public void visitJumpInsn(final int opcode, final Label label){
|
rlm@10
|
935 Label nextInsn = null;
|
rlm@10
|
936 // Label currentBlock = this.currentBlock;
|
rlm@10
|
937 if(currentBlock != null)
|
rlm@10
|
938 {
|
rlm@10
|
939 if(compute == FRAMES)
|
rlm@10
|
940 {
|
rlm@10
|
941 currentBlock.frame.execute(opcode, 0, null, null);
|
rlm@10
|
942 // 'label' is the target of a jump instruction
|
rlm@10
|
943 label.getFirst().status |= Label.TARGET;
|
rlm@10
|
944 // adds 'label' as a successor of this basic block
|
rlm@10
|
945 addSuccessor(Edge.NORMAL, label);
|
rlm@10
|
946 if(opcode != Opcodes.GOTO)
|
rlm@10
|
947 {
|
rlm@10
|
948 // creates a Label for the next basic block
|
rlm@10
|
949 nextInsn = new Label();
|
rlm@10
|
950 }
|
rlm@10
|
951 }
|
rlm@10
|
952 else
|
rlm@10
|
953 {
|
rlm@10
|
954 if(opcode == Opcodes.JSR)
|
rlm@10
|
955 {
|
rlm@10
|
956 jsr = true;
|
rlm@10
|
957 currentBlock.status |= Label.JSR;
|
rlm@10
|
958 addSuccessor(stackSize + 1, label);
|
rlm@10
|
959 // creates a Label for the next basic block
|
rlm@10
|
960 nextInsn = new Label();
|
rlm@10
|
961 /*
|
rlm@10
|
962 * note that, by construction in this method, a JSR block
|
rlm@10
|
963 * has at least two successors in the control flow graph:
|
rlm@10
|
964 * the first one leads the next instruction after the JSR,
|
rlm@10
|
965 * while the second one leads to the JSR target.
|
rlm@10
|
966 */
|
rlm@10
|
967 }
|
rlm@10
|
968 else
|
rlm@10
|
969 {
|
rlm@10
|
970 // updates current stack size (max stack size unchanged
|
rlm@10
|
971 // because stack size variation always negative in this
|
rlm@10
|
972 // case)
|
rlm@10
|
973 stackSize += Frame.SIZE[opcode];
|
rlm@10
|
974 addSuccessor(stackSize, label);
|
rlm@10
|
975 }
|
rlm@10
|
976 }
|
rlm@10
|
977 }
|
rlm@10
|
978 // adds the instruction to the bytecode of the method
|
rlm@10
|
979 if((label.status & Label.RESOLVED) != 0
|
rlm@10
|
980 && label.position - code.length < Short.MIN_VALUE)
|
rlm@10
|
981 {
|
rlm@10
|
982 /*
|
rlm@10
|
983 * case of a backward jump with an offset < -32768. In this case we
|
rlm@10
|
984 * automatically replace GOTO with GOTO_W, JSR with JSR_W and IFxxx
|
rlm@10
|
985 * <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is the
|
rlm@10
|
986 * "opposite" opcode of IFxxx (i.e., IFNE for IFEQ) and where <l'>
|
rlm@10
|
987 * designates the instruction just after the GOTO_W.
|
rlm@10
|
988 */
|
rlm@10
|
989 if(opcode == Opcodes.GOTO)
|
rlm@10
|
990 {
|
rlm@10
|
991 code.putByte(200); // GOTO_W
|
rlm@10
|
992 }
|
rlm@10
|
993 else if(opcode == Opcodes.JSR)
|
rlm@10
|
994 {
|
rlm@10
|
995 code.putByte(201); // JSR_W
|
rlm@10
|
996 }
|
rlm@10
|
997 else
|
rlm@10
|
998 {
|
rlm@10
|
999 // if the IF instruction is transformed into IFNOT GOTO_W the
|
rlm@10
|
1000 // next instruction becomes the target of the IFNOT instruction
|
rlm@10
|
1001 if(nextInsn != null)
|
rlm@10
|
1002 {
|
rlm@10
|
1003 nextInsn.status |= Label.TARGET;
|
rlm@10
|
1004 }
|
rlm@10
|
1005 code.putByte(opcode <= 166
|
rlm@10
|
1006 ? ((opcode + 1) ^ 1) - 1
|
rlm@10
|
1007 : opcode ^ 1);
|
rlm@10
|
1008 code.putShort(8); // jump offset
|
rlm@10
|
1009 code.putByte(200); // GOTO_W
|
rlm@10
|
1010 }
|
rlm@10
|
1011 label.put(this, code, code.length - 1, true);
|
rlm@10
|
1012 }
|
rlm@10
|
1013 else
|
rlm@10
|
1014 {
|
rlm@10
|
1015 /*
|
rlm@10
|
1016 * case of a backward jump with an offset >= -32768, or of a forward
|
rlm@10
|
1017 * jump with, of course, an unknown offset. In these cases we store
|
rlm@10
|
1018 * the offset in 2 bytes (which will be increased in
|
rlm@10
|
1019 * resizeInstructions, if needed).
|
rlm@10
|
1020 */
|
rlm@10
|
1021 code.putByte(opcode);
|
rlm@10
|
1022 label.put(this, code, code.length - 1, false);
|
rlm@10
|
1023 }
|
rlm@10
|
1024 if(currentBlock != null)
|
rlm@10
|
1025 {
|
rlm@10
|
1026 if(nextInsn != null)
|
rlm@10
|
1027 {
|
rlm@10
|
1028 // if the jump instruction is not a GOTO, the next instruction
|
rlm@10
|
1029 // is also a successor of this instruction. Calling visitLabel
|
rlm@10
|
1030 // adds the label of this next instruction as a successor of the
|
rlm@10
|
1031 // current block, and starts a new basic block
|
rlm@10
|
1032 visitLabel(nextInsn);
|
rlm@10
|
1033 }
|
rlm@10
|
1034 if(opcode == Opcodes.GOTO)
|
rlm@10
|
1035 {
|
rlm@10
|
1036 noSuccessor();
|
rlm@10
|
1037 }
|
rlm@10
|
1038 }
|
rlm@10
|
1039 }
|
rlm@10
|
1040
|
rlm@10
|
1041 public void visitLabel(final Label label){
|
rlm@10
|
1042 // resolves previous forward references to label, if any
|
rlm@10
|
1043 resize |= label.resolve(this, code.length, code.data);
|
rlm@10
|
1044 // updates currentBlock
|
rlm@10
|
1045 if((label.status & Label.DEBUG) != 0)
|
rlm@10
|
1046 {
|
rlm@10
|
1047 return;
|
rlm@10
|
1048 }
|
rlm@10
|
1049 if(compute == FRAMES)
|
rlm@10
|
1050 {
|
rlm@10
|
1051 if(currentBlock != null)
|
rlm@10
|
1052 {
|
rlm@10
|
1053 if(label.position == currentBlock.position)
|
rlm@10
|
1054 {
|
rlm@10
|
1055 // successive labels, do not start a new basic block
|
rlm@10
|
1056 currentBlock.status |= (label.status & Label.TARGET);
|
rlm@10
|
1057 label.frame = currentBlock.frame;
|
rlm@10
|
1058 return;
|
rlm@10
|
1059 }
|
rlm@10
|
1060 // ends current block (with one new successor)
|
rlm@10
|
1061 addSuccessor(Edge.NORMAL, label);
|
rlm@10
|
1062 }
|
rlm@10
|
1063 // begins a new current block
|
rlm@10
|
1064 currentBlock = label;
|
rlm@10
|
1065 if(label.frame == null)
|
rlm@10
|
1066 {
|
rlm@10
|
1067 label.frame = new Frame();
|
rlm@10
|
1068 label.frame.owner = label;
|
rlm@10
|
1069 }
|
rlm@10
|
1070 // updates the basic block list
|
rlm@10
|
1071 if(previousBlock != null)
|
rlm@10
|
1072 {
|
rlm@10
|
1073 if(label.position == previousBlock.position)
|
rlm@10
|
1074 {
|
rlm@10
|
1075 previousBlock.status |= (label.status & Label.TARGET);
|
rlm@10
|
1076 label.frame = previousBlock.frame;
|
rlm@10
|
1077 currentBlock = previousBlock;
|
rlm@10
|
1078 return;
|
rlm@10
|
1079 }
|
rlm@10
|
1080 previousBlock.successor = label;
|
rlm@10
|
1081 }
|
rlm@10
|
1082 previousBlock = label;
|
rlm@10
|
1083 }
|
rlm@10
|
1084 else if(compute == MAXS)
|
rlm@10
|
1085 {
|
rlm@10
|
1086 if(currentBlock != null)
|
rlm@10
|
1087 {
|
rlm@10
|
1088 // ends current block (with one new successor)
|
rlm@10
|
1089 currentBlock.outputStackMax = maxStackSize;
|
rlm@10
|
1090 addSuccessor(stackSize, label);
|
rlm@10
|
1091 }
|
rlm@10
|
1092 // begins a new current block
|
rlm@10
|
1093 currentBlock = label;
|
rlm@10
|
1094 // resets the relative current and max stack sizes
|
rlm@10
|
1095 stackSize = 0;
|
rlm@10
|
1096 maxStackSize = 0;
|
rlm@10
|
1097 // updates the basic block list
|
rlm@10
|
1098 if(previousBlock != null)
|
rlm@10
|
1099 {
|
rlm@10
|
1100 previousBlock.successor = label;
|
rlm@10
|
1101 }
|
rlm@10
|
1102 previousBlock = label;
|
rlm@10
|
1103 }
|
rlm@10
|
1104 }
|
rlm@10
|
1105
|
rlm@10
|
1106 public void visitLdcInsn(final Object cst){
|
rlm@10
|
1107 Item i = cw.newConstItem(cst);
|
rlm@10
|
1108 // Label currentBlock = this.currentBlock;
|
rlm@10
|
1109 if(currentBlock != null)
|
rlm@10
|
1110 {
|
rlm@10
|
1111 if(compute == FRAMES)
|
rlm@10
|
1112 {
|
rlm@10
|
1113 currentBlock.frame.execute(Opcodes.LDC, 0, cw, i);
|
rlm@10
|
1114 }
|
rlm@10
|
1115 else
|
rlm@10
|
1116 {
|
rlm@10
|
1117 int size;
|
rlm@10
|
1118 // computes the stack size variation
|
rlm@10
|
1119 if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE)
|
rlm@10
|
1120 {
|
rlm@10
|
1121 size = stackSize + 2;
|
rlm@10
|
1122 }
|
rlm@10
|
1123 else
|
rlm@10
|
1124 {
|
rlm@10
|
1125 size = stackSize + 1;
|
rlm@10
|
1126 }
|
rlm@10
|
1127 // updates current and max stack sizes
|
rlm@10
|
1128 if(size > maxStackSize)
|
rlm@10
|
1129 {
|
rlm@10
|
1130 maxStackSize = size;
|
rlm@10
|
1131 }
|
rlm@10
|
1132 stackSize = size;
|
rlm@10
|
1133 }
|
rlm@10
|
1134 }
|
rlm@10
|
1135 // adds the instruction to the bytecode of the method
|
rlm@10
|
1136 int index = i.index;
|
rlm@10
|
1137 if(i.type == ClassWriter.LONG || i.type == ClassWriter.DOUBLE)
|
rlm@10
|
1138 {
|
rlm@10
|
1139 code.put12(20 /* LDC2_W */, index);
|
rlm@10
|
1140 }
|
rlm@10
|
1141 else if(index >= 256)
|
rlm@10
|
1142 {
|
rlm@10
|
1143 code.put12(19 /* LDC_W */, index);
|
rlm@10
|
1144 }
|
rlm@10
|
1145 else
|
rlm@10
|
1146 {
|
rlm@10
|
1147 code.put11(Opcodes.LDC, index);
|
rlm@10
|
1148 }
|
rlm@10
|
1149 }
|
rlm@10
|
1150
|
rlm@10
|
1151 public void visitIincInsn(final int var, final int increment){
|
rlm@10
|
1152 if(currentBlock != null)
|
rlm@10
|
1153 {
|
rlm@10
|
1154 if(compute == FRAMES)
|
rlm@10
|
1155 {
|
rlm@10
|
1156 currentBlock.frame.execute(Opcodes.IINC, var, null, null);
|
rlm@10
|
1157 }
|
rlm@10
|
1158 }
|
rlm@10
|
1159 if(compute != NOTHING)
|
rlm@10
|
1160 {
|
rlm@10
|
1161 // updates max locals
|
rlm@10
|
1162 int n = var + 1;
|
rlm@10
|
1163 if(n > maxLocals)
|
rlm@10
|
1164 {
|
rlm@10
|
1165 maxLocals = n;
|
rlm@10
|
1166 }
|
rlm@10
|
1167 }
|
rlm@10
|
1168 // adds the instruction to the bytecode of the method
|
rlm@10
|
1169 if((var > 255) || (increment > 127) || (increment < -128))
|
rlm@10
|
1170 {
|
rlm@10
|
1171 code.putByte(196 /* WIDE */)
|
rlm@10
|
1172 .put12(Opcodes.IINC, var)
|
rlm@10
|
1173 .putShort(increment);
|
rlm@10
|
1174 }
|
rlm@10
|
1175 else
|
rlm@10
|
1176 {
|
rlm@10
|
1177 code.putByte(Opcodes.IINC).put11(var, increment);
|
rlm@10
|
1178 }
|
rlm@10
|
1179 }
|
rlm@10
|
1180
|
rlm@10
|
1181 public void visitTableSwitchInsn(
|
rlm@10
|
1182 final int min,
|
rlm@10
|
1183 final int max,
|
rlm@10
|
1184 final Label dflt,
|
rlm@10
|
1185 final Label labels[]){
|
rlm@10
|
1186 // adds the instruction to the bytecode of the method
|
rlm@10
|
1187 int source = code.length;
|
rlm@10
|
1188 code.putByte(Opcodes.TABLESWITCH);
|
rlm@10
|
1189 code.length += (4 - code.length % 4) % 4;
|
rlm@10
|
1190 dflt.put(this, code, source, true);
|
rlm@10
|
1191 code.putInt(min).putInt(max);
|
rlm@10
|
1192 for(int i = 0; i < labels.length; ++i)
|
rlm@10
|
1193 {
|
rlm@10
|
1194 labels[i].put(this, code, source, true);
|
rlm@10
|
1195 }
|
rlm@10
|
1196 // updates currentBlock
|
rlm@10
|
1197 visitSwitchInsn(dflt, labels);
|
rlm@10
|
1198 }
|
rlm@10
|
1199
|
rlm@10
|
1200 public void visitLookupSwitchInsn(
|
rlm@10
|
1201 final Label dflt,
|
rlm@10
|
1202 final int keys[],
|
rlm@10
|
1203 final Label labels[]){
|
rlm@10
|
1204 // adds the instruction to the bytecode of the method
|
rlm@10
|
1205 int source = code.length;
|
rlm@10
|
1206 code.putByte(Opcodes.LOOKUPSWITCH);
|
rlm@10
|
1207 code.length += (4 - code.length % 4) % 4;
|
rlm@10
|
1208 dflt.put(this, code, source, true);
|
rlm@10
|
1209 code.putInt(labels.length);
|
rlm@10
|
1210 for(int i = 0; i < labels.length; ++i)
|
rlm@10
|
1211 {
|
rlm@10
|
1212 code.putInt(keys[i]);
|
rlm@10
|
1213 labels[i].put(this, code, source, true);
|
rlm@10
|
1214 }
|
rlm@10
|
1215 // updates currentBlock
|
rlm@10
|
1216 visitSwitchInsn(dflt, labels);
|
rlm@10
|
1217 }
|
rlm@10
|
1218
|
rlm@10
|
1219 private void visitSwitchInsn(final Label dflt, final Label[] labels){
|
rlm@10
|
1220 // Label currentBlock = this.currentBlock;
|
rlm@10
|
1221 if(currentBlock != null)
|
rlm@10
|
1222 {
|
rlm@10
|
1223 if(compute == FRAMES)
|
rlm@10
|
1224 {
|
rlm@10
|
1225 currentBlock.frame.execute(Opcodes.LOOKUPSWITCH, 0, null, null);
|
rlm@10
|
1226 // adds current block successors
|
rlm@10
|
1227 addSuccessor(Edge.NORMAL, dflt);
|
rlm@10
|
1228 dflt.getFirst().status |= Label.TARGET;
|
rlm@10
|
1229 for(int i = 0; i < labels.length; ++i)
|
rlm@10
|
1230 {
|
rlm@10
|
1231 addSuccessor(Edge.NORMAL, labels[i]);
|
rlm@10
|
1232 labels[i].getFirst().status |= Label.TARGET;
|
rlm@10
|
1233 }
|
rlm@10
|
1234 }
|
rlm@10
|
1235 else
|
rlm@10
|
1236 {
|
rlm@10
|
1237 // updates current stack size (max stack size unchanged)
|
rlm@10
|
1238 --stackSize;
|
rlm@10
|
1239 // adds current block successors
|
rlm@10
|
1240 addSuccessor(stackSize, dflt);
|
rlm@10
|
1241 for(int i = 0; i < labels.length; ++i)
|
rlm@10
|
1242 {
|
rlm@10
|
1243 addSuccessor(stackSize, labels[i]);
|
rlm@10
|
1244 }
|
rlm@10
|
1245 }
|
rlm@10
|
1246 // ends current block
|
rlm@10
|
1247 noSuccessor();
|
rlm@10
|
1248 }
|
rlm@10
|
1249 }
|
rlm@10
|
1250
|
rlm@10
|
1251 public void visitMultiANewArrayInsn(final String desc, final int dims){
|
rlm@10
|
1252 Item i = cw.newClassItem(desc);
|
rlm@10
|
1253 // Label currentBlock = this.currentBlock;
|
rlm@10
|
1254 if(currentBlock != null)
|
rlm@10
|
1255 {
|
rlm@10
|
1256 if(compute == FRAMES)
|
rlm@10
|
1257 {
|
rlm@10
|
1258 currentBlock.frame.execute(Opcodes.MULTIANEWARRAY, dims, cw, i);
|
rlm@10
|
1259 }
|
rlm@10
|
1260 else
|
rlm@10
|
1261 {
|
rlm@10
|
1262 // updates current stack size (max stack size unchanged because
|
rlm@10
|
1263 // stack size variation always negative or null)
|
rlm@10
|
1264 stackSize += 1 - dims;
|
rlm@10
|
1265 }
|
rlm@10
|
1266 }
|
rlm@10
|
1267 // adds the instruction to the bytecode of the method
|
rlm@10
|
1268 code.put12(Opcodes.MULTIANEWARRAY, i.index).putByte(dims);
|
rlm@10
|
1269 }
|
rlm@10
|
1270
|
rlm@10
|
1271 public void visitTryCatchBlock(
|
rlm@10
|
1272 final Label start,
|
rlm@10
|
1273 final Label end,
|
rlm@10
|
1274 final Label handler,
|
rlm@10
|
1275 final String type){
|
rlm@10
|
1276 ++handlerCount;
|
rlm@10
|
1277 Handler h = new Handler();
|
rlm@10
|
1278 h.start = start;
|
rlm@10
|
1279 h.end = end;
|
rlm@10
|
1280 h.handler = handler;
|
rlm@10
|
1281 h.desc = type;
|
rlm@10
|
1282 h.type = type != null ? cw.newClass(type) : 0;
|
rlm@10
|
1283 if(lastHandler == null)
|
rlm@10
|
1284 {
|
rlm@10
|
1285 firstHandler = h;
|
rlm@10
|
1286 }
|
rlm@10
|
1287 else
|
rlm@10
|
1288 {
|
rlm@10
|
1289 lastHandler.next = h;
|
rlm@10
|
1290 }
|
rlm@10
|
1291 lastHandler = h;
|
rlm@10
|
1292 }
|
rlm@10
|
1293
|
rlm@10
|
1294 public void visitLocalVariable(
|
rlm@10
|
1295 final String name,
|
rlm@10
|
1296 final String desc,
|
rlm@10
|
1297 final String signature,
|
rlm@10
|
1298 final Label start,
|
rlm@10
|
1299 final Label end,
|
rlm@10
|
1300 final int index){
|
rlm@10
|
1301 if(signature != null)
|
rlm@10
|
1302 {
|
rlm@10
|
1303 if(localVarType == null)
|
rlm@10
|
1304 {
|
rlm@10
|
1305 localVarType = new ByteVector();
|
rlm@10
|
1306 }
|
rlm@10
|
1307 ++localVarTypeCount;
|
rlm@10
|
1308 localVarType.putShort(start.position)
|
rlm@10
|
1309 .putShort(end.position - start.position)
|
rlm@10
|
1310 .putShort(cw.newUTF8(name))
|
rlm@10
|
1311 .putShort(cw.newUTF8(signature))
|
rlm@10
|
1312 .putShort(index);
|
rlm@10
|
1313 }
|
rlm@10
|
1314 if(localVar == null)
|
rlm@10
|
1315 {
|
rlm@10
|
1316 localVar = new ByteVector();
|
rlm@10
|
1317 }
|
rlm@10
|
1318 ++localVarCount;
|
rlm@10
|
1319 localVar.putShort(start.position)
|
rlm@10
|
1320 .putShort(end.position - start.position)
|
rlm@10
|
1321 .putShort(cw.newUTF8(name))
|
rlm@10
|
1322 .putShort(cw.newUTF8(desc))
|
rlm@10
|
1323 .putShort(index);
|
rlm@10
|
1324 if(compute != NOTHING)
|
rlm@10
|
1325 {
|
rlm@10
|
1326 // updates max locals
|
rlm@10
|
1327 char c = desc.charAt(0);
|
rlm@10
|
1328 int n = index + (c == 'J' || c == 'D' ? 2 : 1);
|
rlm@10
|
1329 if(n > maxLocals)
|
rlm@10
|
1330 {
|
rlm@10
|
1331 maxLocals = n;
|
rlm@10
|
1332 }
|
rlm@10
|
1333 }
|
rlm@10
|
1334 }
|
rlm@10
|
1335
|
rlm@10
|
1336 public void visitLineNumber(final int line, final Label start){
|
rlm@10
|
1337 if(lineNumber == null)
|
rlm@10
|
1338 {
|
rlm@10
|
1339 lineNumber = new ByteVector();
|
rlm@10
|
1340 }
|
rlm@10
|
1341 ++lineNumberCount;
|
rlm@10
|
1342 lineNumber.putShort(start.position);
|
rlm@10
|
1343 lineNumber.putShort(line);
|
rlm@10
|
1344 }
|
rlm@10
|
1345
|
rlm@10
|
1346 public void visitMaxs(final int maxStack, final int maxLocals){
|
rlm@10
|
1347 if(compute == FRAMES)
|
rlm@10
|
1348 {
|
rlm@10
|
1349 // completes the control flow graph with exception handler blocks
|
rlm@10
|
1350 Handler handler = firstHandler;
|
rlm@10
|
1351 while(handler != null)
|
rlm@10
|
1352 {
|
rlm@10
|
1353 Label l = handler.start.getFirst();
|
rlm@10
|
1354 Label h = handler.handler.getFirst();
|
rlm@10
|
1355 Label e = handler.end.getFirst();
|
rlm@10
|
1356 // computes the kind of the edges to 'h'
|
rlm@10
|
1357 String t = handler.desc == null
|
rlm@10
|
1358 ? "java/lang/Throwable"
|
rlm@10
|
1359 : handler.desc;
|
rlm@10
|
1360 int kind = Frame.OBJECT | cw.addType(t);
|
rlm@10
|
1361 // h is an exception handler
|
rlm@10
|
1362 h.status |= Label.TARGET;
|
rlm@10
|
1363 // adds 'h' as a successor of labels between 'start' and 'end'
|
rlm@10
|
1364 while(l != e)
|
rlm@10
|
1365 {
|
rlm@10
|
1366 // creates an edge to 'h'
|
rlm@10
|
1367 Edge b = new Edge();
|
rlm@10
|
1368 b.info = kind;
|
rlm@10
|
1369 b.successor = h;
|
rlm@10
|
1370 // adds it to the successors of 'l'
|
rlm@10
|
1371 b.next = l.successors;
|
rlm@10
|
1372 l.successors = b;
|
rlm@10
|
1373 // goes to the next label
|
rlm@10
|
1374 l = l.successor;
|
rlm@10
|
1375 }
|
rlm@10
|
1376 handler = handler.next;
|
rlm@10
|
1377 }
|
rlm@10
|
1378
|
rlm@10
|
1379 // creates and visits the first (implicit) frame
|
rlm@10
|
1380 Frame f = labels.frame;
|
rlm@10
|
1381 Type[] args = Type.getArgumentTypes(descriptor);
|
rlm@10
|
1382 f.initInputFrame(cw, access, args, this.maxLocals);
|
rlm@10
|
1383 visitFrame(f);
|
rlm@10
|
1384
|
rlm@10
|
1385 /*
|
rlm@10
|
1386 * fix point algorithm: mark the first basic block as 'changed'
|
rlm@10
|
1387 * (i.e. put it in the 'changed' list) and, while there are changed
|
rlm@10
|
1388 * basic blocks, choose one, mark it as unchanged, and update its
|
rlm@10
|
1389 * successors (which can be changed in the process).
|
rlm@10
|
1390 */
|
rlm@10
|
1391 int max = 0;
|
rlm@10
|
1392 Label changed = labels;
|
rlm@10
|
1393 while(changed != null)
|
rlm@10
|
1394 {
|
rlm@10
|
1395 // removes a basic block from the list of changed basic blocks
|
rlm@10
|
1396 Label l = changed;
|
rlm@10
|
1397 changed = changed.next;
|
rlm@10
|
1398 l.next = null;
|
rlm@10
|
1399 f = l.frame;
|
rlm@10
|
1400 // a reacheable jump target must be stored in the stack map
|
rlm@10
|
1401 if((l.status & Label.TARGET) != 0)
|
rlm@10
|
1402 {
|
rlm@10
|
1403 l.status |= Label.STORE;
|
rlm@10
|
1404 }
|
rlm@10
|
1405 // all visited labels are reacheable, by definition
|
rlm@10
|
1406 l.status |= Label.REACHABLE;
|
rlm@10
|
1407 // updates the (absolute) maximum stack size
|
rlm@10
|
1408 int blockMax = f.inputStack.length + l.outputStackMax;
|
rlm@10
|
1409 if(blockMax > max)
|
rlm@10
|
1410 {
|
rlm@10
|
1411 max = blockMax;
|
rlm@10
|
1412 }
|
rlm@10
|
1413 // updates the successors of the current basic block
|
rlm@10
|
1414 Edge e = l.successors;
|
rlm@10
|
1415 while(e != null)
|
rlm@10
|
1416 {
|
rlm@10
|
1417 Label n = e.successor.getFirst();
|
rlm@10
|
1418 boolean change = f.merge(cw, n.frame, e.info);
|
rlm@10
|
1419 if(change && n.next == null)
|
rlm@10
|
1420 {
|
rlm@10
|
1421 // if n has changed and is not already in the 'changed'
|
rlm@10
|
1422 // list, adds it to this list
|
rlm@10
|
1423 n.next = changed;
|
rlm@10
|
1424 changed = n;
|
rlm@10
|
1425 }
|
rlm@10
|
1426 e = e.next;
|
rlm@10
|
1427 }
|
rlm@10
|
1428 }
|
rlm@10
|
1429 this.maxStack = max;
|
rlm@10
|
1430
|
rlm@10
|
1431 // visits all the frames that must be stored in the stack map
|
rlm@10
|
1432 Label l = labels;
|
rlm@10
|
1433 while(l != null)
|
rlm@10
|
1434 {
|
rlm@10
|
1435 f = l.frame;
|
rlm@10
|
1436 if((l.status & Label.STORE) != 0)
|
rlm@10
|
1437 {
|
rlm@10
|
1438 visitFrame(f);
|
rlm@10
|
1439 }
|
rlm@10
|
1440 if((l.status & Label.REACHABLE) == 0)
|
rlm@10
|
1441 {
|
rlm@10
|
1442 // finds start and end of dead basic block
|
rlm@10
|
1443 Label k = l.successor;
|
rlm@10
|
1444 int start = l.position;
|
rlm@10
|
1445 int end = (k == null ? code.length : k.position) - 1;
|
rlm@10
|
1446 // if non empty basic block
|
rlm@10
|
1447 if(end >= start)
|
rlm@10
|
1448 {
|
rlm@10
|
1449 // replaces instructions with NOP ... NOP ATHROW
|
rlm@10
|
1450 for(int i = start; i < end; ++i)
|
rlm@10
|
1451 {
|
rlm@10
|
1452 code.data[i] = Opcodes.NOP;
|
rlm@10
|
1453 }
|
rlm@10
|
1454 code.data[end] = (byte) Opcodes.ATHROW;
|
rlm@10
|
1455 // emits a frame for this unreachable block
|
rlm@10
|
1456 startFrame(start, 0, 1);
|
rlm@10
|
1457 frame[frameIndex++] = Frame.OBJECT
|
rlm@10
|
1458 | cw.addType("java/lang/Throwable");
|
rlm@10
|
1459 endFrame();
|
rlm@10
|
1460 }
|
rlm@10
|
1461 }
|
rlm@10
|
1462 l = l.successor;
|
rlm@10
|
1463 }
|
rlm@10
|
1464 }
|
rlm@10
|
1465 else if(compute == MAXS)
|
rlm@10
|
1466 {
|
rlm@10
|
1467 // completes the control flow graph with exception handler blocks
|
rlm@10
|
1468 Handler handler = firstHandler;
|
rlm@10
|
1469 while(handler != null)
|
rlm@10
|
1470 {
|
rlm@10
|
1471 Label l = handler.start;
|
rlm@10
|
1472 Label h = handler.handler;
|
rlm@10
|
1473 Label e = handler.end;
|
rlm@10
|
1474 // adds 'h' as a successor of labels between 'start' and 'end'
|
rlm@10
|
1475 while(l != e)
|
rlm@10
|
1476 {
|
rlm@10
|
1477 // creates an edge to 'h'
|
rlm@10
|
1478 Edge b = new Edge();
|
rlm@10
|
1479 b.info = Edge.EXCEPTION;
|
rlm@10
|
1480 b.successor = h;
|
rlm@10
|
1481 // adds it to the successors of 'l'
|
rlm@10
|
1482 if((l.status & Label.JSR) != 0)
|
rlm@10
|
1483 {
|
rlm@10
|
1484 // if l is a JSR block, adds b after the first two edges
|
rlm@10
|
1485 // to preserve the hypothesis about JSR block successors
|
rlm@10
|
1486 // order (see {@link #visitJumpInsn})
|
rlm@10
|
1487 b.next = l.successors.next.next;
|
rlm@10
|
1488 l.successors.next.next = b;
|
rlm@10
|
1489 }
|
rlm@10
|
1490 else
|
rlm@10
|
1491 {
|
rlm@10
|
1492 b.next = l.successors;
|
rlm@10
|
1493 l.successors = b;
|
rlm@10
|
1494 }
|
rlm@10
|
1495 // goes to the next label
|
rlm@10
|
1496 l = l.successor;
|
rlm@10
|
1497 }
|
rlm@10
|
1498 handler = handler.next;
|
rlm@10
|
1499 }
|
rlm@10
|
1500
|
rlm@10
|
1501 if(jsr)
|
rlm@10
|
1502 {
|
rlm@10
|
1503 // completes the control flow graph with the RET successors
|
rlm@10
|
1504 /*
|
rlm@10
|
1505 * first step: finds the subroutines. This step determines, for
|
rlm@10
|
1506 * each basic block, to which subroutine(s) it belongs, and
|
rlm@10
|
1507 * stores this set as a bit set in the {@link Label#status}
|
rlm@10
|
1508 * field. Subroutines are numbered with powers of two, from
|
rlm@10
|
1509 * 0x1000 to 0x80000000 (so there must be at most 20 subroutines
|
rlm@10
|
1510 * in a method).
|
rlm@10
|
1511 */
|
rlm@10
|
1512 // finds the basic blocks that belong to the "main" subroutine
|
rlm@10
|
1513 int id = 0x1000;
|
rlm@10
|
1514 findSubroutine(labels, id);
|
rlm@10
|
1515 // finds the basic blocks that belong to the real subroutines
|
rlm@10
|
1516 Label l = labels;
|
rlm@10
|
1517 while(l != null)
|
rlm@10
|
1518 {
|
rlm@10
|
1519 if((l.status & Label.JSR) != 0)
|
rlm@10
|
1520 {
|
rlm@10
|
1521 // the subroutine is defined by l's TARGET, not by l
|
rlm@10
|
1522 Label subroutine = l.successors.next.successor;
|
rlm@10
|
1523 // if this subroutine does not have an id yet...
|
rlm@10
|
1524 if((subroutine.status & ~0xFFF) == 0)
|
rlm@10
|
1525 {
|
rlm@10
|
1526 // ...assigns it a new id and finds its basic blocks
|
rlm@10
|
1527 id = id << 1;
|
rlm@10
|
1528 findSubroutine(subroutine, id);
|
rlm@10
|
1529 }
|
rlm@10
|
1530 }
|
rlm@10
|
1531 l = l.successor;
|
rlm@10
|
1532 }
|
rlm@10
|
1533 // second step: finds the successors of RET blocks
|
rlm@10
|
1534 findSubroutineSuccessors(0x1000, new Label[10], 0);
|
rlm@10
|
1535 }
|
rlm@10
|
1536
|
rlm@10
|
1537 /*
|
rlm@10
|
1538 * control flow analysis algorithm: while the block stack is not
|
rlm@10
|
1539 * empty, pop a block from this stack, update the max stack size,
|
rlm@10
|
1540 * compute the true (non relative) begin stack size of the
|
rlm@10
|
1541 * successors of this block, and push these successors onto the
|
rlm@10
|
1542 * stack (unless they have already been pushed onto the stack).
|
rlm@10
|
1543 * Note: by hypothesis, the {@link Label#inputStackTop} of the
|
rlm@10
|
1544 * blocks in the block stack are the true (non relative) beginning
|
rlm@10
|
1545 * stack sizes of these blocks.
|
rlm@10
|
1546 */
|
rlm@10
|
1547 int max = 0;
|
rlm@10
|
1548 Label stack = labels;
|
rlm@10
|
1549 while(stack != null)
|
rlm@10
|
1550 {
|
rlm@10
|
1551 // pops a block from the stack
|
rlm@10
|
1552 Label l = stack;
|
rlm@10
|
1553 stack = stack.next;
|
rlm@10
|
1554 // computes the true (non relative) max stack size of this block
|
rlm@10
|
1555 int start = l.inputStackTop;
|
rlm@10
|
1556 int blockMax = start + l.outputStackMax;
|
rlm@10
|
1557 // updates the global max stack size
|
rlm@10
|
1558 if(blockMax > max)
|
rlm@10
|
1559 {
|
rlm@10
|
1560 max = blockMax;
|
rlm@10
|
1561 }
|
rlm@10
|
1562 // analyses the successors of the block
|
rlm@10
|
1563 Edge b = l.successors;
|
rlm@10
|
1564 if((l.status & Label.JSR) != 0)
|
rlm@10
|
1565 {
|
rlm@10
|
1566 // ignores the first edge of JSR blocks (virtual successor)
|
rlm@10
|
1567 b = b.next;
|
rlm@10
|
1568 }
|
rlm@10
|
1569 while(b != null)
|
rlm@10
|
1570 {
|
rlm@10
|
1571 l = b.successor;
|
rlm@10
|
1572 // if this successor has not already been pushed...
|
rlm@10
|
1573 if((l.status & Label.PUSHED) == 0)
|
rlm@10
|
1574 {
|
rlm@10
|
1575 // computes its true beginning stack size...
|
rlm@10
|
1576 l.inputStackTop = b.info == Edge.EXCEPTION ? 1 : start
|
rlm@10
|
1577 + b.info;
|
rlm@10
|
1578 // ...and pushes it onto the stack
|
rlm@10
|
1579 l.status |= Label.PUSHED;
|
rlm@10
|
1580 l.next = stack;
|
rlm@10
|
1581 stack = l;
|
rlm@10
|
1582 }
|
rlm@10
|
1583 b = b.next;
|
rlm@10
|
1584 }
|
rlm@10
|
1585 }
|
rlm@10
|
1586 this.maxStack = max;
|
rlm@10
|
1587 }
|
rlm@10
|
1588 else
|
rlm@10
|
1589 {
|
rlm@10
|
1590 this.maxStack = maxStack;
|
rlm@10
|
1591 this.maxLocals = maxLocals;
|
rlm@10
|
1592 }
|
rlm@10
|
1593 }
|
rlm@10
|
1594
|
rlm@10
|
1595 public void visitEnd(){
|
rlm@10
|
1596 }
|
rlm@10
|
1597
|
rlm@10
|
1598 // ------------------------------------------------------------------------
|
rlm@10
|
1599 // Utility methods: control flow analysis algorithm
|
rlm@10
|
1600 // ------------------------------------------------------------------------
|
rlm@10
|
1601
|
rlm@10
|
1602 /**
|
rlm@10
|
1603 * Computes the size of the arguments and of the return value of a method.
|
rlm@10
|
1604 *
|
rlm@10
|
1605 * @param desc the descriptor of a method.
|
rlm@10
|
1606 * @return the size of the arguments of the method (plus one for the
|
rlm@10
|
1607 * implicit this argument), argSize, and the size of its return
|
rlm@10
|
1608 * value, retSize, packed into a single int i =
|
rlm@10
|
1609 * <tt>(argSize << 2) | retSize</tt> (argSize is therefore equal
|
rlm@10
|
1610 * to <tt>i >> 2</tt>, and retSize to <tt>i & 0x03</tt>).
|
rlm@10
|
1611 */
|
rlm@10
|
1612 static int getArgumentsAndReturnSizes(final String desc){
|
rlm@10
|
1613 int n = 1;
|
rlm@10
|
1614 int c = 1;
|
rlm@10
|
1615 while(true)
|
rlm@10
|
1616 {
|
rlm@10
|
1617 char car = desc.charAt(c++);
|
rlm@10
|
1618 if(car == ')')
|
rlm@10
|
1619 {
|
rlm@10
|
1620 car = desc.charAt(c);
|
rlm@10
|
1621 return n << 2
|
rlm@10
|
1622 | (car == 'V' ? 0 : (car == 'D' || car == 'J' ? 2 : 1));
|
rlm@10
|
1623 }
|
rlm@10
|
1624 else if(car == 'L')
|
rlm@10
|
1625 {
|
rlm@10
|
1626 while(desc.charAt(c++) != ';')
|
rlm@10
|
1627 {
|
rlm@10
|
1628 }
|
rlm@10
|
1629 n += 1;
|
rlm@10
|
1630 }
|
rlm@10
|
1631 else if(car == '[')
|
rlm@10
|
1632 {
|
rlm@10
|
1633 while((car = desc.charAt(c)) == '[')
|
rlm@10
|
1634 {
|
rlm@10
|
1635 ++c;
|
rlm@10
|
1636 }
|
rlm@10
|
1637 if(car == 'D' || car == 'J')
|
rlm@10
|
1638 {
|
rlm@10
|
1639 n -= 1;
|
rlm@10
|
1640 }
|
rlm@10
|
1641 }
|
rlm@10
|
1642 else if(car == 'D' || car == 'J')
|
rlm@10
|
1643 {
|
rlm@10
|
1644 n += 2;
|
rlm@10
|
1645 }
|
rlm@10
|
1646 else
|
rlm@10
|
1647 {
|
rlm@10
|
1648 n += 1;
|
rlm@10
|
1649 }
|
rlm@10
|
1650 }
|
rlm@10
|
1651 }
|
rlm@10
|
1652
|
rlm@10
|
1653 /**
|
rlm@10
|
1654 * Adds a successor to the {@link #currentBlock currentBlock} block.
|
rlm@10
|
1655 *
|
rlm@10
|
1656 * @param info information about the control flow edge to be added.
|
rlm@10
|
1657 * @param successor the successor block to be added to the current block.
|
rlm@10
|
1658 */
|
rlm@10
|
1659 private void addSuccessor(final int info, final Label successor){
|
rlm@10
|
1660 // creates and initializes an Edge object...
|
rlm@10
|
1661 Edge b = new Edge();
|
rlm@10
|
1662 b.info = info;
|
rlm@10
|
1663 b.successor = successor;
|
rlm@10
|
1664 // ...and adds it to the successor list of the currentBlock block
|
rlm@10
|
1665 b.next = currentBlock.successors;
|
rlm@10
|
1666 currentBlock.successors = b;
|
rlm@10
|
1667 }
|
rlm@10
|
1668
|
rlm@10
|
1669 /**
|
rlm@10
|
1670 * Ends the current basic block. This method must be used in the case where
|
rlm@10
|
1671 * the current basic block does not have any successor.
|
rlm@10
|
1672 */
|
rlm@10
|
1673 private void noSuccessor(){
|
rlm@10
|
1674 if(compute == FRAMES)
|
rlm@10
|
1675 {
|
rlm@10
|
1676 Label l = new Label();
|
rlm@10
|
1677 l.frame = new Frame();
|
rlm@10
|
1678 l.frame.owner = l;
|
rlm@10
|
1679 l.resolve(this, code.length, code.data);
|
rlm@10
|
1680 previousBlock.successor = l;
|
rlm@10
|
1681 previousBlock = l;
|
rlm@10
|
1682 }
|
rlm@10
|
1683 else
|
rlm@10
|
1684 {
|
rlm@10
|
1685 currentBlock.outputStackMax = maxStackSize;
|
rlm@10
|
1686 }
|
rlm@10
|
1687 currentBlock = null;
|
rlm@10
|
1688 }
|
rlm@10
|
1689
|
rlm@10
|
1690 /**
|
rlm@10
|
1691 * Finds the basic blocks that belong to a given subroutine, and marks these
|
rlm@10
|
1692 * blocks as belonging to this subroutine (by using {@link Label#status} as
|
rlm@10
|
1693 * a bit set (see {@link #visitMaxs}). This recursive method follows the
|
rlm@10
|
1694 * control flow graph to find all the blocks that are reachable from the
|
rlm@10
|
1695 * given block WITHOUT following any JSR target.
|
rlm@10
|
1696 *
|
rlm@10
|
1697 * @param block a block that belongs to the subroutine
|
rlm@10
|
1698 * @param id the id of this subroutine
|
rlm@10
|
1699 */
|
rlm@10
|
1700 private void findSubroutine(final Label block, final int id){
|
rlm@10
|
1701 // if 'block' is already marked as belonging to subroutine 'id', returns
|
rlm@10
|
1702 if((block.status & id) != 0)
|
rlm@10
|
1703 {
|
rlm@10
|
1704 return;
|
rlm@10
|
1705 }
|
rlm@10
|
1706 // marks 'block' as belonging to subroutine 'id'
|
rlm@10
|
1707 block.status |= id;
|
rlm@10
|
1708 // calls this method recursively on each successor, except JSR targets
|
rlm@10
|
1709 Edge e = block.successors;
|
rlm@10
|
1710 while(e != null)
|
rlm@10
|
1711 {
|
rlm@10
|
1712 // if 'block' is a JSR block, then 'block.successors.next' leads
|
rlm@10
|
1713 // to the JSR target (see {@link #visitJumpInsn}) and must therefore
|
rlm@10
|
1714 // not be followed
|
rlm@10
|
1715 if((block.status & Label.JSR) == 0 || e != block.successors.next)
|
rlm@10
|
1716 {
|
rlm@10
|
1717 findSubroutine(e.successor, id);
|
rlm@10
|
1718 }
|
rlm@10
|
1719 e = e.next;
|
rlm@10
|
1720 }
|
rlm@10
|
1721 }
|
rlm@10
|
1722
|
rlm@10
|
1723 /**
|
rlm@10
|
1724 * Finds the successors of the RET blocks of the specified subroutine, and
|
rlm@10
|
1725 * of any nested subroutine it calls.
|
rlm@10
|
1726 *
|
rlm@10
|
1727 * @param id id of the subroutine whose RET block successors must be found.
|
rlm@10
|
1728 * @param JSRs the JSR blocks that were followed to reach this subroutine.
|
rlm@10
|
1729 * @param nJSRs number of JSR blocks in the JSRs array.
|
rlm@10
|
1730 */
|
rlm@10
|
1731 private void findSubroutineSuccessors(
|
rlm@10
|
1732 final int id,
|
rlm@10
|
1733 final Label[] JSRs,
|
rlm@10
|
1734 final int nJSRs){
|
rlm@10
|
1735 // iterates over all the basic blocks...
|
rlm@10
|
1736 Label l = labels;
|
rlm@10
|
1737 while(l != null)
|
rlm@10
|
1738 {
|
rlm@10
|
1739 // for those that belong to subroutine 'id'...
|
rlm@10
|
1740 if((l.status & id) != 0)
|
rlm@10
|
1741 {
|
rlm@10
|
1742 if((l.status & Label.JSR) != 0)
|
rlm@10
|
1743 {
|
rlm@10
|
1744 // finds the subroutine to which 'l' leads by following the
|
rlm@10
|
1745 // second edge of l.successors (see {@link #visitJumpInsn})
|
rlm@10
|
1746 int nId = l.successors.next.successor.status & ~0xFFF;
|
rlm@10
|
1747 if(nId != id)
|
rlm@10
|
1748 {
|
rlm@10
|
1749 // calls this method recursively with l pushed onto the
|
rlm@10
|
1750 // JSRs stack to find the successors of the RET blocks
|
rlm@10
|
1751 // of this nested subroutine 'nId'
|
rlm@10
|
1752 JSRs[nJSRs] = l;
|
rlm@10
|
1753 findSubroutineSuccessors(nId, JSRs, nJSRs + 1);
|
rlm@10
|
1754 }
|
rlm@10
|
1755 }
|
rlm@10
|
1756 else if((l.status & Label.RET) != 0)
|
rlm@10
|
1757 {
|
rlm@10
|
1758 /*
|
rlm@10
|
1759 * finds the JSR block in the JSRs stack that corresponds to
|
rlm@10
|
1760 * this RET block, and updates the successors of this RET
|
rlm@10
|
1761 * block accordingly. This corresponding JSR is the one that
|
rlm@10
|
1762 * leads to the subroutine to which the RET block belongs.
|
rlm@10
|
1763 * But the RET block can belong to several subroutines (if a
|
rlm@10
|
1764 * nested subroutine returns to its parent subroutine
|
rlm@10
|
1765 * implicitely, without a RET). So, in fact, the JSR that
|
rlm@10
|
1766 * corresponds to this RET is the first block in the JSRs
|
rlm@10
|
1767 * stack, starting from the bottom of the stack, that leads
|
rlm@10
|
1768 * to a subroutine to which the RET block belongs.
|
rlm@10
|
1769 */
|
rlm@10
|
1770 for(int i = 0; i < nJSRs; ++i)
|
rlm@10
|
1771 {
|
rlm@10
|
1772 int JSRstatus = JSRs[i].successors.next.successor.status;
|
rlm@10
|
1773 if(((JSRstatus & ~0xFFF) & (l.status & ~0xFFF)) != 0)
|
rlm@10
|
1774 {
|
rlm@10
|
1775 Edge e = new Edge();
|
rlm@10
|
1776 e.info = l.inputStackTop;
|
rlm@10
|
1777 e.successor = JSRs[i].successors.successor;
|
rlm@10
|
1778 e.next = l.successors;
|
rlm@10
|
1779 l.successors = e;
|
rlm@10
|
1780 break;
|
rlm@10
|
1781 }
|
rlm@10
|
1782 }
|
rlm@10
|
1783 }
|
rlm@10
|
1784 }
|
rlm@10
|
1785 l = l.successor;
|
rlm@10
|
1786 }
|
rlm@10
|
1787 }
|
rlm@10
|
1788
|
rlm@10
|
1789 // ------------------------------------------------------------------------
|
rlm@10
|
1790 // Utility methods: stack map frames
|
rlm@10
|
1791 // ------------------------------------------------------------------------
|
rlm@10
|
1792
|
rlm@10
|
1793 /**
|
rlm@10
|
1794 * Visits a frame that has been computed from scratch.
|
rlm@10
|
1795 *
|
rlm@10
|
1796 * @param f the frame that must be visited.
|
rlm@10
|
1797 */
|
rlm@10
|
1798 private void visitFrame(final Frame f){
|
rlm@10
|
1799 int i, t;
|
rlm@10
|
1800 int nTop = 0;
|
rlm@10
|
1801 int nLocal = 0;
|
rlm@10
|
1802 int nStack = 0;
|
rlm@10
|
1803 int[] locals = f.inputLocals;
|
rlm@10
|
1804 int[] stacks = f.inputStack;
|
rlm@10
|
1805 // computes the number of locals (ignores TOP types that are just after
|
rlm@10
|
1806 // a LONG or a DOUBLE, and all trailing TOP types)
|
rlm@10
|
1807 for(i = 0; i < locals.length; ++i)
|
rlm@10
|
1808 {
|
rlm@10
|
1809 t = locals[i];
|
rlm@10
|
1810 if(t == Frame.TOP)
|
rlm@10
|
1811 {
|
rlm@10
|
1812 ++nTop;
|
rlm@10
|
1813 }
|
rlm@10
|
1814 else
|
rlm@10
|
1815 {
|
rlm@10
|
1816 nLocal += nTop + 1;
|
rlm@10
|
1817 nTop = 0;
|
rlm@10
|
1818 }
|
rlm@10
|
1819 if(t == Frame.LONG || t == Frame.DOUBLE)
|
rlm@10
|
1820 {
|
rlm@10
|
1821 ++i;
|
rlm@10
|
1822 }
|
rlm@10
|
1823 }
|
rlm@10
|
1824 // computes the stack size (ignores TOP types that are just after
|
rlm@10
|
1825 // a LONG or a DOUBLE)
|
rlm@10
|
1826 for(i = 0; i < stacks.length; ++i)
|
rlm@10
|
1827 {
|
rlm@10
|
1828 t = stacks[i];
|
rlm@10
|
1829 ++nStack;
|
rlm@10
|
1830 if(t == Frame.LONG || t == Frame.DOUBLE)
|
rlm@10
|
1831 {
|
rlm@10
|
1832 ++i;
|
rlm@10
|
1833 }
|
rlm@10
|
1834 }
|
rlm@10
|
1835 // visits the frame and its content
|
rlm@10
|
1836 startFrame(f.owner.position, nLocal, nStack);
|
rlm@10
|
1837 for(i = 0; nLocal > 0; ++i, --nLocal)
|
rlm@10
|
1838 {
|
rlm@10
|
1839 t = locals[i];
|
rlm@10
|
1840 frame[frameIndex++] = t;
|
rlm@10
|
1841 if(t == Frame.LONG || t == Frame.DOUBLE)
|
rlm@10
|
1842 {
|
rlm@10
|
1843 ++i;
|
rlm@10
|
1844 }
|
rlm@10
|
1845 }
|
rlm@10
|
1846 for(i = 0; i < stacks.length; ++i)
|
rlm@10
|
1847 {
|
rlm@10
|
1848 t = stacks[i];
|
rlm@10
|
1849 frame[frameIndex++] = t;
|
rlm@10
|
1850 if(t == Frame.LONG || t == Frame.DOUBLE)
|
rlm@10
|
1851 {
|
rlm@10
|
1852 ++i;
|
rlm@10
|
1853 }
|
rlm@10
|
1854 }
|
rlm@10
|
1855 endFrame();
|
rlm@10
|
1856 }
|
rlm@10
|
1857
|
rlm@10
|
1858 /**
|
rlm@10
|
1859 * Starts the visit of a stack map frame.
|
rlm@10
|
1860 *
|
rlm@10
|
1861 * @param offset the offset of the instruction to which the frame
|
rlm@10
|
1862 * corresponds.
|
rlm@10
|
1863 * @param nLocal the number of local variables in the frame.
|
rlm@10
|
1864 * @param nStack the number of stack elements in the frame.
|
rlm@10
|
1865 */
|
rlm@10
|
1866 private void startFrame(final int offset, final int nLocal, final int nStack){
|
rlm@10
|
1867 int n = 3 + nLocal + nStack;
|
rlm@10
|
1868 if(frame == null || frame.length < n)
|
rlm@10
|
1869 {
|
rlm@10
|
1870 frame = new int[n];
|
rlm@10
|
1871 }
|
rlm@10
|
1872 frame[0] = offset;
|
rlm@10
|
1873 frame[1] = nLocal;
|
rlm@10
|
1874 frame[2] = nStack;
|
rlm@10
|
1875 frameIndex = 3;
|
rlm@10
|
1876 }
|
rlm@10
|
1877
|
rlm@10
|
1878 /**
|
rlm@10
|
1879 * Checks if the visit of the current frame {@link #frame} is finished, and
|
rlm@10
|
1880 * if yes, write it in the StackMapTable attribute.
|
rlm@10
|
1881 */
|
rlm@10
|
1882 private void endFrame(){
|
rlm@10
|
1883 if(previousFrame != null)
|
rlm@10
|
1884 { // do not write the first frame
|
rlm@10
|
1885 if(stackMap == null)
|
rlm@10
|
1886 {
|
rlm@10
|
1887 stackMap = new ByteVector();
|
rlm@10
|
1888 }
|
rlm@10
|
1889 writeFrame();
|
rlm@10
|
1890 ++frameCount;
|
rlm@10
|
1891 }
|
rlm@10
|
1892 previousFrame = frame;
|
rlm@10
|
1893 frame = null;
|
rlm@10
|
1894 }
|
rlm@10
|
1895
|
rlm@10
|
1896 /**
|
rlm@10
|
1897 * Compress and writes the current frame {@link #frame} in the StackMapTable
|
rlm@10
|
1898 * attribute.
|
rlm@10
|
1899 */
|
rlm@10
|
1900 private void writeFrame(){
|
rlm@10
|
1901 int clocalsSize = frame[1];
|
rlm@10
|
1902 int cstackSize = frame[2];
|
rlm@10
|
1903 if((cw.version & 0xFFFF) < Opcodes.V1_6)
|
rlm@10
|
1904 {
|
rlm@10
|
1905 stackMap.putShort(frame[0]).putShort(clocalsSize);
|
rlm@10
|
1906 writeFrameTypes(3, 3 + clocalsSize);
|
rlm@10
|
1907 stackMap.putShort(cstackSize);
|
rlm@10
|
1908 writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
|
rlm@10
|
1909 return;
|
rlm@10
|
1910 }
|
rlm@10
|
1911 int localsSize = previousFrame[1];
|
rlm@10
|
1912 int type = FULL_FRAME;
|
rlm@10
|
1913 int k = 0;
|
rlm@10
|
1914 int delta;
|
rlm@10
|
1915 if(frameCount == 0)
|
rlm@10
|
1916 {
|
rlm@10
|
1917 delta = frame[0];
|
rlm@10
|
1918 }
|
rlm@10
|
1919 else
|
rlm@10
|
1920 {
|
rlm@10
|
1921 delta = frame[0] - previousFrame[0] - 1;
|
rlm@10
|
1922 }
|
rlm@10
|
1923 if(cstackSize == 0)
|
rlm@10
|
1924 {
|
rlm@10
|
1925 k = clocalsSize - localsSize;
|
rlm@10
|
1926 switch(k)
|
rlm@10
|
1927 {
|
rlm@10
|
1928 case-3:
|
rlm@10
|
1929 case-2:
|
rlm@10
|
1930 case-1:
|
rlm@10
|
1931 type = CHOP_FRAME;
|
rlm@10
|
1932 localsSize = clocalsSize;
|
rlm@10
|
1933 break;
|
rlm@10
|
1934 case 0:
|
rlm@10
|
1935 type = delta < 64 ? SAME_FRAME : SAME_FRAME_EXTENDED;
|
rlm@10
|
1936 break;
|
rlm@10
|
1937 case 1:
|
rlm@10
|
1938 case 2:
|
rlm@10
|
1939 case 3:
|
rlm@10
|
1940 type = APPEND_FRAME;
|
rlm@10
|
1941 break;
|
rlm@10
|
1942 }
|
rlm@10
|
1943 }
|
rlm@10
|
1944 else if(clocalsSize == localsSize && cstackSize == 1)
|
rlm@10
|
1945 {
|
rlm@10
|
1946 type = delta < 63
|
rlm@10
|
1947 ? SAME_LOCALS_1_STACK_ITEM_FRAME
|
rlm@10
|
1948 : SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED;
|
rlm@10
|
1949 }
|
rlm@10
|
1950 if(type != FULL_FRAME)
|
rlm@10
|
1951 {
|
rlm@10
|
1952 // verify if locals are the same
|
rlm@10
|
1953 int l = 3;
|
rlm@10
|
1954 for(int j = 0; j < localsSize; j++)
|
rlm@10
|
1955 {
|
rlm@10
|
1956 if(frame[l] != previousFrame[l])
|
rlm@10
|
1957 {
|
rlm@10
|
1958 type = FULL_FRAME;
|
rlm@10
|
1959 break;
|
rlm@10
|
1960 }
|
rlm@10
|
1961 l++;
|
rlm@10
|
1962 }
|
rlm@10
|
1963 }
|
rlm@10
|
1964 switch(type)
|
rlm@10
|
1965 {
|
rlm@10
|
1966 case SAME_FRAME:
|
rlm@10
|
1967 stackMap.putByte(delta);
|
rlm@10
|
1968 break;
|
rlm@10
|
1969 case SAME_LOCALS_1_STACK_ITEM_FRAME:
|
rlm@10
|
1970 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME + delta);
|
rlm@10
|
1971 writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
|
rlm@10
|
1972 break;
|
rlm@10
|
1973 case SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED:
|
rlm@10
|
1974 stackMap.putByte(SAME_LOCALS_1_STACK_ITEM_FRAME_EXTENDED)
|
rlm@10
|
1975 .putShort(delta);
|
rlm@10
|
1976 writeFrameTypes(3 + clocalsSize, 4 + clocalsSize);
|
rlm@10
|
1977 break;
|
rlm@10
|
1978 case SAME_FRAME_EXTENDED:
|
rlm@10
|
1979 stackMap.putByte(SAME_FRAME_EXTENDED).putShort(delta);
|
rlm@10
|
1980 break;
|
rlm@10
|
1981 case CHOP_FRAME:
|
rlm@10
|
1982 stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
|
rlm@10
|
1983 break;
|
rlm@10
|
1984 case APPEND_FRAME:
|
rlm@10
|
1985 stackMap.putByte(SAME_FRAME_EXTENDED + k).putShort(delta);
|
rlm@10
|
1986 writeFrameTypes(3 + localsSize, 3 + clocalsSize);
|
rlm@10
|
1987 break;
|
rlm@10
|
1988 // case FULL_FRAME:
|
rlm@10
|
1989 default:
|
rlm@10
|
1990 stackMap.putByte(FULL_FRAME)
|
rlm@10
|
1991 .putShort(delta)
|
rlm@10
|
1992 .putShort(clocalsSize);
|
rlm@10
|
1993 writeFrameTypes(3, 3 + clocalsSize);
|
rlm@10
|
1994 stackMap.putShort(cstackSize);
|
rlm@10
|
1995 writeFrameTypes(3 + clocalsSize, 3 + clocalsSize + cstackSize);
|
rlm@10
|
1996 }
|
rlm@10
|
1997 }
|
rlm@10
|
1998
|
rlm@10
|
1999 /**
|
rlm@10
|
2000 * Writes some types of the current frame {@link #frame} into the
|
rlm@10
|
2001 * StackMapTableAttribute. This method converts types from the format used
|
rlm@10
|
2002 * in {@link Label} to the format used in StackMapTable attributes. In
|
rlm@10
|
2003 * particular, it converts type table indexes to constant pool indexes.
|
rlm@10
|
2004 *
|
rlm@10
|
2005 * @param start index of the first type in {@link #frame} to write.
|
rlm@10
|
2006 * @param end index of last type in {@link #frame} to write (exclusive).
|
rlm@10
|
2007 */
|
rlm@10
|
2008 private void writeFrameTypes(final int start, final int end){
|
rlm@10
|
2009 for(int i = start; i < end; ++i)
|
rlm@10
|
2010 {
|
rlm@10
|
2011 int t = frame[i];
|
rlm@10
|
2012 int d = t & Frame.DIM;
|
rlm@10
|
2013 if(d == 0)
|
rlm@10
|
2014 {
|
rlm@10
|
2015 int v = t & Frame.BASE_VALUE;
|
rlm@10
|
2016 switch(t & Frame.BASE_KIND)
|
rlm@10
|
2017 {
|
rlm@10
|
2018 case Frame.OBJECT:
|
rlm@10
|
2019 stackMap.putByte(7)
|
rlm@10
|
2020 .putShort(cw.newClass(cw.typeTable[v].strVal1));
|
rlm@10
|
2021 break;
|
rlm@10
|
2022 case Frame.UNINITIALIZED:
|
rlm@10
|
2023 stackMap.putByte(8).putShort(cw.typeTable[v].intVal);
|
rlm@10
|
2024 break;
|
rlm@10
|
2025 default:
|
rlm@10
|
2026 stackMap.putByte(v);
|
rlm@10
|
2027 }
|
rlm@10
|
2028 }
|
rlm@10
|
2029 else
|
rlm@10
|
2030 {
|
rlm@10
|
2031 StringBuffer buf = new StringBuffer();
|
rlm@10
|
2032 d >>= 28;
|
rlm@10
|
2033 while(d-- > 0)
|
rlm@10
|
2034 {
|
rlm@10
|
2035 buf.append('[');
|
rlm@10
|
2036 }
|
rlm@10
|
2037 if((t & Frame.BASE_KIND) == Frame.OBJECT)
|
rlm@10
|
2038 {
|
rlm@10
|
2039 buf.append('L');
|
rlm@10
|
2040 buf.append(cw.typeTable[t & Frame.BASE_VALUE].strVal1);
|
rlm@10
|
2041 buf.append(';');
|
rlm@10
|
2042 }
|
rlm@10
|
2043 else
|
rlm@10
|
2044 {
|
rlm@10
|
2045 switch(t & 0xF)
|
rlm@10
|
2046 {
|
rlm@10
|
2047 case 1:
|
rlm@10
|
2048 buf.append('I');
|
rlm@10
|
2049 break;
|
rlm@10
|
2050 case 2:
|
rlm@10
|
2051 buf.append('F');
|
rlm@10
|
2052 break;
|
rlm@10
|
2053 case 3:
|
rlm@10
|
2054 buf.append('D');
|
rlm@10
|
2055 break;
|
rlm@10
|
2056 case 9:
|
rlm@10
|
2057 buf.append('Z');
|
rlm@10
|
2058 break;
|
rlm@10
|
2059 case 10:
|
rlm@10
|
2060 buf.append('B');
|
rlm@10
|
2061 break;
|
rlm@10
|
2062 case 11:
|
rlm@10
|
2063 buf.append('C');
|
rlm@10
|
2064 break;
|
rlm@10
|
2065 case 12:
|
rlm@10
|
2066 buf.append('S');
|
rlm@10
|
2067 break;
|
rlm@10
|
2068 default:
|
rlm@10
|
2069 buf.append('J');
|
rlm@10
|
2070 }
|
rlm@10
|
2071 }
|
rlm@10
|
2072 stackMap.putByte(7).putShort(cw.newClass(buf.toString()));
|
rlm@10
|
2073 }
|
rlm@10
|
2074 }
|
rlm@10
|
2075 }
|
rlm@10
|
2076
|
rlm@10
|
2077 private void writeFrameType(final Object type){
|
rlm@10
|
2078 if(type instanceof String)
|
rlm@10
|
2079 {
|
rlm@10
|
2080 stackMap.putByte(7).putShort(cw.newClass((String) type));
|
rlm@10
|
2081 }
|
rlm@10
|
2082 else if(type instanceof Integer)
|
rlm@10
|
2083 {
|
rlm@10
|
2084 stackMap.putByte(((Integer) type).intValue());
|
rlm@10
|
2085 }
|
rlm@10
|
2086 else
|
rlm@10
|
2087 {
|
rlm@10
|
2088 stackMap.putByte(8).putShort(((Label) type).position);
|
rlm@10
|
2089 }
|
rlm@10
|
2090 }
|
rlm@10
|
2091
|
rlm@10
|
2092 // ------------------------------------------------------------------------
|
rlm@10
|
2093 // Utility methods: dump bytecode array
|
rlm@10
|
2094 // ------------------------------------------------------------------------
|
rlm@10
|
2095
|
rlm@10
|
2096 /**
|
rlm@10
|
2097 * Returns the size of the bytecode of this method.
|
rlm@10
|
2098 *
|
rlm@10
|
2099 * @return the size of the bytecode of this method.
|
rlm@10
|
2100 */
|
rlm@10
|
2101 final int getSize(){
|
rlm@10
|
2102 if(classReaderOffset != 0)
|
rlm@10
|
2103 {
|
rlm@10
|
2104 return 6 + classReaderLength;
|
rlm@10
|
2105 }
|
rlm@10
|
2106 if(resize)
|
rlm@10
|
2107 {
|
rlm@10
|
2108 // replaces the temporary jump opcodes introduced by Label.resolve.
|
rlm@10
|
2109 resizeInstructions();
|
rlm@10
|
2110 }
|
rlm@10
|
2111 int size = 8;
|
rlm@10
|
2112 if(code.length > 0)
|
rlm@10
|
2113 {
|
rlm@10
|
2114 cw.newUTF8("Code");
|
rlm@10
|
2115 size += 18 + code.length + 8 * handlerCount;
|
rlm@10
|
2116 if(localVar != null)
|
rlm@10
|
2117 {
|
rlm@10
|
2118 cw.newUTF8("LocalVariableTable");
|
rlm@10
|
2119 size += 8 + localVar.length;
|
rlm@10
|
2120 }
|
rlm@10
|
2121 if(localVarType != null)
|
rlm@10
|
2122 {
|
rlm@10
|
2123 cw.newUTF8("LocalVariableTypeTable");
|
rlm@10
|
2124 size += 8 + localVarType.length;
|
rlm@10
|
2125 }
|
rlm@10
|
2126 if(lineNumber != null)
|
rlm@10
|
2127 {
|
rlm@10
|
2128 cw.newUTF8("LineNumberTable");
|
rlm@10
|
2129 size += 8 + lineNumber.length;
|
rlm@10
|
2130 }
|
rlm@10
|
2131 if(stackMap != null)
|
rlm@10
|
2132 {
|
rlm@10
|
2133 boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
|
rlm@10
|
2134 cw.newUTF8(zip ? "StackMapTable" : "StackMap");
|
rlm@10
|
2135 size += 8 + stackMap.length;
|
rlm@10
|
2136 }
|
rlm@10
|
2137 if(cattrs != null)
|
rlm@10
|
2138 {
|
rlm@10
|
2139 size += cattrs.getSize(cw,
|
rlm@10
|
2140 code.data,
|
rlm@10
|
2141 code.length,
|
rlm@10
|
2142 maxStack,
|
rlm@10
|
2143 maxLocals);
|
rlm@10
|
2144 }
|
rlm@10
|
2145 }
|
rlm@10
|
2146 if(exceptionCount > 0)
|
rlm@10
|
2147 {
|
rlm@10
|
2148 cw.newUTF8("Exceptions");
|
rlm@10
|
2149 size += 8 + 2 * exceptionCount;
|
rlm@10
|
2150 }
|
rlm@10
|
2151 if((access & Opcodes.ACC_SYNTHETIC) != 0
|
rlm@10
|
2152 && (cw.version & 0xffff) < Opcodes.V1_5)
|
rlm@10
|
2153 {
|
rlm@10
|
2154 cw.newUTF8("Synthetic");
|
rlm@10
|
2155 size += 6;
|
rlm@10
|
2156 }
|
rlm@10
|
2157 if((access & Opcodes.ACC_DEPRECATED) != 0)
|
rlm@10
|
2158 {
|
rlm@10
|
2159 cw.newUTF8("Deprecated");
|
rlm@10
|
2160 size += 6;
|
rlm@10
|
2161 }
|
rlm@10
|
2162 if(signature != null)
|
rlm@10
|
2163 {
|
rlm@10
|
2164 cw.newUTF8("Signature");
|
rlm@10
|
2165 cw.newUTF8(signature);
|
rlm@10
|
2166 size += 8;
|
rlm@10
|
2167 }
|
rlm@10
|
2168 if(annd != null)
|
rlm@10
|
2169 {
|
rlm@10
|
2170 cw.newUTF8("AnnotationDefault");
|
rlm@10
|
2171 size += 6 + annd.length;
|
rlm@10
|
2172 }
|
rlm@10
|
2173 if(anns != null)
|
rlm@10
|
2174 {
|
rlm@10
|
2175 cw.newUTF8("RuntimeVisibleAnnotations");
|
rlm@10
|
2176 size += 8 + anns.getSize();
|
rlm@10
|
2177 }
|
rlm@10
|
2178 if(ianns != null)
|
rlm@10
|
2179 {
|
rlm@10
|
2180 cw.newUTF8("RuntimeInvisibleAnnotations");
|
rlm@10
|
2181 size += 8 + ianns.getSize();
|
rlm@10
|
2182 }
|
rlm@10
|
2183 if(panns != null)
|
rlm@10
|
2184 {
|
rlm@10
|
2185 cw.newUTF8("RuntimeVisibleParameterAnnotations");
|
rlm@10
|
2186 size += 7 + 2 * panns.length;
|
rlm@10
|
2187 for(int i = panns.length - 1; i >= 0; --i)
|
rlm@10
|
2188 {
|
rlm@10
|
2189 size += panns[i] == null ? 0 : panns[i].getSize();
|
rlm@10
|
2190 }
|
rlm@10
|
2191 }
|
rlm@10
|
2192 if(ipanns != null)
|
rlm@10
|
2193 {
|
rlm@10
|
2194 cw.newUTF8("RuntimeInvisibleParameterAnnotations");
|
rlm@10
|
2195 size += 7 + 2 * ipanns.length;
|
rlm@10
|
2196 for(int i = ipanns.length - 1; i >= 0; --i)
|
rlm@10
|
2197 {
|
rlm@10
|
2198 size += ipanns[i] == null ? 0 : ipanns[i].getSize();
|
rlm@10
|
2199 }
|
rlm@10
|
2200 }
|
rlm@10
|
2201 if(attrs != null)
|
rlm@10
|
2202 {
|
rlm@10
|
2203 size += attrs.getSize(cw, null, 0, -1, -1);
|
rlm@10
|
2204 }
|
rlm@10
|
2205 return size;
|
rlm@10
|
2206 }
|
rlm@10
|
2207
|
rlm@10
|
2208 /**
|
rlm@10
|
2209 * Puts the bytecode of this method in the given byte vector.
|
rlm@10
|
2210 *
|
rlm@10
|
2211 * @param out the byte vector into which the bytecode of this method must be
|
rlm@10
|
2212 * copied.
|
rlm@10
|
2213 */
|
rlm@10
|
2214 final void put(final ByteVector out){
|
rlm@10
|
2215 out.putShort(access).putShort(name).putShort(desc);
|
rlm@10
|
2216 if(classReaderOffset != 0)
|
rlm@10
|
2217 {
|
rlm@10
|
2218 out.putByteArray(cw.cr.b, classReaderOffset, classReaderLength);
|
rlm@10
|
2219 return;
|
rlm@10
|
2220 }
|
rlm@10
|
2221 int attributeCount = 0;
|
rlm@10
|
2222 if(code.length > 0)
|
rlm@10
|
2223 {
|
rlm@10
|
2224 ++attributeCount;
|
rlm@10
|
2225 }
|
rlm@10
|
2226 if(exceptionCount > 0)
|
rlm@10
|
2227 {
|
rlm@10
|
2228 ++attributeCount;
|
rlm@10
|
2229 }
|
rlm@10
|
2230 if((access & Opcodes.ACC_SYNTHETIC) != 0
|
rlm@10
|
2231 && (cw.version & 0xffff) < Opcodes.V1_5)
|
rlm@10
|
2232 {
|
rlm@10
|
2233 ++attributeCount;
|
rlm@10
|
2234 }
|
rlm@10
|
2235 if((access & Opcodes.ACC_DEPRECATED) != 0)
|
rlm@10
|
2236 {
|
rlm@10
|
2237 ++attributeCount;
|
rlm@10
|
2238 }
|
rlm@10
|
2239 if(signature != null)
|
rlm@10
|
2240 {
|
rlm@10
|
2241 ++attributeCount;
|
rlm@10
|
2242 }
|
rlm@10
|
2243 if(annd != null)
|
rlm@10
|
2244 {
|
rlm@10
|
2245 ++attributeCount;
|
rlm@10
|
2246 }
|
rlm@10
|
2247 if(anns != null)
|
rlm@10
|
2248 {
|
rlm@10
|
2249 ++attributeCount;
|
rlm@10
|
2250 }
|
rlm@10
|
2251 if(ianns != null)
|
rlm@10
|
2252 {
|
rlm@10
|
2253 ++attributeCount;
|
rlm@10
|
2254 }
|
rlm@10
|
2255 if(panns != null)
|
rlm@10
|
2256 {
|
rlm@10
|
2257 ++attributeCount;
|
rlm@10
|
2258 }
|
rlm@10
|
2259 if(ipanns != null)
|
rlm@10
|
2260 {
|
rlm@10
|
2261 ++attributeCount;
|
rlm@10
|
2262 }
|
rlm@10
|
2263 if(attrs != null)
|
rlm@10
|
2264 {
|
rlm@10
|
2265 attributeCount += attrs.getCount();
|
rlm@10
|
2266 }
|
rlm@10
|
2267 out.putShort(attributeCount);
|
rlm@10
|
2268 if(code.length > 0)
|
rlm@10
|
2269 {
|
rlm@10
|
2270 int size = 12 + code.length + 8 * handlerCount;
|
rlm@10
|
2271 if(localVar != null)
|
rlm@10
|
2272 {
|
rlm@10
|
2273 size += 8 + localVar.length;
|
rlm@10
|
2274 }
|
rlm@10
|
2275 if(localVarType != null)
|
rlm@10
|
2276 {
|
rlm@10
|
2277 size += 8 + localVarType.length;
|
rlm@10
|
2278 }
|
rlm@10
|
2279 if(lineNumber != null)
|
rlm@10
|
2280 {
|
rlm@10
|
2281 size += 8 + lineNumber.length;
|
rlm@10
|
2282 }
|
rlm@10
|
2283 if(stackMap != null)
|
rlm@10
|
2284 {
|
rlm@10
|
2285 size += 8 + stackMap.length;
|
rlm@10
|
2286 }
|
rlm@10
|
2287 if(cattrs != null)
|
rlm@10
|
2288 {
|
rlm@10
|
2289 size += cattrs.getSize(cw,
|
rlm@10
|
2290 code.data,
|
rlm@10
|
2291 code.length,
|
rlm@10
|
2292 maxStack,
|
rlm@10
|
2293 maxLocals);
|
rlm@10
|
2294 }
|
rlm@10
|
2295 out.putShort(cw.newUTF8("Code")).putInt(size);
|
rlm@10
|
2296 out.putShort(maxStack).putShort(maxLocals);
|
rlm@10
|
2297 out.putInt(code.length).putByteArray(code.data, 0, code.length);
|
rlm@10
|
2298 out.putShort(handlerCount);
|
rlm@10
|
2299 if(handlerCount > 0)
|
rlm@10
|
2300 {
|
rlm@10
|
2301 Handler h = firstHandler;
|
rlm@10
|
2302 while(h != null)
|
rlm@10
|
2303 {
|
rlm@10
|
2304 out.putShort(h.start.position)
|
rlm@10
|
2305 .putShort(h.end.position)
|
rlm@10
|
2306 .putShort(h.handler.position)
|
rlm@10
|
2307 .putShort(h.type);
|
rlm@10
|
2308 h = h.next;
|
rlm@10
|
2309 }
|
rlm@10
|
2310 }
|
rlm@10
|
2311 attributeCount = 0;
|
rlm@10
|
2312 if(localVar != null)
|
rlm@10
|
2313 {
|
rlm@10
|
2314 ++attributeCount;
|
rlm@10
|
2315 }
|
rlm@10
|
2316 if(localVarType != null)
|
rlm@10
|
2317 {
|
rlm@10
|
2318 ++attributeCount;
|
rlm@10
|
2319 }
|
rlm@10
|
2320 if(lineNumber != null)
|
rlm@10
|
2321 {
|
rlm@10
|
2322 ++attributeCount;
|
rlm@10
|
2323 }
|
rlm@10
|
2324 if(stackMap != null)
|
rlm@10
|
2325 {
|
rlm@10
|
2326 ++attributeCount;
|
rlm@10
|
2327 }
|
rlm@10
|
2328 if(cattrs != null)
|
rlm@10
|
2329 {
|
rlm@10
|
2330 attributeCount += cattrs.getCount();
|
rlm@10
|
2331 }
|
rlm@10
|
2332 out.putShort(attributeCount);
|
rlm@10
|
2333 if(localVar != null)
|
rlm@10
|
2334 {
|
rlm@10
|
2335 out.putShort(cw.newUTF8("LocalVariableTable"));
|
rlm@10
|
2336 out.putInt(localVar.length + 2).putShort(localVarCount);
|
rlm@10
|
2337 out.putByteArray(localVar.data, 0, localVar.length);
|
rlm@10
|
2338 }
|
rlm@10
|
2339 if(localVarType != null)
|
rlm@10
|
2340 {
|
rlm@10
|
2341 out.putShort(cw.newUTF8("LocalVariableTypeTable"));
|
rlm@10
|
2342 out.putInt(localVarType.length + 2).putShort(localVarTypeCount);
|
rlm@10
|
2343 out.putByteArray(localVarType.data, 0, localVarType.length);
|
rlm@10
|
2344 }
|
rlm@10
|
2345 if(lineNumber != null)
|
rlm@10
|
2346 {
|
rlm@10
|
2347 out.putShort(cw.newUTF8("LineNumberTable"));
|
rlm@10
|
2348 out.putInt(lineNumber.length + 2).putShort(lineNumberCount);
|
rlm@10
|
2349 out.putByteArray(lineNumber.data, 0, lineNumber.length);
|
rlm@10
|
2350 }
|
rlm@10
|
2351 if(stackMap != null)
|
rlm@10
|
2352 {
|
rlm@10
|
2353 boolean zip = (cw.version & 0xFFFF) >= Opcodes.V1_6;
|
rlm@10
|
2354 out.putShort(cw.newUTF8(zip ? "StackMapTable" : "StackMap"));
|
rlm@10
|
2355 out.putInt(stackMap.length + 2).putShort(frameCount);
|
rlm@10
|
2356 out.putByteArray(stackMap.data, 0, stackMap.length);
|
rlm@10
|
2357 }
|
rlm@10
|
2358 if(cattrs != null)
|
rlm@10
|
2359 {
|
rlm@10
|
2360 cattrs.put(cw, code.data, code.length, maxLocals, maxStack, out);
|
rlm@10
|
2361 }
|
rlm@10
|
2362 }
|
rlm@10
|
2363 if(exceptionCount > 0)
|
rlm@10
|
2364 {
|
rlm@10
|
2365 out.putShort(cw.newUTF8("Exceptions"))
|
rlm@10
|
2366 .putInt(2 * exceptionCount + 2);
|
rlm@10
|
2367 out.putShort(exceptionCount);
|
rlm@10
|
2368 for(int i = 0; i < exceptionCount; ++i)
|
rlm@10
|
2369 {
|
rlm@10
|
2370 out.putShort(exceptions[i]);
|
rlm@10
|
2371 }
|
rlm@10
|
2372 }
|
rlm@10
|
2373 if((access & Opcodes.ACC_SYNTHETIC) != 0
|
rlm@10
|
2374 && (cw.version & 0xffff) < Opcodes.V1_5)
|
rlm@10
|
2375 {
|
rlm@10
|
2376 out.putShort(cw.newUTF8("Synthetic")).putInt(0);
|
rlm@10
|
2377 }
|
rlm@10
|
2378 if((access & Opcodes.ACC_DEPRECATED) != 0)
|
rlm@10
|
2379 {
|
rlm@10
|
2380 out.putShort(cw.newUTF8("Deprecated")).putInt(0);
|
rlm@10
|
2381 }
|
rlm@10
|
2382 if(signature != null)
|
rlm@10
|
2383 {
|
rlm@10
|
2384 out.putShort(cw.newUTF8("Signature"))
|
rlm@10
|
2385 .putInt(2)
|
rlm@10
|
2386 .putShort(cw.newUTF8(signature));
|
rlm@10
|
2387 }
|
rlm@10
|
2388 if(annd != null)
|
rlm@10
|
2389 {
|
rlm@10
|
2390 out.putShort(cw.newUTF8("AnnotationDefault"));
|
rlm@10
|
2391 out.putInt(annd.length);
|
rlm@10
|
2392 out.putByteArray(annd.data, 0, annd.length);
|
rlm@10
|
2393 }
|
rlm@10
|
2394 if(anns != null)
|
rlm@10
|
2395 {
|
rlm@10
|
2396 out.putShort(cw.newUTF8("RuntimeVisibleAnnotations"));
|
rlm@10
|
2397 anns.put(out);
|
rlm@10
|
2398 }
|
rlm@10
|
2399 if(ianns != null)
|
rlm@10
|
2400 {
|
rlm@10
|
2401 out.putShort(cw.newUTF8("RuntimeInvisibleAnnotations"));
|
rlm@10
|
2402 ianns.put(out);
|
rlm@10
|
2403 }
|
rlm@10
|
2404 if(panns != null)
|
rlm@10
|
2405 {
|
rlm@10
|
2406 out.putShort(cw.newUTF8("RuntimeVisibleParameterAnnotations"));
|
rlm@10
|
2407 AnnotationWriter.put(panns, out);
|
rlm@10
|
2408 }
|
rlm@10
|
2409 if(ipanns != null)
|
rlm@10
|
2410 {
|
rlm@10
|
2411 out.putShort(cw.newUTF8("RuntimeInvisibleParameterAnnotations"));
|
rlm@10
|
2412 AnnotationWriter.put(ipanns, out);
|
rlm@10
|
2413 }
|
rlm@10
|
2414 if(attrs != null)
|
rlm@10
|
2415 {
|
rlm@10
|
2416 attrs.put(cw, null, 0, -1, -1, out);
|
rlm@10
|
2417 }
|
rlm@10
|
2418 }
|
rlm@10
|
2419
|
rlm@10
|
2420 // ------------------------------------------------------------------------
|
rlm@10
|
2421 // Utility methods: instruction resizing (used to handle GOTO_W and JSR_W)
|
rlm@10
|
2422 // ------------------------------------------------------------------------
|
rlm@10
|
2423
|
rlm@10
|
2424 /**
|
rlm@10
|
2425 * Resizes and replaces the temporary instructions inserted by
|
rlm@10
|
2426 * {@link Label#resolve} for wide forward jumps, while keeping jump offsets
|
rlm@10
|
2427 * and instruction addresses consistent. This may require to resize other
|
rlm@10
|
2428 * existing instructions, or even to introduce new instructions: for
|
rlm@10
|
2429 * example, increasing the size of an instruction by 2 at the middle of a
|
rlm@10
|
2430 * method can increases the offset of an IFEQ instruction from 32766 to
|
rlm@10
|
2431 * 32768, in which case IFEQ 32766 must be replaced with IFNEQ 8 GOTO_W
|
rlm@10
|
2432 * 32765. This, in turn, may require to increase the size of another jump
|
rlm@10
|
2433 * instruction, and so on... All these operations are handled automatically
|
rlm@10
|
2434 * by this method. <p> <i>This method must be called after all the method
|
rlm@10
|
2435 * that is being built has been visited</i>. In particular, the
|
rlm@10
|
2436 * {@link Label Label} objects used to construct the method are no longer
|
rlm@10
|
2437 * valid after this method has been called.
|
rlm@10
|
2438 */
|
rlm@10
|
2439 private void resizeInstructions(){
|
rlm@10
|
2440 byte[] b = code.data; // bytecode of the method
|
rlm@10
|
2441 int u, v, label; // indexes in b
|
rlm@10
|
2442 int i, j; // loop indexes
|
rlm@10
|
2443 /*
|
rlm@10
|
2444 * 1st step: As explained above, resizing an instruction may require to
|
rlm@10
|
2445 * resize another one, which may require to resize yet another one, and
|
rlm@10
|
2446 * so on. The first step of the algorithm consists in finding all the
|
rlm@10
|
2447 * instructions that need to be resized, without modifying the code.
|
rlm@10
|
2448 * This is done by the following "fix point" algorithm:
|
rlm@10
|
2449 *
|
rlm@10
|
2450 * Parse the code to find the jump instructions whose offset will need
|
rlm@10
|
2451 * more than 2 bytes to be stored (the future offset is computed from
|
rlm@10
|
2452 * the current offset and from the number of bytes that will be inserted
|
rlm@10
|
2453 * or removed between the source and target instructions). For each such
|
rlm@10
|
2454 * instruction, adds an entry in (a copy of) the indexes and sizes
|
rlm@10
|
2455 * arrays (if this has not already been done in a previous iteration!).
|
rlm@10
|
2456 *
|
rlm@10
|
2457 * If at least one entry has been added during the previous step, go
|
rlm@10
|
2458 * back to the beginning, otherwise stop.
|
rlm@10
|
2459 *
|
rlm@10
|
2460 * In fact the real algorithm is complicated by the fact that the size
|
rlm@10
|
2461 * of TABLESWITCH and LOOKUPSWITCH instructions depends on their
|
rlm@10
|
2462 * position in the bytecode (because of padding). In order to ensure the
|
rlm@10
|
2463 * convergence of the algorithm, the number of bytes to be added or
|
rlm@10
|
2464 * removed from these instructions is over estimated during the previous
|
rlm@10
|
2465 * loop, and computed exactly only after the loop is finished (this
|
rlm@10
|
2466 * requires another pass to parse the bytecode of the method).
|
rlm@10
|
2467 */
|
rlm@10
|
2468 int[] allIndexes = new int[0]; // copy of indexes
|
rlm@10
|
2469 int[] allSizes = new int[0]; // copy of sizes
|
rlm@10
|
2470 boolean[] resize; // instructions to be resized
|
rlm@10
|
2471 int newOffset; // future offset of a jump instruction
|
rlm@10
|
2472
|
rlm@10
|
2473 resize = new boolean[code.length];
|
rlm@10
|
2474
|
rlm@10
|
2475 // 3 = loop again, 2 = loop ended, 1 = last pass, 0 = done
|
rlm@10
|
2476 int state = 3;
|
rlm@10
|
2477 do
|
rlm@10
|
2478 {
|
rlm@10
|
2479 if(state == 3)
|
rlm@10
|
2480 {
|
rlm@10
|
2481 state = 2;
|
rlm@10
|
2482 }
|
rlm@10
|
2483 u = 0;
|
rlm@10
|
2484 while(u < b.length)
|
rlm@10
|
2485 {
|
rlm@10
|
2486 int opcode = b[u] & 0xFF; // opcode of current instruction
|
rlm@10
|
2487 int insert = 0; // bytes to be added after this instruction
|
rlm@10
|
2488
|
rlm@10
|
2489 switch(ClassWriter.TYPE[opcode])
|
rlm@10
|
2490 {
|
rlm@10
|
2491 case ClassWriter.NOARG_INSN:
|
rlm@10
|
2492 case ClassWriter.IMPLVAR_INSN:
|
rlm@10
|
2493 u += 1;
|
rlm@10
|
2494 break;
|
rlm@10
|
2495 case ClassWriter.LABEL_INSN:
|
rlm@10
|
2496 if(opcode > 201)
|
rlm@10
|
2497 {
|
rlm@10
|
2498 // converts temporary opcodes 202 to 217, 218 and
|
rlm@10
|
2499 // 219 to IFEQ ... JSR (inclusive), IFNULL and
|
rlm@10
|
2500 // IFNONNULL
|
rlm@10
|
2501 opcode = opcode < 218 ? opcode - 49 : opcode - 20;
|
rlm@10
|
2502 label = u + readUnsignedShort(b, u + 1);
|
rlm@10
|
2503 }
|
rlm@10
|
2504 else
|
rlm@10
|
2505 {
|
rlm@10
|
2506 label = u + readShort(b, u + 1);
|
rlm@10
|
2507 }
|
rlm@10
|
2508 newOffset = getNewOffset(allIndexes, allSizes, u, label);
|
rlm@10
|
2509 if(newOffset < Short.MIN_VALUE
|
rlm@10
|
2510 || newOffset > Short.MAX_VALUE)
|
rlm@10
|
2511 {
|
rlm@10
|
2512 if(!resize[u])
|
rlm@10
|
2513 {
|
rlm@10
|
2514 if(opcode == Opcodes.GOTO
|
rlm@10
|
2515 || opcode == Opcodes.JSR)
|
rlm@10
|
2516 {
|
rlm@10
|
2517 // two additional bytes will be required to
|
rlm@10
|
2518 // replace this GOTO or JSR instruction with
|
rlm@10
|
2519 // a GOTO_W or a JSR_W
|
rlm@10
|
2520 insert = 2;
|
rlm@10
|
2521 }
|
rlm@10
|
2522 else
|
rlm@10
|
2523 {
|
rlm@10
|
2524 // five additional bytes will be required to
|
rlm@10
|
2525 // replace this IFxxx <l> instruction with
|
rlm@10
|
2526 // IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx
|
rlm@10
|
2527 // is the "opposite" opcode of IFxxx (i.e.,
|
rlm@10
|
2528 // IFNE for IFEQ) and where <l'> designates
|
rlm@10
|
2529 // the instruction just after the GOTO_W.
|
rlm@10
|
2530 insert = 5;
|
rlm@10
|
2531 }
|
rlm@10
|
2532 resize[u] = true;
|
rlm@10
|
2533 }
|
rlm@10
|
2534 }
|
rlm@10
|
2535 u += 3;
|
rlm@10
|
2536 break;
|
rlm@10
|
2537 case ClassWriter.LABELW_INSN:
|
rlm@10
|
2538 u += 5;
|
rlm@10
|
2539 break;
|
rlm@10
|
2540 case ClassWriter.TABL_INSN:
|
rlm@10
|
2541 if(state == 1)
|
rlm@10
|
2542 {
|
rlm@10
|
2543 // true number of bytes to be added (or removed)
|
rlm@10
|
2544 // from this instruction = (future number of padding
|
rlm@10
|
2545 // bytes - current number of padding byte) -
|
rlm@10
|
2546 // previously over estimated variation =
|
rlm@10
|
2547 // = ((3 - newOffset%4) - (3 - u%4)) - u%4
|
rlm@10
|
2548 // = (-newOffset%4 + u%4) - u%4
|
rlm@10
|
2549 // = -(newOffset & 3)
|
rlm@10
|
2550 newOffset = getNewOffset(allIndexes, allSizes, 0, u);
|
rlm@10
|
2551 insert = -(newOffset & 3);
|
rlm@10
|
2552 }
|
rlm@10
|
2553 else if(!resize[u])
|
rlm@10
|
2554 {
|
rlm@10
|
2555 // over estimation of the number of bytes to be
|
rlm@10
|
2556 // added to this instruction = 3 - current number
|
rlm@10
|
2557 // of padding bytes = 3 - (3 - u%4) = u%4 = u & 3
|
rlm@10
|
2558 insert = u & 3;
|
rlm@10
|
2559 resize[u] = true;
|
rlm@10
|
2560 }
|
rlm@10
|
2561 // skips instruction
|
rlm@10
|
2562 u = u + 4 - (u & 3);
|
rlm@10
|
2563 u += 4 * (readInt(b, u + 8) - readInt(b, u + 4) + 1) + 12;
|
rlm@10
|
2564 break;
|
rlm@10
|
2565 case ClassWriter.LOOK_INSN:
|
rlm@10
|
2566 if(state == 1)
|
rlm@10
|
2567 {
|
rlm@10
|
2568 // like TABL_INSN
|
rlm@10
|
2569 newOffset = getNewOffset(allIndexes, allSizes, 0, u);
|
rlm@10
|
2570 insert = -(newOffset & 3);
|
rlm@10
|
2571 }
|
rlm@10
|
2572 else if(!resize[u])
|
rlm@10
|
2573 {
|
rlm@10
|
2574 // like TABL_INSN
|
rlm@10
|
2575 insert = u & 3;
|
rlm@10
|
2576 resize[u] = true;
|
rlm@10
|
2577 }
|
rlm@10
|
2578 // skips instruction
|
rlm@10
|
2579 u = u + 4 - (u & 3);
|
rlm@10
|
2580 u += 8 * readInt(b, u + 4) + 8;
|
rlm@10
|
2581 break;
|
rlm@10
|
2582 case ClassWriter.WIDE_INSN:
|
rlm@10
|
2583 opcode = b[u + 1] & 0xFF;
|
rlm@10
|
2584 if(opcode == Opcodes.IINC)
|
rlm@10
|
2585 {
|
rlm@10
|
2586 u += 6;
|
rlm@10
|
2587 }
|
rlm@10
|
2588 else
|
rlm@10
|
2589 {
|
rlm@10
|
2590 u += 4;
|
rlm@10
|
2591 }
|
rlm@10
|
2592 break;
|
rlm@10
|
2593 case ClassWriter.VAR_INSN:
|
rlm@10
|
2594 case ClassWriter.SBYTE_INSN:
|
rlm@10
|
2595 case ClassWriter.LDC_INSN:
|
rlm@10
|
2596 u += 2;
|
rlm@10
|
2597 break;
|
rlm@10
|
2598 case ClassWriter.SHORT_INSN:
|
rlm@10
|
2599 case ClassWriter.LDCW_INSN:
|
rlm@10
|
2600 case ClassWriter.FIELDORMETH_INSN:
|
rlm@10
|
2601 case ClassWriter.TYPE_INSN:
|
rlm@10
|
2602 case ClassWriter.IINC_INSN:
|
rlm@10
|
2603 u += 3;
|
rlm@10
|
2604 break;
|
rlm@10
|
2605 case ClassWriter.ITFMETH_INSN:
|
rlm@10
|
2606 u += 5;
|
rlm@10
|
2607 break;
|
rlm@10
|
2608 // case ClassWriter.MANA_INSN:
|
rlm@10
|
2609 default:
|
rlm@10
|
2610 u += 4;
|
rlm@10
|
2611 break;
|
rlm@10
|
2612 }
|
rlm@10
|
2613 if(insert != 0)
|
rlm@10
|
2614 {
|
rlm@10
|
2615 // adds a new (u, insert) entry in the allIndexes and
|
rlm@10
|
2616 // allSizes arrays
|
rlm@10
|
2617 int[] newIndexes = new int[allIndexes.length + 1];
|
rlm@10
|
2618 int[] newSizes = new int[allSizes.length + 1];
|
rlm@10
|
2619 System.arraycopy(allIndexes,
|
rlm@10
|
2620 0,
|
rlm@10
|
2621 newIndexes,
|
rlm@10
|
2622 0,
|
rlm@10
|
2623 allIndexes.length);
|
rlm@10
|
2624 System.arraycopy(allSizes, 0, newSizes, 0, allSizes.length);
|
rlm@10
|
2625 newIndexes[allIndexes.length] = u;
|
rlm@10
|
2626 newSizes[allSizes.length] = insert;
|
rlm@10
|
2627 allIndexes = newIndexes;
|
rlm@10
|
2628 allSizes = newSizes;
|
rlm@10
|
2629 if(insert > 0)
|
rlm@10
|
2630 {
|
rlm@10
|
2631 state = 3;
|
rlm@10
|
2632 }
|
rlm@10
|
2633 }
|
rlm@10
|
2634 }
|
rlm@10
|
2635 if(state < 3)
|
rlm@10
|
2636 {
|
rlm@10
|
2637 --state;
|
rlm@10
|
2638 }
|
rlm@10
|
2639 } while(state != 0);
|
rlm@10
|
2640
|
rlm@10
|
2641 // 2nd step:
|
rlm@10
|
2642 // copies the bytecode of the method into a new bytevector, updates the
|
rlm@10
|
2643 // offsets, and inserts (or removes) bytes as requested.
|
rlm@10
|
2644
|
rlm@10
|
2645 ByteVector newCode = new ByteVector(code.length);
|
rlm@10
|
2646
|
rlm@10
|
2647 u = 0;
|
rlm@10
|
2648 while(u < code.length)
|
rlm@10
|
2649 {
|
rlm@10
|
2650 int opcode = b[u] & 0xFF;
|
rlm@10
|
2651 switch(ClassWriter.TYPE[opcode])
|
rlm@10
|
2652 {
|
rlm@10
|
2653 case ClassWriter.NOARG_INSN:
|
rlm@10
|
2654 case ClassWriter.IMPLVAR_INSN:
|
rlm@10
|
2655 newCode.putByte(opcode);
|
rlm@10
|
2656 u += 1;
|
rlm@10
|
2657 break;
|
rlm@10
|
2658 case ClassWriter.LABEL_INSN:
|
rlm@10
|
2659 if(opcode > 201)
|
rlm@10
|
2660 {
|
rlm@10
|
2661 // changes temporary opcodes 202 to 217 (inclusive), 218
|
rlm@10
|
2662 // and 219 to IFEQ ... JSR (inclusive), IFNULL and
|
rlm@10
|
2663 // IFNONNULL
|
rlm@10
|
2664 opcode = opcode < 218 ? opcode - 49 : opcode - 20;
|
rlm@10
|
2665 label = u + readUnsignedShort(b, u + 1);
|
rlm@10
|
2666 }
|
rlm@10
|
2667 else
|
rlm@10
|
2668 {
|
rlm@10
|
2669 label = u + readShort(b, u + 1);
|
rlm@10
|
2670 }
|
rlm@10
|
2671 newOffset = getNewOffset(allIndexes, allSizes, u, label);
|
rlm@10
|
2672 if(resize[u])
|
rlm@10
|
2673 {
|
rlm@10
|
2674 // replaces GOTO with GOTO_W, JSR with JSR_W and IFxxx
|
rlm@10
|
2675 // <l> with IFNOTxxx <l'> GOTO_W <l>, where IFNOTxxx is
|
rlm@10
|
2676 // the "opposite" opcode of IFxxx (i.e., IFNE for IFEQ)
|
rlm@10
|
2677 // and where <l'> designates the instruction just after
|
rlm@10
|
2678 // the GOTO_W.
|
rlm@10
|
2679 if(opcode == Opcodes.GOTO)
|
rlm@10
|
2680 {
|
rlm@10
|
2681 newCode.putByte(200); // GOTO_W
|
rlm@10
|
2682 }
|
rlm@10
|
2683 else if(opcode == Opcodes.JSR)
|
rlm@10
|
2684 {
|
rlm@10
|
2685 newCode.putByte(201); // JSR_W
|
rlm@10
|
2686 }
|
rlm@10
|
2687 else
|
rlm@10
|
2688 {
|
rlm@10
|
2689 newCode.putByte(opcode <= 166
|
rlm@10
|
2690 ? ((opcode + 1) ^ 1) - 1
|
rlm@10
|
2691 : opcode ^ 1);
|
rlm@10
|
2692 newCode.putShort(8); // jump offset
|
rlm@10
|
2693 newCode.putByte(200); // GOTO_W
|
rlm@10
|
2694 // newOffset now computed from start of GOTO_W
|
rlm@10
|
2695 newOffset -= 3;
|
rlm@10
|
2696 }
|
rlm@10
|
2697 newCode.putInt(newOffset);
|
rlm@10
|
2698 }
|
rlm@10
|
2699 else
|
rlm@10
|
2700 {
|
rlm@10
|
2701 newCode.putByte(opcode);
|
rlm@10
|
2702 newCode.putShort(newOffset);
|
rlm@10
|
2703 }
|
rlm@10
|
2704 u += 3;
|
rlm@10
|
2705 break;
|
rlm@10
|
2706 case ClassWriter.LABELW_INSN:
|
rlm@10
|
2707 label = u + readInt(b, u + 1);
|
rlm@10
|
2708 newOffset = getNewOffset(allIndexes, allSizes, u, label);
|
rlm@10
|
2709 newCode.putByte(opcode);
|
rlm@10
|
2710 newCode.putInt(newOffset);
|
rlm@10
|
2711 u += 5;
|
rlm@10
|
2712 break;
|
rlm@10
|
2713 case ClassWriter.TABL_INSN:
|
rlm@10
|
2714 // skips 0 to 3 padding bytes
|
rlm@10
|
2715 v = u;
|
rlm@10
|
2716 u = u + 4 - (v & 3);
|
rlm@10
|
2717 // reads and copies instruction
|
rlm@10
|
2718 newCode.putByte(Opcodes.TABLESWITCH);
|
rlm@10
|
2719 newCode.length += (4 - newCode.length % 4) % 4;
|
rlm@10
|
2720 label = v + readInt(b, u);
|
rlm@10
|
2721 u += 4;
|
rlm@10
|
2722 newOffset = getNewOffset(allIndexes, allSizes, v, label);
|
rlm@10
|
2723 newCode.putInt(newOffset);
|
rlm@10
|
2724 j = readInt(b, u);
|
rlm@10
|
2725 u += 4;
|
rlm@10
|
2726 newCode.putInt(j);
|
rlm@10
|
2727 j = readInt(b, u) - j + 1;
|
rlm@10
|
2728 u += 4;
|
rlm@10
|
2729 newCode.putInt(readInt(b, u - 4));
|
rlm@10
|
2730 for(; j > 0; --j)
|
rlm@10
|
2731 {
|
rlm@10
|
2732 label = v + readInt(b, u);
|
rlm@10
|
2733 u += 4;
|
rlm@10
|
2734 newOffset = getNewOffset(allIndexes, allSizes, v, label);
|
rlm@10
|
2735 newCode.putInt(newOffset);
|
rlm@10
|
2736 }
|
rlm@10
|
2737 break;
|
rlm@10
|
2738 case ClassWriter.LOOK_INSN:
|
rlm@10
|
2739 // skips 0 to 3 padding bytes
|
rlm@10
|
2740 v = u;
|
rlm@10
|
2741 u = u + 4 - (v & 3);
|
rlm@10
|
2742 // reads and copies instruction
|
rlm@10
|
2743 newCode.putByte(Opcodes.LOOKUPSWITCH);
|
rlm@10
|
2744 newCode.length += (4 - newCode.length % 4) % 4;
|
rlm@10
|
2745 label = v + readInt(b, u);
|
rlm@10
|
2746 u += 4;
|
rlm@10
|
2747 newOffset = getNewOffset(allIndexes, allSizes, v, label);
|
rlm@10
|
2748 newCode.putInt(newOffset);
|
rlm@10
|
2749 j = readInt(b, u);
|
rlm@10
|
2750 u += 4;
|
rlm@10
|
2751 newCode.putInt(j);
|
rlm@10
|
2752 for(; j > 0; --j)
|
rlm@10
|
2753 {
|
rlm@10
|
2754 newCode.putInt(readInt(b, u));
|
rlm@10
|
2755 u += 4;
|
rlm@10
|
2756 label = v + readInt(b, u);
|
rlm@10
|
2757 u += 4;
|
rlm@10
|
2758 newOffset = getNewOffset(allIndexes, allSizes, v, label);
|
rlm@10
|
2759 newCode.putInt(newOffset);
|
rlm@10
|
2760 }
|
rlm@10
|
2761 break;
|
rlm@10
|
2762 case ClassWriter.WIDE_INSN:
|
rlm@10
|
2763 opcode = b[u + 1] & 0xFF;
|
rlm@10
|
2764 if(opcode == Opcodes.IINC)
|
rlm@10
|
2765 {
|
rlm@10
|
2766 newCode.putByteArray(b, u, 6);
|
rlm@10
|
2767 u += 6;
|
rlm@10
|
2768 }
|
rlm@10
|
2769 else
|
rlm@10
|
2770 {
|
rlm@10
|
2771 newCode.putByteArray(b, u, 4);
|
rlm@10
|
2772 u += 4;
|
rlm@10
|
2773 }
|
rlm@10
|
2774 break;
|
rlm@10
|
2775 case ClassWriter.VAR_INSN:
|
rlm@10
|
2776 case ClassWriter.SBYTE_INSN:
|
rlm@10
|
2777 case ClassWriter.LDC_INSN:
|
rlm@10
|
2778 newCode.putByteArray(b, u, 2);
|
rlm@10
|
2779 u += 2;
|
rlm@10
|
2780 break;
|
rlm@10
|
2781 case ClassWriter.SHORT_INSN:
|
rlm@10
|
2782 case ClassWriter.LDCW_INSN:
|
rlm@10
|
2783 case ClassWriter.FIELDORMETH_INSN:
|
rlm@10
|
2784 case ClassWriter.TYPE_INSN:
|
rlm@10
|
2785 case ClassWriter.IINC_INSN:
|
rlm@10
|
2786 newCode.putByteArray(b, u, 3);
|
rlm@10
|
2787 u += 3;
|
rlm@10
|
2788 break;
|
rlm@10
|
2789 case ClassWriter.ITFMETH_INSN:
|
rlm@10
|
2790 newCode.putByteArray(b, u, 5);
|
rlm@10
|
2791 u += 5;
|
rlm@10
|
2792 break;
|
rlm@10
|
2793 // case MANA_INSN:
|
rlm@10
|
2794 default:
|
rlm@10
|
2795 newCode.putByteArray(b, u, 4);
|
rlm@10
|
2796 u += 4;
|
rlm@10
|
2797 break;
|
rlm@10
|
2798 }
|
rlm@10
|
2799 }
|
rlm@10
|
2800
|
rlm@10
|
2801 // recomputes the stack map frames
|
rlm@10
|
2802 if(frameCount > 0)
|
rlm@10
|
2803 {
|
rlm@10
|
2804 if(compute == FRAMES)
|
rlm@10
|
2805 {
|
rlm@10
|
2806 frameCount = 0;
|
rlm@10
|
2807 stackMap = null;
|
rlm@10
|
2808 previousFrame = null;
|
rlm@10
|
2809 frame = null;
|
rlm@10
|
2810 Frame f = new Frame();
|
rlm@10
|
2811 f.owner = labels;
|
rlm@10
|
2812 Type[] args = Type.getArgumentTypes(descriptor);
|
rlm@10
|
2813 f.initInputFrame(cw, access, args, maxLocals);
|
rlm@10
|
2814 visitFrame(f);
|
rlm@10
|
2815 Label l = labels;
|
rlm@10
|
2816 while(l != null)
|
rlm@10
|
2817 {
|
rlm@10
|
2818 /*
|
rlm@10
|
2819 * here we need the original label position. getNewOffset
|
rlm@10
|
2820 * must therefore never have been called for this label.
|
rlm@10
|
2821 */
|
rlm@10
|
2822 u = l.position - 3;
|
rlm@10
|
2823 if((l.status & Label.STORE) != 0 || (u >= 0 && resize[u]))
|
rlm@10
|
2824 {
|
rlm@10
|
2825 getNewOffset(allIndexes, allSizes, l);
|
rlm@10
|
2826 // TODO update offsets in UNINITIALIZED values
|
rlm@10
|
2827 visitFrame(l.frame);
|
rlm@10
|
2828 }
|
rlm@10
|
2829 l = l.successor;
|
rlm@10
|
2830 }
|
rlm@10
|
2831 }
|
rlm@10
|
2832 else
|
rlm@10
|
2833 {
|
rlm@10
|
2834 /*
|
rlm@10
|
2835 * Resizing an existing stack map frame table is really hard.
|
rlm@10
|
2836 * Not only the table must be parsed to update the offets, but
|
rlm@10
|
2837 * new frames may be needed for jump instructions that were
|
rlm@10
|
2838 * inserted by this method. And updating the offsets or
|
rlm@10
|
2839 * inserting frames can change the format of the following
|
rlm@10
|
2840 * frames, in case of packed frames. In practice the whole table
|
rlm@10
|
2841 * must be recomputed. For this the frames are marked as
|
rlm@10
|
2842 * potentially invalid. This will cause the whole class to be
|
rlm@10
|
2843 * reread and rewritten with the COMPUTE_FRAMES option (see the
|
rlm@10
|
2844 * ClassWriter.toByteArray method). This is not very efficient
|
rlm@10
|
2845 * but is much easier and requires much less code than any other
|
rlm@10
|
2846 * method I can think of.
|
rlm@10
|
2847 */
|
rlm@10
|
2848 cw.invalidFrames = true;
|
rlm@10
|
2849 }
|
rlm@10
|
2850 }
|
rlm@10
|
2851 // updates the exception handler block labels
|
rlm@10
|
2852 Handler h = firstHandler;
|
rlm@10
|
2853 while(h != null)
|
rlm@10
|
2854 {
|
rlm@10
|
2855 getNewOffset(allIndexes, allSizes, h.start);
|
rlm@10
|
2856 getNewOffset(allIndexes, allSizes, h.end);
|
rlm@10
|
2857 getNewOffset(allIndexes, allSizes, h.handler);
|
rlm@10
|
2858 h = h.next;
|
rlm@10
|
2859 }
|
rlm@10
|
2860 // updates the instructions addresses in the
|
rlm@10
|
2861 // local var and line number tables
|
rlm@10
|
2862 for(i = 0; i < 2; ++i)
|
rlm@10
|
2863 {
|
rlm@10
|
2864 ByteVector bv = i == 0 ? localVar : localVarType;
|
rlm@10
|
2865 if(bv != null)
|
rlm@10
|
2866 {
|
rlm@10
|
2867 b = bv.data;
|
rlm@10
|
2868 u = 0;
|
rlm@10
|
2869 while(u < bv.length)
|
rlm@10
|
2870 {
|
rlm@10
|
2871 label = readUnsignedShort(b, u);
|
rlm@10
|
2872 newOffset = getNewOffset(allIndexes, allSizes, 0, label);
|
rlm@10
|
2873 writeShort(b, u, newOffset);
|
rlm@10
|
2874 label += readUnsignedShort(b, u + 2);
|
rlm@10
|
2875 newOffset = getNewOffset(allIndexes, allSizes, 0, label)
|
rlm@10
|
2876 - newOffset;
|
rlm@10
|
2877 writeShort(b, u + 2, newOffset);
|
rlm@10
|
2878 u += 10;
|
rlm@10
|
2879 }
|
rlm@10
|
2880 }
|
rlm@10
|
2881 }
|
rlm@10
|
2882 if(lineNumber != null)
|
rlm@10
|
2883 {
|
rlm@10
|
2884 b = lineNumber.data;
|
rlm@10
|
2885 u = 0;
|
rlm@10
|
2886 while(u < lineNumber.length)
|
rlm@10
|
2887 {
|
rlm@10
|
2888 writeShort(b, u, getNewOffset(allIndexes,
|
rlm@10
|
2889 allSizes,
|
rlm@10
|
2890 0,
|
rlm@10
|
2891 readUnsignedShort(b, u)));
|
rlm@10
|
2892 u += 4;
|
rlm@10
|
2893 }
|
rlm@10
|
2894 }
|
rlm@10
|
2895 // updates the labels of the other attributes
|
rlm@10
|
2896 Attribute attr = cattrs;
|
rlm@10
|
2897 while(attr != null)
|
rlm@10
|
2898 {
|
rlm@10
|
2899 Label[] labels = attr.getLabels();
|
rlm@10
|
2900 if(labels != null)
|
rlm@10
|
2901 {
|
rlm@10
|
2902 for(i = labels.length - 1; i >= 0; --i)
|
rlm@10
|
2903 {
|
rlm@10
|
2904 getNewOffset(allIndexes, allSizes, labels[i]);
|
rlm@10
|
2905 }
|
rlm@10
|
2906 }
|
rlm@10
|
2907 attr = attr.next;
|
rlm@10
|
2908 }
|
rlm@10
|
2909
|
rlm@10
|
2910 // replaces old bytecodes with new ones
|
rlm@10
|
2911 code = newCode;
|
rlm@10
|
2912 }
|
rlm@10
|
2913
|
rlm@10
|
2914 /**
|
rlm@10
|
2915 * Reads an unsigned short value in the given byte array.
|
rlm@10
|
2916 *
|
rlm@10
|
2917 * @param b a byte array.
|
rlm@10
|
2918 * @param index the start index of the value to be read.
|
rlm@10
|
2919 * @return the read value.
|
rlm@10
|
2920 */
|
rlm@10
|
2921 static int readUnsignedShort(final byte[] b, final int index){
|
rlm@10
|
2922 return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
|
rlm@10
|
2923 }
|
rlm@10
|
2924
|
rlm@10
|
2925 /**
|
rlm@10
|
2926 * Reads a signed short value in the given byte array.
|
rlm@10
|
2927 *
|
rlm@10
|
2928 * @param b a byte array.
|
rlm@10
|
2929 * @param index the start index of the value to be read.
|
rlm@10
|
2930 * @return the read value.
|
rlm@10
|
2931 */
|
rlm@10
|
2932 static short readShort(final byte[] b, final int index){
|
rlm@10
|
2933 return (short) (((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF));
|
rlm@10
|
2934 }
|
rlm@10
|
2935
|
rlm@10
|
2936 /**
|
rlm@10
|
2937 * Reads a signed int value in the given byte array.
|
rlm@10
|
2938 *
|
rlm@10
|
2939 * @param b a byte array.
|
rlm@10
|
2940 * @param index the start index of the value to be read.
|
rlm@10
|
2941 * @return the read value.
|
rlm@10
|
2942 */
|
rlm@10
|
2943 static int readInt(final byte[] b, final int index){
|
rlm@10
|
2944 return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
|
rlm@10
|
2945 | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
|
rlm@10
|
2946 }
|
rlm@10
|
2947
|
rlm@10
|
2948 /**
|
rlm@10
|
2949 * Writes a short value in the given byte array.
|
rlm@10
|
2950 *
|
rlm@10
|
2951 * @param b a byte array.
|
rlm@10
|
2952 * @param index where the first byte of the short value must be written.
|
rlm@10
|
2953 * @param s the value to be written in the given byte array.
|
rlm@10
|
2954 */
|
rlm@10
|
2955 static void writeShort(final byte[] b, final int index, final int s){
|
rlm@10
|
2956 b[index] = (byte) (s >>> 8);
|
rlm@10
|
2957 b[index + 1] = (byte) s;
|
rlm@10
|
2958 }
|
rlm@10
|
2959
|
rlm@10
|
2960 /**
|
rlm@10
|
2961 * Computes the future value of a bytecode offset. <p> Note: it is possible
|
rlm@10
|
2962 * to have several entries for the same instruction in the <tt>indexes</tt>
|
rlm@10
|
2963 * and <tt>sizes</tt>: two entries (index=a,size=b) and (index=a,size=b')
|
rlm@10
|
2964 * are equivalent to a single entry (index=a,size=b+b').
|
rlm@10
|
2965 *
|
rlm@10
|
2966 * @param indexes current positions of the instructions to be resized. Each
|
rlm@10
|
2967 * instruction must be designated by the index of its <i>last</i>
|
rlm@10
|
2968 * byte, plus one (or, in other words, by the index of the <i>first</i>
|
rlm@10
|
2969 * byte of the <i>next</i> instruction).
|
rlm@10
|
2970 * @param sizes the number of bytes to be <i>added</i> to the above
|
rlm@10
|
2971 * instructions. More precisely, for each i < <tt>len</tt>,
|
rlm@10
|
2972 * <tt>sizes</tt>[i] bytes will be added at the end of the
|
rlm@10
|
2973 * instruction designated by <tt>indexes</tt>[i] or, if
|
rlm@10
|
2974 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
|
rlm@10
|
2975 * bytes of the instruction will be removed (the instruction size
|
rlm@10
|
2976 * <i>must not</i> become negative or null).
|
rlm@10
|
2977 * @param begin index of the first byte of the source instruction.
|
rlm@10
|
2978 * @param end index of the first byte of the target instruction.
|
rlm@10
|
2979 * @return the future value of the given bytecode offset.
|
rlm@10
|
2980 */
|
rlm@10
|
2981 static int getNewOffset(
|
rlm@10
|
2982 final int[] indexes,
|
rlm@10
|
2983 final int[] sizes,
|
rlm@10
|
2984 final int begin,
|
rlm@10
|
2985 final int end){
|
rlm@10
|
2986 int offset = end - begin;
|
rlm@10
|
2987 for(int i = 0; i < indexes.length; ++i)
|
rlm@10
|
2988 {
|
rlm@10
|
2989 if(begin < indexes[i] && indexes[i] <= end)
|
rlm@10
|
2990 {
|
rlm@10
|
2991 // forward jump
|
rlm@10
|
2992 offset += sizes[i];
|
rlm@10
|
2993 }
|
rlm@10
|
2994 else if(end < indexes[i] && indexes[i] <= begin)
|
rlm@10
|
2995 {
|
rlm@10
|
2996 // backward jump
|
rlm@10
|
2997 offset -= sizes[i];
|
rlm@10
|
2998 }
|
rlm@10
|
2999 }
|
rlm@10
|
3000 return offset;
|
rlm@10
|
3001 }
|
rlm@10
|
3002
|
rlm@10
|
3003 /**
|
rlm@10
|
3004 * Updates the offset of the given label.
|
rlm@10
|
3005 *
|
rlm@10
|
3006 * @param indexes current positions of the instructions to be resized. Each
|
rlm@10
|
3007 * instruction must be designated by the index of its <i>last</i>
|
rlm@10
|
3008 * byte, plus one (or, in other words, by the index of the <i>first</i>
|
rlm@10
|
3009 * byte of the <i>next</i> instruction).
|
rlm@10
|
3010 * @param sizes the number of bytes to be <i>added</i> to the above
|
rlm@10
|
3011 * instructions. More precisely, for each i < <tt>len</tt>,
|
rlm@10
|
3012 * <tt>sizes</tt>[i] bytes will be added at the end of the
|
rlm@10
|
3013 * instruction designated by <tt>indexes</tt>[i] or, if
|
rlm@10
|
3014 * <tt>sizes</tt>[i] is negative, the <i>last</i> |<tt>sizes[i]</tt>|
|
rlm@10
|
3015 * bytes of the instruction will be removed (the instruction size
|
rlm@10
|
3016 * <i>must not</i> become negative or null).
|
rlm@10
|
3017 * @param label the label whose offset must be updated.
|
rlm@10
|
3018 */
|
rlm@10
|
3019 static void getNewOffset(
|
rlm@10
|
3020 final int[] indexes,
|
rlm@10
|
3021 final int[] sizes,
|
rlm@10
|
3022 final Label label){
|
rlm@10
|
3023 if((label.status & Label.RESIZED) == 0)
|
rlm@10
|
3024 {
|
rlm@10
|
3025 label.position = getNewOffset(indexes, sizes, 0, label.position);
|
rlm@10
|
3026 label.status |= Label.RESIZED;
|
rlm@10
|
3027 }
|
rlm@10
|
3028 }
|
rlm@10
|
3029 }
|