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.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.util.SimplifiedVisitor; 32import proguard.classfile.visitor.*; 33 34import java.util.Arrays; 35 36 37/** 38 * This ClassVisitor removes UTF-8 constant pool entries that are not used. 39 * 40 * @author Eric Lafortune 41 */ 42public class Utf8Shrinker 43extends SimplifiedVisitor 44implements ClassVisitor, 45 MemberVisitor, 46 ConstantVisitor, 47 AttributeVisitor, 48 InnerClassesInfoVisitor, 49 ParameterInfoVisitor, 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 visitMethodParametersAttribute(Clazz clazz, Method method, MethodParametersAttribute methodParametersAttribute) 202 { 203 markCpUtf8Entry(clazz, methodParametersAttribute.u2attributeNameIndex); 204 205 // Mark the UTF-8 entries referenced by the parameter information. 206 methodParametersAttribute.parametersAccept(clazz, method, this); 207 } 208 209 210 public void visitExceptionsAttribute(Clazz clazz, Method method, ExceptionsAttribute exceptionsAttribute) 211 { 212 markCpUtf8Entry(clazz, exceptionsAttribute.u2attributeNameIndex); 213 } 214 215 216 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 217 { 218 markCpUtf8Entry(clazz, codeAttribute.u2attributeNameIndex); 219 220 // Mark the UTF-8 entries referenced by the attributes. 221 codeAttribute.attributesAccept(clazz, method, this); 222 } 223 224 225 public void visitStackMapAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapAttribute stackMapAttribute) 226 { 227 markCpUtf8Entry(clazz, stackMapAttribute.u2attributeNameIndex); 228 } 229 230 231 public void visitStackMapTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, StackMapTableAttribute stackMapTableAttribute) 232 { 233 markCpUtf8Entry(clazz, stackMapTableAttribute.u2attributeNameIndex); 234 } 235 236 237 public void visitLineNumberTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LineNumberTableAttribute lineNumberTableAttribute) 238 { 239 markCpUtf8Entry(clazz, lineNumberTableAttribute.u2attributeNameIndex); 240 } 241 242 243 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 244 { 245 markCpUtf8Entry(clazz, localVariableTableAttribute.u2attributeNameIndex); 246 247 // Mark the UTF-8 entries referenced by the local variables. 248 localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 249 } 250 251 252 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 253 { 254 markCpUtf8Entry(clazz, localVariableTypeTableAttribute.u2attributeNameIndex); 255 256 // Mark the UTF-8 entries referenced by the local variable types. 257 localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); 258 } 259 260 261 public void visitAnyAnnotationsAttribute(Clazz clazz, AnnotationsAttribute annotationsAttribute) 262 { 263 markCpUtf8Entry(clazz, annotationsAttribute.u2attributeNameIndex); 264 265 // Mark the UTF-8 entries referenced by the annotations. 266 annotationsAttribute.annotationsAccept(clazz, this); 267 } 268 269 270 public void visitAnyParameterAnnotationsAttribute(Clazz clazz, Method method, ParameterAnnotationsAttribute parameterAnnotationsAttribute) 271 { 272 markCpUtf8Entry(clazz, parameterAnnotationsAttribute.u2attributeNameIndex); 273 274 // Mark the UTF-8 entries referenced by the annotations. 275 parameterAnnotationsAttribute.annotationsAccept(clazz, method, this); 276 } 277 278 279 public void visitAnnotationDefaultAttribute(Clazz clazz, Method method, AnnotationDefaultAttribute annotationDefaultAttribute) 280 { 281 markCpUtf8Entry(clazz, annotationDefaultAttribute.u2attributeNameIndex); 282 283 // Mark the UTF-8 entries referenced by the element value. 284 annotationDefaultAttribute.defaultValueAccept(clazz, this); 285 } 286 287 288 // Implementations for InnerClassesInfoVisitor. 289 290 public void visitInnerClassesInfo(Clazz clazz, InnerClassesInfo innerClassesInfo) 291 { 292 if (innerClassesInfo.u2innerNameIndex != 0) 293 { 294 markCpUtf8Entry(clazz, innerClassesInfo.u2innerNameIndex); 295 } 296 } 297 298 299 // Implementations for ParameterInfoVisitor. 300 301 public void visitParameterInfo(Clazz clazz, Method method, int parameterIndex, ParameterInfo parameterInfo) 302 { 303 if (parameterInfo.u2nameIndex != 0) 304 { 305 markCpUtf8Entry(clazz, parameterInfo.u2nameIndex); 306 } 307 } 308 309 310 // Implementations for LocalVariableInfoVisitor. 311 312 public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) 313 { 314 markCpUtf8Entry(clazz, localVariableInfo.u2nameIndex); 315 markCpUtf8Entry(clazz, localVariableInfo.u2descriptorIndex); 316 } 317 318 319 // Implementations for LocalVariableTypeInfoVisitor. 320 321 public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) 322 { 323 markCpUtf8Entry(clazz, localVariableTypeInfo.u2nameIndex); 324 markCpUtf8Entry(clazz, localVariableTypeInfo.u2signatureIndex); 325 } 326 327 328 // Implementations for AnnotationVisitor. 329 330 public void visitAnnotation(Clazz clazz, Annotation annotation) 331 { 332 markCpUtf8Entry(clazz, annotation.u2typeIndex); 333 334 // Mark the UTF-8 entries referenced by the element values. 335 annotation.elementValuesAccept(clazz, this); 336 } 337 338 339 // Implementations for ElementValueVisitor. 340 341 public void visitConstantElementValue(Clazz clazz, Annotation annotation, ConstantElementValue constantElementValue) 342 { 343 if (constantElementValue.u2elementNameIndex != 0) 344 { 345 markCpUtf8Entry(clazz, constantElementValue.u2elementNameIndex); 346 } 347 348 // Only the string constant element value refers to a UTF-8 entry. 349 if (constantElementValue.u1tag == ClassConstants.ELEMENT_VALUE_STRING_CONSTANT) 350 { 351 markCpUtf8Entry(clazz, constantElementValue.u2constantValueIndex); 352 } 353 } 354 355 356 public void visitEnumConstantElementValue(Clazz clazz, Annotation annotation, EnumConstantElementValue enumConstantElementValue) 357 { 358 if (enumConstantElementValue.u2elementNameIndex != 0) 359 { 360 markCpUtf8Entry(clazz, enumConstantElementValue.u2elementNameIndex); 361 } 362 363 markCpUtf8Entry(clazz, enumConstantElementValue.u2typeNameIndex); 364 markCpUtf8Entry(clazz, enumConstantElementValue.u2constantNameIndex); 365 } 366 367 368 public void visitClassElementValue(Clazz clazz, Annotation annotation, ClassElementValue classElementValue) 369 { 370 if (classElementValue.u2elementNameIndex != 0) 371 { 372 markCpUtf8Entry(clazz, classElementValue.u2elementNameIndex); 373 } 374 375 markCpUtf8Entry(clazz, classElementValue.u2classInfoIndex); 376 } 377 378 379 public void visitAnnotationElementValue(Clazz clazz, Annotation annotation, AnnotationElementValue annotationElementValue) 380 { 381 if (annotationElementValue.u2elementNameIndex != 0) 382 { 383 markCpUtf8Entry(clazz, annotationElementValue.u2elementNameIndex); 384 } 385 386 // Mark the UTF-8 entries referenced by the annotation. 387 annotationElementValue.annotationAccept(clazz, this); 388 } 389 390 391 public void visitArrayElementValue(Clazz clazz, Annotation annotation, ArrayElementValue arrayElementValue) 392 { 393 if (arrayElementValue.u2elementNameIndex != 0) 394 { 395 markCpUtf8Entry(clazz, arrayElementValue.u2elementNameIndex); 396 } 397 398 // Mark the UTF-8 entries referenced by the element values. 399 arrayElementValue.elementValuesAccept(clazz, annotation, this); 400 } 401 402 403 // Small utility methods. 404 405 /** 406 * Marks the given UTF-8 constant pool entry of the given class. 407 */ 408 private void markCpUtf8Entry(Clazz clazz, int index) 409 { 410 markAsUsed((Utf8Constant)((ProgramClass)clazz).getConstant(index)); 411 } 412 413 414 /** 415 * Marks the given VisitorAccepter as being used. 416 * In this context, the VisitorAccepter will be a Utf8Constant object. 417 */ 418 private void markAsUsed(VisitorAccepter visitorAccepter) 419 { 420 visitorAccepter.setVisitorInfo(USED); 421 } 422 423 424 /** 425 * Returns whether the given VisitorAccepter has been marked as being used. 426 * In this context, the VisitorAccepter will be a Utf8Constant object. 427 */ 428 private boolean isUsed(VisitorAccepter visitorAccepter) 429 { 430 return visitorAccepter.getVisitorInfo() == USED; 431 } 432 433 434 /** 435 * Removes all UTF-8 entries that are not marked as being used 436 * from the given constant pool. 437 * @return the new number of entries. 438 */ 439 private int shrinkConstantPool(Constant[] constantPool, int length) 440 { 441 // Create a new index map, if necessary. 442 if (constantIndexMap.length < length) 443 { 444 constantIndexMap = new int[length]; 445 } 446 447 int counter = 1; 448 boolean isUsed = false; 449 450 // Shift the used constant pool entries together. 451 for (int index = 1; index < length; index++) 452 { 453 Constant constant = constantPool[index]; 454 455 // Is the constant being used? Don't update the flag if this is the 456 // second half of a long entry. 457 if (constant != null) 458 { 459 isUsed = constant.getTag() != ClassConstants.CONSTANT_Utf8 || 460 isUsed(constant); 461 } 462 463 if (isUsed) 464 { 465 // Remember the new index. 466 constantIndexMap[index] = counter; 467 468 // Shift the constant pool entry. 469 constantPool[counter++] = constant; 470 } 471 else 472 { 473 // Remember an invalid index. 474 constantIndexMap[index] = -1; 475 } 476 } 477 478 // Clear the remaining constant pool elements. 479 Arrays.fill(constantPool, counter, length, null); 480 481 return counter; 482 } 483} 484