41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. Neither the name of the copyright holders nor the names of its 44 * contributors may be used to endorse or promote products derived from 45 * this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 57 * THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 package jdk.internal.org.objectweb.asm.commons; 60 61 import jdk.internal.org.objectweb.asm.Label; 62 import jdk.internal.org.objectweb.asm.MethodVisitor; 63 import jdk.internal.org.objectweb.asm.Opcodes; 64 import jdk.internal.org.objectweb.asm.Type; 65 66 /** 67 * A {@link MethodVisitor} that renumbers local variables in their order of 68 * appearance. This adapter allows one to easily add new local variables to a 69 * method. It may be used by inheriting from this class, but the preferred way 70 * of using it is via delegation: the next visitor in the chain can indeed add 71 * new locals when needed by calling {@link #newLocal} on this adapter (this 72 * requires a reference back to this {@link LocalVariablesSorter}). 73 * 74 * @author Chris Nokleberg 75 * @author Eugene Kuleshov 76 * @author Eric Bruneton 77 */ 78 public class LocalVariablesSorter extends MethodVisitor { 79 80 private static final Type OBJECT_TYPE = Type.getObjectType("java/lang/Object"); 81 82 /** 83 * Mapping from old to new local variable indexes. A local variable at index 84 * i of size 1 is remapped to 'mapping[2*i]', while a local variable at 85 * index i of size 2 is remapped to 'mapping[2*i+1]'. 86 */ 87 private int[] mapping = new int[40]; 88 89 /** 90 * Array used to store stack map local variable types after remapping. 91 */ 92 private Object[] newLocals = new Object[20]; 93 94 /** 95 * Index of the first local variable, after formal parameters. 96 */ 97 protected final int firstLocal; 98 99 /** 100 * Index of the next local variable to be created by {@link #newLocal}. 101 */ 102 protected int nextLocal; 103 104 /** 105 * Indicates if at least one local variable has moved due to remapping. 106 */ 107 private boolean changed; 108 109 /** 110 * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use 111 * this constructor</i>. Instead, they must use the 112 * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version. 113 * 114 * @param access access flags of the adapted method. 115 * @param desc the method's descriptor (see {@link Type Type}). 116 * @param mv the method visitor to which this adapter delegates calls. 117 */ 118 public LocalVariablesSorter( 119 final int access, 120 final String desc, 121 final MethodVisitor mv) 122 { 123 this(Opcodes.ASM4, access, desc, mv); 124 } 125 126 /** 127 * Creates a new {@link LocalVariablesSorter}. 128 * 129 * @param api the ASM API version implemented by this visitor. Must be one 130 * of {@link Opcodes#ASM4}. 131 * @param access access flags of the adapted method. 132 * @param desc the method's descriptor (see {@link Type Type}). 133 * @param mv the method visitor to which this adapter delegates calls. 134 */ 135 protected LocalVariablesSorter( 136 final int api, 137 final int access, 138 final String desc, 139 final MethodVisitor mv) 140 { 141 super(api, mv); 142 Type[] args = Type.getArgumentTypes(desc); 143 nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; 144 for (int i = 0; i < args.length; i++) { 145 nextLocal += args[i].getSize(); 146 } 147 firstLocal = nextLocal; 148 } 149 150 @Override 151 public void visitVarInsn(final int opcode, final int var) { 152 Type type; 153 switch (opcode) { 154 case Opcodes.LLOAD: 155 case Opcodes.LSTORE: 156 type = Type.LONG_TYPE; 157 break; 158 159 case Opcodes.DLOAD: 160 case Opcodes.DSTORE: 175 // case Opcodes.ALOAD: 176 // case Opcodes.ASTORE: 177 // case RET: 178 type = OBJECT_TYPE; 179 break; 180 } 181 mv.visitVarInsn(opcode, remap(var, type)); 182 } 183 184 @Override 185 public void visitIincInsn(final int var, final int increment) { 186 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); 187 } 188 189 @Override 190 public void visitMaxs(final int maxStack, final int maxLocals) { 191 mv.visitMaxs(maxStack, nextLocal); 192 } 193 194 @Override 195 public void visitLocalVariable( 196 final String name, 197 final String desc, 198 final String signature, 199 final Label start, 200 final Label end, 201 final int index) 202 { 203 int newIndex = remap(index, Type.getType(desc)); 204 mv.visitLocalVariable(name, desc, signature, start, end, newIndex); 205 } 206 207 @Override 208 public void visitFrame( 209 final int type, 210 final int nLocal, 211 final Object[] local, 212 final int nStack, 213 final Object[] stack) 214 { 215 if (type != Opcodes.F_NEW) { // uncompressed frame 216 throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag"); 217 } 218 219 if (!changed) { // optimization for the case where mapping = identity 220 mv.visitFrame(type, nLocal, local, nStack, stack); 221 return; 222 } 223 224 // creates a copy of newLocals 225 Object[] oldLocals = new Object[newLocals.length]; 226 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); 227 228 // copies types from 'local' to 'newLocals' 229 // 'newLocals' already contains the variables added with 'newLocal' 230 231 int index = 0; // old local variable index 232 int number = 0; // old local variable number 233 for (; number < nLocal; ++number) { 234 Object t = local[number]; 235 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; 236 if (t != Opcodes.TOP) { 237 Type typ = OBJECT_TYPE; 238 if (t == Opcodes.INTEGER) { 239 typ = Type.INT_TYPE; 240 } else if (t == Opcodes.FLOAT) { 241 typ = Type.FLOAT_TYPE; 242 } else if (t == Opcodes.LONG) { 243 typ = Type.LONG_TYPE; 244 } else if (t == Opcodes.DOUBLE) { 245 typ = Type.DOUBLE_TYPE; 246 } else if (t instanceof String) { 247 typ = Type.getObjectType((String) t); 263 if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { 264 index += 1; 265 } 266 } else { 267 newLocals[i] = Opcodes.TOP; 268 } 269 } 270 271 // visits remapped frame 272 mv.visitFrame(type, number, newLocals, nStack, stack); 273 274 // restores original value of 'newLocals' 275 newLocals = oldLocals; 276 } 277 278 // ------------- 279 280 /** 281 * Creates a new local variable of the given type. 282 * 283 * @param type the type of the local variable to be created. 284 * @return the identifier of the newly created local variable. 285 */ 286 public int newLocal(final Type type) { 287 Object t; 288 switch (type.getSort()) { 289 case Type.BOOLEAN: 290 case Type.CHAR: 291 case Type.BYTE: 292 case Type.SHORT: 293 case Type.INT: 294 t = Opcodes.INTEGER; 295 break; 296 case Type.FLOAT: 297 t = Opcodes.FLOAT; 298 break; 299 case Type.LONG: 300 t = Opcodes.LONG; 301 break; 302 case Type.DOUBLE: 303 t = Opcodes.DOUBLE; 304 break; 305 case Type.ARRAY: 306 t = type.getDescriptor(); 307 break; 308 // case Type.OBJECT: 309 default: 310 t = type.getInternalName(); 311 break; 312 } 313 int local = nextLocal; 314 nextLocal += type.getSize(); 315 setLocalType(local, type); 316 setFrameLocal(local, t); 317 return local; 318 } 319 320 /** 321 * Sets the current type of the given local variable. The default 322 * implementation of this method does nothing. 323 * 324 * @param local a local variable identifier, as returned by {@link #newLocal 325 * newLocal()}. 326 * @param type the type of the value being stored in the local variable 327 */ 328 protected void setLocalType(final int local, final Type type) { 329 } 330 331 private void setFrameLocal(final int local, final Object type) { 332 int l = newLocals.length; 333 if (local >= l) { 334 Object[] a = new Object[Math.max(2 * l, local + 1)]; 335 System.arraycopy(newLocals, 0, a, 0, l); 336 newLocals = a; 337 } 338 newLocals[local] = type; 339 } 340 341 private int remap(final int var, final Type type) { 342 if (var + type.getSize() <= firstLocal) { 343 return var; 344 } 345 int key = 2 * var + type.getSize() - 1; 346 int size = mapping.length; | 41 * notice, this list of conditions and the following disclaimer in the 42 * documentation and/or other materials provided with the distribution. 43 * 3. Neither the name of the copyright holders nor the names of its 44 * contributors may be used to endorse or promote products derived from 45 * this software without specific prior written permission. 46 * 47 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 48 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 49 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 50 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 51 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 52 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 53 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 54 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 55 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 56 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 57 * THE POSSIBILITY OF SUCH DAMAGE. 58 */ 59 package jdk.internal.org.objectweb.asm.commons; 60 61 import jdk.internal.org.objectweb.asm.AnnotationVisitor; 62 import jdk.internal.org.objectweb.asm.Label; 63 import jdk.internal.org.objectweb.asm.MethodVisitor; 64 import jdk.internal.org.objectweb.asm.Opcodes; 65 import jdk.internal.org.objectweb.asm.Type; 66 import jdk.internal.org.objectweb.asm.TypePath; 67 68 /** 69 * A {@link MethodVisitor} that renumbers local variables in their order of 70 * appearance. This adapter allows one to easily add new local variables to a 71 * method. It may be used by inheriting from this class, but the preferred way 72 * of using it is via delegation: the next visitor in the chain can indeed add 73 * new locals when needed by calling {@link #newLocal} on this adapter (this 74 * requires a reference back to this {@link LocalVariablesSorter}). 75 * 76 * @author Chris Nokleberg 77 * @author Eugene Kuleshov 78 * @author Eric Bruneton 79 */ 80 public class LocalVariablesSorter extends MethodVisitor { 81 82 private static final Type OBJECT_TYPE = Type 83 .getObjectType("java/lang/Object"); 84 85 /** 86 * Mapping from old to new local variable indexes. A local variable at index 87 * i of size 1 is remapped to 'mapping[2*i]', while a local variable at 88 * index i of size 2 is remapped to 'mapping[2*i+1]'. 89 */ 90 private int[] mapping = new int[40]; 91 92 /** 93 * Array used to store stack map local variable types after remapping. 94 */ 95 private Object[] newLocals = new Object[20]; 96 97 /** 98 * Index of the first local variable, after formal parameters. 99 */ 100 protected final int firstLocal; 101 102 /** 103 * Index of the next local variable to be created by {@link #newLocal}. 104 */ 105 protected int nextLocal; 106 107 /** 108 * Indicates if at least one local variable has moved due to remapping. 109 */ 110 private boolean changed; 111 112 /** 113 * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use 114 * this constructor</i>. Instead, they must use the 115 * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version. 116 * 117 * @param access 118 * access flags of the adapted method. 119 * @param desc 120 * the method's descriptor (see {@link Type Type}). 121 * @param mv 122 * the method visitor to which this adapter delegates calls. 123 */ 124 public LocalVariablesSorter(final int access, final String desc, 125 final MethodVisitor mv) { 126 this(Opcodes.ASM5, access, desc, mv); 127 } 128 129 /** 130 * Creates a new {@link LocalVariablesSorter}. 131 * 132 * @param api 133 * the ASM API version implemented by this visitor. Must be one 134 * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. 135 * @param access 136 * access flags of the adapted method. 137 * @param desc 138 * the method's descriptor (see {@link Type Type}). 139 * @param mv 140 * the method visitor to which this adapter delegates calls. 141 */ 142 protected LocalVariablesSorter(final int api, final int access, 143 final String desc, final MethodVisitor mv) { 144 super(api, mv); 145 Type[] args = Type.getArgumentTypes(desc); 146 nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; 147 for (int i = 0; i < args.length; i++) { 148 nextLocal += args[i].getSize(); 149 } 150 firstLocal = nextLocal; 151 } 152 153 @Override 154 public void visitVarInsn(final int opcode, final int var) { 155 Type type; 156 switch (opcode) { 157 case Opcodes.LLOAD: 158 case Opcodes.LSTORE: 159 type = Type.LONG_TYPE; 160 break; 161 162 case Opcodes.DLOAD: 163 case Opcodes.DSTORE: 178 // case Opcodes.ALOAD: 179 // case Opcodes.ASTORE: 180 // case RET: 181 type = OBJECT_TYPE; 182 break; 183 } 184 mv.visitVarInsn(opcode, remap(var, type)); 185 } 186 187 @Override 188 public void visitIincInsn(final int var, final int increment) { 189 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); 190 } 191 192 @Override 193 public void visitMaxs(final int maxStack, final int maxLocals) { 194 mv.visitMaxs(maxStack, nextLocal); 195 } 196 197 @Override 198 public void visitLocalVariable(final String name, final String desc, 199 final String signature, final Label start, final Label end, 200 final int index) { 201 int newIndex = remap(index, Type.getType(desc)); 202 mv.visitLocalVariable(name, desc, signature, start, end, newIndex); 203 } 204 205 @Override 206 public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, 207 TypePath typePath, Label[] start, Label[] end, int[] index, 208 String desc, boolean visible) { 209 Type t = Type.getType(desc); 210 int[] newIndex = new int[index.length]; 211 for (int i = 0; i < newIndex.length; ++i) { 212 newIndex[i] = remap(index[i], t); 213 } 214 return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, 215 newIndex, desc, visible); 216 } 217 218 @Override 219 public void visitFrame(final int type, final int nLocal, 220 final Object[] local, final int nStack, final Object[] stack) { 221 if (type != Opcodes.F_NEW) { // uncompressed frame 222 throw new IllegalStateException( 223 "ClassReader.accept() should be called with EXPAND_FRAMES flag"); 224 } 225 226 if (!changed) { // optimization for the case where mapping = identity 227 mv.visitFrame(type, nLocal, local, nStack, stack); 228 return; 229 } 230 231 // creates a copy of newLocals 232 Object[] oldLocals = new Object[newLocals.length]; 233 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); 234 235 updateNewLocals(newLocals); 236 237 // copies types from 'local' to 'newLocals' 238 // 'newLocals' already contains the variables added with 'newLocal' 239 240 int index = 0; // old local variable index 241 int number = 0; // old local variable number 242 for (; number < nLocal; ++number) { 243 Object t = local[number]; 244 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; 245 if (t != Opcodes.TOP) { 246 Type typ = OBJECT_TYPE; 247 if (t == Opcodes.INTEGER) { 248 typ = Type.INT_TYPE; 249 } else if (t == Opcodes.FLOAT) { 250 typ = Type.FLOAT_TYPE; 251 } else if (t == Opcodes.LONG) { 252 typ = Type.LONG_TYPE; 253 } else if (t == Opcodes.DOUBLE) { 254 typ = Type.DOUBLE_TYPE; 255 } else if (t instanceof String) { 256 typ = Type.getObjectType((String) t); 272 if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { 273 index += 1; 274 } 275 } else { 276 newLocals[i] = Opcodes.TOP; 277 } 278 } 279 280 // visits remapped frame 281 mv.visitFrame(type, number, newLocals, nStack, stack); 282 283 // restores original value of 'newLocals' 284 newLocals = oldLocals; 285 } 286 287 // ------------- 288 289 /** 290 * Creates a new local variable of the given type. 291 * 292 * @param type 293 * the type of the local variable to be created. 294 * @return the identifier of the newly created local variable. 295 */ 296 public int newLocal(final Type type) { 297 Object t; 298 switch (type.getSort()) { 299 case Type.BOOLEAN: 300 case Type.CHAR: 301 case Type.BYTE: 302 case Type.SHORT: 303 case Type.INT: 304 t = Opcodes.INTEGER; 305 break; 306 case Type.FLOAT: 307 t = Opcodes.FLOAT; 308 break; 309 case Type.LONG: 310 t = Opcodes.LONG; 311 break; 312 case Type.DOUBLE: 313 t = Opcodes.DOUBLE; 314 break; 315 case Type.ARRAY: 316 t = type.getDescriptor(); 317 break; 318 // case Type.OBJECT: 319 default: 320 t = type.getInternalName(); 321 break; 322 } 323 int local = newLocalMapping(type); 324 setLocalType(local, type); 325 setFrameLocal(local, t); 326 return local; 327 } 328 329 /** 330 * Notifies subclasses that a new stack map frame is being visited. The 331 * array argument contains the stack map frame types corresponding to the 332 * local variables added with {@link #newLocal}. This method can update 333 * these types in place for the stack map frame being visited. The default 334 * implementation of this method does nothing, i.e. a local variable added 335 * with {@link #newLocal} will have the same type in all stack map frames. 336 * But this behavior is not always the desired one, for instance if a local 337 * variable is added in the middle of a try/catch block: the frame for the 338 * exception handler should have a TOP type for this new local. 339 * 340 * @param newLocals 341 * the stack map frame types corresponding to the local variables 342 * added with {@link #newLocal} (and null for the others). The 343 * format of this array is the same as in 344 * {@link MethodVisitor#visitFrame}, except that long and double 345 * types use two slots. The types for the current stack map frame 346 * must be updated in place in this array. 347 */ 348 protected void updateNewLocals(Object[] newLocals) { 349 } 350 351 /** 352 * Notifies subclasses that a local variable has been added or remapped. The 353 * default implementation of this method does nothing. 354 * 355 * @param local 356 * a local variable identifier, as returned by {@link #newLocal 357 * newLocal()}. 358 * @param type 359 * the type of the value being stored in the local variable. 360 */ 361 protected void setLocalType(final int local, final Type type) { 362 } 363 364 private void setFrameLocal(final int local, final Object type) { 365 int l = newLocals.length; 366 if (local >= l) { 367 Object[] a = new Object[Math.max(2 * l, local + 1)]; 368 System.arraycopy(newLocals, 0, a, 0, l); 369 newLocals = a; 370 } 371 newLocals[local] = type; 372 } 373 374 private int remap(final int var, final Type type) { 375 if (var + type.getSize() <= firstLocal) { 376 return var; 377 } 378 int key = 2 * var + type.getSize() - 1; 379 int size = mapping.length; |