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 Liveness {
     21     protected static final byte UNKNOWN = 0;
     22     protected static final byte READ = 1;
     23     protected static final byte UPDATED = 2;
     24     protected byte[] localsUsage;
     25 
     26     /**
     27      * If true, all the arguments become alive within the whole method body.
     28      *
     29      * To correctly compute a stack map table, all the arguments must
     30      * be alive (localsUsage[?] must be READ) at least in the first block.
     31      */
     32     public static boolean useArgs = true;
     33 
     34     public void compute(CodeIterator ci, TypedBlock[] blocks, int maxLocals,
     35                         TypeData[] args)
     36         throws BadBytecode
     37     {
     38         computeUsage(ci, blocks, maxLocals);
     39         if (useArgs)
     40             useAllArgs(blocks, args);
     41 
     42         computeLiveness1(blocks[0]);
     43         while (hasChanged(blocks))
     44             computeLiveness2(blocks[0]);
     45     }
     46 
     47     private void useAllArgs(TypedBlock[] blocks, TypeData[] args) {
     48         for (int k = 0; k < blocks.length; k++) {
     49             byte[] usage = blocks[k].localsUsage;
     50             for (int i = 0; i < args.length; i++)
     51                 if (args[i] != TypeTag.TOP)
     52                     usage[i] = READ;
     53         }
     54     }
     55 
     56     static final int NOT_YET = 0;
     57     static final int CHANGED_LAST = 1;
     58     static final int DONE = 2;
     59     static final int CHANGED_NOW = 3;
     60 
     61     private void computeLiveness1(TypedBlock tb) {
     62         if (tb.updating) {
     63             // a loop was detected.
     64             computeLiveness1u(tb);
     65             return;
     66         }
     67 
     68         if (tb.inputs != null)
     69             return;
     70 
     71         tb.updating = true;
     72         byte[] usage = tb.localsUsage;
     73         int n = usage.length;
     74         boolean[] in = new boolean[n];
     75         for (int i = 0; i < n; i++)
     76             in[i] = usage[i] == READ;
     77 
     78         BasicBlock.Catch handlers = tb.toCatch;
     79         while (handlers != null) {
     80             TypedBlock h = (TypedBlock)handlers.body;
     81             computeLiveness1(h);
     82             for (int k = 0; k < n; k++)
     83                 if (h.inputs[k])
     84                     in[k] = true;
     85 
     86             handlers = handlers.next;
     87         }
     88 
     89         if (tb.exit != null) {
     90             for (int i = 0; i < tb.exit.length; i++) {
     91                 TypedBlock e = (TypedBlock)tb.exit[i];
     92                 computeLiveness1(e);
     93                 for (int k = 0; k < n; k++)
     94                     if (!in[k])
     95                         in[k] = usage[k] == UNKNOWN && e.inputs[k];
     96             }
     97         }
     98 
     99         tb.updating = false;
    100         if (tb.inputs == null) {
    101             tb.inputs = in;
    102             tb.status = DONE;
    103         }
    104         else {
    105             for (int i = 0; i < n; i++)
    106                 if (in[i] && !tb.inputs[i]) {
    107                     tb.inputs[i] = true;
    108                     tb.status = CHANGED_NOW;
    109                 }
    110         }
    111     }
    112 
    113     private void computeLiveness1u(TypedBlock tb) {
    114         if (tb.inputs == null) {
    115             byte[] usage = tb.localsUsage;
    116             int n = usage.length;
    117             boolean[] in = new boolean[n];
    118             for (int i = 0; i < n; i++)
    119                 in[i] = usage[i] == READ;
    120 
    121             tb.inputs = in;
    122             tb.status = DONE;
    123         }
    124     }
    125 
    126     private void computeLiveness2(TypedBlock tb) {
    127         if (tb.updating || tb.status >= DONE)
    128             return;
    129 
    130         tb.updating = true;
    131         if (tb.exit == null)
    132             tb.status = DONE;
    133         else {
    134             boolean changed = false;
    135             for (int i = 0; i < tb.exit.length; i++) {
    136                 TypedBlock e = (TypedBlock)tb.exit[i];
    137                 computeLiveness2(e);
    138                 if (e.status != DONE)
    139                     changed = true;
    140             }
    141 
    142             if (changed) {
    143                 changed = false;
    144                 byte[] usage = tb.localsUsage;
    145                 int n = usage.length;
    146                 for (int i = 0; i < tb.exit.length; i++) {
    147                     TypedBlock e = (TypedBlock)tb.exit[i];
    148                     if (e.status != DONE)
    149                         for (int k = 0; k < n; k++)
    150                             if (!tb.inputs[k]) {
    151                                 if (usage[k] == UNKNOWN && e.inputs[k]) {
    152                                     tb.inputs[k] = true;
    153                                     changed = true;
    154                                 }
    155                             }
    156                 }
    157 
    158                 tb.status = changed ? CHANGED_NOW : DONE;
    159             }
    160             else
    161                 tb.status = DONE;
    162         }
    163 
    164         if (computeLiveness2except(tb))
    165             tb.status = CHANGED_NOW;
    166 
    167         tb.updating = false;
    168     }
    169 
    170     private boolean computeLiveness2except(TypedBlock tb) {
    171         BasicBlock.Catch handlers = tb.toCatch;
    172         boolean changed = false;
    173         while (handlers != null) {
    174             TypedBlock h = (TypedBlock)handlers.body;
    175             computeLiveness2(h);
    176             if (h.status != DONE) {
    177                 boolean[] in = tb.inputs;
    178                 int n = in.length;
    179                 for (int k = 0; k < n; k++)
    180                     if (!in[k] && h.inputs[k]) {
    181                         in[k] = true;
    182                         changed = true;
    183                     }
    184             }
    185 
    186             handlers = handlers.next;
    187         }
    188 
    189         return changed;
    190     }
    191 
    192     private boolean hasChanged(TypedBlock[] blocks) {
    193         int n = blocks.length;
    194         boolean changed = false;
    195         for (int i = 0; i < n; i++) {
    196             TypedBlock tb = blocks[i];
    197             if (tb.status == CHANGED_NOW) {
    198                 tb.status = CHANGED_LAST;
    199                 changed = true;
    200             }
    201             else
    202                 tb.status = NOT_YET;
    203         }
    204 
    205         return changed;
    206     }
    207 
    208     private void computeUsage(CodeIterator ci, TypedBlock[] blocks, int maxLocals)
    209         throws BadBytecode
    210     {
    211         int n = blocks.length;
    212         for (int i = 0; i < n; i++) {
    213             TypedBlock tb = blocks[i];
    214             localsUsage = tb.localsUsage = new byte[maxLocals];
    215             int pos = tb.position;
    216             analyze(ci, pos, pos + tb.length);
    217             localsUsage = null;
    218         }
    219     }
    220 
    221     protected final void readLocal(int reg) {
    222         if (localsUsage[reg] == UNKNOWN)
    223             localsUsage[reg] = READ;
    224     }
    225 
    226     protected final void writeLocal(int reg) {
    227         if (localsUsage[reg] == UNKNOWN)
    228             localsUsage[reg] = UPDATED;
    229     }
    230 
    231     protected void analyze(CodeIterator ci, int begin, int end)
    232         throws BadBytecode
    233     {
    234         ci.begin();
    235         ci.move(begin);
    236         while (ci.hasNext()) {
    237             int index = ci.next();
    238             if (index >= end)
    239                 break;
    240 
    241             int op = ci.byteAt(index);
    242             if (op < 96)
    243                 if (op < 54)
    244                     doOpcode0_53(ci, index, op);
    245                 else
    246                     doOpcode54_95(ci, index, op);
    247             else
    248                 if (op == Opcode.IINC) {
    249                     // this does not call writeLocal().
    250                     readLocal(ci.byteAt(index + 1));
    251                 }
    252                 else if (op == Opcode.WIDE)
    253                     doWIDE(ci, index);
    254         }
    255     }
    256 
    257     private void doOpcode0_53(CodeIterator ci, int pos, int op) {
    258         switch (op) {
    259         case Opcode.ILOAD :
    260         case Opcode.LLOAD :
    261         case Opcode.FLOAD :
    262         case Opcode.DLOAD :
    263         case Opcode.ALOAD :
    264             readLocal(ci.byteAt(pos + 1));
    265             break;
    266         case Opcode.ILOAD_0 :
    267         case Opcode.ILOAD_1 :
    268         case Opcode.ILOAD_2 :
    269         case Opcode.ILOAD_3 :
    270             readLocal(op - Opcode.ILOAD_0);
    271             break;
    272         case Opcode.LLOAD_0 :
    273         case Opcode.LLOAD_1 :
    274         case Opcode.LLOAD_2 :
    275         case Opcode.LLOAD_3 :
    276             readLocal(op - Opcode.LLOAD_0);
    277             break;
    278         case Opcode.FLOAD_0 :
    279         case Opcode.FLOAD_1 :
    280         case Opcode.FLOAD_2 :
    281         case Opcode.FLOAD_3 :
    282             readLocal(op - Opcode.FLOAD_0);
    283             break;
    284         case Opcode.DLOAD_0 :
    285         case Opcode.DLOAD_1 :
    286         case Opcode.DLOAD_2 :
    287         case Opcode.DLOAD_3 :
    288             readLocal(op - Opcode.DLOAD_0);
    289             break;
    290         case Opcode.ALOAD_0 :
    291         case Opcode.ALOAD_1 :
    292         case Opcode.ALOAD_2 :
    293         case Opcode.ALOAD_3 :
    294             readLocal(op - Opcode.ALOAD_0);
    295             break;
    296         }
    297     }
    298 
    299     private void doOpcode54_95(CodeIterator ci, int pos, int op) {
    300         switch (op) {
    301         case Opcode.ISTORE :
    302         case Opcode.LSTORE :
    303         case Opcode.FSTORE :
    304         case Opcode.DSTORE :
    305         case Opcode.ASTORE :
    306             writeLocal(ci.byteAt(pos + 1));
    307             break;
    308         case Opcode.ISTORE_0 :
    309         case Opcode.ISTORE_1 :
    310         case Opcode.ISTORE_2 :
    311         case Opcode.ISTORE_3 :
    312             writeLocal(op - Opcode.ISTORE_0);
    313             break;
    314         case Opcode.LSTORE_0 :
    315         case Opcode.LSTORE_1 :
    316         case Opcode.LSTORE_2 :
    317         case Opcode.LSTORE_3 :
    318             writeLocal(op - Opcode.LSTORE_0);
    319             break;
    320         case Opcode.FSTORE_0 :
    321         case Opcode.FSTORE_1 :
    322         case Opcode.FSTORE_2 :
    323         case Opcode.FSTORE_3 :
    324             writeLocal(op - Opcode.FSTORE_0);
    325             break;
    326         case Opcode.DSTORE_0 :
    327         case Opcode.DSTORE_1 :
    328         case Opcode.DSTORE_2 :
    329         case Opcode.DSTORE_3 :
    330             writeLocal(op - Opcode.DSTORE_0);
    331             break;
    332         case Opcode.ASTORE_0 :
    333         case Opcode.ASTORE_1 :
    334         case Opcode.ASTORE_2 :
    335         case Opcode.ASTORE_3 :
    336             writeLocal(op - Opcode.ASTORE_0);
    337             break;
    338         }
    339     }
    340 
    341     private void doWIDE(CodeIterator ci, int pos) throws BadBytecode {
    342         int op = ci.byteAt(pos + 1);
    343         int var = ci.u16bitAt(pos + 2);
    344         switch (op) {
    345         case Opcode.ILOAD :
    346         case Opcode.LLOAD :
    347         case Opcode.FLOAD :
    348         case Opcode.DLOAD :
    349         case Opcode.ALOAD :
    350             readLocal(var);
    351             break;
    352         case Opcode.ISTORE :
    353         case Opcode.LSTORE :
    354         case Opcode.FSTORE :
    355         case Opcode.DSTORE :
    356         case Opcode.ASTORE :
    357             writeLocal(var);
    358             break;
    359         case Opcode.IINC :
    360             readLocal(var);
    361             // this does not call writeLocal().
    362             break;
    363         }
    364     }
    365 }
    366