Mercurial > lasercutter
comparison src/clojure/asm/Label.java @ 10:ef7dbbd6452c
added clojure source goodness
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Sat, 21 Aug 2010 06:25:44 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
9:35cf337adfcf | 10:ef7dbbd6452c |
---|---|
1 /*** | |
2 * ASM: a very small and fast Java bytecode manipulation framework | |
3 * Copyright (c) 2000-2005 INRIA, France Telecom | |
4 * All rights reserved. | |
5 * | |
6 * Redistribution and use in source and binary forms, with or without | |
7 * modification, are permitted provided that the following conditions | |
8 * are met: | |
9 * 1. Redistributions of source code must retain the above copyright | |
10 * notice, this list of conditions and the following disclaimer. | |
11 * 2. Redistributions in binary form must reproduce the above copyright | |
12 * notice, this list of conditions and the following disclaimer in the | |
13 * documentation and/or other materials provided with the distribution. | |
14 * 3. Neither the name of the copyright holders nor the names of its | |
15 * contributors may be used to endorse or promote products derived from | |
16 * this software without specific prior written permission. | |
17 * | |
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
28 * THE POSSIBILITY OF SUCH DAMAGE. | |
29 */ | |
30 package clojure.asm; | |
31 | |
32 /** | |
33 * A label represents a position in the bytecode of a method. Labels are used | |
34 * for jump, goto, and switch instructions, and for try catch blocks. | |
35 * | |
36 * @author Eric Bruneton | |
37 */ | |
38 public class Label{ | |
39 | |
40 /** | |
41 * Indicates if this label is only used for debug attributes. Such a label | |
42 * is not the start of a basic block, the target of a jump instruction, or | |
43 * an exception handler. It can be safely ignored in control flow graph | |
44 * analysis algorithms (for optimization purposes). | |
45 */ | |
46 final static int DEBUG = 1; | |
47 | |
48 /** | |
49 * Indicates if the position of this label is known. | |
50 */ | |
51 final static int RESOLVED = 2; | |
52 | |
53 /** | |
54 * Indicates if this label has been updated, after instruction resizing. | |
55 */ | |
56 final static int RESIZED = 4; | |
57 | |
58 /** | |
59 * Indicates if this basic block has been pushed in the basic block stack. | |
60 * See {@link MethodWriter#visitMaxs visitMaxs}. | |
61 */ | |
62 final static int PUSHED = 8; | |
63 | |
64 /** | |
65 * Indicates if this label is the target of a jump instruction, or the start | |
66 * of an exception handler. | |
67 */ | |
68 final static int TARGET = 16; | |
69 | |
70 /** | |
71 * Indicates if a stack map frame must be stored for this label. | |
72 */ | |
73 final static int STORE = 32; | |
74 | |
75 /** | |
76 * Indicates if this label corresponds to a reachable basic block. | |
77 */ | |
78 final static int REACHABLE = 64; | |
79 | |
80 /** | |
81 * Indicates if this basic block ends with a JSR instruction. | |
82 */ | |
83 final static int JSR = 128; | |
84 | |
85 /** | |
86 * Indicates if this basic block ends with a RET instruction. | |
87 */ | |
88 final static int RET = 256; | |
89 | |
90 /** | |
91 * Field used to associate user information to a label. | |
92 */ | |
93 public Object info; | |
94 | |
95 /** | |
96 * Flags that indicate the status of this label. | |
97 * | |
98 * @see #DEBUG | |
99 * @see #RESOLVED | |
100 * @see #RESIZED | |
101 * @see #PUSHED | |
102 * @see #TARGET | |
103 * @see #STORE | |
104 * @see #REACHABLE | |
105 * @see #JSR | |
106 * @see #RET | |
107 */ | |
108 int status; | |
109 | |
110 /** | |
111 * The line number corresponding to this label, if known. | |
112 */ | |
113 int line; | |
114 | |
115 /** | |
116 * The position of this label in the code, if known. | |
117 */ | |
118 int position; | |
119 | |
120 /** | |
121 * Number of forward references to this label, times two. | |
122 */ | |
123 private int referenceCount; | |
124 | |
125 /** | |
126 * Informations about forward references. Each forward reference is | |
127 * described by two consecutive integers in this array: the first one is the | |
128 * position of the first byte of the bytecode instruction that contains the | |
129 * forward reference, while the second is the position of the first byte of | |
130 * the forward reference itself. In fact the sign of the first integer | |
131 * indicates if this reference uses 2 or 4 bytes, and its absolute value | |
132 * gives the position of the bytecode instruction. | |
133 */ | |
134 private int[] srcAndRefPositions; | |
135 | |
136 // ------------------------------------------------------------------------ | |
137 | |
138 /* | |
139 * Fields for the control flow and data flow graph analysis algorithms (used | |
140 * to compute the maximum stack size or the stack map frames). A control | |
141 * flow graph contains one node per "basic block", and one edge per "jump" | |
142 * from one basic block to another. Each node (i.e., each basic block) is | |
143 * represented by the Label object that corresponds to the first instruction | |
144 * of this basic block. Each node also stores the list of its successors in | |
145 * the graph, as a linked list of Edge objects. | |
146 * | |
147 * The control flow analysis algorithms used to compute the maximum stack | |
148 * size or the stack map frames are similar and use two steps. The first | |
149 * step, during the visit of each instruction, builds information about the | |
150 * state of the local variables and the operand stack at the end of each | |
151 * basic block, called the "output frame", <i>relatively</i> to the frame | |
152 * state at the beginning of the basic block, which is called the "input | |
153 * frame", and which is <i>unknown</i> during this step. The second step, | |
154 * in {@link MethodWriter#visitMaxs}, is a fix point algorithm that | |
155 * computes information about the input frame of each basic block, from the | |
156 * input state of the first basic block (known from the method signature), | |
157 * and by the using the previously computed relative output frames. | |
158 * | |
159 * The algorithm used to compute the maximum stack size only computes the | |
160 * relative output and absolute input stack heights, while the algorithm | |
161 * used to compute stack map frames computes relative output frames and | |
162 * absolute input frames. | |
163 */ | |
164 | |
165 /** | |
166 * Start of the output stack relatively to the input stack. The exact | |
167 * semantics of this field depends on the algorithm that is used. | |
168 * <p/> | |
169 * When only the maximum stack size is computed, this field is the number of | |
170 * elements in the input stack. | |
171 * <p/> | |
172 * When the stack map frames are completely computed, this field is the | |
173 * offset of the first output stack element relatively to the top of the | |
174 * input stack. This offset is always negative or null. A null offset means | |
175 * that the output stack must be appended to the input stack. A -n offset | |
176 * means that the first n output stack elements must replace the top n input | |
177 * stack elements, and that the other elements must be appended to the input | |
178 * stack. | |
179 */ | |
180 int inputStackTop; | |
181 | |
182 /** | |
183 * Maximum height reached by the output stack, relatively to the top of the | |
184 * input stack. This maximum is always positive or null. | |
185 */ | |
186 int outputStackMax; | |
187 | |
188 /** | |
189 * Information about the input and output stack map frames of this basic | |
190 * block. This field is only used when {@link ClassWriter#COMPUTE_FRAMES} | |
191 * option is used. | |
192 */ | |
193 Frame frame; | |
194 | |
195 /** | |
196 * The successor of this label, in the order they are visited. This linked | |
197 * list does not include labels used for debug info only. If | |
198 * {@link ClassWriter#COMPUTE_FRAMES} option is used then, in addition, it | |
199 * does not contain successive labels that denote the same bytecode position | |
200 * (in this case only the first label appears in this list). | |
201 */ | |
202 Label successor; | |
203 | |
204 /** | |
205 * The successors of this node in the control flow graph. These successors | |
206 * are stored in a linked list of {@link Edge Edge} objects, linked to each | |
207 * other by their {@link Edge#next} field. | |
208 */ | |
209 Edge successors; | |
210 | |
211 /** | |
212 * The next basic block in the basic block stack. This stack is used in the | |
213 * main loop of the fix point algorithm used in the second step of the | |
214 * control flow analysis algorithms. | |
215 * | |
216 * @see MethodWriter#visitMaxs | |
217 */ | |
218 Label next; | |
219 | |
220 // ------------------------------------------------------------------------ | |
221 // Constructor | |
222 // ------------------------------------------------------------------------ | |
223 | |
224 /** | |
225 * Constructs a new label. | |
226 */ | |
227 public Label(){ | |
228 } | |
229 | |
230 /** | |
231 * Constructs a new label. | |
232 * | |
233 * @param debug if this label is only used for debug attributes. | |
234 */ | |
235 Label(final boolean debug){ | |
236 this.status = debug ? DEBUG : 0; | |
237 } | |
238 | |
239 // ------------------------------------------------------------------------ | |
240 // Methods to compute offsets and to manage forward references | |
241 // ------------------------------------------------------------------------ | |
242 | |
243 /** | |
244 * Returns the offset corresponding to this label. This offset is computed | |
245 * from the start of the method's bytecode. <i>This method is intended for | |
246 * {@link Attribute} sub classes, and is normally not needed by class | |
247 * generators or adapters.</i> | |
248 * | |
249 * @return the offset corresponding to this label. | |
250 * @throws IllegalStateException if this label is not resolved yet. | |
251 */ | |
252 public int getOffset(){ | |
253 if((status & RESOLVED) == 0) | |
254 { | |
255 throw new IllegalStateException("Label offset position has not been resolved yet"); | |
256 } | |
257 return position; | |
258 } | |
259 | |
260 /** | |
261 * Puts a reference to this label in the bytecode of a method. If the | |
262 * position of the label is known, the offset is computed and written | |
263 * directly. Otherwise, a null offset is written and a new forward reference | |
264 * is declared for this label. | |
265 * | |
266 * @param owner the code writer that calls this method. | |
267 * @param out the bytecode of the method. | |
268 * @param source the position of first byte of the bytecode instruction that | |
269 * contains this label. | |
270 * @param wideOffset <tt>true</tt> if the reference must be stored in 4 | |
271 * bytes, or <tt>false</tt> if it must be stored with 2 bytes. | |
272 * @throws IllegalArgumentException if this label has not been created by | |
273 * the given code writer. | |
274 */ | |
275 void put( | |
276 final MethodWriter owner, | |
277 final ByteVector out, | |
278 final int source, | |
279 final boolean wideOffset){ | |
280 if((status & RESOLVED) != 0) | |
281 { | |
282 if(wideOffset) | |
283 { | |
284 out.putInt(position - source); | |
285 } | |
286 else | |
287 { | |
288 out.putShort(position - source); | |
289 } | |
290 } | |
291 else | |
292 { | |
293 if(wideOffset) | |
294 { | |
295 addReference(-1 - source, out.length); | |
296 out.putInt(-1); | |
297 } | |
298 else | |
299 { | |
300 addReference(source, out.length); | |
301 out.putShort(-1); | |
302 } | |
303 } | |
304 } | |
305 | |
306 /** | |
307 * Adds a forward reference to this label. This method must be called only | |
308 * for a true forward reference, i.e. only if this label is not resolved | |
309 * yet. For backward references, the offset of the reference can be, and | |
310 * must be, computed and stored directly. | |
311 * | |
312 * @param sourcePosition the position of the referencing instruction. This | |
313 * position will be used to compute the offset of this forward | |
314 * reference. | |
315 * @param referencePosition the position where the offset for this forward | |
316 * reference must be stored. | |
317 */ | |
318 private void addReference( | |
319 final int sourcePosition, | |
320 final int referencePosition){ | |
321 if(srcAndRefPositions == null) | |
322 { | |
323 srcAndRefPositions = new int[6]; | |
324 } | |
325 if(referenceCount >= srcAndRefPositions.length) | |
326 { | |
327 int[] a = new int[srcAndRefPositions.length + 6]; | |
328 System.arraycopy(srcAndRefPositions, | |
329 0, | |
330 a, | |
331 0, | |
332 srcAndRefPositions.length); | |
333 srcAndRefPositions = a; | |
334 } | |
335 srcAndRefPositions[referenceCount++] = sourcePosition; | |
336 srcAndRefPositions[referenceCount++] = referencePosition; | |
337 } | |
338 | |
339 /** | |
340 * Resolves all forward references to this label. This method must be called | |
341 * when this label is added to the bytecode of the method, i.e. when its | |
342 * position becomes known. This method fills in the blanks that where left | |
343 * in the bytecode by each forward reference previously added to this label. | |
344 * | |
345 * @param owner the code writer that calls this method. | |
346 * @param position the position of this label in the bytecode. | |
347 * @param data the bytecode of the method. | |
348 * @return <tt>true</tt> if a blank that was left for this label was to | |
349 * small to store the offset. In such a case the corresponding jump | |
350 * instruction is replaced with a pseudo instruction (using unused | |
351 * opcodes) using an unsigned two bytes offset. These pseudo | |
352 * instructions will need to be replaced with true instructions with | |
353 * wider offsets (4 bytes instead of 2). This is done in | |
354 * {@link MethodWriter#resizeInstructions}. | |
355 * @throws IllegalArgumentException if this label has already been resolved, | |
356 * or if it has not been created by the given code writer. | |
357 */ | |
358 boolean resolve( | |
359 final MethodWriter owner, | |
360 final int position, | |
361 final byte[] data){ | |
362 boolean needUpdate = false; | |
363 this.status |= RESOLVED; | |
364 this.position = position; | |
365 int i = 0; | |
366 while(i < referenceCount) | |
367 { | |
368 int source = srcAndRefPositions[i++]; | |
369 int reference = srcAndRefPositions[i++]; | |
370 int offset; | |
371 if(source >= 0) | |
372 { | |
373 offset = position - source; | |
374 if(offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) | |
375 { | |
376 /* | |
377 * changes the opcode of the jump instruction, in order to | |
378 * be able to find it later (see resizeInstructions in | |
379 * MethodWriter). These temporary opcodes are similar to | |
380 * jump instruction opcodes, except that the 2 bytes offset | |
381 * is unsigned (and can therefore represent values from 0 to | |
382 * 65535, which is sufficient since the size of a method is | |
383 * limited to 65535 bytes). | |
384 */ | |
385 int opcode = data[reference - 1] & 0xFF; | |
386 if(opcode <= Opcodes.JSR) | |
387 { | |
388 // changes IFEQ ... JSR to opcodes 202 to 217 | |
389 data[reference - 1] = (byte) (opcode + 49); | |
390 } | |
391 else | |
392 { | |
393 // changes IFNULL and IFNONNULL to opcodes 218 and 219 | |
394 data[reference - 1] = (byte) (opcode + 20); | |
395 } | |
396 needUpdate = true; | |
397 } | |
398 data[reference++] = (byte) (offset >>> 8); | |
399 data[reference] = (byte) offset; | |
400 } | |
401 else | |
402 { | |
403 offset = position + source + 1; | |
404 data[reference++] = (byte) (offset >>> 24); | |
405 data[reference++] = (byte) (offset >>> 16); | |
406 data[reference++] = (byte) (offset >>> 8); | |
407 data[reference] = (byte) offset; | |
408 } | |
409 } | |
410 return needUpdate; | |
411 } | |
412 | |
413 /** | |
414 * Returns the first label of the series to which this label belongs. For an | |
415 * isolated label or for the first label in a series of successive labels, | |
416 * this method returns the label itself. For other labels it returns the | |
417 * first label of the series. | |
418 * | |
419 * @return the first label of the series to which this label belongs. | |
420 */ | |
421 Label getFirst(){ | |
422 return frame == null ? this : frame.owner; | |
423 } | |
424 | |
425 // ------------------------------------------------------------------------ | |
426 // Overriden Object methods | |
427 // ------------------------------------------------------------------------ | |
428 | |
429 /** | |
430 * Returns a string representation of this label. | |
431 * | |
432 * @return a string representation of this label. | |
433 */ | |
434 public String toString(){ | |
435 return "L" + System.identityHashCode(this); | |
436 } | |
437 } |