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