Home | History | Annotate | Download | only in stackmap
      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