1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 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.AttributeVisitor; 28import proguard.classfile.constant.*; 29import proguard.classfile.editor.*; 30import proguard.classfile.util.*; 31import proguard.classfile.visitor.*; 32 33/** 34 * This ClassVisitor removes constant pool entries and class members that 35 * are not marked as being used. 36 * 37 * @see UsageMarker 38 * 39 * @author Eric Lafortune 40 */ 41public class ClassShrinker 42extends SimplifiedVisitor 43implements ClassVisitor, 44 MemberVisitor, 45 AttributeVisitor, 46 AnnotationVisitor, 47 ElementValueVisitor 48{ 49 private final UsageMarker usageMarker; 50 51 private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; 52 53 private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); 54 55 56 /** 57 * Creates a new ClassShrinker. 58 * @param usageMarker the usage marker that is used to mark the classes 59 * and class members. 60 */ 61 public ClassShrinker(UsageMarker usageMarker) 62 { 63 this.usageMarker = usageMarker; 64 } 65 66 67 // Implementations for ClassVisitor. 68 69 public void visitProgramClass(ProgramClass programClass) 70 { 71 // Shrink the arrays for constant pool, interfaces, fields, methods, 72 // and class attributes. 73 programClass.u2interfacesCount = 74 shrinkConstantIndexArray(programClass.constantPool, 75 programClass.u2interfaces, 76 programClass.u2interfacesCount); 77 78 // Shrinking the constant pool also sets up an index map. 79 programClass.u2constantPoolCount = 80 shrinkConstantPool(programClass.constantPool, 81 programClass.u2constantPoolCount); 82 83 programClass.u2fieldsCount = 84 shrinkArray(programClass.fields, 85 programClass.u2fieldsCount); 86 87 programClass.u2methodsCount = 88 shrinkArray(programClass.methods, 89 programClass.u2methodsCount); 90 91 programClass.u2attributesCount = 92 shrinkArray(programClass.attributes, 93 programClass.u2attributesCount); 94 95 // Compact the remaining fields, methods, and attributes, 96 // and remap their references to the constant pool. 97 programClass.fieldsAccept(this); 98 programClass.methodsAccept(this); 99 programClass.attributesAccept(this); 100 101 // Remap all constant pool references. 102 constantPoolRemapper.setConstantIndexMap(constantIndexMap); 103 constantPoolRemapper.visitProgramClass(programClass); 104 105 // Remove the unused interfaces from the class signature. 106 programClass.attributesAccept(new SignatureShrinker()); 107 108 // Compact the extra field pointing to the subclasses of this class. 109 programClass.subClasses = 110 shrinkToNewArray(programClass.subClasses); 111 } 112 113 114 public void visitLibraryClass(LibraryClass libraryClass) 115 { 116 // Library classes are left unchanged. 117 118 // Compact the extra field pointing to the subclasses of this class. 119 libraryClass.subClasses = 120 shrinkToNewArray(libraryClass.subClasses); 121 } 122 123 124 // Implementations for MemberVisitor. 125 126 public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) 127 { 128 // Shrink the attributes array. 129 programMember.u2attributesCount = 130 shrinkArray(programMember.attributes, 131 programMember.u2attributesCount); 132 133 // Shrink any attributes. 134 programMember.attributesAccept(programClass, this); 135 } 136 137 138 // Implementations for AttributeVisitor. 139 140 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 141 142 143 public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) 144 { 145 // Shrink the array of InnerClassesInfo objects. 146 innerClassesAttribute.u2classesCount = 147 shrinkArray(innerClassesAttribute.classes, 148 innerClassesAttribute.u2classesCount); 149 } 150 151 152 public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) 153 { 154 // Sometimes, a class is still referenced (apparently as a dummy class), 155 // but its enclosing method is not. Then remove the reference to 156 // the enclosing method. 157 // E.g. the anonymous inner class javax.swing.JList$1 is defined inside 158 // a constructor of javax.swing.JList, but it is also referenced as a 159 // dummy argument in a constructor of javax.swing.JList$ListSelectionHandler. 160 if (enclosingMethodAttribute.referencedMethod != null && 161 !usageMarker.isUsed(enclosingMethodAttribute.referencedMethod)) 162 { 163 enclosingMethodAttribute.u2nameAndTypeIndex = 0; 164 165 enclosingMethodAttribute.referencedMethod = null; 166 } 167 } 168 169 170 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 171 { 172 // Shrink the attributes array. 173 codeAttribute.u2attributesCount = 174 shrinkArray(codeAttribute.attributes, 175 codeAttribute.u2attributesCount); 176 } 177 178 179 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 180 { 181 // Shrink the annotations array. 182 annotationsAttribute.u2annotationsCount = 183 shrinkArray(annotationsAttribute.annotations, 184 annotationsAttribute.u2annotationsCount); 185 186 // Shrink the annotations themselves. 187 annotationsAttribute.annotationsAccept(clazz, this); 188 } 189 190 191 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 192 { 193 // Loop over all parameters. 194 for (int parameterIndex = 0; parameterIndex < parameterAnnotationsAttribute.u2parametersCount; parameterIndex++) 195 { 196 // Shrink the parameter annotations array. 197 parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex] = 198 shrinkArray(parameterAnnotationsAttribute.parameterAnnotations[parameterIndex], 199 parameterAnnotationsAttribute.u2parameterAnnotationsCount[parameterIndex]); 200 } 201 202 // Shrink the annotations themselves. 203 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 204 } 205 206 207 // Implementations for AnnotationVisitor. 208 209 public void visitAnnotation(Clazz clazz, Annotation annotation) 210 { 211 // Shrink the element values array. 212 annotation.u2elementValuesCount = 213 shrinkArray(annotation.elementValues, 214 annotation.u2elementValuesCount); 215 216 // Shrink the element values themselves. 217 annotation.elementValuesAccept(clazz, this); 218 } 219 220 221 /** 222 * This AttributeVisitor updates the Utf8 constants of class signatures, 223 * removing any unused interfaces. 224 */ 225 private class SignatureShrinker 226 extends SimplifiedVisitor 227 implements AttributeVisitor 228 { 229 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 230 231 232 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 233 { 234 Clazz[] referencedClasses = signatureAttribute.referencedClasses; 235 if (referencedClasses != null) 236 { 237 // Go over the generic definitions, superclass and implemented interfaces. 238 String signature = clazz.getString(signatureAttribute.u2signatureIndex); 239 240 InternalTypeEnumeration internalTypeEnumeration = 241 new InternalTypeEnumeration(signature); 242 243 StringBuffer newSignatureBuffer = new StringBuffer(); 244 245 int referencedClassIndex = 0; 246 int newReferencedClassIndex = 0; 247 248 while (internalTypeEnumeration.hasMoreTypes()) 249 { 250 // Consider the classes referenced by this signature. 251 String type = internalTypeEnumeration.nextType(); 252 int classCount = new DescriptorClassEnumeration(type).classCount(); 253 254 Clazz referencedClass = referencedClasses[referencedClassIndex]; 255 if (referencedClass == null || 256 usageMarker.isUsed(referencedClass)) 257 { 258 // Append the superclass or interface. 259 newSignatureBuffer.append(type); 260 261 // Copy the referenced classes. 262 for (int counter = 0; counter < classCount; counter++) 263 { 264 referencedClasses[newReferencedClassIndex++] = 265 referencedClasses[referencedClassIndex++]; 266 } 267 } 268 else 269 { 270 // Skip the referenced classes. 271 referencedClassIndex += classCount; 272 } 273 } 274 275 if (newReferencedClassIndex < referencedClassIndex) 276 { 277 // Update the signature. 278 ((Utf8Constant)((ProgramClass)clazz).constantPool[signatureAttribute.u2signatureIndex]).setString(newSignatureBuffer.toString()); 279 280 // Clear the unused entries. 281 while (newReferencedClassIndex < referencedClassIndex) 282 { 283 referencedClasses[newReferencedClassIndex++] = null; 284 } 285 } 286 } 287 } 288 } 289 290 291 // Implementations for ElementValueVisitor. 292 293 public void visitAnyElementValue(Clazz clazz, Annotation annotation, ElementValue elementValue) {} 294 295 296 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 297 { 298 // Shrink the contained annotation. 299 annotationElementValue.annotationAccept(clazz, this); 300 } 301 302 303 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 304 { 305 // Shrink the element values array. 306 arrayElementValue.u2elementValuesCount = 307 shrinkArray(arrayElementValue.elementValues, 308 arrayElementValue.u2elementValuesCount); 309 310 // Shrink the element values themselves. 311 arrayElementValue.elementValuesAccept(clazz, annotation, this); 312 } 313 314 315 // Small utility methods. 316 317 /** 318 * Removes all entries that are not marked as being used from the given 319 * constant pool. 320 * @return the new number of entries. 321 */ 322 private int shrinkConstantPool(Constant[] constantPool, int length) 323 { 324 if (constantIndexMap.length < length) 325 { 326 constantIndexMap = new int[length]; 327 } 328 329 int counter = 1; 330 boolean isUsed = false; 331 332 // Shift the used constant pool entries together. 333 for (int index = 1; index < length; index++) 334 { 335 constantIndexMap[index] = counter; 336 337 Constant constant = constantPool[index]; 338 339 // Don't update the flag if this is the second half of a long entry. 340 if (constant != null) 341 { 342 isUsed = usageMarker.isUsed(constant); 343 } 344 345 if (isUsed) 346 { 347 constantPool[counter++] = constant; 348 } 349 } 350 351 // Clear the remaining constant pool elements. 352 for (int index = counter; index < length; index++) 353 { 354 constantPool[index] = null; 355 } 356 357 return counter; 358 } 359 360 361 /** 362 * Removes all indices that point to unused constant pool entries 363 * from the given array. 364 * @return the new number of indices. 365 */ 366 private int shrinkConstantIndexArray(Constant[] constantPool, int[] array, int length) 367 { 368 int counter = 0; 369 370 // Shift the used objects together. 371 for (int index = 0; index < length; index++) 372 { 373 if (usageMarker.isUsed(constantPool[array[index]])) 374 { 375 array[counter++] = array[index]; 376 } 377 } 378 379 // Clear the remaining array elements. 380 for (int index = counter; index < length; index++) 381 { 382 array[index] = 0; 383 } 384 385 return counter; 386 } 387 388 389 /** 390 * Removes all Clazz objects that are not marked as being used 391 * from the given array and returns the remaining objects in a an array 392 * of the right size. 393 * @return the new array. 394 */ 395 private Clazz[] shrinkToNewArray(Clazz[] array) 396 { 397 if (array == null) 398 { 399 return null; 400 } 401 402 // Shrink the given array in-place. 403 int length = shrinkArray(array, array.length); 404 if (length == 0) 405 { 406 return null; 407 } 408 409 // Return immediately if the array is of right size already. 410 if (length == array.length) 411 { 412 return array; 413 } 414 415 // Copy the remaining elements into a new array of the right size. 416 Clazz[] newArray = new Clazz[length]; 417 System.arraycopy(array, 0, newArray, 0, length); 418 return newArray; 419 } 420 421 422 /** 423 * Removes all VisitorAccepter objects that are not marked as being used 424 * from the given array. 425 * @return the new number of VisitorAccepter objects. 426 */ 427 private int shrinkArray(VisitorAccepter[] array, int length) 428 { 429 int counter = 0; 430 431 // Shift the used objects together. 432 for (int index = 0; index < length; index++) 433 { 434 if (usageMarker.isUsed(array[index])) 435 { 436 array[counter++] = array[index]; 437 } 438 } 439 440 // Clear the remaining array elements. 441 for (int index = counter; index < length; index++) 442 { 443 array[index] = null; 444 } 445 446 return counter; 447 } 448} 449