Home | History | Annotate | Download | only in instruction
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2014 Eric Lafortune (eric (at) graphics.cornell.edu)
      6  *
      7  * This program is free software; you can redistribute it and/or modify it
      8  * under the terms of the GNU General Public License as published by the Free
      9  * Software Foundation; either version 2 of the License, or (at your option)
     10  * any later version.
     11  *
     12  * This program is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
     15  * more details.
     16  *
     17  * You should have received a copy of the GNU General Public License along
     18  * with this program; if not, write to the Free Software Foundation, Inc.,
     19  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
     20  */
     21 package proguard.classfile.instruction;
     22 
     23 import proguard.classfile.*;
     24 import proguard.classfile.attribute.CodeAttribute;
     25 import proguard.classfile.instruction.visitor.InstructionVisitor;
     26 
     27 /**
     28  * This Instruction represents an instruction that refers to a variable on the
     29  * local variable stack.
     30  *
     31  * @author Eric Lafortune
     32  */
     33 public class VariableInstruction extends Instruction
     34 {
     35     public boolean wide;
     36     public int     variableIndex;
     37     public int     constant;
     38 
     39 
     40     /**
     41      * Creates an uninitialized VariableInstruction.
     42      */
     43     public VariableInstruction() {}
     44 
     45 
     46     public VariableInstruction(boolean wide)
     47     {
     48         this.wide = wide;
     49     }
     50 
     51 
     52     public VariableInstruction(byte opcode)
     53     {
     54         this(opcode, embeddedVariable(opcode), 0);
     55     }
     56 
     57 
     58     public VariableInstruction(byte opcode,
     59                                int  variableIndex)
     60     {
     61         this(opcode, variableIndex, 0);
     62     }
     63 
     64 
     65     public VariableInstruction(byte opcode,
     66                                int  variableIndex,
     67                                int  constant)
     68     {
     69         this.opcode        = opcode;
     70         this.variableIndex = variableIndex;
     71         this.constant      = constant;
     72         this.wide          = requiredVariableIndexSize() > 1 ||
     73                              requiredConstantSize()      > 1;
     74     }
     75 
     76 
     77     /**
     78      * Copies the given instruction into this instruction.
     79      * @param variableInstruction the instruction to be copied.
     80      * @return this instruction.
     81      */
     82     public VariableInstruction copy(VariableInstruction variableInstruction)
     83     {
     84         this.opcode        = variableInstruction.opcode;
     85         this.variableIndex = variableInstruction.variableIndex;
     86         this.constant      = variableInstruction.constant;
     87         this.wide          = variableInstruction.wide;
     88 
     89         return this;
     90     }
     91 
     92 
     93     /**
     94      * Return the embedded variable of the given opcode, or 0 if the opcode
     95      * doesn't have one.
     96      */
     97     private static int embeddedVariable(byte opcode)
     98     {
     99         switch (opcode)
    100         {
    101             case InstructionConstants.OP_ILOAD_1:
    102             case InstructionConstants.OP_LLOAD_1:
    103             case InstructionConstants.OP_FLOAD_1:
    104             case InstructionConstants.OP_DLOAD_1:
    105             case InstructionConstants.OP_ALOAD_1:
    106             case InstructionConstants.OP_ISTORE_1:
    107             case InstructionConstants.OP_LSTORE_1:
    108             case InstructionConstants.OP_FSTORE_1:
    109             case InstructionConstants.OP_DSTORE_1:
    110             case InstructionConstants.OP_ASTORE_1: return 1;
    111 
    112             case InstructionConstants.OP_ILOAD_2:
    113             case InstructionConstants.OP_LLOAD_2:
    114             case InstructionConstants.OP_FLOAD_2:
    115             case InstructionConstants.OP_DLOAD_2:
    116             case InstructionConstants.OP_ALOAD_2:
    117             case InstructionConstants.OP_ISTORE_2:
    118             case InstructionConstants.OP_LSTORE_2:
    119             case InstructionConstants.OP_FSTORE_2:
    120             case InstructionConstants.OP_DSTORE_2:
    121             case InstructionConstants.OP_ASTORE_2: return 2;
    122 
    123             case InstructionConstants.OP_ILOAD_3:
    124             case InstructionConstants.OP_LLOAD_3:
    125             case InstructionConstants.OP_FLOAD_3:
    126             case InstructionConstants.OP_DLOAD_3:
    127             case InstructionConstants.OP_ALOAD_3:
    128             case InstructionConstants.OP_ISTORE_3:
    129             case InstructionConstants.OP_LSTORE_3:
    130             case InstructionConstants.OP_FSTORE_3:
    131             case InstructionConstants.OP_DSTORE_3:
    132             case InstructionConstants.OP_ASTORE_3: return 3;
    133 
    134             default: return 0;
    135         }
    136     }
    137 
    138 
    139     /**
    140      * Returns whether this instruction stores the value of a variable.
    141      * The value is false for the ret instruction, but true for the iinc
    142      * instruction.
    143      */
    144     public boolean isStore()
    145     {
    146         // A store instruction can be recognized as follows. Note that this
    147         // excludes the ret instruction, which has a negative opcode.
    148         return opcode >= InstructionConstants.OP_ISTORE ||
    149                opcode == InstructionConstants.OP_IINC;
    150     }
    151 
    152 
    153     /**
    154      * Returns whether this instruction loads the value of a variable.
    155      * The value is true for the ret instruction and for the iinc
    156      * instruction.
    157      */
    158     public boolean isLoad()
    159     {
    160         // A load instruction can be recognized as follows. Note that this
    161         // includes the ret instruction, which has a negative opcode.
    162         return opcode < InstructionConstants.OP_ISTORE;
    163     }
    164 
    165 
    166     // Implementations for Instruction.
    167 
    168     public byte canonicalOpcode()
    169     {
    170         // Remove the _0, _1, _2, _3 extension, if any.
    171         switch (opcode)
    172         {
    173             case InstructionConstants.OP_ILOAD_0:
    174             case InstructionConstants.OP_ILOAD_1:
    175             case InstructionConstants.OP_ILOAD_2:
    176             case InstructionConstants.OP_ILOAD_3: return InstructionConstants.OP_ILOAD;
    177             case InstructionConstants.OP_LLOAD_0:
    178             case InstructionConstants.OP_LLOAD_1:
    179             case InstructionConstants.OP_LLOAD_2:
    180             case InstructionConstants.OP_LLOAD_3: return InstructionConstants.OP_LLOAD;
    181             case InstructionConstants.OP_FLOAD_0:
    182             case InstructionConstants.OP_FLOAD_1:
    183             case InstructionConstants.OP_FLOAD_2:
    184             case InstructionConstants.OP_FLOAD_3: return InstructionConstants.OP_FLOAD;
    185             case InstructionConstants.OP_DLOAD_0:
    186             case InstructionConstants.OP_DLOAD_1:
    187             case InstructionConstants.OP_DLOAD_2:
    188             case InstructionConstants.OP_DLOAD_3: return InstructionConstants.OP_DLOAD;
    189             case InstructionConstants.OP_ALOAD_0:
    190             case InstructionConstants.OP_ALOAD_1:
    191             case InstructionConstants.OP_ALOAD_2:
    192             case InstructionConstants.OP_ALOAD_3: return InstructionConstants.OP_ALOAD;
    193 
    194             case InstructionConstants.OP_ISTORE_0:
    195             case InstructionConstants.OP_ISTORE_1:
    196             case InstructionConstants.OP_ISTORE_2:
    197             case InstructionConstants.OP_ISTORE_3: return InstructionConstants.OP_ISTORE;
    198             case InstructionConstants.OP_LSTORE_0:
    199             case InstructionConstants.OP_LSTORE_1:
    200             case InstructionConstants.OP_LSTORE_2:
    201             case InstructionConstants.OP_LSTORE_3: return InstructionConstants.OP_LSTORE;
    202             case InstructionConstants.OP_FSTORE_0:
    203             case InstructionConstants.OP_FSTORE_1:
    204             case InstructionConstants.OP_FSTORE_2:
    205             case InstructionConstants.OP_FSTORE_3: return InstructionConstants.OP_FSTORE;
    206             case InstructionConstants.OP_DSTORE_0:
    207             case InstructionConstants.OP_DSTORE_1:
    208             case InstructionConstants.OP_DSTORE_2:
    209             case InstructionConstants.OP_DSTORE_3: return InstructionConstants.OP_DSTORE;
    210             case InstructionConstants.OP_ASTORE_0:
    211             case InstructionConstants.OP_ASTORE_1:
    212             case InstructionConstants.OP_ASTORE_2:
    213             case InstructionConstants.OP_ASTORE_3: return InstructionConstants.OP_ASTORE;
    214 
    215             default: return opcode;
    216         }
    217     }
    218 
    219     public Instruction shrink()
    220     {
    221         opcode = canonicalOpcode();
    222 
    223         // Is this instruction pointing to a variable with index from 0 to 3?
    224         if (variableIndex <= 3)
    225         {
    226             switch (opcode)
    227             {
    228                 case InstructionConstants.OP_ILOAD: opcode = (byte)(InstructionConstants.OP_ILOAD_0 + variableIndex); break;
    229                 case InstructionConstants.OP_LLOAD: opcode = (byte)(InstructionConstants.OP_LLOAD_0 + variableIndex); break;
    230                 case InstructionConstants.OP_FLOAD: opcode = (byte)(InstructionConstants.OP_FLOAD_0 + variableIndex); break;
    231                 case InstructionConstants.OP_DLOAD: opcode = (byte)(InstructionConstants.OP_DLOAD_0 + variableIndex); break;
    232                 case InstructionConstants.OP_ALOAD: opcode = (byte)(InstructionConstants.OP_ALOAD_0 + variableIndex); break;
    233 
    234                 case InstructionConstants.OP_ISTORE: opcode = (byte)(InstructionConstants.OP_ISTORE_0 + variableIndex); break;
    235                 case InstructionConstants.OP_LSTORE: opcode = (byte)(InstructionConstants.OP_LSTORE_0 + variableIndex); break;
    236                 case InstructionConstants.OP_FSTORE: opcode = (byte)(InstructionConstants.OP_FSTORE_0 + variableIndex); break;
    237                 case InstructionConstants.OP_DSTORE: opcode = (byte)(InstructionConstants.OP_DSTORE_0 + variableIndex); break;
    238                 case InstructionConstants.OP_ASTORE: opcode = (byte)(InstructionConstants.OP_ASTORE_0 + variableIndex); break;
    239             }
    240         }
    241 
    242         // Only make the instruction wide if necessary.
    243         wide = requiredVariableIndexSize() > 1 ||
    244                requiredConstantSize()      > 1;
    245 
    246         return this;
    247     }
    248 
    249 
    250     protected boolean isWide()
    251     {
    252         return wide;
    253     }
    254 
    255 
    256     protected void readInfo(byte[] code, int offset)
    257     {
    258         int variableIndexSize = variableIndexSize();
    259         int constantSize      = constantSize();
    260 
    261         // Also initialize embedded variable indexes.
    262         if (variableIndexSize == 0)
    263         {
    264             // An embedded variable index can be decoded as follows.
    265             variableIndex = opcode < InstructionConstants.OP_ISTORE_0 ?
    266                 (opcode - InstructionConstants.OP_ILOAD_0 ) & 3 :
    267                 (opcode - InstructionConstants.OP_ISTORE_0) & 3;
    268         }
    269         else
    270         {
    271             variableIndex = readValue(code, offset, variableIndexSize); offset += variableIndexSize;
    272         }
    273 
    274         constant = readSignedValue(code, offset, constantSize);
    275     }
    276 
    277 
    278     protected void writeInfo(byte[] code, int offset)
    279     {
    280         int variableIndexSize = variableIndexSize();
    281         int constantSize      = constantSize();
    282 
    283         if (requiredVariableIndexSize() > variableIndexSize)
    284         {
    285             throw new IllegalArgumentException("Instruction has invalid variable index size ("+this.toString(offset)+")");
    286         }
    287 
    288         if (requiredConstantSize() > constantSize)
    289         {
    290             throw new IllegalArgumentException("Instruction has invalid constant size ("+this.toString(offset)+")");
    291         }
    292 
    293         writeValue(code, offset, variableIndex, variableIndexSize); offset += variableIndexSize;
    294         writeSignedValue(code, offset, constant, constantSize);
    295     }
    296 
    297 
    298     public int length(int offset)
    299     {
    300         return (wide ? 2 : 1) + variableIndexSize() + constantSize();
    301     }
    302 
    303 
    304     public void accept(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, InstructionVisitor instructionVisitor)
    305     {
    306         instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, this);
    307     }
    308 
    309 
    310     // Implementations for Object.
    311 
    312     public String toString()
    313     {
    314         return getName() +
    315                (wide ? "_w" : "") +
    316                " v"+variableIndex +
    317                (constantSize() > 0 ? ", "+constant : "");
    318     }
    319 
    320 
    321     // Small utility methods.
    322 
    323     /**
    324      * Returns the variable index size for this instruction.
    325      */
    326     private int variableIndexSize()
    327     {
    328         return (opcode >= InstructionConstants.OP_ILOAD_0 &&
    329                 opcode <= InstructionConstants.OP_ALOAD_3) ||
    330                (opcode >= InstructionConstants.OP_ISTORE_0 &&
    331                 opcode <= InstructionConstants.OP_ASTORE_3) ? 0 :
    332                wide                                         ? 2 :
    333                                                               1;
    334     }
    335 
    336 
    337     /**
    338      * Computes the required variable index size for this instruction's variable
    339      * index.
    340      */
    341     private int requiredVariableIndexSize()
    342     {
    343         return (variableIndex &    0x3) == variableIndex ? 0 :
    344                (variableIndex &   0xff) == variableIndex ? 1 :
    345                (variableIndex & 0xffff) == variableIndex ? 2 :
    346                                                            4;
    347 
    348     }
    349 
    350 
    351     /**
    352      * Returns the constant size for this instruction.
    353      */
    354     private int constantSize()
    355     {
    356         return opcode != InstructionConstants.OP_IINC ? 0 :
    357                wide                                   ? 2 :
    358                                                         1;
    359     }
    360 
    361 
    362     /**
    363      * Computes the required constant size for this instruction's constant.
    364      */
    365     private int requiredConstantSize()
    366     {
    367         return opcode != InstructionConstants.OP_IINC ? 0 :
    368                (byte)constant  == constant            ? 1 :
    369                (short)constant == constant            ? 2 :
    370                                                         4;
    371     }
    372 }
    373