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 */ 21 package proguard.evaluation.value; 22 23 import 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 */ 31 public class InstructionOffsetValue extends Category1Value 32 { 33 public static final InstructionOffsetValue EMPTY_VALUE = new InstructionOffsetValue(); 34 35 36 private int[] values; 37 38 InstructionOffsetValue()39 private InstructionOffsetValue() 40 { 41 } 42 43 InstructionOffsetValue(int value)44 public InstructionOffsetValue(int value) 45 { 46 this.values = new int[] { value }; 47 } 48 49 InstructionOffsetValue(int[] values)50 public InstructionOffsetValue(int[] values) 51 { 52 this.values = values; 53 } 54 55 instructionOffsetCount()56 public int instructionOffsetCount() 57 { 58 return values == null ? 0 : values.length; 59 } 60 61 instructionOffset(int index)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 */ contains(int value)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 */ minimumValue()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 */ maximumValue()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 */ generalize(InstructionOffsetValue other)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 instructionOffsetValue()220 public final InstructionOffsetValue instructionOffsetValue() 221 { 222 return this; 223 } 224 isSpecific()225 public boolean isSpecific() 226 { 227 return true; 228 } 229 isParticular()230 public boolean isParticular() 231 { 232 return true; 233 } 234 generalize(Value other)235 public final Value generalize(Value other) 236 { 237 return this.generalize(other.instructionOffsetValue()); 238 } 239 computationalType()240 public final int computationalType() 241 { 242 return TYPE_INSTRUCTION_OFFSET; 243 } 244 internalType()245 public final String internalType() 246 { 247 return String.valueOf(ClassConstants.TYPE_INT); 248 } 249 250 251 // Implementations for Object. 252 equals(Object object)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 hashCode()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 toString()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