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.shrink; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.annotation.*; 26import proguard.classfile.attribute.annotation.visitor.*; 27import proguard.classfile.attribute.visitor.*; 28import proguard.classfile.constant.*; 29import proguard.classfile.editor.*; 30import proguard.classfile.util.*; 31import proguard.classfile.visitor.*; 32 33import java.util.Arrays; 34 35/** 36 * This ClassVisitor removes constant pool entries, class members, and other 37 * class elements that are not marked as being used. 38 * 39 * @see UsageMarker 40 * 41 * @author Eric Lafortune 42 */ 43public class ClassShrinker 44extends SimplifiedVisitor 45implements ClassVisitor, 46 MemberVisitor, 47 AttributeVisitor, 48 AnnotationVisitor, 49 ElementValueVisitor 50{ 51 private final UsageMarker usageMarker; 52 53 private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; 54 private int[] bootstrapMethodIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; 55 private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); 56 private final BootstrapMethodRemapper bootstrapMethodRemapper = new BootstrapMethodRemapper(); 57 58 59 /** 60 * Creates a new ClassShrinker. 61 * @param usageMarker the usage marker that is used to mark the classes 62 * and class members. 63 */ 64 public ClassShrinker(UsageMarker usageMarker) 65 { 66 this.usageMarker = usageMarker; 67 } 68 69 70 // Implementations for ClassVisitor. 71 72 public void visitProgramClass(ProgramClass programClass) 73 { 74 // Shrink the arrays for constant pool, interfaces, fields, methods, 75 // and class attributes. 76 if (programClass.u2interfacesCount > 0) 77 { 78 new InterfaceDeleter(shrinkFlags(programClass.constantPool, 79 programClass.u2interfaces, 80 programClass.u2interfacesCount)) 81 .visitProgramClass(programClass); 82 } 83 84 // Shrinking the constant pool also sets up an index map. 85 int newConstantPoolCount = 86 shrinkConstantPool(programClass.constantPool, 87 programClass.u2constantPoolCount); 88 89 programClass.u2fieldsCount = 90 shrinkArray(programClass.fields, 91 programClass.u2fieldsCount); 92 93 programClass.u2methodsCount = 94 shrinkArray(programClass.methods, 95 programClass.u2methodsCount); 96 97 programClass.u2attributesCount = 98 shrinkArray(programClass.attributes, 99 programClass.u2attributesCount); 100 101 // Compact the remaining fields, methods, and attributes, 102 // and remap their references to the constant pool. 103 programClass.fieldsAccept(this); 104 programClass.methodsAccept(this); 105 programClass.attributesAccept(this); 106 107 // Remap the references to the constant pool if it has shrunk. 108 if (newConstantPoolCount < programClass.u2constantPoolCount) 109 { 110 programClass.u2constantPoolCount = newConstantPoolCount; 111 112 // Remap all constant pool references. 113 constantPoolRemapper.setConstantIndexMap(constantIndexMap); 114 constantPoolRemapper.visitProgramClass(programClass); 115 } 116 117 // Replace any unused classes in the signatures. 118 MySignatureCleaner signatureCleaner = new MySignatureCleaner(); 119 programClass.fieldsAccept(new AllAttributeVisitor(signatureCleaner)); 120 programClass.methodsAccept(new AllAttributeVisitor(signatureCleaner)); 121 programClass.attributesAccept(signatureCleaner); 122 123 // Compact the extra field pointing to the subclasses of this class. 124 programClass.subClasses = 125 shrinkToNewArray(programClass.subClasses); 126 } 127 128 129 public void visitLibraryClass(LibraryClass libraryClass) 130 { 131 // Library classes are left unchanged. 132 133 // Compact the extra field pointing to the subclasses of this class. 134 libraryClass.subClasses = 135 shrinkToNewArray(libraryClass.subClasses); 136 } 137 138 139 // Implementations for MemberVisitor. 140 141 public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) 142 { 143 // Shrink the attributes array. 144 programMember.u2attributesCount = 145 shrinkArray(programMember.attributes, 146 programMember.u2attributesCount); 147 148 // Shrink any attributes. 149 programMember.attributesAccept(programClass, this); 150 } 151 152 153 // Implementations for AttributeVisitor. 154 155 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 156 157 158 public void visitBootstrapMethodsAttribute(Clazz clazz, BootstrapMethodsAttribute bootstrapMethodsAttribute) 159 { 160 // Shrink the array of BootstrapMethodInfo objects. 161 int newBootstrapMethodsCount = 162 shrinkBootstrapMethodArray(bootstrapMethodsAttribute.bootstrapMethods, 163 bootstrapMethodsAttribute.u2bootstrapMethodsCount); 164 165 if (newBootstrapMethodsCount < bootstrapMethodsAttribute.u2bootstrapMethodsCount) 166 { 167 bootstrapMethodsAttribute.u2bootstrapMethodsCount = newBootstrapMethodsCount; 168 169 // Remap all constant pool references. 170 bootstrapMethodRemapper.setConstantIndexMap(bootstrapMethodIndexMap); 171 clazz.constantPoolEntriesAccept(bootstrapMethodRemapper); 172 } 173 } 174 175 176 public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) 177 { 178 // Shrink the array of InnerClassesInfo objects. 179 innerClassesAttribute.u2classesCount = 180 shrinkArray(innerClassesAttribute.classes, 181 innerClassesAttribute.u2classesCount); 182 } 183 184 185 public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) 186 { 187 // Sometimes, a class is still referenced (apparently as a dummy class), 188 // but its enclosing method is not. Then remove the reference to 189 // the enclosing method. 190 // E.g. the anonymous inner class javax.swing.JList$1 is defined inside 191 // a constructor of javax.swing.JList, but it is also referenced as a 192 // dummy argument in a constructor of javax.swing.JList$ListSelectionHandler. 193 if (enclosingMethodAttribute.referencedMethod != null && 194 !usageMarker.isUsed(enclosingMethodAttribute.referencedMethod)) 195 { 196 enclosingMethodAttribute.u2nameAndTypeIndex = 0; 197 198 enclosingMethodAttribute.referencedMethod = null; 199 } 200 } 201 202 203 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 204 { 205 // Shrink the attributes array. 206 codeAttribute.u2attributesCount = 207 shrinkArray(codeAttribute.attributes, 208 codeAttribute.u2attributesCount); 209 210 // Shrink the attributes themselves. 211 codeAttribute.attributesAccept(clazz, method, this); 212 } 213 214 215 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 216 { 217 // Shrink the local variable info array. 218 localVariableTableAttribute.u2localVariableTableLength = 219 shrinkArray(localVariableTableAttribute.localVariableTable, 220 localVariableTableAttribute.u2localVariableTableLength); 221 } 222 223 224 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 225 { 226 // Shrink the local variable type info array. 227 localVariableTypeTableAttribute.u2localVariableTypeTableLength = 228 shrinkArray(localVariableTypeTableAttribute.localVariableTypeTable, 229 localVariableTypeTableAttribute.u2localVariableTypeTableLength); 230 } 231 232 233 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 234 { 235 // Shrink the annotations array. 236 annotationsAttribute.u2annotationsCount = 237 shrinkArray(annotationsAttribute.annotations, 238 annotationsAttribute.u2annotationsCount); 239 240 // Shrink the annotations themselves. 241 annotationsAttribute.annotationsAccept(clazz, this); 242 } 243 244 245 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 246 { 247 // Loop over all parameters. 248 for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u1parametersCount; parameterIndex++) 249 { 250 // Shrink the parameter annotations array. 251 parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex] = 252 shrinkArray(parameterAnnotationsAttribute.parameterAnnotations[parameterIndex], 253 parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]); 254 } 255 256 // Shrink the annotations themselves. 257 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 258 } 259 260 261 // Implementations for AnnotationVisitor. 262 263 public void visitAnnotation(Clazz clazz, Annotation annotation) 264 { 265 // Shrink the element values array. 266 annotation.u2elementValuesCount = 267 shrinkArray(annotation.elementValues, 268 annotation.u2elementValuesCount); 269 270 // Shrink the element values themselves. 271 annotation.elementValuesAccept(clazz, this); 272 } 273 274 275 /** 276 * This AttributeVisitor updates the Utf8 constants of signatures 277 * of classes, fields, and methods. 278 */ 279 private class MySignatureCleaner 280 extends SimplifiedVisitor 281 implements AttributeVisitor 282 { 283 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 284 285 286 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 287 { 288 Clazz[] referencedClasses = signatureAttribute.referencedClasses; 289 if (referencedClasses != null) 290 { 291 // Go over the classes in the signature. 292 String signature = signatureAttribute.getSignature(clazz); 293 294 DescriptorClassEnumeration classEnumeration = 295 new DescriptorClassEnumeration(signature); 296 297 int referencedClassIndex = 0; 298 299 // Start construction a new signature. 300 StringBuffer newSignatureBuffer = new StringBuffer(); 301 302 newSignatureBuffer.append(classEnumeration.nextFluff()); 303 304 while (classEnumeration.hasMoreClassNames()) 305 { 306 String className = classEnumeration.nextClassName(); 307 308 // Replace the class name if it is unused. 309 Clazz referencedClass = referencedClasses[referencedClassIndex]; 310 if (referencedClass != null && 311 !usageMarker.isUsed(referencedClass)) 312 { 313 className = ClassConstants.NAME_JAVA_LANG_OBJECT; 314 315 referencedClasses[referencedClassIndex] = null; 316 } 317 318 referencedClassIndex++; 319 320 newSignatureBuffer.append(className); 321 newSignatureBuffer.append(classEnumeration.nextFluff()); 322 } 323 324 // Update the signature. 325 ((Utf8Constant)((ProgramClass)clazz).constantPool[signatureAttribute.u2signatureIndex]).setString(newSignatureBuffer.toString()); 326 } 327 } 328 } 329 330 331 // Implementations for ElementValueVisitor. 332 333 public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {} 334 335 336 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 337 { 338 // Shrink the contained annotation. 339 annotationElementValue.annotationAccept(clazz, this); 340 } 341 342 343 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 344 { 345 // Shrink the element values array. 346 arrayElementValue.u2elementValuesCount = 347 shrinkArray(arrayElementValue.elementValues, 348 arrayElementValue.u2elementValuesCount); 349 350 // Shrink the element values themselves. 351 arrayElementValue.elementValuesAccept(clazz, annotation, this); 352 } 353 354 355 // Small utility methods. 356 357 /** 358 * Removes all entries that are not marked as being used from the given 359 * constant pool. Creates a map from the old indices to the new indices 360 * as a side effect. 361 * @return the new number of entries. 362 */ 363 private int shrinkConstantPool(Constant[] constantPool, int length) 364 { 365 if (constantIndexMap.length < length) 366 { 367 constantIndexMap = new int[length]; 368 } 369 370 int counter = 1; 371 boolean isUsed = false; 372 373 // Shift the used constant pool entries together. 374 for (int index = 1; index < length; index++) 375 { 376 constantIndexMap[index] = counter; 377 378 Constant constant = constantPool[index]; 379 380 // Is the constant being used? Don't update the flag if this is the 381 // second half of a long entry. 382 if (constant != null) 383 { 384 isUsed = usageMarker.isUsed(constant); 385 } 386 387 if (isUsed) 388 { 389 // Remember the new index. 390 constantIndexMap[index] = counter; 391 392 // Shift the constant pool entry. 393 constantPool[counter++] = constant; 394 } 395 else 396 { 397 // Remember an invalid index. 398 constantIndexMap[index] = -1; 399 } 400 } 401 402 // Clear the remaining constant pool elements. 403 Arrays.fill(constantPool, counter, length, null); 404 405 return counter; 406 } 407 408 409 /** 410 * Creates an array marking unused constant pool entries for all the 411 * elements in the given array of constant pool indices. 412 * @return an array of flags indicating unused elements. 413 */ 414 private boolean[] shrinkFlags(Constant[] constantPool, int[] array, int length) 415 { 416 boolean[] unused = new boolean[length]; 417 418 // Shift the used objects together. 419 for (int index = 0; index < length; index++) 420 { 421 if (!usageMarker.isUsed(constantPool[array[index]])) 422 { 423 unused[index] = true; 424 } 425 } 426 427 return unused; 428 } 429 430 431 /** 432 * Removes all indices that point to unused constant pool entries 433 * from the given array. 434 * @return the new number of indices. 435 */ 436 private int shrinkConstantIndexArray(Constant[] constantPool, int[] array, int length) 437 { 438 int counter = 0; 439 440 // Shift the used objects together. 441 for (int index = 0; index < length; index++) 442 { 443 if (usageMarker.isUsed(constantPool[array[index]])) 444 { 445 array[counter++] = array[index]; 446 } 447 } 448 449 // Clear the remaining array elements. 450 Arrays.fill(array, counter, length, 0); 451 452 return counter; 453 } 454 455 456 /** 457 * Removes all Clazz objects that are not marked as being used 458 * from the given array and returns the remaining objects in a an array 459 * of the right size. 460 * @return the new array. 461 */ 462 private Clazz[] shrinkToNewArray(Clazz[] array) 463 { 464 if (array == null) 465 { 466 return null; 467 } 468 469 // Shrink the given array in-place. 470 int length = shrinkArray(array, array.length); 471 if (length == 0) 472 { 473 return null; 474 } 475 476 // Return immediately if the array is of right size already. 477 if (length == array.length) 478 { 479 return array; 480 } 481 482 // Copy the remaining elements into a new array of the right size. 483 Clazz[] newArray = new Clazz[length]; 484 System.arraycopy(array, 0, newArray, 0, length); 485 return newArray; 486 } 487 488 489 /** 490 * Removes all entries that are not marked as being used from the given 491 * array of bootstrap methods. Creates a map from the old indices to the 492 * new indices as a side effect. 493 * @return the new number of entries. 494 */ 495 private int shrinkBootstrapMethodArray(BootstrapMethodInfo[] bootstrapMethods, int length) 496 { 497 if (bootstrapMethodIndexMap.length < length) 498 { 499 bootstrapMethodIndexMap = new int[length]; 500 } 501 502 int counter = 0; 503 504 // Shift the used bootstrap methods together. 505 for (int index = 0; index < length; index++) 506 { 507 BootstrapMethodInfo bootstrapMethod = bootstrapMethods[index]; 508 509 // Is the entry being used? 510 if (usageMarker.isUsed(bootstrapMethod)) 511 { 512 // Remember the new index. 513 bootstrapMethodIndexMap[index] = counter; 514 515 // Shift the entry. 516 bootstrapMethods[counter++] = bootstrapMethod; 517 } 518 else 519 { 520 // Remember an invalid index. 521 bootstrapMethodIndexMap[index] = -1; 522 } 523 } 524 525 // Clear the remaining bootstrap methods. 526 Arrays.fill(bootstrapMethods, counter, length, null); 527 528 return counter; 529 } 530 531 532 /** 533 * Removes all VisitorAccepter objects that are not marked as being used 534 * from the given array. 535 * @return the new number of VisitorAccepter objects. 536 */ 537 private int shrinkArray(VisitorAccepter[] array, int length) 538 { 539 int counter = 0; 540 541 // Shift the used objects together. 542 for (int index = 0; index < length; index++) 543 { 544 VisitorAccepter visitorAccepter = array[index]; 545 546 if (usageMarker.isUsed(visitorAccepter)) 547 { 548 array[counter++] = visitorAccepter; 549 } 550 } 551 552 // Clear any remaining array elements. 553 if (counter < length) 554 { 555 Arrays.fill(array, counter, length, null); 556 } 557 558 return counter; 559 } 560} 561