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.value;
22
23import proguard.classfile.ClassConstants;
24
25/**
26 * This class represents a partially evaluated instruction offset. It can
27 * contain 0 or more specific instruction offsets.
28 *
29 * @author Eric Lafortune
30 */
31public class InstructionOffsetValue extends Category1Value
32{
33    public static final InstructionOffsetValue EMPTY_VALUE = new InstructionOffsetValue();
34
35
36    private int[] values;
37
38
39    private InstructionOffsetValue()
40    {
41    }
42
43
44    public InstructionOffsetValue(int value)
45    {
46        this.values = new int[] { value };
47    }
48
49
50    public InstructionOffsetValue(int[] values)
51    {
52        this.values = values;
53    }
54
55
56    public int instructionOffsetCount()
57    {
58        return values == null ? 0 : values.length;
59    }
60
61
62    public int instructionOffset(int index)
63    {
64        return values[index];
65    }
66
67
68    /**
69     * Returns whether the given value is present in this list of instruction
70     * offsets.
71     */
72    public boolean contains(int value)
73    {
74        if (values != null)
75        {
76            for (int index = 0; index < values.length; index++)
77            {
78                if (values[index] == value)
79                {
80                    return true;
81                }
82            }
83        }
84
85        return false;
86    }
87
88
89    /**
90     * Returns the minimum value from this list of instruction offsets.
91     * Returns <code>Integer.MAX_VALUE</code> if the list is empty.
92     */
93    public int minimumValue()
94    {
95        int minimumValue = Integer.MAX_VALUE;
96
97        if (values != null)
98        {
99            for (int index = 0; index < values.length; index++)
100            {
101                int value = values[index];
102
103                if (minimumValue > value)
104                {
105                    minimumValue = value;
106                }
107            }
108        }
109
110        return minimumValue;
111    }
112
113
114    /**
115     * Returns the maximum value from this list of instruction offsets.
116     * Returns <code>Integer.MIN_VALUE</code> if the list is empty.
117     */
118    public int maximumValue()
119    {
120        int maximumValue = Integer.MIN_VALUE;
121
122        if (values != null)
123        {
124            for (int index = 0; index < values.length; index++)
125            {
126                int value = values[index];
127
128                if (maximumValue < value)
129                {
130                    maximumValue = value;
131                }
132            }
133        }
134
135        return maximumValue;
136    }
137
138
139    /**
140     * Returns the generalization of this InstructionOffsetValue and the given
141     * other InstructionOffsetValue. The values of the other InstructionOffsetValue
142     * are guaranteed to remain at the end of the list, in the same order.
143     */
144    public final Value generalize(InstructionOffsetValue other)
145    {
146        // If the values array of either is null, we can return the other one.
147        int[] thisValues = this.values;
148        if (thisValues == null)
149        {
150            return other;
151        }
152
153        int[] otherValues = other.values;
154        if (otherValues == null)
155        {
156            return this;
157        }
158
159        // Compute the length of the union of the arrays.
160        int newLength = thisValues.length;
161        for (int index = 0; index < otherValues.length; index++)
162        {
163            if (!this.contains(otherValues[index]))
164            {
165                newLength++;
166            }
167        }
168
169        // If the length of the union array is equal to the length of the other
170        // values array, we can return it.
171        if (newLength == otherValues.length)
172        {
173            return other;
174        }
175
176        // If the length of the union array is equal to the length of this
177        // values array, we can return it. We have to make sure that the other
178        // values are at the end. We'll just test one special case, with a
179        // single other value.
180        if (newLength == this.values.length &&
181            otherValues.length == 1 &&
182            thisValues[thisValues.length-1] == otherValues[0])
183        {
184            return this;
185        }
186
187        // Create the union array.
188        int newIndex = 0;
189        int[] newValues = new int[newLength];
190
191        // Is the length of the union array is equal to the sum of the lengths?
192        if (newLength == thisValues.length + otherValues.length)
193        {
194            // We can just copy all values, because they are unique.
195            System.arraycopy(thisValues, 0, newValues, 0, thisValues.length);
196
197            newIndex = thisValues.length;
198        }
199        else
200        {
201            // Copy the values that are different from the other array.
202            for (int index = 0; index < thisValues.length; index++)
203            {
204                if (!other.contains(thisValues[index]))
205                {
206                    newValues[newIndex++] = thisValues[index];
207                }
208            }
209        }
210
211        // Copy the values from the other array.
212        System.arraycopy(otherValues, 0, newValues, newIndex, otherValues.length);
213
214        return new InstructionOffsetValue(newValues);
215    }
216
217
218    // Implementations for Value.
219
220    public final InstructionOffsetValue instructionOffsetValue()
221    {
222        return this;
223    }
224
225    public boolean isSpecific()
226    {
227        return true;
228    }
229
230    public boolean isParticular()
231    {
232        return true;
233    }
234
235    public final Value generalize(Value other)
236    {
237        return this.generalize(other.instructionOffsetValue());
238    }
239
240    public final int computationalType()
241    {
242        return TYPE_INSTRUCTION_OFFSET;
243    }
244
245    public final String internalType()
246    {
247        return String.valueOf(ClassConstants.TYPE_INT);
248    }
249
250
251    // Implementations for Object.
252
253    public boolean equals(Object object)
254    {
255        if (object == null ||
256            this.getClass() != object.getClass())
257        {
258            return false;
259        }
260
261        InstructionOffsetValue other = (InstructionOffsetValue)object;
262        if (this.values == other.values)
263        {
264            return true;
265        }
266
267        if (this.values  == null ||
268            other.values == null ||
269            this.values.length != other.values.length)
270        {
271            return false;
272        }
273
274        for (int index = 0; index < other.values.length; index++)
275        {
276            if (!this.contains(other.values[index]))
277            {
278                return false;
279            }
280        }
281
282        return true;
283    }
284
285
286    public int hashCode()
287    {
288        int hashCode = this.getClass().hashCode();
289
290        if (values != null)
291        {
292            for (int index = 0; index < values.length; index++)
293            {
294                hashCode ^= values[index];
295            }
296        }
297
298        return hashCode;
299    }
300
301
302    public String toString()
303    {
304        StringBuffer buffer = new StringBuffer();
305
306        if (values != null)
307        {
308            for (int index = 0; index < values.length; index++)
309            {
310                if (index > 0)
311                {
312                    buffer.append(',');
313                }
314                buffer.append(values[index]);
315            }
316        }
317
318        return buffer.append(':').toString();
319    }
320}
321