• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // ASM: a very small and fast Java bytecode manipulation framework
2 // Copyright (c) 2000-2011 INRIA, France Telecom
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions
7 // are met:
8 // 1. Redistributions of source code must retain the above copyright
9 //    notice, this list of conditions and the following disclaimer.
10 // 2. Redistributions in binary form must reproduce the above copyright
11 //    notice, this list of conditions and the following disclaimer in the
12 //    documentation and/or other materials provided with the distribution.
13 // 3. Neither the name of the copyright holders nor the names of its
14 //    contributors may be used to endorse or promote products derived from
15 //    this software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 // THE POSSIBILITY OF SUCH DAMAGE.
28 package org.objectweb.asm.commons;
29 
30 import org.objectweb.asm.AnnotationVisitor;
31 import org.objectweb.asm.Label;
32 import org.objectweb.asm.MethodVisitor;
33 import org.objectweb.asm.Opcodes;
34 import org.objectweb.asm.Type;
35 import org.objectweb.asm.TypePath;
36 
37 /**
38  * A {@link MethodVisitor} that renumbers local variables in their order of appearance. This adapter
39  * allows one to easily add new local variables to a method. It may be used by inheriting from this
40  * class, but the preferred way of using it is via delegation: the next visitor in the chain can
41  * indeed add new locals when needed by calling {@link #newLocal} on this adapter (this requires a
42  * reference back to this {@link LocalVariablesSorter}).
43  *
44  * @author Chris Nokleberg
45  * @author Eugene Kuleshov
46  * @author Eric Bruneton
47  */
48 public class LocalVariablesSorter extends MethodVisitor {
49 
50   /** The type of the java.lang.Object class. */
51   private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
52 
53   /**
54    * The mapping from old to new local variable indices. A local variable at index i of size 1 is
55    * remapped to 'mapping[2*i]', while a local variable at index i of size 2 is remapped to
56    * 'mapping[2*i+1]'.
57    */
58   private int[] remappedVariableIndices = new int[40];
59 
60   /**
61    * The local variable types after remapping. The format of this array is the same as in {@link
62    * MethodVisitor#visitFrame}, except that long and double types use two slots.
63    */
64   private Object[] remappedLocalTypes = new Object[20];
65 
66   /** The index of the first local variable, after formal parameters. */
67   protected final int firstLocal;
68 
69   /** The index of the next local variable to be created by {@link #newLocal}. */
70   protected int nextLocal;
71 
72   /**
73    * Constructs a new {@link LocalVariablesSorter}. <i>Subclasses must not use this constructor</i>.
74    * Instead, they must use the {@link #LocalVariablesSorter(int, int, String, MethodVisitor)}
75    * version.
76    *
77    * @param access access flags of the adapted method.
78    * @param descriptor the method's descriptor (see {@link Type}).
79    * @param methodVisitor the method visitor to which this adapter delegates calls.
80    * @throws IllegalStateException if a subclass calls this constructor.
81    */
LocalVariablesSorter( final int access, final String descriptor, final MethodVisitor methodVisitor)82   public LocalVariablesSorter(
83       final int access, final String descriptor, final MethodVisitor methodVisitor) {
84     this(/* latest api = */ Opcodes.ASM9, access, descriptor, methodVisitor);
85     if (getClass() != LocalVariablesSorter.class) {
86       throw new IllegalStateException();
87     }
88   }
89 
90   /**
91    * Constructs a new {@link LocalVariablesSorter}.
92    *
93    * @param api the ASM API version implemented by this visitor. Must be one of the {@code
94    *     ASM}<i>x</i> values in {@link Opcodes}.
95    * @param access access flags of the adapted method.
96    * @param descriptor the method's descriptor (see {@link Type}).
97    * @param methodVisitor the method visitor to which this adapter delegates calls.
98    */
LocalVariablesSorter( final int api, final int access, final String descriptor, final MethodVisitor methodVisitor)99   protected LocalVariablesSorter(
100       final int api, final int access, final String descriptor, final MethodVisitor methodVisitor) {
101     super(api, methodVisitor);
102     nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0;
103     for (Type argumentType : Type.getArgumentTypes(descriptor)) {
104       nextLocal += argumentType.getSize();
105     }
106     firstLocal = nextLocal;
107   }
108 
109   @Override
visitVarInsn(final int opcode, final int varIndex)110   public void visitVarInsn(final int opcode, final int varIndex) {
111     Type varType;
112     switch (opcode) {
113       case Opcodes.LLOAD:
114       case Opcodes.LSTORE:
115         varType = Type.LONG_TYPE;
116         break;
117       case Opcodes.DLOAD:
118       case Opcodes.DSTORE:
119         varType = Type.DOUBLE_TYPE;
120         break;
121       case Opcodes.FLOAD:
122       case Opcodes.FSTORE:
123         varType = Type.FLOAT_TYPE;
124         break;
125       case Opcodes.ILOAD:
126       case Opcodes.ISTORE:
127         varType = Type.INT_TYPE;
128         break;
129       case Opcodes.ALOAD:
130       case Opcodes.ASTORE:
131       case Opcodes.RET:
132         varType = OBJECT_TYPE;
133         break;
134       default:
135         throw new IllegalArgumentException("Invalid opcode " + opcode);
136     }
137     super.visitVarInsn(opcode, remap(varIndex, varType));
138   }
139 
140   @Override
visitIincInsn(final int varIndex, final int increment)141   public void visitIincInsn(final int varIndex, final int increment) {
142     super.visitIincInsn(remap(varIndex, Type.INT_TYPE), increment);
143   }
144 
145   @Override
visitMaxs(final int maxStack, final int maxLocals)146   public void visitMaxs(final int maxStack, final int maxLocals) {
147     super.visitMaxs(maxStack, nextLocal);
148   }
149 
150   @Override
visitLocalVariable( final String name, final String descriptor, final String signature, final Label start, final Label end, final int index)151   public void visitLocalVariable(
152       final String name,
153       final String descriptor,
154       final String signature,
155       final Label start,
156       final Label end,
157       final int index) {
158     int remappedIndex = remap(index, Type.getType(descriptor));
159     super.visitLocalVariable(name, descriptor, signature, start, end, remappedIndex);
160   }
161 
162   @Override
visitLocalVariableAnnotation( final int typeRef, final TypePath typePath, final Label[] start, final Label[] end, final int[] index, final String descriptor, final boolean visible)163   public AnnotationVisitor visitLocalVariableAnnotation(
164       final int typeRef,
165       final TypePath typePath,
166       final Label[] start,
167       final Label[] end,
168       final int[] index,
169       final String descriptor,
170       final boolean visible) {
171     Type type = Type.getType(descriptor);
172     int[] remappedIndex = new int[index.length];
173     for (int i = 0; i < remappedIndex.length; ++i) {
174       remappedIndex[i] = remap(index[i], type);
175     }
176     return super.visitLocalVariableAnnotation(
177         typeRef, typePath, start, end, remappedIndex, descriptor, visible);
178   }
179 
180   @Override
visitFrame( final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack)181   public void visitFrame(
182       final int type,
183       final int numLocal,
184       final Object[] local,
185       final int numStack,
186       final Object[] stack) {
187     if (type != Opcodes.F_NEW) { // Uncompressed frame.
188       throw new IllegalArgumentException(
189           "LocalVariablesSorter only accepts expanded frames (see ClassReader.EXPAND_FRAMES)");
190     }
191 
192     // Create a copy of remappedLocals.
193     Object[] oldRemappedLocals = new Object[remappedLocalTypes.length];
194     System.arraycopy(remappedLocalTypes, 0, oldRemappedLocals, 0, oldRemappedLocals.length);
195 
196     updateNewLocals(remappedLocalTypes);
197 
198     // Copy the types from 'local' to 'remappedLocals'. 'remappedLocals' already contains the
199     // variables added with 'newLocal'.
200     int oldVar = 0; // Old local variable index.
201     for (int i = 0; i < numLocal; ++i) {
202       Object localType = local[i];
203       if (localType != Opcodes.TOP) {
204         Type varType = OBJECT_TYPE;
205         if (localType == Opcodes.INTEGER) {
206           varType = Type.INT_TYPE;
207         } else if (localType == Opcodes.FLOAT) {
208           varType = Type.FLOAT_TYPE;
209         } else if (localType == Opcodes.LONG) {
210           varType = Type.LONG_TYPE;
211         } else if (localType == Opcodes.DOUBLE) {
212           varType = Type.DOUBLE_TYPE;
213         } else if (localType instanceof String) {
214           varType = Type.getObjectType((String) localType);
215         }
216         setFrameLocal(remap(oldVar, varType), localType);
217       }
218       oldVar += localType == Opcodes.LONG || localType == Opcodes.DOUBLE ? 2 : 1;
219     }
220 
221     // Remove TOP after long and double types as well as trailing TOPs.
222     oldVar = 0;
223     int newVar = 0;
224     int remappedNumLocal = 0;
225     while (oldVar < remappedLocalTypes.length) {
226       Object localType = remappedLocalTypes[oldVar];
227       oldVar += localType == Opcodes.LONG || localType == Opcodes.DOUBLE ? 2 : 1;
228       if (localType != null && localType != Opcodes.TOP) {
229         remappedLocalTypes[newVar++] = localType;
230         remappedNumLocal = newVar;
231       } else {
232         remappedLocalTypes[newVar++] = Opcodes.TOP;
233       }
234     }
235 
236     // Visit the remapped frame.
237     super.visitFrame(type, remappedNumLocal, remappedLocalTypes, numStack, stack);
238 
239     // Restore the original value of 'remappedLocals'.
240     remappedLocalTypes = oldRemappedLocals;
241   }
242 
243   // -----------------------------------------------------------------------------------------------
244 
245   /**
246    * Constructs a new local variable of the given type.
247    *
248    * @param type the type of the local variable to be created.
249    * @return the identifier of the newly created local variable.
250    */
newLocal(final Type type)251   public int newLocal(final Type type) {
252     Object localType;
253     switch (type.getSort()) {
254       case Type.BOOLEAN:
255       case Type.CHAR:
256       case Type.BYTE:
257       case Type.SHORT:
258       case Type.INT:
259         localType = Opcodes.INTEGER;
260         break;
261       case Type.FLOAT:
262         localType = Opcodes.FLOAT;
263         break;
264       case Type.LONG:
265         localType = Opcodes.LONG;
266         break;
267       case Type.DOUBLE:
268         localType = Opcodes.DOUBLE;
269         break;
270       case Type.ARRAY:
271         localType = type.getDescriptor();
272         break;
273       case Type.OBJECT:
274         localType = type.getInternalName();
275         break;
276       default:
277         throw new AssertionError();
278     }
279     int local = newLocalMapping(type);
280     setLocalType(local, type);
281     setFrameLocal(local, localType);
282     return local;
283   }
284 
285   /**
286    * Notifies subclasses that a new stack map frame is being visited. The array argument contains
287    * the stack map frame types corresponding to the local variables added with {@link #newLocal}.
288    * This method can update these types in place for the stack map frame being visited. The default
289    * implementation of this method does nothing, i.e. a local variable added with {@link #newLocal}
290    * will have the same type in all stack map frames. But this behavior is not always the desired
291    * one, for instance if a local variable is added in the middle of a try/catch block: the frame
292    * for the exception handler should have a TOP type for this new local.
293    *
294    * @param newLocals the stack map frame types corresponding to the local variables added with
295    *     {@link #newLocal} (and null for the others). The format of this array is the same as in
296    *     {@link MethodVisitor#visitFrame}, except that long and double types use two slots. The
297    *     types for the current stack map frame must be updated in place in this array.
298    */
updateNewLocals(final Object[] newLocals)299   protected void updateNewLocals(final Object[] newLocals) {
300     // The default implementation does nothing.
301   }
302 
303   /**
304    * Notifies subclasses that a local variable has been added or remapped. The default
305    * implementation of this method does nothing.
306    *
307    * @param local a local variable identifier, as returned by {@link #newLocal}.
308    * @param type the type of the value being stored in the local variable.
309    */
setLocalType(final int local, final Type type)310   protected void setLocalType(final int local, final Type type) {
311     // The default implementation does nothing.
312   }
313 
setFrameLocal(final int local, final Object type)314   private void setFrameLocal(final int local, final Object type) {
315     int numLocals = remappedLocalTypes.length;
316     if (local >= numLocals) {
317       Object[] newRemappedLocalTypes = new Object[Math.max(2 * numLocals, local + 1)];
318       System.arraycopy(remappedLocalTypes, 0, newRemappedLocalTypes, 0, numLocals);
319       remappedLocalTypes = newRemappedLocalTypes;
320     }
321     remappedLocalTypes[local] = type;
322   }
323 
remap(final int varIndex, final Type type)324   private int remap(final int varIndex, final Type type) {
325     if (varIndex + type.getSize() <= firstLocal) {
326       return varIndex;
327     }
328     int key = 2 * varIndex + type.getSize() - 1;
329     int size = remappedVariableIndices.length;
330     if (key >= size) {
331       int[] newRemappedVariableIndices = new int[Math.max(2 * size, key + 1)];
332       System.arraycopy(remappedVariableIndices, 0, newRemappedVariableIndices, 0, size);
333       remappedVariableIndices = newRemappedVariableIndices;
334     }
335     int value = remappedVariableIndices[key];
336     if (value == 0) {
337       value = newLocalMapping(type);
338       setLocalType(value, type);
339       remappedVariableIndices[key] = value + 1;
340     } else {
341       value--;
342     }
343     return value;
344   }
345 
newLocalMapping(final Type type)346   protected int newLocalMapping(final Type type) {
347     int local = nextLocal;
348     nextLocal += type.getSize();
349     return local;
350   }
351 }
352