1 21 package proguard.evaluation.value; 22 23 import proguard.classfile.*; 24 import proguard.classfile.visitor.ClassCollector; 25 import proguard.classfile.util.ClassUtil; 26 27 import java.util.*; 28 29 36 public class ReferenceValue extends Category1Value 37 { 38 private static final boolean DEBUG = false; 39 40 41 private String type; 42 private Clazz referencedClass; 43 private boolean mayBeNull; 44 45 46 49 public ReferenceValue(String type, 50 Clazz referencedClass, 51 boolean mayBeNull) 52 { 53 this.type = type; 54 this.referencedClass = referencedClass; 55 this.mayBeNull = mayBeNull; 56 } 57 58 59 62 public String getType() 63 { 64 return type; 65 } 66 67 68 71 public Clazz getReferencedClass() 72 { 73 return referencedClass; 74 } 75 76 77 79 82 public int isNull() 83 { 84 return type == null ? ALWAYS : 85 mayBeNull ? MAYBE : 86 NEVER; 87 } 88 89 90 93 public int instanceOf(String otherType, Clazz otherReferencedClass) 94 { 95 String thisType = this.type; 96 97 if (thisType == null) 99 { 100 return NEVER; 101 } 102 103 int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); 105 int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); 106 int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); 107 108 thisType = thisType.substring(commonDimensionCount); 110 otherType = otherType.substring(commonDimensionCount); 111 112 if (commonDimensionCount > 0 && 114 (ClassUtil.isInternalPrimitiveType(thisType.charAt(0)) || 115 ClassUtil.isInternalPrimitiveType(otherType.charAt(0)))) 116 { 117 return !thisType.equals(otherType) ? NEVER : 118 mayBeNull ? MAYBE : 119 ALWAYS; 120 } 121 122 if (thisDimensionCount == commonDimensionCount) 124 { 125 thisType = ClassUtil.internalClassNameFromClassType(thisType); 126 } 127 128 if (otherDimensionCount == commonDimensionCount) 130 { 131 otherType = ClassUtil.internalClassNameFromClassType(otherType); 132 } 133 134 if (thisDimensionCount > otherDimensionCount && 138 !ClassUtil.isInternalArrayInterfaceName(otherType)) 139 { 140 return NEVER; 141 } 142 143 if (thisDimensionCount < otherDimensionCount && 147 !ClassUtil.isInternalArrayInterfaceName(thisType)) 148 { 149 return NEVER; 150 } 151 152 if (mayBeNull) 154 { 155 return MAYBE; 156 } 157 158 if (thisType.equals(otherType) || 161 ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT.equals(otherType)) 162 { 163 return ALWAYS; 164 } 165 166 if (thisDimensionCount > otherDimensionCount) 168 { 169 return ALWAYS; 170 } 171 172 if (thisDimensionCount < otherDimensionCount) 174 { 175 return MAYBE; 176 } 177 178 return referencedClass != null && 180 otherReferencedClass != null && 181 referencedClass.extendsOrImplements(otherReferencedClass) ? 182 ALWAYS : 183 MAYBE; 184 } 185 186 187 190 public IntegerValue arrayLength(ValueFactory valueFactory) 191 { 192 return valueFactory.createIntegerValue(); 193 } 194 195 196 200 public Value arrayLoad(IntegerValue integerValue, ValueFactory valueFactory) 201 { 202 return 203 type == null ? ValueFactory.REFERENCE_VALUE_NULL : 204 !ClassUtil.isInternalArrayType(type) ? ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : 205 valueFactory.createValue(type.substring(1), 206 referencedClass, 207 true); 208 } 209 210 211 213 217 public ReferenceValue generalize(ReferenceValue other) 218 { 219 String thisType = this.type; 220 String otherType = other.type; 221 222 if (thisType == null && otherType == null) 224 { 225 return ValueFactory.REFERENCE_VALUE_NULL; 226 } 227 228 if (thisType == null) 230 { 231 return other.generalizeMayBeNull(true); 232 } 233 234 if (otherType == null) 236 { 237 return this.generalizeMayBeNull(true); 238 } 239 240 boolean mayBeNull = this.mayBeNull || other.mayBeNull; 241 242 if (thisType.equals(otherType)) 244 { 245 return this.generalizeMayBeNull(mayBeNull); 246 } 247 248 int thisDimensionCount = ClassUtil.internalArrayTypeDimensionCount(thisType); 250 int otherDimensionCount = ClassUtil.internalArrayTypeDimensionCount(otherType); 251 int commonDimensionCount = Math.min(thisDimensionCount, otherDimensionCount); 252 253 if (thisDimensionCount == otherDimensionCount) 254 { 255 Clazz thisReferencedClass = this.referencedClass; 257 Clazz otherReferencedClass = other.referencedClass; 258 259 if (thisReferencedClass != null && 260 otherReferencedClass != null) 261 { 262 if (thisReferencedClass.extendsOrImplements(otherReferencedClass)) 263 { 264 return other.generalizeMayBeNull(mayBeNull); 265 } 266 267 if (otherReferencedClass.extendsOrImplements(thisReferencedClass)) 268 { 269 return this.generalizeMayBeNull(mayBeNull); 270 } 271 272 Set thisSuperClasses = new HashSet(); 274 thisReferencedClass.hierarchyAccept(false, true, true, false, 275 new ClassCollector(thisSuperClasses)); 276 277 Set otherSuperClasses = new HashSet(); 279 otherReferencedClass.hierarchyAccept(false, true, true, false, 280 new ClassCollector(otherSuperClasses)); 281 282 if (DEBUG) 283 { 284 System.out.println("ReferenceValue.generalize this ["+thisReferencedClass.getName()+"] with other ["+otherReferencedClass.getName()+"]"); 285 System.out.println(" This super classes: "+thisSuperClasses); 286 System.out.println(" Other super classes: "+otherSuperClasses); 287 } 288 289 thisSuperClasses.retainAll(otherSuperClasses); 291 292 if (DEBUG) 293 { 294 System.out.println(" Common super classes: "+thisSuperClasses); 295 } 296 297 Clazz commonClazz = null; 300 301 int maximumSuperClassCount = -1; 302 303 Iterator commonSuperClasses = thisSuperClasses.iterator(); 307 while (commonSuperClasses.hasNext()) 308 { 309 Clazz commonSuperClass = (Clazz)commonSuperClasses.next(); 310 311 int superClassCount = superClassCount(commonSuperClass, thisSuperClasses); 312 if (maximumSuperClassCount < superClassCount || 313 (maximumSuperClassCount == superClassCount && 314 commonClazz.getName().compareTo(commonSuperClass.getName()) > 0)) 315 { 316 commonClazz = commonSuperClass; 317 maximumSuperClassCount = superClassCount; 318 } 319 } 320 321 if (commonClazz == null) 322 { 323 throw new IllegalArgumentException ("Can't find common super class of ["+thisType+"] and ["+otherType+"]"); 324 } 325 326 if (DEBUG) 327 { 328 System.out.println(" Best common class: ["+commonClazz.getName()+"]"); 329 } 330 331 333 return new ReferenceValue(commonDimensionCount == 0 ? 334 commonClazz.getName() : 335 ClassUtil.internalArrayTypeFromClassName(commonClazz.getName(), 336 commonDimensionCount), 337 commonClazz, 338 mayBeNull); 339 } 340 } 341 else if (thisDimensionCount > otherDimensionCount) 342 { 343 if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(otherType))) 345 { 346 return other.generalizeMayBeNull(mayBeNull); 347 } 348 } 349 else if (thisDimensionCount < otherDimensionCount) 350 { 351 if (ClassUtil.isInternalArrayInterfaceName(ClassUtil.internalClassNameFromClassType(thisType))) 353 { 354 return this.generalizeMayBeNull(mayBeNull); 355 } 356 } 357 358 if (commonDimensionCount > 0 && 361 (ClassUtil.isInternalPrimitiveType(otherType.charAt(commonDimensionCount))) || 362 ClassUtil.isInternalPrimitiveType(thisType.charAt(commonDimensionCount))) 363 { 364 commonDimensionCount--; 365 } 366 367 return commonDimensionCount == 0 ? 369 mayBeNull ? 370 ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_MAYBE_NULL : 371 ValueFactory.REFERENCE_VALUE_JAVA_LANG_OBJECT_NOT_NULL : 372 new ReferenceValue(ClassUtil.internalArrayTypeFromClassName(ClassConstants.INTERNAL_NAME_JAVA_LANG_OBJECT, 373 commonDimensionCount), 374 null, 375 mayBeNull); 376 } 377 378 379 383 private int superClassCount(Clazz subClass, Set classes) 384 { 385 int count = 0; 386 387 Iterator iterator = classes.iterator(); 388 389 while (iterator.hasNext()) 390 { 391 Clazz clazz = (Clazz)iterator.next(); 392 if (subClass.extendsOrImplements(clazz)) 393 { 394 count++; 395 } 396 } 397 398 400 return count; 401 } 402 403 404 409 public int equal(ReferenceValue other) 410 { 411 return this.type == null && other.type == null ? ALWAYS : MAYBE; 412 } 413 414 415 417 421 public final int isNotNull() 422 { 423 return -isNull(); 424 } 425 426 427 431 private ReferenceValue generalizeMayBeNull(boolean mayBeNull) 432 { 433 return this.mayBeNull || !mayBeNull ? 434 this : 435 new ReferenceValue(this.type, this.referencedClass, true); 436 } 437 438 439 441 445 public final int notEqual(ReferenceValue other) 446 { 447 return -equal(other); 448 } 449 450 451 453 public final ReferenceValue referenceValue() 454 { 455 return this; 456 } 457 458 public final Value generalize(Value other) 459 { 460 return this.generalize(other.referenceValue()); 461 } 462 463 public boolean isSpecific() 464 { 465 return type == null; 466 } 467 468 public final int computationalType() 469 { 470 return TYPE_REFERENCE; 471 } 472 473 public final String internalType() 474 { 475 return 476 type == null ? ClassConstants.INTERNAL_TYPE_JAVA_LANG_OBJECT : 477 ClassUtil.isInternalArrayType(type) ? type : 478 ClassConstants.INTERNAL_TYPE_CLASS_START + 479 type + 480 ClassConstants.INTERNAL_TYPE_CLASS_END; 481 } 482 483 484 486 public boolean equals(Object object) 487 { 488 if (object == null || 489 this.getClass() != object.getClass()) 490 { 491 return false; 492 } 493 494 ReferenceValue other = (ReferenceValue)object; 495 return this.type == null ? other.type == null : 496 (this.mayBeNull == other.mayBeNull && 497 this.type.equals(other.type)); 498 } 499 500 501 public int hashCode() 502 { 503 return this.getClass().hashCode() ^ 504 (type == null ? 0 : type.hashCode() ^ (mayBeNull ? 0 : 1)); 505 } 506 507 508 public String toString() 509 { 510 return "a:" + (type == null ? 511 "null" : 512 type + (referencedClass == null ? "?" : "") + (mayBeNull ? "" : "!")); 513 } 514 } 515 | Popular Tags |