view src/clojure/asm/commons/LocalVariablesSorter.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
line wrap: on
line source
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.commons;
32 import clojure.asm.Label;
33 import clojure.asm.MethodAdapter;
34 import clojure.asm.MethodVisitor;
35 import clojure.asm.Opcodes;
36 import clojure.asm.Type;
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 */
50 public class LocalVariablesSorter extends MethodAdapter{
52 private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
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];
61 /**
62 * Array used to store stack map local variable types after remapping.
63 */
64 private Object[] newLocals = new Object[20];
66 /**
67 * Index of the first local variable, after formal parameters.
68 */
69 protected final int firstLocal;
71 /**
72 * Index of the next local variable to be created by {@link #newLocal}.
73 */
74 protected int nextLocal;
76 /**
77 * Indicates if at least one local variable has moved due to remapping.
78 */
79 private boolean changed;
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 super(mv);
93 Type[] args = Type.getArgumentTypes(desc);
94 nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
95 for(int i = 0; i < args.length; i++)
96 {
97 nextLocal += args[i].getSize();
98 }
99 firstLocal = nextLocal;
100 }
102 public void visitVarInsn(final int opcode, final int var){
103 Type type;
104 switch(opcode)
105 {
106 case Opcodes.LLOAD:
107 case Opcodes.LSTORE:
108 type = Type.LONG_TYPE;
109 break;
111 case Opcodes.DLOAD:
112 case Opcodes.DSTORE:
113 type = Type.DOUBLE_TYPE;
114 break;
116 case Opcodes.FLOAD:
117 case Opcodes.FSTORE:
118 type = Type.FLOAT_TYPE;
119 break;
121 case Opcodes.ILOAD:
122 case Opcodes.ISTORE:
123 type = Type.INT_TYPE;
124 break;
126 case Opcodes.ALOAD:
127 case Opcodes.ASTORE:
128 type = OBJECT_TYPE;
129 break;
131 // case RET:
132 default:
133 type = Type.VOID_TYPE;
134 }
135 mv.visitVarInsn(opcode, remap(var, type));
136 }
138 public void visitIincInsn(final int var, final int increment){
139 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
140 }
142 public void visitMaxs(final int maxStack, final int maxLocals){
143 mv.visitMaxs(maxStack, nextLocal);
144 }
146 public void visitLocalVariable(
147 final String name,
148 final String desc,
149 final String signature,
150 final Label start,
151 final Label end,
152 final int index){
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 }
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 if(type != Opcodes.F_NEW)
165 { // uncompressed frame
166 throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
167 }
169 if(!changed)
170 { // optimization for the case where mapping = identity
171 mv.visitFrame(type, nLocal, local, nStack, stack);
172 return;
173 }
175 // creates a copy of newLocals
176 Object[] oldLocals = new Object[newLocals.length];
177 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
179 // copies types from 'local' to 'newLocals'
180 // 'newLocals' already contains the variables added with 'newLocal'
182 int index = 0; // old local variable index
183 int number = 0; // old local variable number
184 for(; number < nLocal; ++number)
185 {
186 Object t = local[number];
187 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
188 if(t != Opcodes.TOP)
189 {
190 setFrameLocal(remap(index, size), t);
191 }
192 index += size;
193 }
195 // removes TOP after long and double types as well as trailing TOPs
197 index = 0;
198 number = 0;
199 for(int i = 0; index < newLocals.length; ++i)
200 {
201 Object t = newLocals[index++];
202 if(t != null && t != Opcodes.TOP)
203 {
204 newLocals[i] = t;
205 number = i + 1;
206 if(t == Opcodes.LONG || t == Opcodes.DOUBLE)
207 {
208 index += 1;
209 }
210 }
211 else
212 {
213 newLocals[i] = Opcodes.TOP;
214 }
215 }
217 // visits remapped frame
218 mv.visitFrame(type, number, newLocals, nStack, stack);
220 // restores original value of 'newLocals'
221 newLocals = oldLocals;
222 }
224 // -------------
226 /**
227 * Creates a new local variable of the given type.
228 *
229 * @param type the type of the local variable to be created.
230 * @return the identifier of the newly created local variable.
231 */
232 public int newLocal(final Type type){
233 Object t;
234 switch(type.getSort())
235 {
236 case Type.BOOLEAN:
237 case Type.CHAR:
238 case Type.BYTE:
239 case Type.SHORT:
240 case Type.INT:
241 t = Opcodes.INTEGER;
242 break;
243 case Type.FLOAT:
244 t = Opcodes.FLOAT;
245 break;
246 case Type.LONG:
247 t = Opcodes.LONG;
248 break;
249 case Type.DOUBLE:
250 t = Opcodes.DOUBLE;
251 break;
252 case Type.ARRAY:
253 t = type.getDescriptor();
254 break;
255 // case Type.OBJECT:
256 default:
257 t = type.getInternalName();
258 break;
259 }
260 int local = nextLocal;
261 setLocalType(local, type);
262 setFrameLocal(local, t);
263 nextLocal += type.getSize();
264 return local;
265 }
267 /**
268 * Sets the current type of the given local variable. The default
269 * implementation of this method does nothing.
270 *
271 * @param local a local variable identifier, as returned by {@link #newLocal
272 * newLocal()}.
273 * @param type the type of the value being stored in the local variable
274 */
275 protected void setLocalType(final int local, final Type type){
276 }
278 private void setFrameLocal(final int local, final Object type){
279 int l = newLocals.length;
280 if(local >= l)
281 {
282 Object[] a = new Object[Math.max(2 * l, local + 1)];
283 System.arraycopy(newLocals, 0, a, 0, l);
284 newLocals = a;
285 }
286 newLocals[local] = type;
287 }
289 private int remap(final int var, final Type type){
290 if(var < firstLocal)
291 {
292 return var;
293 }
294 int key = 2 * var + type.getSize() - 1;
295 int size = mapping.length;
296 if(key >= size)
297 {
298 int[] newMapping = new int[Math.max(2 * size, key + 1)];
299 System.arraycopy(mapping, 0, newMapping, 0, size);
300 mapping = newMapping;
301 }
302 int value = mapping[key];
303 if(value == 0)
304 {
305 value = nextLocal + 1;
306 mapping[key] = value;
307 setLocalType(nextLocal, type);
308 nextLocal += type.getSize();
309 }
310 if(value - 1 != var)
311 {
312 changed = true;
313 }
314 return value - 1;
315 }
317 private int remap(final int var, final int size){
318 if(var < firstLocal || !changed)
319 {
320 return var;
321 }
322 int key = 2 * var + size - 1;
323 int value = key < mapping.length ? mapping[key] : 0;
324 if(value == 0)
325 {
326 throw new IllegalStateException("Unknown local variable " + var);
327 }
328 return value - 1;
329 }
330 }