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 */ 15 16 package javassist.bytecode.stackmap; 17 18 import javassist.bytecode.*; 19 20 public class TypedBlock extends BasicBlock { 21 public int stackTop, numLocals; 22 public TypeData[] stackTypes, localsTypes; 23 24 // set by a Liveness object. 25 // inputs[i] is true if the i-th variable is used within this block. 26 public boolean[] inputs; 27 28 // working area for Liveness class. 29 public boolean updating; 30 public int status; 31 public byte[] localsUsage; 32 33 /** 34 * Divides the method body into basic blocks. 35 * The type information of the first block is initialized. 36 * 37 * @param optmize if it is true and the method does not include 38 * branches, this method returns null. 39 */ 40 public static TypedBlock[] makeBlocks(MethodInfo minfo, CodeAttribute ca, 41 boolean optimize) 42 throws BadBytecode 43 { 44 TypedBlock[] blocks = (TypedBlock[])new Maker().make(minfo); 45 if (optimize && blocks.length < 2) 46 if (blocks.length == 0 || blocks[0].incoming == 0) 47 return null; 48 49 ConstPool pool = minfo.getConstPool(); 50 boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0; 51 blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(), 52 pool.getClassName(), minfo.getDescriptor(), 53 isStatic, minfo.isConstructor()); 54 new Liveness().compute(ca.iterator(), blocks, ca.getMaxLocals(), 55 blocks[0].localsTypes); 56 return blocks; 57 } 58 59 protected TypedBlock(int pos) { 60 super(pos); 61 localsTypes = null; 62 inputs = null; 63 updating = false; 64 } 65 66 protected void toString2(StringBuffer sbuf) { 67 super.toString2(sbuf); 68 sbuf.append(",\n stack={"); 69 printTypes(sbuf, stackTop, stackTypes); 70 sbuf.append("}, locals={"); 71 printTypes(sbuf, numLocals, localsTypes); 72 sbuf.append("}, inputs={"); 73 if (inputs != null) 74 for (int i = 0; i < inputs.length; i++) 75 sbuf.append(inputs[i] ? "1, " : "0, "); 76 77 sbuf.append('}'); 78 } 79 80 private void printTypes(StringBuffer sbuf, int size, 81 TypeData[] types) { 82 if (types == null) 83 return; 84 85 for (int i = 0; i < size; i++) { 86 if (i > 0) 87 sbuf.append(", "); 88 89 TypeData td = types[i]; 90 sbuf.append(td == null ? "<>" : td.toString()); 91 } 92 } 93 94 public boolean alreadySet() { 95 return localsTypes != null; 96 } 97 98 public void setStackMap(int st, TypeData[] stack, int nl, TypeData[] locals) 99 throws BadBytecode 100 { 101 stackTop = st; 102 stackTypes = stack; 103 numLocals = nl; 104 localsTypes = locals; 105 } 106 107 /* 108 * Computes the correct value of numLocals. 109 */ 110 public void resetNumLocals() { 111 if (localsTypes != null) { 112 int nl = localsTypes.length; 113 while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) { 114 if (nl > 1) { 115 TypeData td = localsTypes[nl - 2]; 116 if (td == TypeTag.LONG || td == TypeTag.DOUBLE) 117 break; 118 } 119 120 --nl; 121 } 122 123 numLocals = nl; 124 } 125 } 126 127 public static class Maker extends BasicBlock.Maker { 128 protected BasicBlock makeBlock(int pos) { 129 return new TypedBlock(pos); 130 } 131 132 protected BasicBlock[] makeArray(int size) { 133 return new TypedBlock[size]; 134 } 135 } 136 137 /** 138 * Initializes the first block by the given method descriptor. 139 * 140 * @param block the first basic block that this method initializes. 141 * @param className a dot-separated fully qualified class name. 142 * For example, <code>javassist.bytecode.stackmap.BasicBlock</code>. 143 * @param methodDesc method descriptor. 144 * @param isStatic true if the method is a static method. 145 * @param isConstructor true if the method is a constructor. 146 */ 147 void initFirstBlock(int maxStack, int maxLocals, String className, 148 String methodDesc, boolean isStatic, boolean isConstructor) 149 throws BadBytecode 150 { 151 if (methodDesc.charAt(0) != '(') 152 throw new BadBytecode("no method descriptor: " + methodDesc); 153 154 stackTop = 0; 155 stackTypes = new TypeData[maxStack]; 156 TypeData[] locals = new TypeData[maxLocals]; 157 if (isConstructor) 158 locals[0] = new TypeData.UninitThis(className); 159 else if (!isStatic) 160 locals[0] = new TypeData.ClassName(className); 161 162 int n = isStatic ? -1 : 0; 163 int i = 1; 164 try { 165 while ((i = descToTag(methodDesc, i, ++n, locals)) > 0) 166 if (locals[n].is2WordType()) 167 locals[++n] = TypeTag.TOP; 168 } 169 catch (StringIndexOutOfBoundsException e) { 170 throw new BadBytecode("bad method descriptor: " 171 + methodDesc); 172 } 173 174 numLocals = n; 175 localsTypes = locals; 176 } 177 178 private static int descToTag(String desc, int i, 179 int n, TypeData[] types) 180 throws BadBytecode 181 { 182 int i0 = i; 183 int arrayDim = 0; 184 char c = desc.charAt(i); 185 if (c == ')') 186 return 0; 187 188 while (c == '[') { 189 ++arrayDim; 190 c = desc.charAt(++i); 191 } 192 193 if (c == 'L') { 194 int i2 = desc.indexOf(';', ++i); 195 if (arrayDim > 0) 196 types[n] = new TypeData.ClassName(desc.substring(i0, ++i2)); 197 else 198 types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1) 199 .replace('/', '.')); 200 return i2; 201 } 202 else if (arrayDim > 0) { 203 types[n] = new TypeData.ClassName(desc.substring(i0, ++i)); 204 return i; 205 } 206 else { 207 TypeData t = toPrimitiveTag(c); 208 if (t == null) 209 throw new BadBytecode("bad method descriptor: " + desc); 210 211 types[n] = t; 212 return i + 1; 213 } 214 } 215 216 private static TypeData toPrimitiveTag(char c) { 217 switch (c) { 218 case 'Z' : 219 case 'C' : 220 case 'B' : 221 case 'S' : 222 case 'I' : 223 return TypeTag.INTEGER; 224 case 'J' : 225 return TypeTag.LONG; 226 case 'F' : 227 return TypeTag.FLOAT; 228 case 'D' : 229 return TypeTag.DOUBLE; 230 case 'V' : 231 default : 232 return null; 233 } 234 } 235 236 public static String getRetType(String desc) { 237 int i = desc.indexOf(')'); 238 if (i < 0) 239 return "java.lang.Object"; 240 241 char c = desc.charAt(i + 1); 242 if (c == '[') 243 return desc.substring(i + 1); 244 else if (c == 'L') 245 return desc.substring(i + 2, desc.length() - 1).replace('/', '.'); 246 else 247 return "java.lang.Object"; 248 } 249 } 250