src/share/classes/jdk/internal/org/objectweb/asm/commons/LocalVariablesSorter.java

Print this page




  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;