Utf8Shrinker.java revision 8a6199f0c36a778f22394364347a301b0b28e94b
1/* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2013 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.classfile.editor; 22 23import proguard.classfile.*; 24import proguard.classfile.attribute.*; 25import proguard.classfile.attribute.annotation.*; 26import proguard.classfile.attribute.annotation.visitor.*; 27import proguard.classfile.attribute.preverification.*; 28import proguard.classfile.attribute.visitor.*; 29import proguard.classfile.constant.*; 30import proguard.classfile.constant.visitor.ConstantVisitor; 31import proguard.classfile.editor.ConstantPoolRemapper; 32import proguard.classfile.util.SimplifiedVisitor; 33import proguard.classfile.visitor.*; 34 35import java.util.Arrays; 36 37 38/** 39 * This ClassVisitor removes UTF-8 constant pool entries that are not used. 40 * 41 * @author Eric Lafortune 42 */ 43public class Utf8Shrinker 44extends SimplifiedVisitor 45implements ClassVisitor, 46 MemberVisitor, 47 ConstantVisitor, 48 AttributeVisitor, 49 InnerClassesInfoVisitor, 50 LocalVariableInfoVisitor, 51 LocalVariableTypeInfoVisitor, 52 AnnotationVisitor, 53 ElementValueVisitor 54{ 55 // A visitor info flag to indicate the UTF-8 constant pool entry is being used. 56 private static final Object USED = new Object(); 57 58 private int[] constantIndexMap = new int[ClassConstants.TYPICAL_CONSTANT_POOL_SIZE]; 59 private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); 60 61 62 // Implementations for ClassVisitor. 63 64 public void visitProgramClass(ProgramClass programClass) 65 { 66 // Mark the UTF-8 entries referenced by the other constant pool entries. 67 programClass.constantPoolEntriesAccept(this); 68 69 // Mark the UTF-8 entries referenced by the fields and methods. 70 programClass.fieldsAccept(this); 71 programClass.methodsAccept(this); 72 73 // Mark the UTF-8 entries referenced by the attributes. 74 programClass.attributesAccept(this); 75 76 // Shift the used constant pool entries together, filling out the 77 // index map. 78 int newConstantPoolCount = 79 shrinkConstantPool(programClass.constantPool, 80 programClass.u2constantPoolCount); 81 82 // Remap the references to the constant pool if it has shrunk. 83 if (newConstantPoolCount < programClass.u2constantPoolCount) 84 { 85 programClass.u2constantPoolCount = newConstantPoolCount; 86 87 // Remap all constant pool references. 88 constantPoolRemapper.setConstantIndexMap(constantIndexMap); 89 constantPoolRemapper.visitProgramClass(programClass); 90 } 91 } 92 93 94 // Implementations for MemberVisitor. 95 96 public void visitProgramMember(ProgramClass programClass, ProgramMember programMember) 97 { 98 // Mark the name and descriptor UTF-8 entries. 99 markCpUtf8Entry(programClass, programMember.u2nameIndex); 100 markCpUtf8Entry(programClass, programMember.u2descriptorIndex); 101 102 // Mark the UTF-8 entries referenced by the attributes. 103 programMember.attributesAccept(programClass, this); 104 } 105 106 107 // Implementations for ConstantVisitor. 108 109 public void visitAnyConstant(Clazz clazz, Constant constant) {} 110 111 112 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 113 { 114 markCpUtf8Entry(clazz, stringConstant.u2stringIndex); 115 } 116 117 118 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 119 { 120 markCpUtf8Entry(clazz, classConstant.u2nameIndex); 121 } 122 123 124 public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) 125 { 126 markCpUtf8Entry(clazz, nameAndTypeConstant.u2nameIndex); 127 markCpUtf8Entry(clazz, nameAndTypeConstant.u2descriptorIndex); 128 } 129 130 131 // Implementations for AttributeVisitor. 132 133 public void visitUnknownAttribute(Clazz clazz, UnknownAttribute unknownAttribute) 134 { 135 // This is the best we can do for unknown attributes. 136 markCpUtf8Entry(clazz, unknownAttribute.u2attributeNameIndex); 137 } 138 139 140 public void visitSourceFileAttribute(Clazz clazz, SourceFileAttribute sourceFileAttribute) 141 { 142 markCpUtf8Entry(clazz, sourceFileAttribute.u2attributeNameIndex); 143 144 markCpUtf8Entry(clazz, sourceFileAttribute.u2sourceFileIndex); 145 } 146 147 148 public void visitSourceDirAttribute(Clazz clazz, SourceDirAttribute sourceDirAttribute) 149 { 150 markCpUtf8Entry(clazz, sourceDirAttribute.u2attributeNameIndex); 151 152 markCpUtf8Entry(clazz, sourceDirAttribute.u2sourceDirIndex); 153 } 154 155 156 public void visitInnerClassesAttribute(Clazz clazz, InnerClassesAttribute innerClassesAttribute) 157 { 158 markCpUtf8Entry(clazz, innerClassesAttribute.u2attributeNameIndex); 159 160 // Mark the UTF-8 entries referenced by the inner classes. 161 innerClassesAttribute.innerClassEntriesAccept(clazz, this); 162 } 163 164 165 public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) 166 { 167 markCpUtf8Entry(clazz, enclosingMethodAttribute.u2attributeNameIndex); 168 169 // These entries have already been marked in the constant pool. 170 //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2classIndex); 171 //clazz.constantPoolEntryAccept(this, enclosingMethodAttribute.u2nameAndTypeIndex); 172 } 173 174 175 public void visitDeprecatedAttribute(Clazz clazz, DeprecatedAttribute deprecatedAttribute) 176 { 177 markCpUtf8Entry(clazz, deprecatedAttribute.u2attributeNameIndex); 178 } 179 180 181 public void visitSyntheticAttribute(Clazz clazz, SyntheticAttribute syntheticAttribute) 182 { 183 markCpUtf8Entry(clazz, syntheticAttribute.u2attributeNameIndex); 184 } 185 186 187 public void visitSignatureAttribute(Clazz clazz, SignatureAttribute signatureAttribute) 188 { 189 markCpUtf8Entry(clazz, signatureAttribute.u2attributeNameIndex); 190 191 markCpUtf8Entry(clazz, signatureAttribute.u2signatureIndex); 192 } 193 194 195 public void visitConstantValueAttribute(Clazz clazz, Field field, ConstantValueAttribute constantValueAttribute) 196 { 197 markCpUtf8Entry(clazz, constantValueAttribute.u2attributeNameIndex); 198 } 199 200 201 public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) 202 { 203 markCpUtf8Entry(clazz, exceptionsAttribute.u2attributeNameIndex); 204 } 205 206 207 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 208 { 209 markCpUtf8Entry(clazz, codeAttribute.u2attributeNameIndex); 210 211 // Mark the UTF-8 entries referenced by the attributes. 212 codeAttribute.attributesAccept(clazz, method, this); 213 } 214 215 216 public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) 217 { 218 markCpUtf8Entry(clazz, stackMapAttribute.u2attributeNameIndex); 219 } 220 221 222 public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) 223 { 224 markCpUtf8Entry(clazz, stackMapTableAttribute.u2attributeNameIndex); 225 } 226 227 228 public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) 229 { 230 markCpUtf8Entry(clazz, lineNumberTableAttribute.u2attributeNameIndex); 231 } 232 233 234 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 235 { 236 markCpUtf8Entry(clazz, localVariableTableAttribute.u2attributeNameIndex); 237 238 // Mark the UTF-8 entries referenced by the local variables. 239 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 240 } 241 242 243 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 244 { 245 markCpUtf8Entry(clazz, localVariableTypeTableAttribute.u2attributeNameIndex); 246 247 // Mark the UTF-8 entries referenced by the local variable types. 248 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 249 } 250 251 252 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 253 { 254 markCpUtf8Entry(clazz, annotationsAttribute.u2attributeNameIndex); 255 256 // Mark the UTF-8 entries referenced by the annotations. 257 annotationsAttribute.annotationsAccept(clazz, this); 258 } 259 260 261 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 262 { 263 markCpUtf8Entry(clazz, parameterAnnotationsAttribute.u2attributeNameIndex); 264 265 // Mark the UTF-8 entries referenced by the annotations. 266 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 267 } 268 269 270 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 271 { 272 markCpUtf8Entry(clazz, annotationDefaultAttribute.u2attributeNameIndex); 273 274 // Mark the UTF-8 entries referenced by the element value. 275 annotationDefaultAttribute.defaultValueAccept(clazz, this); 276 } 277 278 279 // Implementations for InnerClassesInfoVisitor. 280 281 public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) 282 { 283 if (innerClassesInfo.u2innerNameIndex != 0) 284 { 285 markCpUtf8Entry(clazz, innerClassesInfo.u2innerNameIndex); 286 } 287 } 288 289 290 // Implementations for LocalVariableInfoVisitor. 291 292 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 293 { 294 markCpUtf8Entry(clazz, localVariableInfo.u2nameIndex); 295 markCpUtf8Entry(clazz, localVariableInfo.u2descriptorIndex); 296 } 297 298 299 // Implementations for LocalVariableTypeInfoVisitor. 300 301 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 302 { 303 markCpUtf8Entry(clazz, localVariableTypeInfo.u2nameIndex); 304 markCpUtf8Entry(clazz, localVariableTypeInfo.u2signatureIndex); 305 } 306 307 308 // Implementations for AnnotationVisitor. 309 310 public void visitAnnotation(Clazz clazz, Annotation annotation) 311 { 312 markCpUtf8Entry(clazz, annotation.u2typeIndex); 313 314 // Mark the UTF-8 entries referenced by the element values. 315 annotation.elementValuesAccept(clazz, this); 316 } 317 318 319 // Implementations for ElementValueVisitor. 320 321 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 322 { 323 if (constantElementValue.u2elementNameIndex != 0) 324 { 325 markCpUtf8Entry(clazz, constantElementValue.u2elementNameIndex); 326 } 327 328 // Only the string constant element value refers to a UTF-8 entry. 329 if (constantElementValue.u1tag == ClassConstants.ELEMENT_VALUE_STRING_CONSTANT) 330 { 331 markCpUtf8Entry(clazz, constantElementValue.u2constantValueIndex); 332 } 333 } 334 335 336 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 337 { 338 if (enumConstantElementValue.u2elementNameIndex != 0) 339 { 340 markCpUtf8Entry(clazz, enumConstantElementValue.u2elementNameIndex); 341 } 342 343 markCpUtf8Entry(clazz, enumConstantElementValue.u2typeNameIndex); 344 markCpUtf8Entry(clazz, enumConstantElementValue.u2constantNameIndex); 345 } 346 347 348 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 349 { 350 if (classElementValue.u2elementNameIndex != 0) 351 { 352 markCpUtf8Entry(clazz, classElementValue.u2elementNameIndex); 353 } 354 355 markCpUtf8Entry(clazz, classElementValue.u2classInfoIndex); 356 } 357 358 359 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 360 { 361 if (annotationElementValue.u2elementNameIndex != 0) 362 { 363 markCpUtf8Entry(clazz, annotationElementValue.u2elementNameIndex); 364 } 365 366 // Mark the UTF-8 entries referenced by the annotation. 367 annotationElementValue.annotationAccept(clazz, this); 368 } 369 370 371 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 372 { 373 if (arrayElementValue.u2elementNameIndex != 0) 374 { 375 markCpUtf8Entry(clazz, arrayElementValue.u2elementNameIndex); 376 } 377 378 // Mark the UTF-8 entries referenced by the element values. 379 arrayElementValue.elementValuesAccept(clazz, annotation, this); 380 } 381 382 383 // Small utility methods. 384 385 /** 386 * Marks the given UTF-8 constant pool entry of the given class. 387 */ 388 private void markCpUtf8Entry(Clazz clazz, int index) 389 { 390 markAsUsed((Utf8Constant)((ProgramClass)clazz).getConstant(index)); 391 } 392 393 394 /** 395 * Marks the given VisitorAccepter as being used. 396 * In this context, the VisitorAccepter will be a Utf8Constant object. 397 */ 398 private void markAsUsed(VisitorAccepter visitorAccepter) 399 { 400 visitorAccepter.setVisitorInfo(USED); 401 } 402 403 404 /** 405 * Returns whether the given VisitorAccepter has been marked as being used. 406 * In this context, the VisitorAccepter will be a Utf8Constant object. 407 */ 408 private boolean isUsed(VisitorAccepter visitorAccepter) 409 { 410 return visitorAccepter.getVisitorInfo() == USED; 411 } 412 413 414 /** 415 * Removes all UTF-8 entries that are not marked as being used 416 * from the given constant pool. 417 * @return the new number of entries. 418 */ 419 private int shrinkConstantPool(Constant[] constantPool, int length) 420 { 421 // Create a new index map, if necessary. 422 if (constantIndexMap.length < length) 423 { 424 constantIndexMap = new int[length]; 425 } 426 427 int counter = 1; 428 boolean isUsed = false; 429 430 // Shift the used constant pool entries together. 431 for (int index = 1; index < length; index++) 432 { 433 constantIndexMap[index] = counter; 434 435 Constant constant = constantPool[index]; 436 437 // Don't update the flag if this is the second half of a long entry. 438 if (constant != null) 439 { 440 isUsed = constant.getTag() != ClassConstants.CONSTANT_Utf8 || 441 isUsed(constant); 442 } 443 444 if (isUsed) 445 { 446 constantPool[counter++] = constant; 447 } 448 } 449 450 // Clear the remaining constant pool elements. 451 Arrays.fill(constantPool, counter, length, null); 452 453 return counter; 454 } 455} 456