1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2014 Eric Lafortune (eric@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 */
21package proguard.evaluation;
22
23import proguard.evaluation.value.*;
24
25import 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 */
34public 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