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.visitor.AttributeVisitor; 26import proguard.classfile.constant.*; 27import proguard.classfile.constant.visitor.ConstantVisitor; 28import proguard.classfile.editor.ConstantPoolRemapper; 29import proguard.classfile.util.SimplifiedVisitor; 30import proguard.classfile.visitor.ClassVisitor; 31 32import java.util.Arrays; 33 34 35/** 36 * This ClassVisitor removes NameAndType constant pool entries that are not 37 * used. 38 * 39 * @author Eric Lafortune 40 */ 41public class NameAndTypeShrinker 42extends SimplifiedVisitor 43implements ClassVisitor, 44 ConstantVisitor, 45 AttributeVisitor 46{ 47 // A visitor info flag to indicate the NameAndType constant pool entry is being used. 48 private static final Object USED = new Object(); 49 50 private int[] constantIndexMap; 51 private final ConstantPoolRemapper constantPoolRemapper = new ConstantPoolRemapper(); 52 53 54 // Implementations for ClassVisitor. 55 56 public void visitProgramClass(ProgramClass programClass) 57 { 58 // Mark the NameAndType entries referenced by all other constant pool 59 // entries. 60 programClass.constantPoolEntriesAccept(this); 61 62 // Mark the NameAndType entries referenced by all EnclosingMethod 63 // attributes. 64 programClass.attributesAccept(this); 65 66 // Shift the used constant pool entries together, filling out the 67 // index map. 68 int newConstantPoolCount = 69 shrinkConstantPool(programClass.constantPool, 70 programClass.u2constantPoolCount); 71 72 // Remap the references to the constant pool if it has shrunk. 73 if (newConstantPoolCount < programClass.u2constantPoolCount) 74 { 75 programClass.u2constantPoolCount = newConstantPoolCount; 76 77 // Remap all constant pool references. 78 constantPoolRemapper.setConstantIndexMap(constantIndexMap); 79 constantPoolRemapper.visitProgramClass(programClass); 80 } 81 } 82 83 84 // Implementations for ConstantVisitor. 85 86 public void visitAnyConstant(Clazz clazz, Constant constant) {} 87 88 89 public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) 90 { 91 markNameAndTypeConstant(clazz, invokeDynamicConstant.u2nameAndTypeIndex); 92 } 93 94 95 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 96 { 97 markNameAndTypeConstant(clazz, refConstant.u2nameAndTypeIndex); 98 } 99 100 101 // Implementations for AttributeVisitor. 102 103 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 104 105 106 public void visitEnclosingMethodAttribute(Clazz clazz, EnclosingMethodAttribute enclosingMethodAttribute) 107 { 108 if (enclosingMethodAttribute.u2nameAndTypeIndex != 0) 109 { 110 markNameAndTypeConstant(clazz, enclosingMethodAttribute.u2nameAndTypeIndex); 111 } 112 } 113 114 115 // Small utility methods. 116 117 /** 118 * Marks the given UTF-8 constant pool entry of the given class. 119 */ 120 private void markNameAndTypeConstant(Clazz clazz, int index) 121 { 122 markAsUsed((NameAndTypeConstant)((ProgramClass)clazz).getConstant(index)); 123 } 124 125 126 /** 127 * Marks the given VisitorAccepter as being used. 128 * In this context, the VisitorAccepter will be a NameAndTypeConstant object. 129 */ 130 private void markAsUsed(VisitorAccepter visitorAccepter) 131 { 132 visitorAccepter.setVisitorInfo(USED); 133 } 134 135 136 /** 137 * Returns whether the given VisitorAccepter has been marked as being used. 138 * In this context, the VisitorAccepter will be a NameAndTypeConstant object. 139 */ 140 private boolean isUsed(VisitorAccepter visitorAccepter) 141 { 142 return visitorAccepter.getVisitorInfo() == USED; 143 } 144 145 146 /** 147 * Removes all NameAndType entries that are not marked as being used 148 * from the given constant pool. 149 * @return the new number of entries. 150 */ 151 private int shrinkConstantPool(Constant[] constantPool, int length) 152 { 153 // Create a new index map, if necessary. 154 if (constantIndexMap == null || 155 constantIndexMap.length < length) 156 { 157 constantIndexMap = new int[length]; 158 } 159 160 int counter = 1; 161 boolean isUsed = false; 162 163 // Shift the used constant pool entries together. 164 for (int index = 1; index < length; index++) 165 { 166 constantIndexMap[index] = counter; 167 168 Constant constant = constantPool[index]; 169 170 // Don't update the flag if this is the second half of a long entry. 171 if (constant != null) 172 { 173 isUsed = constant.getTag() != ClassConstants.CONSTANT_NameAndType || 174 isUsed(constant); 175 } 176 177 if (isUsed) 178 { 179 constantPool[counter++] = constant; 180 } 181 } 182 183 // Clear the remaining constant pool elements. 184 Arrays.fill(constantPool, counter, length, null); 185 186 return counter; 187 } 188} 189