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 * 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 * @throws IllegalStateException 124 * If a subclass calls this constructor. 125 */ 126 public LocalVariablesSorter(final int access, final String desc, 127 final MethodVisitor mv) { 128 this(Opcodes.ASM5, access, desc, mv); 129 if (getClass() != LocalVariablesSorter.class) { 130 throw new IllegalStateException(); 131 } 132 } 133 134 /** 135 * Creates a new {@link LocalVariablesSorter}. 136 * 137 * @param api 138 * the ASM API version implemented by this visitor. Must be one 139 * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. 140 * @param access 141 * access flags of the adapted method. 142 * @param desc 143 * the method's descriptor (see {@link Type Type}). 144 * @param mv 145 * the method visitor to which this adapter delegates calls. 146 */ 147 protected LocalVariablesSorter(final int api, final int access, 148 final String desc, final MethodVisitor mv) { 149 super(api, mv); 150 Type[] args = Type.getArgumentTypes(desc); 151 nextLocal = (Opcodes.ACC_STATIC & access) == 0 ? 1 : 0; 152 for (int i = 0; i < args.length; i++) { 153 nextLocal += args[i].getSize(); 154 } 155 firstLocal = nextLocal; 156 } 157 158 @Override 159 public void visitVarInsn(final int opcode, final int var) { 160 Type type; 161 switch (opcode) { 162 case Opcodes.LLOAD: 163 case Opcodes.LSTORE: 164 type = Type.LONG_TYPE; 165 break; 166 167 case Opcodes.DLOAD: 168 case Opcodes.DSTORE: 169 type = Type.DOUBLE_TYPE; 170 break; 171 172 case Opcodes.FLOAD: 173 case Opcodes.FSTORE: 174 type = Type.FLOAT_TYPE; 175 break; 176 177 case Opcodes.ILOAD: 178 case Opcodes.ISTORE: 179 type = Type.INT_TYPE; 180 break; 181 182 default: 183 // case Opcodes.ALOAD: 184 // case Opcodes.ASTORE: 185 // case RET: 186 type = OBJECT_TYPE; 187 break; 188 } 189 mv.visitVarInsn(opcode, remap(var, type)); 190 } 191 192 @Override 193 public void visitIincInsn(final int var, final int increment) { 194 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment); 195 } 196 197 @Override 198 public void visitMaxs(final int maxStack, final int maxLocals) { 199 mv.visitMaxs(maxStack, nextLocal); 200 } 201 202 @Override 203 public void visitLocalVariable(final String name, final String desc, 204 final String signature, final Label start, final Label end, 205 final int index) { 206 int newIndex = remap(index, Type.getType(desc)); 207 mv.visitLocalVariable(name, desc, signature, start, end, newIndex); 208 } 209 210 @Override 211 public AnnotationVisitor visitLocalVariableAnnotation(int typeRef, 212 TypePath typePath, Label[] start, Label[] end, int[] index, 213 String desc, boolean visible) { 214 Type t = Type.getType(desc); 215 int[] newIndex = new int[index.length]; 216 for (int i = 0; i < newIndex.length; ++i) { 217 newIndex[i] = remap(index[i], t); 218 } 219 return mv.visitLocalVariableAnnotation(typeRef, typePath, start, end, 220 newIndex, desc, visible); 221 } 222 223 @Override 224 public void visitFrame(final int type, final int nLocal, 225 final Object[] local, final int nStack, final Object[] stack) { 226 if (type != Opcodes.F_NEW) { // uncompressed frame 227 throw new IllegalStateException( 228 "ClassReader.accept() should be called with EXPAND_FRAMES flag"); 229 } 230 231 if (!changed) { // optimization for the case where mapping = identity 232 mv.visitFrame(type, nLocal, local, nStack, stack); 233 return; 234 } 235 236 // creates a copy of newLocals 237 Object[] oldLocals = new Object[newLocals.length]; 238 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length); 239 240 updateNewLocals(newLocals); 241 242 // copies types from 'local' to 'newLocals' 243 // 'newLocals' already contains the variables added with 'newLocal' 244 245 int index = 0; // old local variable index 246 int number = 0; // old local variable number 247 for (; number < nLocal; ++number) { 248 Object t = local[number]; 249 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1; 250 if (t != Opcodes.TOP) { 251 Type typ = OBJECT_TYPE; 252 if (t == Opcodes.INTEGER) { 253 typ = Type.INT_TYPE; 254 } else if (t == Opcodes.FLOAT) { 255 typ = Type.FLOAT_TYPE; 256 } else if (t == Opcodes.LONG) { 257 typ = Type.LONG_TYPE; 258 } else if (t == Opcodes.DOUBLE) { 259 typ = Type.DOUBLE_TYPE; 260 } else if (t instanceof String) { 261 typ = Type.getObjectType((String) t); 262 } 263 setFrameLocal(remap(index, typ), t); 264 } 265 index += size; 266 } 267 268 // removes TOP after long and double types as well as trailing TOPs 269 270 index = 0; 271 number = 0; 272 for (int i = 0; index < newLocals.length; ++i) { 273 Object t = newLocals[index++]; 274 if (t != null && t != Opcodes.TOP) { 275 newLocals[i] = t; 276 number = i + 1; 277 if (t == Opcodes.LONG || t == Opcodes.DOUBLE) { 278 index += 1; 279 } 280 } else { 281 newLocals[i] = Opcodes.TOP; 282 } 283 } 284 285 // visits remapped frame 286 mv.visitFrame(type, number, newLocals, nStack, stack); 287 288 // restores original value of 'newLocals' 289 newLocals = oldLocals; 290 } 291 292 // ------------- 293 294 /** 295 * Creates a new local variable of the given type. 296 * 297 * @param type 298 * the type of the local variable to be created. 299 * @return the identifier of the newly created local variable. 300 */ 301 public int newLocal(final Type type) { 302 Object t; 303 switch (type.getSort()) { 304 case Type.BOOLEAN: 305 case Type.CHAR: 306 case Type.BYTE: 307 case Type.SHORT: 308 case Type.INT: 309 t = Opcodes.INTEGER; 310 break; 311 case Type.FLOAT: 312 t = Opcodes.FLOAT; 313 break; 314 case Type.LONG: 315 t = Opcodes.LONG; 316 break; 317 case Type.DOUBLE: 318 t = Opcodes.DOUBLE; 319 break; 320 case Type.ARRAY: 321 t = type.getDescriptor(); 322 break; 323 // case Type.OBJECT: 324 default: 325 t = type.getInternalName(); 326 break; 327 } 328 int local = newLocalMapping(type); 329 setLocalType(local, type); 330 setFrameLocal(local, t); 331 changed = true; 332 return local; 333 } 334 335 /** 336 * Notifies subclasses that a new stack map frame is being visited. The 337 * array argument contains the stack map frame types corresponding to the 338 * local variables added with {@link #newLocal}. This method can update 339 * these types in place for the stack map frame being visited. The default 340 * implementation of this method does nothing, i.e. a local variable added 341 * with {@link #newLocal} will have the same type in all stack map frames. 342 * But this behavior is not always the desired one, for instance if a local 343 * variable is added in the middle of a try/catch block: the frame for the 344 * exception handler should have a TOP type for this new local. 345 * 346 * @param newLocals 347 * the stack map frame types corresponding to the local variables 348 * added with {@link #newLocal} (and null for the others). The 349 * format of this array is the same as in 350 * {@link MethodVisitor#visitFrame}, except that long and double 351 * types use two slots. The types for the current stack map frame 352 * must be updated in place in this array. 353 */ 354 protected void updateNewLocals(Object[] newLocals) { 355 } 356 357 /** 358 * Notifies subclasses that a local variable has been added or remapped. The 359 * default implementation of this method does nothing. 360 * 361 * @param local 362 * a local variable identifier, as returned by {@link #newLocal 363 * newLocal()}. 364 * @param type 365 * the type of the value being stored in the local variable. 366 */ 367 protected void setLocalType(final int local, final Type type) { 368 } 369 370 private void setFrameLocal(final int local, final Object type) { 371 int l = newLocals.length; 372 if (local >= l) { 373 Object[] a = new Object[Math.max(2 * l, local + 1)]; 374 System.arraycopy(newLocals, 0, a, 0, l); 375 newLocals = a; 376 } 377 newLocals[local] = type; 378 } 379 380 private int remap(final int var, final Type type) { 381 if (var + type.getSize() <= firstLocal) { 382 return var; 383 } 384 int key = 2 * var + type.getSize() - 1; 385 int size = mapping.length; 386 if (key >= size) { 387 int[] newMapping = new int[Math.max(2 * size, key + 1)]; 388 System.arraycopy(mapping, 0, newMapping, 0, size); 389 mapping = newMapping; 390 } 391 int value = mapping[key]; 392 if (value == 0) { 393 value = newLocalMapping(type); 394 setLocalType(value, type); 395 mapping[key] = value + 1; 396 } else { 397 value--; 398 } 399 if (value != var) { 400 changed = true; 401 } 402 return value; 403 } 404 405 protected int newLocalMapping(final Type type) { 406 int local = nextLocal; 407 nextLocal += type.getSize(); 408 return local; 409 } 410 }