• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 org.objectweb.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      * The line number corresponding to this label, if known.
42      */
43     int line;
44 
45     /**
46      * Indicates if the position of this label is known.
47      */
48     boolean resolved;
49 
50     /**
51      * The position of this label in the code, if known.
52      */
53     int position;
54 
55     /**
56      * If the label position has been updated, after instruction resizing.
57      */
58     boolean resized;
59 
60     /**
61      * Number of forward references to this label, times two.
62      */
63     private int referenceCount;
64 
65     /**
66      * Informations about forward references. Each forward reference is
67      * described by two consecutive integers in this array: the first one is the
68      * position of the first byte of the bytecode instruction that contains the
69      * forward reference, while the second is the position of the first byte of
70      * the forward reference itself. In fact the sign of the first integer
71      * indicates if this reference uses 2 or 4 bytes, and its absolute value
72      * gives the position of the bytecode instruction.
73      */
74     private int[] srcAndRefPositions;
75 
76     /*
77      * Fields for the control flow graph analysis algorithm (used to compute the
78      * maximum stack size). A control flow graph contains one node per "basic
79      * block", and one edge per "jump" from one basic block to another. Each
80      * node (i.e., each basic block) is represented by the Label object that
81      * corresponds to the first instruction of this basic block. Each node also
82      * stores the list of it successors in the graph, as a linked list of Edge
83      * objects.
84      */
85 
86     /**
87      * The stack size at the beginning of this basic block. This size is
88      * initially unknown. It is computed by the control flow analysis algorithm
89      * (see {@link MethodWriter#visitMaxs visitMaxs}).
90      */
91     int beginStackSize;
92 
93     /**
94      * The (relative) maximum stack size corresponding to this basic block. This
95      * size is relative to the stack size at the beginning of the basic block,
96      * i.e., the true maximum stack size is equal to {@link #beginStackSize
97      * beginStackSize} + {@link #maxStackSize maxStackSize}.
98      */
99     int maxStackSize;
100 
101     /**
102      * The successors of this node in the control flow graph. These successors
103      * are stored in a linked list of {@link Edge Edge} objects, linked to each
104      * other by their {@link Edge#next} field.
105      */
106     Edge successors;
107 
108     /**
109      * The next basic block in the basic block stack. See
110      * {@link MethodWriter#visitMaxs visitMaxs}.
111      */
112     Label next;
113 
114     /**
115      * <tt>true</tt> if this basic block has been pushed in the basic block
116      * stack. See {@link MethodWriter#visitMaxs visitMaxs}.
117      */
118     boolean pushed;
119 
120     // ------------------------------------------------------------------------
121     // Constructor
122     // ------------------------------------------------------------------------
123 
124     /**
125      * Constructs a new label.
126      */
Label()127     public Label() {
128     }
129 
130     // ------------------------------------------------------------------------
131     // Methods to compute offsets and to manage forward references
132     // ------------------------------------------------------------------------
133 
134     /**
135      * Returns the offset corresponding to this label. This offset is computed
136      * from the start of the method's bytecode. <i>This method is intended for
137      * {@link Attribute} sub classes, and is normally not needed by class
138      * generators or adapters.</i>
139      *
140      * @return the offset corresponding to this label.
141      * @throws IllegalStateException if this label is not resolved yet.
142      */
getOffset()143     public int getOffset() {
144         if (!resolved) {
145             throw new IllegalStateException("Label offset position has not been resolved yet");
146         }
147         return position;
148     }
149 
150     /**
151      * Puts a reference to this label in the bytecode of a method. If the
152      * position of the label is known, the offset is computed and written
153      * directly. Otherwise, a null offset is written and a new forward reference
154      * is declared for this label.
155      *
156      * @param owner the code writer that calls this method.
157      * @param out the bytecode of the method.
158      * @param source the position of first byte of the bytecode instruction that
159      *        contains this label.
160      * @param wideOffset <tt>true</tt> if the reference must be stored in 4
161      *        bytes, or <tt>false</tt> if it must be stored with 2 bytes.
162      * @throws IllegalArgumentException if this label has not been created by
163      *         the given code writer.
164      */
put( final MethodWriter owner, final ByteVector out, final int source, final boolean wideOffset)165     void put(
166         final MethodWriter owner,
167         final ByteVector out,
168         final int source,
169         final boolean wideOffset)
170     {
171         if (resolved) {
172             if (wideOffset) {
173                 out.putInt(position - source);
174             } else {
175                 out.putShort(position - source);
176             }
177         } else {
178             if (wideOffset) {
179                 addReference(-1 - source, out.length);
180                 out.putInt(-1);
181             } else {
182                 addReference(source, out.length);
183                 out.putShort(-1);
184             }
185         }
186     }
187 
188     /**
189      * Adds a forward reference to this label. This method must be called only
190      * for a true forward reference, i.e. only if this label is not resolved
191      * yet. For backward references, the offset of the reference can be, and
192      * must be, computed and stored directly.
193      *
194      * @param sourcePosition the position of the referencing instruction. This
195      *        position will be used to compute the offset of this forward
196      *        reference.
197      * @param referencePosition the position where the offset for this forward
198      *        reference must be stored.
199      */
addReference( final int sourcePosition, final int referencePosition)200     private void addReference(
201         final int sourcePosition,
202         final int referencePosition)
203     {
204         if (srcAndRefPositions == null) {
205             srcAndRefPositions = new int[6];
206         }
207         if (referenceCount >= srcAndRefPositions.length) {
208             int[] a = new int[srcAndRefPositions.length + 6];
209             System.arraycopy(srcAndRefPositions,
210                     0,
211                     a,
212                     0,
213                     srcAndRefPositions.length);
214             srcAndRefPositions = a;
215         }
216         srcAndRefPositions[referenceCount++] = sourcePosition;
217         srcAndRefPositions[referenceCount++] = referencePosition;
218     }
219 
220     /**
221      * Resolves all forward references to this label. This method must be called
222      * when this label is added to the bytecode of the method, i.e. when its
223      * position becomes known. This method fills in the blanks that where left
224      * in the bytecode by each forward reference previously added to this label.
225      *
226      * @param owner the code writer that calls this method.
227      * @param position the position of this label in the bytecode.
228      * @param data the bytecode of the method.
229      * @return <tt>true</tt> if a blank that was left for this label was to
230      *         small to store the offset. In such a case the corresponding jump
231      *         instruction is replaced with a pseudo instruction (using unused
232      *         opcodes) using an unsigned two bytes offset. These pseudo
233      *         instructions will need to be replaced with true instructions with
234      *         wider offsets (4 bytes instead of 2). This is done in
235      *         {@link MethodWriter#resizeInstructions}.
236      * @throws IllegalArgumentException if this label has already been resolved,
237      *         or if it has not been created by the given code writer.
238      */
resolve( final MethodWriter owner, final int position, final byte[] data)239     boolean resolve(
240         final MethodWriter owner,
241         final int position,
242         final byte[] data)
243     {
244         boolean needUpdate = false;
245         this.resolved = true;
246         this.position = position;
247         int i = 0;
248         while (i < referenceCount) {
249             int source = srcAndRefPositions[i++];
250             int reference = srcAndRefPositions[i++];
251             int offset;
252             if (source >= 0) {
253                 offset = position - source;
254                 if (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE) {
255                     /*
256                      * changes the opcode of the jump instruction, in order to
257                      * be able to find it later (see resizeInstructions in
258                      * MethodWriter). These temporary opcodes are similar to
259                      * jump instruction opcodes, except that the 2 bytes offset
260                      * is unsigned (and can therefore represent values from 0 to
261                      * 65535, which is sufficient since the size of a method is
262                      * limited to 65535 bytes).
263                      */
264                     int opcode = data[reference - 1] & 0xFF;
265                     if (opcode <= Opcodes.JSR) {
266                         // changes IFEQ ... JSR to opcodes 202 to 217
267                         data[reference - 1] = (byte) (opcode + 49);
268                     } else {
269                         // changes IFNULL and IFNONNULL to opcodes 218 and 219
270                         data[reference - 1] = (byte) (opcode + 20);
271                     }
272                     needUpdate = true;
273                 }
274                 data[reference++] = (byte) (offset >>> 8);
275                 data[reference] = (byte) offset;
276             } else {
277                 offset = position + source + 1;
278                 data[reference++] = (byte) (offset >>> 24);
279                 data[reference++] = (byte) (offset >>> 16);
280                 data[reference++] = (byte) (offset >>> 8);
281                 data[reference] = (byte) offset;
282             }
283         }
284         return needUpdate;
285     }
286 
287     // ------------------------------------------------------------------------
288     // Overriden Object methods
289     // ------------------------------------------------------------------------
290 
291     /**
292      * Returns a string representation of this label.
293      *
294      * @return a string representation of this label.
295      */
toString()296     public String toString() {
297         return "L" + System.identityHashCode(this);
298     }
299 }
300