blob: 96c8283a1bdf74772c1cfec551dd28018efcb0f2 [file] [log] [blame]
Clement Escoffier042a0ec2007-06-24 15:11:33 +00001/***
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 */
30package org.objectweb.asm.commons;
31
32import org.objectweb.asm.Label;
33import org.objectweb.asm.MethodAdapter;
34import org.objectweb.asm.MethodVisitor;
35import org.objectweb.asm.Opcodes;
36import org.objectweb.asm.Type;
37
38/**
39 * A {@link MethodAdapter} that renumbers local variables in their order of
40 * appearance. This adapter allows one to easily add new local variables to a
41 * method. It may be used by inheriting from this class, but the preferred way
42 * of using it is via delegation: the next visitor in the chain can indeed add
43 * new locals when needed by calling {@link #newLocal} on this adapter (this
44 * requires a reference back to this {@link LocalVariablesSorter}).
45 *
46 * @author Chris Nokleberg
47 * @author Eugene Kuleshov
48 * @author Eric Bruneton
49 */
50public class LocalVariablesSorter extends MethodAdapter {
51
52 private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
53
54 /**
55 * Mapping from old to new local variable indexes. A local variable at index
56 * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
57 * index i of size 2 is remapped to 'mapping[2*i+1]'.
58 */
59 private int[] mapping = new int[40];
60
61 /**
62 * Array used to store stack map local variable types after remapping.
63 */
64 private Object[] newLocals = new Object[20];
65
66 /**
67 * Index of the first local variable, after formal parameters.
68 */
69 protected final int firstLocal;
70
71 /**
72 * Index of the next local variable to be created by {@link #newLocal}.
73 */
74 protected int nextLocal;
75
76 /**
77 * Indicates if at least one local variable has moved due to remapping.
78 */
79 private boolean changed;
80
81 /**
82 * Creates a new {@link LocalVariablesSorter}.
83 *
84 * @param access access flags of the adapted method.
85 * @param desc the method's descriptor (see {@link Type Type}).
86 * @param mv the method visitor to which this adapter delegates calls.
87 */
88 public LocalVariablesSorter(
89 final int access,
90 final String desc,
91 final MethodVisitor mv)
92 {
93 super(mv);
94 Type[] args = Type.getArgumentTypes(desc);
95 nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
96 for (int i = 0; i < args.length; i++) {
97 nextLocal += args[i].getSize();
98 }
99 firstLocal = nextLocal;
100 }
101
102 public void visitVarInsn(final int opcode, final int var) {
103 Type type;
104 switch (opcode) {
105 case Opcodes.LLOAD:
106 case Opcodes.LSTORE:
107 type = Type.LONG_TYPE;
108 break;
109
110 case Opcodes.DLOAD:
111 case Opcodes.DSTORE:
112 type = Type.DOUBLE_TYPE;
113 break;
114
115 case Opcodes.FLOAD:
116 case Opcodes.FSTORE:
117 type = Type.FLOAT_TYPE;
118 break;
119
120 case Opcodes.ILOAD:
121 case Opcodes.ISTORE:
122 type = Type.INT_TYPE;
123 break;
124
125 case Opcodes.ALOAD:
126 case Opcodes.ASTORE:
127 type = OBJECT_TYPE;
128 break;
129
130 // case RET:
131 default:
132 type = Type.VOID_TYPE;
133 }
134 mv.visitVarInsn(opcode, remap(var, type));
135 }
136
137 public void visitIincInsn(final int var, final int increment) {
138 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
139 }
140
141 public void visitMaxs(final int maxStack, final int maxLocals) {
142 mv.visitMaxs(maxStack, nextLocal);
143 }
144
145 public void visitLocalVariable(
146 final String name,
147 final String desc,
148 final String signature,
149 final Label start,
150 final Label end,
151 final int index)
152 {
153 int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1;
154 int newIndex = remap(index, size);
155 mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
156 }
157
158 public void visitFrame(
159 final int type,
160 final int nLocal,
161 final Object[] local,
162 final int nStack,
163 final Object[] stack)
164 {
165 if (type != Opcodes.F_NEW) { // uncompressed frame
166 throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
167 }
168
169 if (!changed) { // optimization for the case where mapping = identity
170 mv.visitFrame(type, nLocal, local, nStack, stack);
171 return;
172 }
173
174 // creates a copy of newLocals
175 Object[] oldLocals = new Object[newLocals.length];
176 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
177
178 // copies types from 'local' to 'newLocals'
179 // 'newLocals' already contains the variables added with 'newLocal'
180
181 int index = 0; // old local variable index
182 int number = 0; // old local variable number
183 for (; number < nLocal; ++number) {
184 Object t = local[number];
185 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
186 if (t != Opcodes.TOP) {
187 setFrameLocal(remap(index, size), t);
188 }
189 index += size;
190 }
191
192 // removes TOP after long and double types as well as trailing TOPs
193
194 index = 0;
195 number = 0;
196 for (int i = 0; index < newLocals.length; ++i) {
197 Object t = newLocals[index++];
198 if (t != null && t != Opcodes.TOP) {
199 newLocals[i] = t;
200 number = i + 1;
201 if (t == Opcodes.LONG || t == Opcodes.DOUBLE) {
202 index += 1;
203 }
204 } else {
205 newLocals[i] = Opcodes.TOP;
206 }
207 }
208
209 // visits remapped frame
210 mv.visitFrame(type, number, newLocals, nStack, stack);
211
212 // restores original value of 'newLocals'
213 newLocals = oldLocals;
214 }
215
216 // -------------
217
218 /**
219 * Creates a new local variable of the given type.
220 *
221 * @param type the type of the local variable to be created.
222 * @return the identifier of the newly created local variable.
223 */
224 public int newLocal(final Type type) {
225 Object t;
226 switch (type.getSort()) {
227 case Type.BOOLEAN:
228 case Type.CHAR:
229 case Type.BYTE:
230 case Type.SHORT:
231 case Type.INT:
232 t = Opcodes.INTEGER;
233 break;
234 case Type.FLOAT:
235 t = Opcodes.FLOAT;
236 break;
237 case Type.LONG:
238 t = Opcodes.LONG;
239 break;
240 case Type.DOUBLE:
241 t = Opcodes.DOUBLE;
242 break;
243 case Type.ARRAY:
244 t = type.getDescriptor();
245 break;
246 // case Type.OBJECT:
247 default:
248 t = type.getInternalName();
249 break;
250 }
251 int local = nextLocal;
252 setLocalType(local, type);
253 setFrameLocal(local, t);
254 nextLocal += type.getSize();
255 return local;
256 }
257
258 /**
259 * Sets the current type of the given local variable. The default
260 * implementation of this method does nothing.
261 *
262 * @param local a local variable identifier, as returned by {@link #newLocal
263 * newLocal()}.
264 * @param type the type of the value being stored in the local variable
265 */
266 protected void setLocalType(final int local, final Type type) {
267 }
268
269 private void setFrameLocal(final int local, final Object type) {
270 int l = newLocals.length;
271 if (local >= l) {
272 Object[] a = new Object[Math.max(2 * l, local + 1)];
273 System.arraycopy(newLocals, 0, a, 0, l);
274 newLocals = a;
275 }
276 newLocals[local] = type;
277 }
278
279 private int remap(final int var, final Type type) {
280 if (var < firstLocal) {
281 return var;
282 }
283 int key = 2 * var + type.getSize() - 1;
284 int size = mapping.length;
285 if (key >= size) {
286 int[] newMapping = new int[Math.max(2 * size, key + 1)];
287 System.arraycopy(mapping, 0, newMapping, 0, size);
288 mapping = newMapping;
289 }
290 int value = mapping[key];
291 if (value == 0) {
292 value = nextLocal + 1;
293 mapping[key] = value;
294 setLocalType(nextLocal, type);
295 nextLocal += type.getSize();
296 }
297 if (value - 1 != var) {
298 changed = true;
299 }
300 return value - 1;
301 }
302
303 private int remap(final int var, final int size) {
304 if (var < firstLocal || !changed) {
305 return var;
306 }
307 int key = 2 * var + size - 1;
308 int value = key < mapping.length ? mapping[key] : 0;
309 if (value == 0) {
310 throw new IllegalStateException("Unknown local variable " + var);
311 }
312 return value - 1;
313 }
314}