001/*** 002 * ASM: a very small and fast Java bytecode manipulation framework 003 * Copyright (c) 2000-2011 INRIA, France Telecom 004 * All rights reserved. 005 * 006 * Redistribution and use in source and binary forms, with or without 007 * modification, are permitted provided that the following conditions 008 * are met: 009 * 1. Redistributions of source code must retain the above copyright 010 * notice, this list of conditions and the following disclaimer. 011 * 2. Redistributions in binary form must reproduce the above copyright 012 * notice, this list of conditions and the following disclaimer in the 013 * documentation and/or other materials provided with the distribution. 014 * 3. Neither the name of the copyright holders nor the names of its 015 * contributors may be used to endorse or promote products derived from 016 * this software without specific prior written permission. 017 * 018 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 019 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 020 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 021 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 022 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 023 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 024 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 025 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 026 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 027 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 028 * THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package io.ebean.enhance.asm.commons; 031 032import io.ebean.enhance.asm.AnnotationVisitor; 033import io.ebean.enhance.asm.Label; 034import io.ebean.enhance.asm.MethodVisitor; 035import io.ebean.enhance.asm.Opcodes; 036import io.ebean.enhance.asm.Type; 037import io.ebean.enhance.asm.TypePath; 038 039/** 040 * A {@link MethodVisitor} that renumbers local variables in their order of 041 * appearance. This adapter allows one to easily add new local variables to a 042 * method. It may be used by inheriting from this class, but the preferred way 043 * of using it is via delegation: the next visitor in the chain can indeed add 044 * new locals when needed by calling {@link #newLocal} on this adapter (this 045 * requires a reference back to this {@link LocalVariablesSorter}). 046 * 047 * @author Chris Nokleberg 048 * @author Eugene Kuleshov 049 * @author Eric Bruneton 050 */ 051public class LocalVariablesSorter extends MethodVisitor { 052 053 private static final Type OBJECT_TYPE = Type 054 .getObjectType("java/lang/Object"); 055 056 /** 057 * Mapping from old to new local variable indexes. A local variable at index 058 * i of size 1 is remapped to 'mapping[2*i]', while a local variable at 059 * index i of size 2 is remapped to 'mapping[2*i+1]'. 060 */ 061 private int[] mapping = new int[40]; 062 063 /** 064 * Array used to store stack map local variable types after remapping. 065 */ 066 private Object[] newLocals = new Object[20]; 067 068 /** 069 * Index of the first local variable, after formal parameters. 070 */ 071 protected final int firstLocal; 072 073 /** 074 * Index of the next local variable to be created by {@link #newLocal}. 075 */ 076 protected int nextLocal; 077 078 /** 079 * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use 080 * this constructor</i>. Instead, they must use the 081 * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version. 082 * 083 * @param access 084 * access flags of the adapted method. 085 * @param desc 086 * the method's descriptor (see {@link Type Type}). 087 * @param mv 088 * the method visitor to which this adapter delegates calls. 089 * @throws IllegalStateException 090 * If a subclass calls this constructor. 091 */ 092 public LocalVariablesSorter(final int access, final String desc, 093 final MethodVisitor mv) { 094 this(Opcodes.ASM6, access, desc, mv); 095 if (getClass() != LocalVariablesSorter.class) { 096 throw new IllegalStateException(); 097 } 098 } 099 100 /** 101 * Creates a new {@link LocalVariablesSorter}. 102 * 103 * @param api 104 * the ASM API version implemented by this visitor. Must be one 105 * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}. 106 * @param access 107 * access flags of the adapted method. 108 * @param desc 109 * the method's descriptor (see {@link Type Type}). 110 * @param mv 111 * the method visitor to which this adapter delegates calls. 112 */ 113 protected LocalVariablesSorter(final int api, final int access, 114 final String desc, final MethodVisitor mv) { 115 super(api, mv); 116 Type[] args = Type.getArgumentTypes(desc); 117 nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; 118 for (int i = 0; i < args.length; i++) { 119 nextLocal += args[i].getSize(); 120 } 121 firstLocal = nextLocal; 122 } 123 124 @Override 125 public void visitVarInsn(final int opcode, final int var) { 126 Type type; 127 switch (opcode) { 128 case Opcodes.LLOAD: 129 case Opcodes.LSTORE: 130 type = Type.LONG_TYPE; 131 break; 132 133 case Opcodes.DLOAD: 134 case Opcodes.DSTORE: 135 type = Type.DOUBLE_TYPE; 136 break; 137 138 case Opcodes.FLOAD: 139 case Opcodes.FSTORE: 140 type = Type.FLOAT_TYPE; 141 break; 142 143 case Opcodes.ILOAD: 144 case Opcodes.ISTORE: 145 type = Type.INT_TYPE; 146 break; 147 148 default: 149 // case Opcodes.ALOAD: 150 // case Opcodes.ASTORE: 151 // case RET: 152 type = OBJECT_TYPE; 153 break; 154 } 155 mv.visitVarInsn(opcode, remap(var, type)); 156 } 157 158 @Override 159 public void visitIincInsn(final int var, final int increment) { 160 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); 161 } 162 163 @Override 164 public void visitMaxs(final int maxStack, final int maxLocals) { 165 mv.visitMaxs(maxStack, nextLocal); 166 } 167 168 @Override 169 public void visitLocalVariable(final String name, final String desc, 170 final String signature, final Label start, final Label end, 171 final int index) { 172 int newIndex = remap(index, Type.getType(desc)); 173 mv.visitLocalVariable(name, desc, signature, start, end, newIndex); 174 } 175 176 @Override 177 public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, 178 TypePath typePath, Label[] start, Label[] end, int[] index, 179 String desc, boolean visible) { 180 Type t = Type.getType(desc); 181 int[] newIndex = new int[index.length]; 182 for (int i = 0; i < newIndex.length; ++i) { 183 newIndex[i] = remap(index[i], t); 184 } 185 return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, 186 newIndex, desc, visible); 187 } 188 189 @Override 190 public void visitFrame(final int type, final int nLocal, 191 final Object[] local, final int nStack, final Object[] stack) { 192 if (type != Opcodes.F_NEW) { // uncompressed frame 193 throw new IllegalStateException( 194 "ClassReader.accept() should be called with EXPAND_FRAMES flag"); 195 } 196 197 // creates a copy of newLocals 198 Object[] oldLocals = new Object[newLocals.length]; 199 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); 200 201 updateNewLocals(newLocals); 202 203 // copies types from 'local' to 'newLocals' 204 // 'newLocals' already contains the variables added with 'newLocal' 205 206 int index = 0; // old local variable index 207 int number = 0; // old local variable number 208 for (; number < nLocal; ++number) { 209 Object t = local[number]; 210 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; 211 if (t != Opcodes.TOP) { 212 Type typ = OBJECT_TYPE; 213 if (t == Opcodes.INTEGER) { 214 typ = Type.INT_TYPE; 215 } else if (t == Opcodes.FLOAT) { 216 typ = Type.FLOAT_TYPE; 217 } else if (t == Opcodes.LONG) { 218 typ = Type.LONG_TYPE; 219 } else if (t == Opcodes.DOUBLE) { 220 typ = Type.DOUBLE_TYPE; 221 } else if (t instanceof String) { 222 typ = Type.getObjectType((String) t); 223 } 224 setFrameLocal(remap(index, typ), t); 225 } 226 index += size; 227 } 228 229 // removes TOP after long and double types as well as trailing TOPs 230 231 index = 0; 232 number = 0; 233 for (int i = 0; index < newLocals.length; ++i) { 234 Object t = newLocals[index++]; 235 if (t != null && t != Opcodes.TOP) { 236 newLocals[i] = t; 237 number = i + 1; 238 if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { 239 index += 1; 240 } 241 } else { 242 newLocals[i] = Opcodes.TOP; 243 } 244 } 245 246 // visits remapped frame 247 mv.visitFrame(type, number, newLocals, nStack, stack); 248 249 // restores original value of 'newLocals' 250 newLocals = oldLocals; 251 } 252 253 // ------------- 254 255 /** 256 * Creates a new local variable of the given type. 257 * 258 * @param type 259 * the type of the local variable to be created. 260 * @return the identifier of the newly created local variable. 261 */ 262 public int newLocal(final Type type) { 263 Object t; 264 switch (type.getSort()) { 265 case Type.BOOLEAN: 266 case Type.CHAR: 267 case Type.BYTE: 268 case Type.SHORT: 269 case Type.INT: 270 t = Opcodes.INTEGER; 271 break; 272 case Type.FLOAT: 273 t = Opcodes.FLOAT; 274 break; 275 case Type.LONG: 276 t = Opcodes.LONG; 277 break; 278 case Type.DOUBLE: 279 t = Opcodes.DOUBLE; 280 break; 281 case Type.ARRAY: 282 t = type.getDescriptor(); 283 break; 284 // case Type.OBJECT: 285 default: 286 t = type.getInternalName(); 287 break; 288 } 289 int local = newLocalMapping(type); 290 setLocalType(local, type); 291 setFrameLocal(local, t); 292 return local; 293 } 294 295 /** 296 * Notifies subclasses that a new stack map frame is being visited. The 297 * array argument contains the stack map frame types corresponding to the 298 * local variables added with {@link #newLocal}. This method can update 299 * these types in place for the stack map frame being visited. The default 300 * implementation of this method does nothing, i.e. a local variable added 301 * with {@link #newLocal} will have the same type in all stack map frames. 302 * But this behavior is not always the desired one, for instance if a local 303 * variable is added in the middle of a try/catch block: the frame for the 304 * exception handler should have a TOP type for this new local. 305 * 306 * @param newLocals 307 * the stack map frame types corresponding to the local variables 308 * added with {@link #newLocal} (and null for the others). The 309 * format of this array is the same as in 310 * {@link MethodVisitor#visitFrame}, except that long and double 311 * types use two slots. The types for the current stack map frame 312 * must be updated in place in this array. 313 */ 314 protected void updateNewLocals(Object[] newLocals) { 315 } 316 317 /** 318 * Notifies subclasses that a local variable has been added or remapped. The 319 * default implementation of this method does nothing. 320 * 321 * @param local 322 * a local variable identifier, as returned by {@link #newLocal 323 * newLocal()}. 324 * @param type 325 * the type of the value being stored in the local variable. 326 */ 327 protected void setLocalType(final int local, final Type type) { 328 } 329 330 private void setFrameLocal(final int local, final Object type) { 331 int l = newLocals.length; 332 if (local >= l) { 333 Object[] a = new Object[Math.max(2 * l, local + 1)]; 334 System.arraycopy(newLocals, 0, a, 0, l); 335 newLocals = a; 336 } 337 newLocals[local] = type; 338 } 339 340 private int remap(final int var, final Type type) { 341 if (var + type.getSize() <= firstLocal) { 342 return var; 343 } 344 int key = 2 * var + type.getSize() - 1; 345 int size = mapping.length; 346 if (key >= size) { 347 int[] newMapping = new int[Math.max(2 * size, key + 1)]; 348 System.arraycopy(mapping, 0, newMapping, 0, size); 349 mapping = newMapping; 350 } 351 int value = mapping[key]; 352 if (value == 0) { 353 value = newLocalMapping(type); 354 setLocalType(value, type); 355 mapping[key] = value + 1; 356 } else { 357 value--; 358 } 359 return value; 360 } 361 362 protected int newLocalMapping(final Type type) { 363 int local = nextLocal; 364 nextLocal += type.getSize(); 365 return local; 366 } 367}