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