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