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