1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * This file is available under and governed by the GNU General Public 27 * License version 2 only, as published by the Free Software Foundation. 28 * However, the following notice accompanied the original version of this 29 * file: 30 * 31 * ASM: a very small and fast Java bytecode manipulation framework 32 * Copyright (c) 2000-2011 INRIA, France Telecom 33 * All rights reserved. 34 * 35 * Redistribution and use in source and binary forms, with or without 36 * modification, are permitted provided that the following conditions 37 * are met: 38 * 1. Redistributions of source code must retain the above copyright 39 * notice, this list of conditions and the following disclaimer. 40 * 2. Redistributions in binary form must reproduce the above copyright 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 * Creates a new {@link LocalVariablesSorter}. <i>Subclasses must not use 109 * this constructor</i>. Instead, they must use the 110 * {@link #LocalVariablesSorter(int, int, String, MethodVisitor)} version. 111 * 112 * @param access 113 * access flags of the adapted method. 114 * @param desc 115 * the method's descriptor (see {@link Type Type}). 116 * @param mv 117 * the method visitor to which this adapter delegates calls. 118 * @throws IllegalStateException 119 * If a subclass calls this constructor. 120 */ 121 public LocalVariablesSorter(final int access, final String desc, 122 final MethodVisitor mv) { 123 this(Opcodes.ASM5, access, desc, mv); 124 if (getClass() != LocalVariablesSorter.class) { 125 throw new IllegalStateException(); 126 } 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: 164 type = Type.DOUBLE_TYPE; 165 break; 166 167 case Opcodes.FLOAD: 168 case Opcodes.FSTORE: 169 type = Type.FLOAT_TYPE; 170 break; 171 172 case Opcodes.ILOAD: 173 case Opcodes.ISTORE: 174 type = Type.INT_TYPE; 175 break; 176 177 default: 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 // creates a copy of newLocals 227 Object[] oldLocals = new Object[newLocals.length]; 228 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); 229 230 updateNewLocals(newLocals); 231 232 // copies types from 'local' to 'newLocals' 233 // 'newLocals' already contains the variables added with 'newLocal' 234 235 int index = 0; // old local variable index 236 int number = 0; // old local variable number 237 for (; number < nLocal; ++number) { 238 Object t = local[number]; 239 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; 240 if (t != Opcodes.TOP) { 241 Type typ = OBJECT_TYPE; 242 if (t == Opcodes.INTEGER) { 243 typ = Type.INT_TYPE; 244 } else if (t == Opcodes.FLOAT) { 245 typ = Type.FLOAT_TYPE; 246 } else if (t == Opcodes.LONG) { 247 typ = Type.LONG_TYPE; 248 } else if (t == Opcodes.DOUBLE) { 249 typ = Type.DOUBLE_TYPE; 250 } else if (t instanceof String) { 251 typ = Type.getObjectType((String) t); 252 } 253 setFrameLocal(remap(index, typ), t); 254 } 255 index += size; 256 } 257 258 // removes TOP after long and double types as well as trailing TOPs 259 260 index = 0; 261 number = 0; 262 for (int i = 0; index < newLocals.length; ++i) { 263 Object t = newLocals[index++]; 264 if (t != null && t != Opcodes.TOP) { 265 newLocals[i] = t; 266 number = i + 1; 267 if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { 268 index += 1; 269 } 270 } else { 271 newLocals[i] = Opcodes.TOP; 272 } 273 } 274 275 // visits remapped frame 276 mv.visitFrame(type, number, newLocals, nStack, stack); 277 278 // restores original value of 'newLocals' 279 newLocals = oldLocals; 280 } 281 282 // ------------- 283 284 /** 285 * Creates a new local variable of the given type. 286 * 287 * @param type 288 * the type of the local variable to be created. 289 * @return the identifier of the newly created local variable. 290 */ 291 public int newLocal(final Type type) { 292 Object t; 293 switch (type.getSort()) { 294 case Type.BOOLEAN: 295 case Type.CHAR: 296 case Type.BYTE: 297 case Type.SHORT: 298 case Type.INT: 299 t = Opcodes.INTEGER; 300 break; 301 case Type.FLOAT: 302 t = Opcodes.FLOAT; 303 break; 304 case Type.LONG: 305 t = Opcodes.LONG; 306 break; 307 case Type.DOUBLE: 308 t = Opcodes.DOUBLE; 309 break; 310 case Type.ARRAY: 311 t = type.getDescriptor(); 312 break; 313 // case Type.OBJECT: 314 default: 315 t = type.getInternalName(); 316 break; 317 } 318 int local = newLocalMapping(type); 319 setLocalType(local, type); 320 setFrameLocal(local, t); 321 return local; 322 } 323 324 /** 325 * Notifies subclasses that a new stack map frame is being visited. The 326 * array argument contains the stack map frame types corresponding to the 327 * local variables added with {@link #newLocal}. This method can update 328 * these types in place for the stack map frame being visited. The default 329 * implementation of this method does nothing, i.e. a local variable added 330 * with {@link #newLocal} will have the same type in all stack map frames. 331 * But this behavior is not always the desired one, for instance if a local 332 * variable is added in the middle of a try/catch block: the frame for the 333 * exception handler should have a TOP type for this new local. 334 * 335 * @param newLocals 336 * the stack map frame types corresponding to the local variables 337 * added with {@link #newLocal} (and null for the others). The 338 * format of this array is the same as in 339 * {@link MethodVisitor#visitFrame}, except that long and double 340 * types use two slots. The types for the current stack map frame 341 * must be updated in place in this array. 342 */ 343 protected void updateNewLocals(Object[] newLocals) { 344 } 345 346 /** 347 * Notifies subclasses that a local variable has been added or remapped. The 348 * default implementation of this method does nothing. 349 * 350 * @param local 351 * a local variable identifier, as returned by {@link #newLocal 352 * newLocal()}. 353 * @param type 354 * the type of the value being stored in the local variable. 355 */ 356 protected void setLocalType(final int local, final Type type) { 357 } 358 359 private void setFrameLocal(final int local, final Object type) { 360 int l = newLocals.length; 361 if (local >= l) { 362 Object[] a = new Object[Math.max(2 * l, local + 1)]; 363 System.arraycopy(newLocals, 0, a, 0, l); 364 newLocals = a; 365 } 366 newLocals[local] = type; 367 } 368 369 private int remap(final int var, final Type type) { 370 if (var + type.getSize() <= firstLocal) { 371 return var; 372 } 373 int key = 2 * var + type.getSize() - 1; 374 int size = mapping.length; 375 if (key >= size) { 376 int[] newMapping = new int[Math.max(2 * size, key + 1)]; 377 System.arraycopy(mapping, 0, newMapping, 0, size); 378 mapping = newMapping; 379 } 380 int value = mapping[key]; 381 if (value == 0) { 382 value = newLocalMapping(type); 383 setLocalType(value, type); 384 mapping[key] = value + 1; 385 } else { 386 value--; 387 } 388 return value; 389 } 390 391 protected int newLocalMapping(final Type type) { 392 int local = nextLocal; 393 nextLocal += type.getSize(); 394 return local; 395 } 396 }