Home | History | Annotate | Download | only in evaluation
      1 /*
      2  * ProGuard -- shrinking, optimization, obfuscation, and preverification
      3  *             of Java bytecode.
      4  *
      5  * Copyright (c) 2002-2009 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.evaluation;
     22 
     23 import proguard.evaluation.value.*;
     24 
     25 /**
     26  * This class represents a local variable frame that contains <code>Value</code>
     27  * objects. Values are generalizations of all values that have been stored in
     28  * the respective variables.
     29  *
     30  * @author Eric Lafortune
     31  */
     32 public class Variables
     33 {
     34     private static final TopValue TOP_VALUE = new TopValue();
     35 
     36 
     37     protected Value[] values;
     38     protected int     size;
     39 
     40 
     41     /**
     42      * Creates a new Variables object with a given maximum number of variables.
     43      */
     44     public Variables(int size)
     45     {
     46         this.values = new Value[size];
     47         this.size   = size;
     48     }
     49 
     50 
     51     /**
     52      * Creates a Variables object that is a copy of the given Variables object.
     53      */
     54     public Variables(Variables variables)
     55     {
     56         // Create the values array.
     57         this(variables.size);
     58 
     59         // Copy the values.
     60         initialize(variables);
     61     }
     62 
     63 
     64     /**
     65      * Resets this Variables object, so that it can be reused.
     66      */
     67     public void reset(int size)
     68     {
     69         // Is the values array large enough?
     70         if (size > values.length)
     71         {
     72             // Create a new one.
     73             values = new Value[size];
     74         }
     75         else
     76         {
     77             // Clear the variables.
     78             for (int index = 0; index < values.length; index++)
     79             {
     80                 values[index] = null;
     81             }
     82         }
     83 
     84         this.size = size;
     85     }
     86 
     87 
     88     /**
     89      * Initializes the values of this Variables object with the values of the
     90      * given Variables object. The other object may have fewer values, in which
     91      * case the remaining values are left unchanged.
     92      */
     93     public void initialize(Variables other)
     94     {
     95         if (this.size < other.size)
     96         {
     97             throw new IllegalArgumentException("Variable frame is too small ["+this.size+"] compared to other frame ["+other.size+"]");
     98         }
     99 
    100         // Copy the values.
    101         System.arraycopy(other.values, 0, this.values, 0, other.size);
    102     }
    103 
    104 
    105     /**
    106      * Generalizes the values of this Variables object with the values of the
    107      * given Variables object.
    108      * @param clearConflictingOtherVariables specifies whether the other
    109      *                                       variables should be cleared too,
    110      *                                       in case of conflicts.
    111      * @return whether the generalization has made any difference.
    112      */
    113     public boolean generalize(Variables other,
    114                               boolean   clearConflictingOtherVariables)
    115     {
    116         if (this.size != other.size)
    117         {
    118             throw new IllegalArgumentException("Variable frames have different sizes ["+this.size+"] and ["+other.size+"]");
    119         }
    120 
    121         boolean changed = false;
    122 
    123         for (int index = 0; index < size; index++)
    124         {
    125             Value thisValue  = this.values[index];
    126             Value otherValue = other.values[index];
    127 
    128             // Occasionally, two values of different types might be present
    129             // in the same variable in a variable frame (corresponding to
    130             // two local variables that share the same index), at some point
    131             // outside of their scopes. Don't generalize the variable then,
    132             // but let it clear instead.
    133             if (thisValue  != null &&
    134                 otherValue != null &&
    135                 thisValue.computationalType() == otherValue.computationalType())
    136             {
    137                 Value newValue = thisValue.generalize(otherValue);
    138 
    139                 changed = changed || !thisValue.equals(newValue);
    140 
    141                 this.values[index] = newValue;
    142             }
    143             else
    144             {
    145                 changed = changed || thisValue != null;
    146 
    147                 this.values[index] = null;
    148 
    149                 if (clearConflictingOtherVariables)
    150                 {
    151                     other.values[index] = null;
    152                 }
    153             }
    154         }
    155 
    156         return changed;
    157     }
    158 
    159 
    160     /**
    161      * Returns the number of variables.
    162      */
    163     public int size()
    164     {
    165         return size;
    166     }
    167 
    168 
    169     /**
    170      * Gets the Value of the variable with the given index, without disturbing it.
    171      */
    172     public Value getValue(int index)
    173     {
    174         if (index < 0 ||
    175             index >= size)
    176         {
    177             throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]");
    178         }
    179 
    180         return values[index];
    181     }
    182 
    183 
    184     /**
    185      * Stores the given Value at the given variable index.
    186      */
    187     public void store(int index, Value value)
    188     {
    189         if (index < 0 ||
    190             index >= size)
    191         {
    192             throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]");
    193         }
    194 
    195         // Store the value.
    196         values[index] = value;
    197 
    198         // Account for the extra space required by Category 2 values.
    199         if (value.isCategory2())
    200         {
    201             values[index + 1] = TOP_VALUE;
    202         }
    203     }
    204 
    205 
    206     /**
    207      * Loads the Value from the variable with the given index.
    208      */
    209     public Value load(int index)
    210     {
    211         if (index < 0 ||
    212             index >= size)
    213         {
    214             throw new IndexOutOfBoundsException("Variable index ["+index+"] out of bounds ["+size+"]");
    215         }
    216 
    217         return values[index];
    218     }
    219 
    220 
    221     // Load methods that provide convenient casts to the expected value types.
    222 
    223     /**
    224      * Loads the IntegerValue from the variable with the given index.
    225      */
    226     public IntegerValue iload(int index)
    227     {
    228         return load(index).integerValue();
    229     }
    230 
    231 
    232     /**
    233      * Loads the LongValue from the variable with the given index.
    234      */
    235     public LongValue lload(int index)
    236     {
    237         return load(index).longValue();
    238     }
    239 
    240 
    241     /**
    242      * Loads the FloatValue from the variable with the given index.
    243      */
    244     public FloatValue fload(int index)
    245     {
    246         return load(index).floatValue();
    247     }
    248 
    249 
    250     /**
    251      * Loads the DoubleValue from the variable with the given index.
    252      */
    253     public DoubleValue dload(int index)
    254     {
    255         return load(index).doubleValue();
    256     }
    257 
    258 
    259     /**
    260      * Loads the ReferenceValue from the variable with the given index.
    261      */
    262     public ReferenceValue aload(int index)
    263     {
    264         return load(index).referenceValue();
    265     }
    266 
    267 
    268     /**
    269      * Loads the InstructionOffsetValue from the variable with the given index.
    270      */
    271     public InstructionOffsetValue oload(int index)
    272     {
    273         return load(index).instructionOffsetValue();
    274     }
    275 
    276 
    277     // Implementations for Object.
    278 
    279     public boolean equals(Object object)
    280     {
    281         if (object == null ||
    282             this.getClass() != object.getClass())
    283         {
    284             return false;
    285         }
    286 
    287         Variables other = (Variables)object;
    288 
    289         if (this.size != other.size)
    290         {
    291             return false;
    292         }
    293 
    294         for (int index = 0; index < size; index++)
    295         {
    296             Value thisValue  = this.values[index];
    297             Value otherValue = other.values[index];
    298 
    299             // Occasionally, two values of different types might be
    300             // present in the same variable in a variable frame
    301             // (corresponding to two local variables that share the
    302             // same index), at some point outside of their scopes.
    303             // We'll ignore these.
    304             if (thisValue  != null &&
    305                 otherValue != null &&
    306                 thisValue.computationalType() == otherValue.computationalType() &&
    307                 !thisValue.equals(otherValue))
    308             {
    309                 return false;
    310             }
    311         }
    312 
    313         return true;
    314     }
    315 
    316 
    317     public int hashCode()
    318     {
    319         int hashCode = size;
    320 
    321         for (int index = 0; index < size; index++)
    322         {
    323             Value value = values[index];
    324             if (value != null)
    325             {
    326                 hashCode ^= value.hashCode();
    327             }
    328         }
    329 
    330         return hashCode;
    331     }
    332 
    333 
    334     public String toString()
    335     {
    336         StringBuffer buffer = new StringBuffer();
    337 
    338         for (int index = 0; index < size; index++)
    339         {
    340             Value value = values[index];
    341             buffer = buffer.append('[')
    342                            .append(value == null ? "empty" : value.toString())
    343                            .append(']');
    344         }
    345 
    346         return buffer.toString();
    347     }
    348 }
    349