/* * Javassist, a Java-bytecode translator toolkit. * Copyright (C) 1999-2007 Shigeru Chiba. All Rights Reserved. * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. Alternatively, the contents of this file may be used under * the terms of the GNU Lesser General Public License Version 2.1 or later. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. */ package javassist.bytecode.stackmap; import javassist.bytecode.*; public class TypedBlock extends BasicBlock { public int stackTop, numLocals; public TypeData[] stackTypes, localsTypes; // set by a Liveness object. // inputs[i] is true if the i-th variable is used within this block. public boolean[] inputs; // working area for Liveness class. public boolean updating; public int status; public byte[] localsUsage; /** * Divides the method body into basic blocks. * The type information of the first block is initialized. * * @param optmize if it is true and the method does not include * branches, this method returns null. */ public static TypedBlock[] makeBlocks(MethodInfo minfo, CodeAttribute ca, boolean optimize) throws BadBytecode { TypedBlock[] blocks = (TypedBlock[])new Maker().make(minfo); if (optimize && blocks.length < 2) if (blocks.length == 0 || blocks[0].incoming == 0) return null; ConstPool pool = minfo.getConstPool(); boolean isStatic = (minfo.getAccessFlags() & AccessFlag.STATIC) != 0; blocks[0].initFirstBlock(ca.getMaxStack(), ca.getMaxLocals(), pool.getClassName(), minfo.getDescriptor(), isStatic, minfo.isConstructor()); new Liveness().compute(ca.iterator(), blocks, ca.getMaxLocals(), blocks[0].localsTypes); return blocks; } protected TypedBlock(int pos) { super(pos); localsTypes = null; inputs = null; updating = false; } protected void toString2(StringBuffer sbuf) { super.toString2(sbuf); sbuf.append(",\n stack={"); printTypes(sbuf, stackTop, stackTypes); sbuf.append("}, locals={"); printTypes(sbuf, numLocals, localsTypes); sbuf.append("}, inputs={"); if (inputs != null) for (int i = 0; i < inputs.length; i++) sbuf.append(inputs[i] ? "1, " : "0, "); sbuf.append('}'); } private void printTypes(StringBuffer sbuf, int size, TypeData[] types) { if (types == null) return; for (int i = 0; i < size; i++) { if (i > 0) sbuf.append(", "); TypeData td = types[i]; sbuf.append(td == null ? "<>" : td.toString()); } } public boolean alreadySet() { return localsTypes != null; } public void setStackMap(int st, TypeData[] stack, int nl, TypeData[] locals) throws BadBytecode { stackTop = st; stackTypes = stack; numLocals = nl; localsTypes = locals; } /* * Computes the correct value of numLocals. */ public void resetNumLocals() { if (localsTypes != null) { int nl = localsTypes.length; while (nl > 0 && localsTypes[nl - 1] == TypeTag.TOP) { if (nl > 1) { TypeData td = localsTypes[nl - 2]; if (td == TypeTag.LONG || td == TypeTag.DOUBLE) break; } --nl; } numLocals = nl; } } public static class Maker extends BasicBlock.Maker { protected BasicBlock makeBlock(int pos) { return new TypedBlock(pos); } protected BasicBlock[] makeArray(int size) { return new TypedBlock[size]; } } /** * Initializes the first block by the given method descriptor. * * @param block the first basic block that this method initializes. * @param className a dot-separated fully qualified class name. * For example, javassist.bytecode.stackmap.BasicBlock. * @param methodDesc method descriptor. * @param isStatic true if the method is a static method. * @param isConstructor true if the method is a constructor. */ void initFirstBlock(int maxStack, int maxLocals, String className, String methodDesc, boolean isStatic, boolean isConstructor) throws BadBytecode { if (methodDesc.charAt(0) != '(') throw new BadBytecode("no method descriptor: " + methodDesc); stackTop = 0; stackTypes = new TypeData[maxStack]; TypeData[] locals = new TypeData[maxLocals]; if (isConstructor) locals[0] = new TypeData.UninitThis(className); else if (!isStatic) locals[0] = new TypeData.ClassName(className); int n = isStatic ? -1 : 0; int i = 1; try { while ((i = descToTag(methodDesc, i, ++n, locals)) > 0) if (locals[n].is2WordType()) locals[++n] = TypeTag.TOP; } catch (StringIndexOutOfBoundsException e) { throw new BadBytecode("bad method descriptor: " + methodDesc); } numLocals = n; localsTypes = locals; } private static int descToTag(String desc, int i, int n, TypeData[] types) throws BadBytecode { int i0 = i; int arrayDim = 0; char c = desc.charAt(i); if (c == ')') return 0; while (c == '[') { ++arrayDim; c = desc.charAt(++i); } if (c == 'L') { int i2 = desc.indexOf(';', ++i); if (arrayDim > 0) types[n] = new TypeData.ClassName(desc.substring(i0, ++i2)); else types[n] = new TypeData.ClassName(desc.substring(i0 + 1, ++i2 - 1) .replace('/', '.')); return i2; } else if (arrayDim > 0) { types[n] = new TypeData.ClassName(desc.substring(i0, ++i)); return i; } else { TypeData t = toPrimitiveTag(c); if (t == null) throw new BadBytecode("bad method descriptor: " + desc); types[n] = t; return i + 1; } } private static TypeData toPrimitiveTag(char c) { switch (c) { case 'Z' : case 'C' : case 'B' : case 'S' : case 'I' : return TypeTag.INTEGER; case 'J' : return TypeTag.LONG; case 'F' : return TypeTag.FLOAT; case 'D' : return TypeTag.DOUBLE; case 'V' : default : return null; } } public static String getRetType(String desc) { int i = desc.indexOf(')'); if (i < 0) return "java.lang.Object"; char c = desc.charAt(i + 1); if (c == '[') return desc.substring(i + 1); else if (c == 'L') return desc.substring(i + 2, desc.length() - 1).replace('/', '.'); else return "java.lang.Object"; } }