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