1/*
2 * ProGuard -- shrinking, optimization, obfuscation, and preverification
3 *             of Java bytecode.
4 *
5 * Copyright (c) 2002-2009 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
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 */
32public 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