1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric (at) 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.optimize.evaluation; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.*; 25 import proguard.classfile.attribute.visitor.*; 26 import proguard.classfile.constant.*; 27 import proguard.classfile.constant.visitor.ConstantVisitor; 28 import proguard.classfile.editor.*; 29 import proguard.classfile.instruction.visitor.InstructionVisitor; 30 import proguard.classfile.util.*; 31 import proguard.classfile.visitor.*; 32 import proguard.optimize.info.*; 33 34 /** 35 * This ClassVisitor simplifies the descriptors that contain simple enums in 36 * the program classes that it visits. 37 * 38 * @see SimpleEnumMarker 39 * @see MemberReferenceFixer 40 * @author Eric Lafortune 41 */ 42 public class SimpleEnumDescriptorSimplifier 43 extends SimplifiedVisitor 44 implements ClassVisitor, 45 ConstantVisitor, 46 MemberVisitor, 47 AttributeVisitor, 48 LocalVariableInfoVisitor, 49 LocalVariableTypeInfoVisitor 50 { 51 //* 52 private static final boolean DEBUG = false; 53 /*/ 54 private static boolean DEBUG = System.getProperty("enum") != null; 55 //*/ 56 57 58 // Implementations for ClassVisitor. 59 60 public void visitProgramClass(ProgramClass programClass) 61 { 62 if (DEBUG) 63 { 64 System.out.println("SimpleEnumDescriptorSimplifier: "+programClass.getName()); 65 } 66 67 // Simplify the class members. 68 programClass.fieldsAccept(this); 69 programClass.methodsAccept(this); 70 71 // Simplify the attributes. 72 //programClass.attributesAccept(this); 73 74 // Simplify the simple enum array constants. 75 programClass.constantPoolEntriesAccept(this); 76 } 77 78 79 // Implementations for ConstantVisitor. 80 81 public void visitAnyConstant(Clazz clazz, Constant constant) {} 82 83 84 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 85 { 86 // Does the constant refer to a simple enum type? 87 Clazz referencedClass = stringConstant.referencedClass; 88 if (isSimpleEnum(referencedClass)) 89 { 90 // Is it an array type? 91 String name = stringConstant.getString(clazz); 92 if (ClassUtil.isInternalArrayType(name)) 93 { 94 // Update the type. 95 ConstantPoolEditor constantPoolEditor = 96 new ConstantPoolEditor((ProgramClass)clazz); 97 98 String newName = simplifyDescriptor(name, referencedClass); 99 100 stringConstant.u2stringIndex = 101 constantPoolEditor.addUtf8Constant(newName); 102 103 // Clear the referenced class. 104 stringConstant.referencedClass = null; 105 } 106 } 107 } 108 109 110 public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) 111 { 112 // Update the descriptor if it has any simple enum classes. 113 String descriptor = invokeDynamicConstant.getType(clazz); 114 String newDescriptor = simplifyDescriptor(descriptor, invokeDynamicConstant.referencedClasses); 115 116 if (!descriptor.equals(newDescriptor)) 117 { 118 // Update the descriptor. 119 ConstantPoolEditor constantPoolEditor = 120 new ConstantPoolEditor((ProgramClass)clazz); 121 122 invokeDynamicConstant.u2nameAndTypeIndex = 123 constantPoolEditor.addNameAndTypeConstant(invokeDynamicConstant.getName(clazz), 124 newDescriptor); 125 126 // Update the referenced classes. 127 invokeDynamicConstant.referencedClasses = 128 simplifyReferencedClasses(descriptor, invokeDynamicConstant.referencedClasses); 129 } 130 } 131 132 133 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 134 { 135 // Does the constant refer to a simple enum type? 136 Clazz referencedClass = classConstant.referencedClass; 137 if (isSimpleEnum(referencedClass)) 138 { 139 // Is it an array type? 140 String name = classConstant.getName(clazz); 141 if (ClassUtil.isInternalArrayType(name)) 142 { 143 // Update the type. 144 ConstantPoolEditor constantPoolEditor = 145 new ConstantPoolEditor((ProgramClass)clazz); 146 147 String newName = simplifyDescriptor(name, referencedClass); 148 149 classConstant.u2nameIndex = 150 constantPoolEditor.addUtf8Constant(newName); 151 152 // Clear the referenced class. 153 classConstant.referencedClass = null; 154 } 155 } 156 } 157 158 159 // Implementations for MemberVisitor. 160 161 public void visitProgramField(ProgramClass programClass, ProgramField programField) 162 { 163 // Update the descriptor if it has a simple enum class. 164 String descriptor = programField.getDescriptor(programClass); 165 String newDescriptor = simplifyDescriptor(descriptor, programField.referencedClass); 166 167 if (!descriptor.equals(newDescriptor)) 168 { 169 String name = programField.getName(programClass); 170 String newName = name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); 171 172 if (DEBUG) 173 { 174 System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+" "+descriptor + "] -> ["+newName+" "+newDescriptor+"]"); 175 } 176 177 ConstantPoolEditor constantPoolEditor = 178 new ConstantPoolEditor(programClass); 179 180 // Update the name. 181 programField.u2nameIndex = 182 constantPoolEditor.addUtf8Constant(newName); 183 184 // Update the descriptor itself. 185 programField.u2descriptorIndex = 186 constantPoolEditor.addUtf8Constant(newDescriptor); 187 188 // Clear the referenced class. 189 programField.referencedClass = null; 190 191 // Clear the field value. 192 FieldOptimizationInfo fieldOptimizationInfo = 193 FieldOptimizationInfo.getFieldOptimizationInfo(programField); 194 if (fieldOptimizationInfo != null) 195 { 196 fieldOptimizationInfo.resetValue(programClass, programField); 197 } 198 199 // Simplify the signature. 200 programField.attributesAccept(programClass, this); 201 } 202 } 203 204 205 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 206 { 207 // // Skip the valueOf method. 208 // if (programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_VALUEOF)) 209 // { 210 // return; 211 // } 212 213 // Simplify the code, the signature, and the parameter annotations, 214 // before simplifying the descriptor. 215 programMethod.attributesAccept(programClass, this); 216 217 // Update the descriptor if it has any simple enum classes. 218 String descriptor = programMethod.getDescriptor(programClass); 219 String newDescriptor = simplifyDescriptor(descriptor, programMethod.referencedClasses); 220 221 if (!descriptor.equals(newDescriptor)) 222 { 223 String name = programMethod.getName(programClass); 224 String newName = name; 225 226 // Append a code, if the method isn't a class instance initializer. 227 if (!name.equals(ClassConstants.METHOD_NAME_INIT)) 228 { 229 newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); 230 } 231 232 if (DEBUG) 233 { 234 System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+descriptor+"] -> ["+newName+newDescriptor+"]"); 235 } 236 237 ConstantPoolEditor constantPoolEditor = 238 new ConstantPoolEditor(programClass); 239 240 // Update the name, if necessary. 241 if (!newName.equals(name)) 242 { 243 programMethod.u2nameIndex = 244 constantPoolEditor.addUtf8Constant(newName); 245 } 246 247 // Update the descriptor itself. 248 programMethod.u2descriptorIndex = 249 constantPoolEditor.addUtf8Constant(newDescriptor); 250 251 // Update the referenced classes. 252 programMethod.referencedClasses = 253 simplifyReferencedClasses(descriptor, programMethod.referencedClasses); 254 } 255 } 256 257 258 // Implementations for AttributeVisitor. 259 260 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 261 262 263 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 264 { 265 // Simplify the local variable descriptors. 266 codeAttribute.attributesAccept(clazz, method, this); 267 } 268 269 270 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 271 { 272 // Change the references of the local variables. 273 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 274 } 275 276 277 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 278 { 279 // Change the references of the local variables. 280 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 281 } 282 283 284 public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) 285 { 286 // We're only looking at the base type for now. 287 if (signatureAttribute.referencedClasses != null && 288 signatureAttribute.referencedClasses.length > 0) 289 { 290 // Update the signature if it has any simple enum classes. 291 String signature = signatureAttribute.getSignature(clazz); 292 String newSignature = simplifyDescriptor(signature, 293 signatureAttribute.referencedClasses[0]); 294 295 if (!signature.equals(newSignature)) 296 { 297 // Update the signature. 298 signatureAttribute.u2signatureIndex = 299 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); 300 301 // Clear the referenced class. 302 signatureAttribute.referencedClasses[0] = null; 303 } 304 } 305 } 306 307 308 public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) 309 { 310 // Compute the new signature. 311 String signature = signatureAttribute.getSignature(clazz); 312 String newSignature = simplifyDescriptor(signature, 313 signatureAttribute.referencedClasses); 314 315 if (!signature.equals(newSignature)) 316 { 317 // Update the signature. 318 signatureAttribute.u2signatureIndex = 319 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); 320 } 321 } 322 323 324 // Implementations for LocalVariableInfoVisitor. 325 326 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 327 { 328 // Update the descriptor if it has a simple enum class. 329 String descriptor = localVariableInfo.getDescriptor(clazz); 330 String newDescriptor = simplifyDescriptor(descriptor, localVariableInfo.referencedClass); 331 332 if (!descriptor.equals(newDescriptor)) 333 { 334 // Update the descriptor. 335 localVariableInfo.u2descriptorIndex = 336 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor); 337 338 // Clear the referenced class. 339 localVariableInfo.referencedClass = null; 340 } 341 } 342 343 344 // Implementations for LocalVariableTypeInfoVisitor. 345 346 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 347 { 348 // We're only looking at the base type for now. 349 if (localVariableTypeInfo.referencedClasses != null && 350 localVariableTypeInfo.referencedClasses.length > 0) 351 { 352 // Update the signature if it has any simple enum classes. 353 String signature = localVariableTypeInfo.getSignature(clazz); 354 String newSignature = simplifyDescriptor(signature, 355 localVariableTypeInfo.referencedClasses[0]); 356 357 if (!signature.equals(newSignature)) 358 { 359 // Update the signature. 360 localVariableTypeInfo.u2signatureIndex = 361 new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); 362 363 // Clear the referenced class. 364 localVariableTypeInfo.referencedClasses[0] = null; 365 } 366 } 367 } 368 369 370 // Small utility methods. 371 372 /** 373 * Returns the descriptor with simplified enum type. 374 */ 375 private String simplifyDescriptor(String descriptor, 376 Clazz referencedClass) 377 { 378 return isSimpleEnum(referencedClass) ? 379 descriptor.substring(0, ClassUtil.internalArrayTypeDimensionCount(descriptor)) + ClassConstants.TYPE_INT : 380 descriptor; 381 } 382 383 384 /** 385 * Returns the descriptor with simplified enum types. 386 */ 387 private String simplifyDescriptor(String descriptor, 388 Clazz[] referencedClasses) 389 { 390 if (referencedClasses != null) 391 { 392 InternalTypeEnumeration internalTypeEnumeration = 393 new InternalTypeEnumeration(descriptor); 394 395 int referencedClassIndex = 0; 396 397 StringBuffer newDescriptorBuffer = 398 new StringBuffer(descriptor.length()); 399 400 // Go over the formal type parameters. 401 { 402 // Consider the classes referenced by this formal type 403 // parameter. 404 String type = internalTypeEnumeration.formalTypeParameters(); 405 int count = new DescriptorClassEnumeration(type).classCount(); 406 407 // Replace simple enum types. 408 for (int counter = 0; counter < count; counter++) 409 { 410 Clazz referencedClass = 411 referencedClasses[referencedClassIndex++]; 412 413 if (isSimpleEnum(referencedClass)) 414 { 415 // Let's replace the simple enum type by 416 // java.lang.Integer. 417 type = 418 type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + 419 ClassConstants.NAME_JAVA_LANG_INTEGER; 420 } 421 } 422 423 newDescriptorBuffer.append(type); 424 } 425 426 newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_OPEN); 427 428 // Go over the parameters. 429 while (internalTypeEnumeration.hasMoreTypes()) 430 { 431 // Consider the classes referenced by this parameter type. 432 String type = internalTypeEnumeration.nextType(); 433 int count = new DescriptorClassEnumeration(type).classCount(); 434 435 // Replace simple enum types. 436 for (int counter = 0; counter < count; counter++) 437 { 438 Clazz referencedClass = 439 referencedClasses[referencedClassIndex++]; 440 441 if (isSimpleEnum(referencedClass)) 442 { 443 type = 444 type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + 445 ClassConstants.TYPE_INT; 446 } 447 } 448 449 newDescriptorBuffer.append(type); 450 } 451 452 newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_CLOSE); 453 454 // Go over the return value. 455 { 456 String type = internalTypeEnumeration.returnType(); 457 int count = new DescriptorClassEnumeration(type).classCount(); 458 459 // Replace simple enum types. 460 for (int counter = 0; counter < count; counter++) 461 { 462 Clazz referencedClass = 463 referencedClasses[referencedClassIndex++]; 464 465 if (isSimpleEnum(referencedClass)) 466 { 467 type = 468 type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + 469 ClassConstants.TYPE_INT; 470 } 471 } 472 473 newDescriptorBuffer.append(type); 474 } 475 476 descriptor = newDescriptorBuffer.toString(); 477 } 478 479 return descriptor; 480 } 481 482 483 /** 484 * Returns the simplified and shrunk array of referenced classes for the 485 * given descriptor. 486 */ 487 private Clazz[] simplifyReferencedClasses(String descriptor, 488 Clazz[] referencedClasses) 489 { 490 if (referencedClasses != null) 491 { 492 InternalTypeEnumeration internalTypeEnumeration = 493 new InternalTypeEnumeration(descriptor); 494 495 int referencedClassIndex = 0; 496 int newReferencedClassIndex = 0; 497 498 // Go over the formal type parameters. 499 { 500 String type = internalTypeEnumeration.formalTypeParameters(); 501 int count = new DescriptorClassEnumeration(type).classCount(); 502 503 // Clear all non-simple enum classes 504 // (now java.lang.Integer). 505 for (int counter = 0; counter < count; counter++) 506 { 507 Clazz referencedClass = 508 referencedClasses[referencedClassIndex++]; 509 510 referencedClasses[newReferencedClassIndex++] = 511 isSimpleEnum(referencedClass) ? null : referencedClass; 512 } 513 } 514 515 // Go over the parameters. 516 while (internalTypeEnumeration.hasMoreTypes()) 517 { 518 // Consider the classes referenced by this parameter type. 519 String type = internalTypeEnumeration.nextType(); 520 int count = new DescriptorClassEnumeration(type).classCount(); 521 522 // Copy all non-simple enum classes. 523 for (int counter = 0; counter < count; counter++) 524 { 525 Clazz referencedClass = 526 referencedClasses[referencedClassIndex++]; 527 528 if (!isSimpleEnum(referencedClass)) 529 { 530 referencedClasses[newReferencedClassIndex++] = 531 referencedClass; 532 } 533 } 534 } 535 536 // Go over the return type. 537 { 538 String type = internalTypeEnumeration.returnType(); 539 int count = new DescriptorClassEnumeration(type).classCount(); 540 541 // Copy all non-simple enum classes. 542 for (int counter = 0; counter < count; counter++) 543 { 544 Clazz referencedClass = 545 referencedClasses[referencedClassIndex++]; 546 547 if (!isSimpleEnum(referencedClass)) 548 { 549 referencedClasses[newReferencedClassIndex++] = 550 referencedClass; 551 } 552 } 553 } 554 555 // Shrink the array to the proper size. 556 if (newReferencedClassIndex == 0) 557 { 558 referencedClasses = null; 559 } 560 else if (newReferencedClassIndex < referencedClassIndex) 561 { 562 Clazz[] newReferencedClasses = new Clazz[newReferencedClassIndex]; 563 System.arraycopy(referencedClasses, 0, 564 newReferencedClasses, 0, 565 newReferencedClassIndex); 566 567 referencedClasses = newReferencedClasses; 568 } 569 } 570 571 return referencedClasses; 572 } 573 574 575 /** 576 * Returns whether the given class is not null and a simple enum class. 577 */ 578 private boolean isSimpleEnum(Clazz clazz) 579 { 580 return clazz != null && 581 SimpleEnumMarker.isSimpleEnum(clazz); 582 } 583 } 584