1 |
779 |
jeremybenn |
/***
|
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 |
|
|
*/
|
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 |
|
|
*/
|
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 |
|
|
*/
|
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 |
|
|
*/
|
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 |
|
|
*/
|
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 |
|
|
*/
|
296 |
|
|
public String toString() {
|
297 |
|
|
return "L" + System.identityHashCode(this);
|
298 |
|
|
}
|
299 |
|
|
}
|