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 an operand stack that contains <code>Value</code>
     27  * objects.
     28  *
     29  * @author Eric Lafortune
     30  */
     31 public class Stack
     32 {
     33     private static final TopValue TOP_VALUE = new TopValue();
     34 
     35 
     36     protected Value[] values;
     37     protected int     currentSize;
     38     protected int     actualMaxSize;
     39 
     40 
     41     /**
     42      * Creates a new Stack with a given maximum size, accounting for the double
     43      * space required by Category 2 values.
     44      */
     45     public Stack(int maxSize)
     46     {
     47         values = new Value[maxSize];
     48     }
     49 
     50 
     51     /**
     52      * Creates a Stack that is a copy of the given Stack.
     53      */
     54     public Stack(Stack stack)
     55     {
     56         // Create the values array.
     57         this(stack.values.length);
     58 
     59         // Copy the stack contents.
     60         copy(stack);
     61     }
     62 
     63 
     64     /**
     65      * Returns the actual maximum stack size that was required for all stack
     66      * operations, accounting for the double space required by Category 2 values.
     67      */
     68     public int getActualMaxSize()
     69     {
     70         return actualMaxSize;
     71     }
     72 
     73 
     74     /**
     75      * Resets this Stack, so that it can be reused.
     76      */
     77     public void reset(int maxSize)
     78     {
     79         // Is the values array large enough?
     80         if (maxSize > values.length)
     81         {
     82             // Create a new one.
     83             values = new Value[maxSize];
     84         }
     85 
     86         // Clear the sizes.
     87         clear();
     88 
     89         actualMaxSize = 0;
     90     }
     91 
     92 
     93     /**
     94      * Copies the values of the given Stack into this Stack.
     95      */
     96     public void copy(Stack other)
     97     {
     98         // Is the values array large enough?
     99         if (other.values.length > values.length)
    100         {
    101             // Create a new one.
    102             values = new Value[other.values.length];
    103         }
    104 
    105         // Copy the stack contents.
    106         System.arraycopy(other.values, 0, this.values, 0, other.currentSize);
    107 
    108         // Copy the sizes.
    109         currentSize   = other.currentSize;
    110         actualMaxSize = other.actualMaxSize;
    111     }
    112 
    113 
    114     /**
    115      * Generalizes the values of this Stack with the values of the given Stack.
    116      * The stacks must have the same current sizes.
    117      * @return whether the generalization has made any difference.
    118      */
    119     public boolean generalize(Stack other)
    120     {
    121         if (this.currentSize != other.currentSize)
    122         {
    123             throw new IllegalArgumentException("Stacks have different current sizes ["+this.currentSize+"] and ["+other.currentSize+"]");
    124         }
    125 
    126         boolean changed = false;
    127 
    128         // Generalize the stack values.
    129         for (int index = 0; index < currentSize; index++)
    130         {
    131             Value thisValue  = this.values[index];
    132 
    133             if (thisValue != null)
    134             {
    135                 Value newValue = null;
    136 
    137                 Value otherValue = other.values[index];
    138 
    139                 if (otherValue != null)
    140                 {
    141                     newValue = thisValue.generalize(otherValue);
    142                 }
    143 
    144                 changed = changed || !thisValue.equals(newValue);
    145 
    146                 values[index] = newValue;
    147             }
    148         }
    149 
    150         // Check if the other stack extends beyond this one.
    151         if (this.actualMaxSize < other.actualMaxSize)
    152         {
    153             this.actualMaxSize = other.actualMaxSize;
    154         }
    155 
    156         return changed;
    157     }
    158 
    159 
    160     /**
    161      * Clears the stack.
    162      */
    163     public void clear()
    164     {
    165         // Clear the stack contents.
    166         for (int index = 0; index < currentSize; index++)
    167         {
    168             values[index] = null;
    169         }
    170 
    171         currentSize = 0;
    172     }
    173 
    174 
    175     /**
    176      * Returns the number of elements currently on the stack, accounting for the
    177      * double space required by Category 2 values.
    178      */
    179     public int size()
    180     {
    181         return currentSize;
    182     }
    183 
    184 
    185     /**
    186      * Gets the specified Value from the stack, without disturbing it.
    187      * @param index the index of the stack element, counting from the bottom
    188      *              of the stack.
    189      * @return the value at the specified position.
    190      */
    191     public Value getBottom(int index)
    192     {
    193         return values[index];
    194     }
    195 
    196 
    197     /**
    198      * Sets the specified Value on the stack, without disturbing it.
    199      * @param index the index of the stack element, counting from the bottom
    200      *              of the stack.
    201      * @param value the value to set.
    202      */
    203     public void setBottom(int index, Value value)
    204     {
    205         values[index] = value;
    206     }
    207 
    208 
    209     /**
    210      * Gets the specified Value from the stack, without disturbing it.
    211      * @param index the index of the stack element, counting from the top
    212      *              of the stack.
    213      * @return the value at the specified position.
    214      */
    215     public Value getTop(int index)
    216     {
    217         return values[currentSize - index - 1];
    218     }
    219 
    220 
    221     /**
    222      * Sets the specified Value on the stack, without disturbing it.
    223      * @param index the index of the stack element, counting from the top
    224      *              of the stack.
    225      * @param value the value to set.
    226      */
    227     public void setTop(int index, Value value)
    228     {
    229         values[currentSize - index - 1] = value;
    230     }
    231 
    232 
    233     /**
    234      * Removes the specified Value from the stack.
    235      * @param index the index of the stack element, counting from the top
    236      *              of the stack.
    237      */
    238     public void removeTop(int index)
    239     {
    240         System.arraycopy(values, currentSize - index,
    241                          values, currentSize - index - 1,
    242                          index);
    243         currentSize--;
    244     }
    245 
    246 
    247     /**
    248      * Pushes the given Value onto the stack.
    249      */
    250     public void push(Value value)
    251     {
    252         // Account for the extra space required by Category 2 values.
    253         if (value.isCategory2())
    254         {
    255             values[currentSize++] = TOP_VALUE;
    256         }
    257 
    258         // Push the value.
    259         values[currentSize++] = value;
    260 
    261         // Update the maximum actual size;
    262         if (actualMaxSize < currentSize)
    263         {
    264             actualMaxSize = currentSize;
    265         }
    266     }
    267 
    268 
    269     /**
    270      * Pops the top Value from the stack.
    271      */
    272     public Value pop()
    273     {
    274         Value value = values[--currentSize];
    275 
    276         values[currentSize] = null;
    277 
    278         // Account for the extra space required by Category 2 values.
    279         if (value.isCategory2())
    280         {
    281             values[--currentSize] = null;
    282         }
    283 
    284         return value;
    285     }
    286 
    287 
    288     // Pop methods that provide convenient casts to the expected value types.
    289 
    290     /**
    291      * Pops the top IntegerValue from the stack.
    292      */
    293     public IntegerValue ipop()
    294     {
    295         return pop().integerValue();
    296     }
    297 
    298 
    299     /**
    300      * Pops the top LongValue from the stack.
    301      */
    302     public LongValue lpop()
    303     {
    304         return pop().longValue();
    305     }
    306 
    307 
    308     /**
    309      * Pops the top FloatValue from the stack.
    310      */
    311     public FloatValue fpop()
    312     {
    313         return pop().floatValue();
    314     }
    315 
    316 
    317     /**
    318      * Pops the top DoubleValue from the stack.
    319      */
    320     public DoubleValue dpop()
    321     {
    322         return pop().doubleValue();
    323     }
    324 
    325 
    326     /**
    327      * Pops the top ReferenceValue from the stack.
    328      */
    329     public ReferenceValue apop()
    330     {
    331         return pop().referenceValue();
    332     }
    333 
    334 
    335     /**
    336      * Pops the top InstructionOffsetValue from the stack.
    337      */
    338     public InstructionOffsetValue opop()
    339     {
    340         return pop().instructionOffsetValue();
    341     }
    342 
    343 
    344     /**
    345      * Pops the top category 1 value from the stack.
    346      */
    347     public void pop1()
    348     {
    349         values[--currentSize] = null;
    350     }
    351 
    352 
    353     /**
    354      * Pops the top category 2 value from the stack (or alternatively, two
    355      * Category 1 stack elements).
    356      */
    357     public void pop2()
    358     {
    359         values[--currentSize] = null;
    360         values[--currentSize] = null;
    361     }
    362 
    363 
    364     /**
    365      * Duplicates the top Category 1 value.
    366      */
    367     public void dup()
    368     {
    369         values[currentSize] = values[currentSize - 1].category1Value();
    370 
    371         currentSize++;
    372 
    373         // Update the maximum actual size;
    374         if (actualMaxSize < currentSize)
    375         {
    376             actualMaxSize = currentSize;
    377         }
    378     }
    379 
    380 
    381     /**
    382      * Duplicates the top Category 1 value, one Category 1 element down the
    383      * stack.
    384      */
    385     public void dup_x1()
    386     {
    387         values[currentSize]     = values[currentSize - 1].category1Value();
    388         values[currentSize - 1] = values[currentSize - 2].category1Value();
    389         values[currentSize - 2] = values[currentSize    ];
    390 
    391         currentSize++;
    392 
    393         // Update the maximum actual size;
    394         if (actualMaxSize < currentSize)
    395         {
    396             actualMaxSize = currentSize;
    397         }
    398     }
    399 
    400 
    401     /**
    402      * Duplicates the top Category 1 value, two Category 1 elements (or one
    403      * Category 2 element) down the stack.
    404      */
    405     public void dup_x2()
    406     {
    407         values[currentSize]     = values[currentSize - 1].category1Value();
    408         values[currentSize - 1] = values[currentSize - 2];
    409         values[currentSize - 2] = values[currentSize - 3];
    410         values[currentSize - 3] = values[currentSize    ];
    411 
    412         currentSize++;
    413 
    414         // Update the maximum actual size;
    415         if (actualMaxSize < currentSize)
    416         {
    417             actualMaxSize = currentSize;
    418         }
    419     }
    420 
    421     /**
    422      * Duplicates the top Category 2 value (or alternatively, the equivalent
    423      * Category 1 stack elements).
    424      */
    425     public void dup2()
    426     {
    427         values[currentSize    ] = values[currentSize - 2];
    428         values[currentSize + 1] = values[currentSize - 1];
    429 
    430         currentSize += 2;
    431 
    432         // Update the maximum actual size;
    433         if (actualMaxSize < currentSize)
    434         {
    435             actualMaxSize = currentSize;
    436         }
    437     }
    438 
    439 
    440     /**
    441      * Duplicates the top Category 2 value, one Category 1 element down the
    442      * stack (or alternatively, the equivalent Category 1 stack values).
    443      */
    444     public void dup2_x1()
    445     {
    446         values[currentSize + 1] = values[currentSize - 1];
    447         values[currentSize    ] = values[currentSize - 2];
    448         values[currentSize - 1] = values[currentSize - 3];
    449         values[currentSize - 2] = values[currentSize + 1];
    450         values[currentSize - 3] = values[currentSize    ];
    451 
    452         currentSize += 2;
    453 
    454         // Update the maximum actual size;
    455         if (actualMaxSize < currentSize)
    456         {
    457             actualMaxSize = currentSize;
    458         }
    459     }
    460 
    461 
    462     /**
    463      * Duplicates the top Category 2 value, one Category 2 stack element down
    464      * the stack (or alternatively, the equivalent Category 1 stack values).
    465      */
    466     public void dup2_x2()
    467     {
    468         values[currentSize + 1] = values[currentSize - 1];
    469         values[currentSize    ] = values[currentSize - 2];
    470         values[currentSize - 1] = values[currentSize - 3];
    471         values[currentSize - 2] = values[currentSize - 4];
    472         values[currentSize - 3] = values[currentSize + 1];
    473         values[currentSize - 4] = values[currentSize    ];
    474 
    475         currentSize += 2;
    476 
    477         // Update the maximum actual size;
    478         if (actualMaxSize < currentSize)
    479         {
    480             actualMaxSize = currentSize;
    481         }
    482     }
    483 
    484 
    485     /**
    486      * Swaps the top two Category 1 values.
    487      */
    488     public void swap()
    489     {
    490         Value value1 = values[currentSize - 1].category1Value();
    491         Value value2 = values[currentSize - 2].category1Value();
    492 
    493         values[currentSize - 1] = value2;
    494         values[currentSize - 2] = value1;
    495     }
    496 
    497 
    498     // Implementations for Object.
    499 
    500     public boolean equals(Object object)
    501     {
    502         if (object == null ||
    503             this.getClass() != object.getClass())
    504         {
    505             return false;
    506         }
    507 
    508         Stack other = (Stack)object;
    509 
    510         if (this.currentSize != other.currentSize)
    511         {
    512             return false;
    513         }
    514 
    515         for (int index = 0; index < currentSize; index++)
    516         {
    517             Value thisValue  = this.values[index];
    518             Value otherValue = other.values[index];
    519             if (thisValue == null ? otherValue != null :
    520                                     !thisValue.equals(otherValue))
    521             {
    522                 return false;
    523             }
    524         }
    525 
    526         return true;
    527     }
    528 
    529 
    530     public int hashCode()
    531     {
    532         int hashCode = currentSize;
    533 
    534         for (int index = 0; index < currentSize; index++)
    535         {
    536             Value value = values[index];
    537             if (value != null)
    538             {
    539                 hashCode ^= value.hashCode();
    540             }
    541         }
    542 
    543         return hashCode;
    544     }
    545 
    546 
    547     public String toString()
    548     {
    549         StringBuffer buffer = new StringBuffer();
    550 
    551         for (int index = 0; index < currentSize; index++)
    552         {
    553             Value value = values[index];
    554             buffer = buffer.append('[')
    555                            .append(value == null ? "empty" : value.toString())
    556                            .append(']');
    557         }
    558 
    559         return buffer.toString();
    560     }
    561 }
    562