1/* 2 * Javassist, a Java-bytecode translator toolkit. 3 * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. 4 * 5 * The contents of this file are subject to the Mozilla Public License Version 6 * 1.1 (the "License"); you may not use this file except in compliance with 7 * the License. Alternatively, the contents of this file may be used under 8 * the terms of the GNU Lesser General Public License Version 2.1 or later. 9 * 10 * Software distributed under the License is distributed on an "AS IS" basis, 11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 12 * for the specific language governing rights and limitations under the 13 * License. 14 */ 15package javassist.convert; 16 17import javassist.CannotCompileException; 18import javassist.ClassPool; 19import javassist.CtClass; 20import javassist.NotFoundException; 21import javassist.CodeConverter.ArrayAccessReplacementMethodNames; 22import javassist.bytecode.BadBytecode; 23import javassist.bytecode.CodeIterator; 24import javassist.bytecode.ConstPool; 25import javassist.bytecode.Descriptor; 26import javassist.bytecode.MethodInfo; 27import javassist.bytecode.analysis.Analyzer; 28import javassist.bytecode.analysis.Frame; 29 30/** 31 * A transformer which replaces array access with static method invocations. 32 * 33 * @author <a href="kabir.khan@jboss.com">Kabir Khan</a> 34 * @author Jason T. Greene 35 * @version $Revision: 1.8 $ 36 */ 37public final class TransformAccessArrayField extends Transformer { 38 private final String methodClassname; 39 private final ArrayAccessReplacementMethodNames names; 40 private Frame[] frames; 41 private int offset; 42 43 public TransformAccessArrayField(Transformer next, String methodClassname, 44 ArrayAccessReplacementMethodNames names) throws NotFoundException { 45 super(next); 46 this.methodClassname = methodClassname; 47 this.names = names; 48 49 } 50 51 public void initialize(ConstPool cp, CtClass clazz, MethodInfo minfo) throws CannotCompileException { 52 /* 53 * This transformer must be isolated from other transformers, since some 54 * of them affect the local variable and stack maximums without updating 55 * the code attribute to reflect the changes. This screws up the 56 * data-flow analyzer, since it relies on consistent code state. Even 57 * if the attribute values were updated correctly, we would have to 58 * detect it, and redo analysis, which is not cheap. Instead, we are 59 * better off doing all changes in initialize() before everyone else has 60 * a chance to muck things up. 61 */ 62 CodeIterator iterator = minfo.getCodeAttribute().iterator(); 63 while (iterator.hasNext()) { 64 try { 65 int pos = iterator.next(); 66 int c = iterator.byteAt(pos); 67 68 if (c == AALOAD) 69 initFrames(clazz, minfo); 70 71 if (c == AALOAD || c == BALOAD || c == CALOAD || c == DALOAD 72 || c == FALOAD || c == IALOAD || c == LALOAD 73 || c == SALOAD) { 74 pos = replace(cp, iterator, pos, c, getLoadReplacementSignature(c)); 75 } else if (c == AASTORE || c == BASTORE || c == CASTORE 76 || c == DASTORE || c == FASTORE || c == IASTORE 77 || c == LASTORE || c == SASTORE) { 78 pos = replace(cp, iterator, pos, c, getStoreReplacementSignature(c)); 79 } 80 81 } catch (Exception e) { 82 throw new CannotCompileException(e); 83 } 84 } 85 } 86 87 public void clean() { 88 frames = null; 89 offset = -1; 90 } 91 92 public int transform(CtClass tclazz, int pos, CodeIterator iterator, 93 ConstPool cp) throws BadBytecode { 94 // Do nothing, see above comment 95 return pos; 96 } 97 98 private Frame getFrame(int pos) throws BadBytecode { 99 return frames[pos - offset]; // Adjust pos 100 } 101 102 private void initFrames(CtClass clazz, MethodInfo minfo) throws BadBytecode { 103 if (frames == null) { 104 frames = ((new Analyzer())).analyze(clazz, minfo); 105 offset = 0; // start tracking changes 106 } 107 } 108 109 private int updatePos(int pos, int increment) { 110 if (offset > -1) 111 offset += increment; 112 113 return pos + increment; 114 } 115 116 private String getTopType(int pos) throws BadBytecode { 117 Frame frame = getFrame(pos); 118 if (frame == null) 119 return null; 120 121 CtClass clazz = frame.peek().getCtClass(); 122 return clazz != null ? Descriptor.toJvmName(clazz) : null; 123 } 124 125 private int replace(ConstPool cp, CodeIterator iterator, int pos, 126 int opcode, String signature) throws BadBytecode { 127 String castType = null; 128 String methodName = getMethodName(opcode); 129 if (methodName != null) { 130 // See if the object must be cast 131 if (opcode == AALOAD) { 132 castType = getTopType(iterator.lookAhead()); 133 // Do not replace an AALOAD instruction that we do not have a type for 134 // This happens when the state is guaranteed to be null (Type.UNINIT) 135 // So we don't really care about this case. 136 if (castType == null) 137 return pos; 138 if ("java/lang/Object".equals(castType)) 139 castType = null; 140 } 141 142 // The gap may include extra padding 143 // Write a nop in case the padding pushes the instruction forward 144 iterator.writeByte(NOP, pos); 145 CodeIterator.Gap gap 146 = iterator.insertGapAt(pos, castType != null ? 5 : 2, false); 147 pos = gap.position; 148 int mi = cp.addClassInfo(methodClassname); 149 int methodref = cp.addMethodrefInfo(mi, methodName, signature); 150 iterator.writeByte(INVOKESTATIC, pos); 151 iterator.write16bit(methodref, pos + 1); 152 153 if (castType != null) { 154 int index = cp.addClassInfo(castType); 155 iterator.writeByte(CHECKCAST, pos + 3); 156 iterator.write16bit(index, pos + 4); 157 } 158 159 pos = updatePos(pos, gap.length); 160 } 161 162 return pos; 163 } 164 165 private String getMethodName(int opcode) { 166 String methodName = null; 167 switch (opcode) { 168 case AALOAD: 169 methodName = names.objectRead(); 170 break; 171 case BALOAD: 172 methodName = names.byteOrBooleanRead(); 173 break; 174 case CALOAD: 175 methodName = names.charRead(); 176 break; 177 case DALOAD: 178 methodName = names.doubleRead(); 179 break; 180 case FALOAD: 181 methodName = names.floatRead(); 182 break; 183 case IALOAD: 184 methodName = names.intRead(); 185 break; 186 case SALOAD: 187 methodName = names.shortRead(); 188 break; 189 case LALOAD: 190 methodName = names.longRead(); 191 break; 192 case AASTORE: 193 methodName = names.objectWrite(); 194 break; 195 case BASTORE: 196 methodName = names.byteOrBooleanWrite(); 197 break; 198 case CASTORE: 199 methodName = names.charWrite(); 200 break; 201 case DASTORE: 202 methodName = names.doubleWrite(); 203 break; 204 case FASTORE: 205 methodName = names.floatWrite(); 206 break; 207 case IASTORE: 208 methodName = names.intWrite(); 209 break; 210 case SASTORE: 211 methodName = names.shortWrite(); 212 break; 213 case LASTORE: 214 methodName = names.longWrite(); 215 break; 216 } 217 218 if (methodName.equals("")) 219 methodName = null; 220 221 return methodName; 222 } 223 224 private String getLoadReplacementSignature(int opcode) throws BadBytecode { 225 switch (opcode) { 226 case AALOAD: 227 return "(Ljava/lang/Object;I)Ljava/lang/Object;"; 228 case BALOAD: 229 return "(Ljava/lang/Object;I)B"; 230 case CALOAD: 231 return "(Ljava/lang/Object;I)C"; 232 case DALOAD: 233 return "(Ljava/lang/Object;I)D"; 234 case FALOAD: 235 return "(Ljava/lang/Object;I)F"; 236 case IALOAD: 237 return "(Ljava/lang/Object;I)I"; 238 case SALOAD: 239 return "(Ljava/lang/Object;I)S"; 240 case LALOAD: 241 return "(Ljava/lang/Object;I)J"; 242 } 243 244 throw new BadBytecode(opcode); 245 } 246 247 private String getStoreReplacementSignature(int opcode) throws BadBytecode { 248 switch (opcode) { 249 case AASTORE: 250 return "(Ljava/lang/Object;ILjava/lang/Object;)V"; 251 case BASTORE: 252 return "(Ljava/lang/Object;IB)V"; 253 case CASTORE: 254 return "(Ljava/lang/Object;IC)V"; 255 case DASTORE: 256 return "(Ljava/lang/Object;ID)V"; 257 case FASTORE: 258 return "(Ljava/lang/Object;IF)V"; 259 case IASTORE: 260 return "(Ljava/lang/Object;II)V"; 261 case SASTORE: 262 return "(Ljava/lang/Object;IS)V"; 263 case LASTORE: 264 return "(Ljava/lang/Object;IJ)V"; 265 } 266 267 throw new BadBytecode(opcode); 268 } 269} 270