annotate src/clojure/asm/commons/LocalVariablesSorter.java @ 10:ef7dbbd6452c

added clojure source goodness
author Robert McIntyre <rlm@mit.edu>
date Sat, 21 Aug 2010 06:25:44 -0400
parents
children
rev   line source
rlm@10 1 /***
rlm@10 2 * ASM: a very small and fast Java bytecode manipulation framework
rlm@10 3 * Copyright (c) 2000-2005 INRIA, France Telecom
rlm@10 4 * All rights reserved.
rlm@10 5 *
rlm@10 6 * Redistribution and use in source and binary forms, with or without
rlm@10 7 * modification, are permitted provided that the following conditions
rlm@10 8 * are met:
rlm@10 9 * 1. Redistributions of source code must retain the above copyright
rlm@10 10 * notice, this list of conditions and the following disclaimer.
rlm@10 11 * 2. Redistributions in binary form must reproduce the above copyright
rlm@10 12 * notice, this list of conditions and the following disclaimer in the
rlm@10 13 * documentation and/or other materials provided with the distribution.
rlm@10 14 * 3. Neither the name of the copyright holders nor the names of its
rlm@10 15 * contributors may be used to endorse or promote products derived from
rlm@10 16 * this software without specific prior written permission.
rlm@10 17 *
rlm@10 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
rlm@10 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
rlm@10 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
rlm@10 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
rlm@10 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
rlm@10 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
rlm@10 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
rlm@10 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
rlm@10 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
rlm@10 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
rlm@10 28 * THE POSSIBILITY OF SUCH DAMAGE.
rlm@10 29 */
rlm@10 30 package clojure.asm.commons;
rlm@10 31
rlm@10 32 import clojure.asm.Label;
rlm@10 33 import clojure.asm.MethodAdapter;
rlm@10 34 import clojure.asm.MethodVisitor;
rlm@10 35 import clojure.asm.Opcodes;
rlm@10 36 import clojure.asm.Type;
rlm@10 37
rlm@10 38 /**
rlm@10 39 * A {@link MethodAdapter} that renumbers local variables in their order of
rlm@10 40 * appearance. This adapter allows one to easily add new local variables to a
rlm@10 41 * method. It may be used by inheriting from this class, but the preferred way
rlm@10 42 * of using it is via delegation: the next visitor in the chain can indeed add
rlm@10 43 * new locals when needed by calling {@link #newLocal} on this adapter (this
rlm@10 44 * requires a reference back to this {@link LocalVariablesSorter}).
rlm@10 45 *
rlm@10 46 * @author Chris Nokleberg
rlm@10 47 * @author Eugene Kuleshov
rlm@10 48 * @author Eric Bruneton
rlm@10 49 */
rlm@10 50 public class LocalVariablesSorter extends MethodAdapter{
rlm@10 51
rlm@10 52 private final static Type OBJECT_TYPE = Type.getObjectType("java/lang/Object");
rlm@10 53
rlm@10 54 /**
rlm@10 55 * Mapping from old to new local variable indexes. A local variable at index
rlm@10 56 * i of size 1 is remapped to 'mapping[2*i]', while a local variable at
rlm@10 57 * index i of size 2 is remapped to 'mapping[2*i+1]'.
rlm@10 58 */
rlm@10 59 private int[] mapping = new int[40];
rlm@10 60
rlm@10 61 /**
rlm@10 62 * Array used to store stack map local variable types after remapping.
rlm@10 63 */
rlm@10 64 private Object[] newLocals = new Object[20];
rlm@10 65
rlm@10 66 /**
rlm@10 67 * Index of the first local variable, after formal parameters.
rlm@10 68 */
rlm@10 69 protected final int firstLocal;
rlm@10 70
rlm@10 71 /**
rlm@10 72 * Index of the next local variable to be created by {@link #newLocal}.
rlm@10 73 */
rlm@10 74 protected int nextLocal;
rlm@10 75
rlm@10 76 /**
rlm@10 77 * Indicates if at least one local variable has moved due to remapping.
rlm@10 78 */
rlm@10 79 private boolean changed;
rlm@10 80
rlm@10 81 /**
rlm@10 82 * Creates a new {@link LocalVariablesSorter}.
rlm@10 83 *
rlm@10 84 * @param access access flags of the adapted method.
rlm@10 85 * @param desc the method's descriptor (see {@link Type Type}).
rlm@10 86 * @param mv the method visitor to which this adapter delegates calls.
rlm@10 87 */
rlm@10 88 public LocalVariablesSorter(
rlm@10 89 final int access,
rlm@10 90 final String desc,
rlm@10 91 final MethodVisitor mv){
rlm@10 92 super(mv);
rlm@10 93 Type[] args = Type.getArgumentTypes(desc);
rlm@10 94 nextLocal = (Opcodes.ACC_STATIC & access) != 0 ? 0 : 1;
rlm@10 95 for(int i = 0; i < args.length; i++)
rlm@10 96 {
rlm@10 97 nextLocal += args[i].getSize();
rlm@10 98 }
rlm@10 99 firstLocal = nextLocal;
rlm@10 100 }
rlm@10 101
rlm@10 102 public void visitVarInsn(final int opcode, final int var){
rlm@10 103 Type type;
rlm@10 104 switch(opcode)
rlm@10 105 {
rlm@10 106 case Opcodes.LLOAD:
rlm@10 107 case Opcodes.LSTORE:
rlm@10 108 type = Type.LONG_TYPE;
rlm@10 109 break;
rlm@10 110
rlm@10 111 case Opcodes.DLOAD:
rlm@10 112 case Opcodes.DSTORE:
rlm@10 113 type = Type.DOUBLE_TYPE;
rlm@10 114 break;
rlm@10 115
rlm@10 116 case Opcodes.FLOAD:
rlm@10 117 case Opcodes.FSTORE:
rlm@10 118 type = Type.FLOAT_TYPE;
rlm@10 119 break;
rlm@10 120
rlm@10 121 case Opcodes.ILOAD:
rlm@10 122 case Opcodes.ISTORE:
rlm@10 123 type = Type.INT_TYPE;
rlm@10 124 break;
rlm@10 125
rlm@10 126 case Opcodes.ALOAD:
rlm@10 127 case Opcodes.ASTORE:
rlm@10 128 type = OBJECT_TYPE;
rlm@10 129 break;
rlm@10 130
rlm@10 131 // case RET:
rlm@10 132 default:
rlm@10 133 type = Type.VOID_TYPE;
rlm@10 134 }
rlm@10 135 mv.visitVarInsn(opcode, remap(var, type));
rlm@10 136 }
rlm@10 137
rlm@10 138 public void visitIincInsn(final int var, final int increment){
rlm@10 139 mv.visitIincInsn(remap(var, Type.INT_TYPE), increment);
rlm@10 140 }
rlm@10 141
rlm@10 142 public void visitMaxs(final int maxStack, final int maxLocals){
rlm@10 143 mv.visitMaxs(maxStack, nextLocal);
rlm@10 144 }
rlm@10 145
rlm@10 146 public void visitLocalVariable(
rlm@10 147 final String name,
rlm@10 148 final String desc,
rlm@10 149 final String signature,
rlm@10 150 final Label start,
rlm@10 151 final Label end,
rlm@10 152 final int index){
rlm@10 153 int size = "J".equals(desc) || "D".equals(desc) ? 2 : 1;
rlm@10 154 int newIndex = remap(index, size);
rlm@10 155 mv.visitLocalVariable(name, desc, signature, start, end, newIndex);
rlm@10 156 }
rlm@10 157
rlm@10 158 public void visitFrame(
rlm@10 159 final int type,
rlm@10 160 final int nLocal,
rlm@10 161 final Object[] local,
rlm@10 162 final int nStack,
rlm@10 163 final Object[] stack){
rlm@10 164 if(type != Opcodes.F_NEW)
rlm@10 165 { // uncompressed frame
rlm@10 166 throw new IllegalStateException("ClassReader.accept() should be called with EXPAND_FRAMES flag");
rlm@10 167 }
rlm@10 168
rlm@10 169 if(!changed)
rlm@10 170 { // optimization for the case where mapping = identity
rlm@10 171 mv.visitFrame(type, nLocal, local, nStack, stack);
rlm@10 172 return;
rlm@10 173 }
rlm@10 174
rlm@10 175 // creates a copy of newLocals
rlm@10 176 Object[] oldLocals = new Object[newLocals.length];
rlm@10 177 System.arraycopy(newLocals, 0, oldLocals, 0, oldLocals.length);
rlm@10 178
rlm@10 179 // copies types from 'local' to 'newLocals'
rlm@10 180 // 'newLocals' already contains the variables added with 'newLocal'
rlm@10 181
rlm@10 182 int index = 0; // old local variable index
rlm@10 183 int number = 0; // old local variable number
rlm@10 184 for(; number < nLocal; ++number)
rlm@10 185 {
rlm@10 186 Object t = local[number];
rlm@10 187 int size = t == Opcodes.LONG || t == Opcodes.DOUBLE ? 2 : 1;
rlm@10 188 if(t != Opcodes.TOP)
rlm@10 189 {
rlm@10 190 setFrameLocal(remap(index, size), t);
rlm@10 191 }
rlm@10 192 index += size;
rlm@10 193 }
rlm@10 194
rlm@10 195 // removes TOP after long and double types as well as trailing TOPs
rlm@10 196
rlm@10 197 index = 0;
rlm@10 198 number = 0;
rlm@10 199 for(int i = 0; index < newLocals.length; ++i)
rlm@10 200 {
rlm@10 201 Object t = newLocals[index++];
rlm@10 202 if(t != null && t != Opcodes.TOP)
rlm@10 203 {
rlm@10 204 newLocals[i] = t;
rlm@10 205 number = i + 1;
rlm@10 206 if(t == Opcodes.LONG || t == Opcodes.DOUBLE)
rlm@10 207 {
rlm@10 208 index += 1;
rlm@10 209 }
rlm@10 210 }
rlm@10 211 else
rlm@10 212 {
rlm@10 213 newLocals[i] = Opcodes.TOP;
rlm@10 214 }
rlm@10 215 }
rlm@10 216
rlm@10 217 // visits remapped frame
rlm@10 218 mv.visitFrame(type, number, newLocals, nStack, stack);
rlm@10 219
rlm@10 220 // restores original value of 'newLocals'
rlm@10 221 newLocals = oldLocals;
rlm@10 222 }
rlm@10 223
rlm@10 224 // -------------
rlm@10 225
rlm@10 226 /**
rlm@10 227 * Creates a new local variable of the given type.
rlm@10 228 *
rlm@10 229 * @param type the type of the local variable to be created.
rlm@10 230 * @return the identifier of the newly created local variable.
rlm@10 231 */
rlm@10 232 public int newLocal(final Type type){
rlm@10 233 Object t;
rlm@10 234 switch(type.getSort())
rlm@10 235 {
rlm@10 236 case Type.BOOLEAN:
rlm@10 237 case Type.CHAR:
rlm@10 238 case Type.BYTE:
rlm@10 239 case Type.SHORT:
rlm@10 240 case Type.INT:
rlm@10 241 t = Opcodes.INTEGER;
rlm@10 242 break;
rlm@10 243 case Type.FLOAT:
rlm@10 244 t = Opcodes.FLOAT;
rlm@10 245 break;
rlm@10 246 case Type.LONG:
rlm@10 247 t = Opcodes.LONG;
rlm@10 248 break;
rlm@10 249 case Type.DOUBLE:
rlm@10 250 t = Opcodes.DOUBLE;
rlm@10 251 break;
rlm@10 252 case Type.ARRAY:
rlm@10 253 t = type.getDescriptor();
rlm@10 254 break;
rlm@10 255 // case Type.OBJECT:
rlm@10 256 default:
rlm@10 257 t = type.getInternalName();
rlm@10 258 break;
rlm@10 259 }
rlm@10 260 int local = nextLocal;
rlm@10 261 setLocalType(local, type);
rlm@10 262 setFrameLocal(local, t);
rlm@10 263 nextLocal += type.getSize();
rlm@10 264 return local;
rlm@10 265 }
rlm@10 266
rlm@10 267 /**
rlm@10 268 * Sets the current type of the given local variable. The default
rlm@10 269 * implementation of this method does nothing.
rlm@10 270 *
rlm@10 271 * @param local a local variable identifier, as returned by {@link #newLocal
rlm@10 272 * newLocal()}.
rlm@10 273 * @param type the type of the value being stored in the local variable
rlm@10 274 */
rlm@10 275 protected void setLocalType(final int local, final Type type){
rlm@10 276 }
rlm@10 277
rlm@10 278 private void setFrameLocal(final int local, final Object type){
rlm@10 279 int l = newLocals.length;
rlm@10 280 if(local >= l)
rlm@10 281 {
rlm@10 282 Object[] a = new Object[Math.max(2 * l, local + 1)];
rlm@10 283 System.arraycopy(newLocals, 0, a, 0, l);
rlm@10 284 newLocals = a;
rlm@10 285 }
rlm@10 286 newLocals[local] = type;
rlm@10 287 }
rlm@10 288
rlm@10 289 private int remap(final int var, final Type type){
rlm@10 290 if(var < firstLocal)
rlm@10 291 {
rlm@10 292 return var;
rlm@10 293 }
rlm@10 294 int key = 2 * var + type.getSize() - 1;
rlm@10 295 int size = mapping.length;
rlm@10 296 if(key >= size)
rlm@10 297 {
rlm@10 298 int[] newMapping = new int[Math.max(2 * size, key + 1)];
rlm@10 299 System.arraycopy(mapping, 0, newMapping, 0, size);
rlm@10 300 mapping = newMapping;
rlm@10 301 }
rlm@10 302 int value = mapping[key];
rlm@10 303 if(value == 0)
rlm@10 304 {
rlm@10 305 value = nextLocal + 1;
rlm@10 306 mapping[key] = value;
rlm@10 307 setLocalType(nextLocal, type);
rlm@10 308 nextLocal += type.getSize();
rlm@10 309 }
rlm@10 310 if(value - 1 != var)
rlm@10 311 {
rlm@10 312 changed = true;
rlm@10 313 }
rlm@10 314 return value - 1;
rlm@10 315 }
rlm@10 316
rlm@10 317 private int remap(final int var, final int size){
rlm@10 318 if(var < firstLocal || !changed)
rlm@10 319 {
rlm@10 320 return var;
rlm@10 321 }
rlm@10 322 int key = 2 * var + size - 1;
rlm@10 323 int value = key < mapping.length ? mapping[key] : 0;
rlm@10 324 if(value == 0)
rlm@10 325 {
rlm@10 326 throw new IllegalStateException("Unknown local variable " + var);
rlm@10 327 }
rlm@10 328 return value - 1;
rlm@10 329 }
rlm@10 330 }