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.util.SimplifiedVisitor; 27 28 import java.util.Arrays; 29 30 /** 31 * This AttributeVisitor cleans up variable tables in all code attributes that 32 * it visits. It trims overlapping local variables. It removes empty local 33 * variables and empty local variable tables. 34 * 35 * @author Eric Lafortune 36 */ 37 public class VariableCleaner 38 extends SimplifiedVisitor 39 implements AttributeVisitor 40 { 41 private boolean deleteLocalVariableTableAttribute; 42 private boolean deleteLocalVariableTypeTableAttribute; 43 44 45 // Implementations for AttributeVisitor. 46 47 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 48 49 50 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 51 { 52 deleteLocalVariableTableAttribute = false; 53 deleteLocalVariableTypeTableAttribute = false; 54 55 // Trim the local variable table and the local variable type table. 56 codeAttribute.attributesAccept(clazz, method, this); 57 58 // Delete the local variable table if it ended up empty. 59 if (deleteLocalVariableTableAttribute) 60 { 61 AttributesEditor editor = 62 new AttributesEditor((ProgramClass)clazz, 63 (ProgramMember)method, 64 codeAttribute, 65 true); 66 67 editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTable); 68 } 69 70 // Delete the local variable type table if it ended up empty. 71 if (deleteLocalVariableTypeTableAttribute) 72 { 73 AttributesEditor editor = 74 new AttributesEditor((ProgramClass)clazz, 75 (ProgramMember)method, 76 codeAttribute, 77 true); 78 79 editor.deleteAttribute(ClassConstants.ATTR_LocalVariableTypeTable); 80 } 81 } 82 83 84 public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) 85 { 86 // Clean up local variables that aren't used. 87 localVariableTableAttribute.u2localVariableTableLength = 88 removeUnusedLocalVariables(localVariableTableAttribute.localVariableTable, 89 localVariableTableAttribute.u2localVariableTableLength, 90 codeAttribute.u2maxLocals); 91 92 // Trim the code blocks of the local variables. 93 trimLocalVariables(localVariableTableAttribute.localVariableTable, 94 localVariableTableAttribute.u2localVariableTableLength, 95 codeAttribute.u2maxLocals); 96 97 // Delete the attribute in a moment, if it is empty. 98 if (localVariableTableAttribute.u2localVariableTableLength == 0) 99 { 100 deleteLocalVariableTableAttribute = true; 101 } 102 } 103 104 105 public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) 106 { 107 // Clean up local variables that aren't used. 108 localVariableTypeTableAttribute.u2localVariableTypeTableLength = 109 removeUnusedLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, 110 localVariableTypeTableAttribute.u2localVariableTypeTableLength, 111 codeAttribute.u2maxLocals); 112 113 // Trim the code blocks of the local variables. 114 trimLocalVariableTypes(localVariableTypeTableAttribute.localVariableTypeTable, 115 localVariableTypeTableAttribute.u2localVariableTypeTableLength, 116 codeAttribute.u2maxLocals); 117 118 // Delete the attribute in a moment, if it is empty. 119 if (localVariableTypeTableAttribute.u2localVariableTypeTableLength == 0) 120 { 121 deleteLocalVariableTypeTableAttribute = true; 122 } 123 } 124 125 126 // Small utility methods. 127 128 /** 129 * Returns the given list of local variables, without the ones that aren't 130 * used. 131 */ 132 private int removeUnusedLocalVariables(LocalVariableInfo[] localVariableInfos, 133 int localVariableInfoCount, 134 int maxLocals) 135 { 136 // Overwrite all empty local variable entries. 137 // Do keep parameter entries. 138 int newIndex = 0; 139 for (int index = 0; index < localVariableInfoCount; index++) 140 { 141 LocalVariableInfo localVariableInfo = localVariableInfos[index]; 142 143 if (localVariableInfo.u2index >= 0 && 144 localVariableInfo.u2index < maxLocals && 145 (localVariableInfo.u2startPC == 0 || 146 localVariableInfo.u2length > 0)) 147 { 148 localVariableInfos[newIndex++] = localVariableInfos[index]; 149 } 150 } 151 152 // Clean up any remaining array elements. 153 Arrays.fill(localVariableInfos, newIndex, localVariableInfoCount, null); 154 155 return newIndex; 156 } 157 158 159 /** 160 * Returns the given list of local variable types, without the ones that 161 * aren't used. 162 */ 163 private int removeUnusedLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, 164 int localVariableTypeInfoCount, 165 int maxLocals) 166 { 167 // Overwrite all empty local variable type entries. 168 // Do keep parameter entries. 169 int newIndex = 0; 170 for (int index = 0; index < localVariableTypeInfoCount; index++) 171 { 172 LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; 173 174 if (localVariableTypeInfo.u2index >= 0 && 175 localVariableTypeInfo.u2index < maxLocals && 176 (localVariableTypeInfo.u2startPC == 0 || 177 localVariableTypeInfo.u2length > 0)) 178 { 179 localVariableTypeInfos[newIndex++] = localVariableTypeInfos[index]; 180 } 181 } 182 183 // Clean up any remaining array elements. 184 Arrays.fill(localVariableTypeInfos, newIndex, localVariableTypeInfoCount, null); 185 186 return newIndex; 187 } 188 189 190 /** 191 * Sorts the given list of local variables and trims their code blocks 192 * when necessary. The block is trimmed at the end, which is a bit 193 * arbitrary. 194 */ 195 private void trimLocalVariables(LocalVariableInfo[] localVariableInfos, 196 int localVariableInfoCount, 197 int maxLocals) 198 { 199 // Sort the local variable entries. 200 Arrays.sort(localVariableInfos, 0, localVariableInfoCount); 201 202 int[] startPCs = createMaxArray(maxLocals); 203 204 // Trim the local variable entries, starting at the last one. 205 for (int index = localVariableInfoCount-1; index >= 0; index--) 206 { 207 LocalVariableInfo localVariableInfo = localVariableInfos[index]; 208 209 // Make sure the variable's code block doesn't overlap with the 210 // next one for the same variable. 211 int maxLength = startPCs[localVariableInfo.u2index] - 212 localVariableInfo.u2startPC; 213 214 if (localVariableInfo.u2length > maxLength) 215 { 216 localVariableInfo.u2length = maxLength; 217 } 218 219 startPCs[localVariableInfo.u2index] = localVariableInfo.u2startPC; 220 } 221 } 222 223 224 /** 225 * Sorts the given list of local variable types and trims their code blocks 226 * when necessary. The block is trimmed at the end, which is a bit 227 * arbitrary. 228 */ 229 private void trimLocalVariableTypes(LocalVariableTypeInfo[] localVariableTypeInfos, 230 int localVariableTypeInfoCount, 231 int maxLocals) 232 { 233 // Sort the local variable entries. 234 Arrays.sort(localVariableTypeInfos, 0, localVariableTypeInfoCount); 235 236 int[] startPCs = createMaxArray(maxLocals); 237 238 // Trim the local variable entries, starting at the last one. 239 for (int index = localVariableTypeInfoCount-1; index >= 0; index--) 240 { 241 LocalVariableTypeInfo localVariableTypeInfo = localVariableTypeInfos[index]; 242 243 // Make sure the variable's code block doesn't overlap with the 244 // next one for the same variable. 245 int maxLength = startPCs[localVariableTypeInfo.u2index] - 246 localVariableTypeInfo.u2startPC; 247 248 if (localVariableTypeInfo.u2length > maxLength) 249 { 250 localVariableTypeInfo.u2length = maxLength; 251 } 252 253 startPCs[localVariableTypeInfo.u2index] = localVariableTypeInfo.u2startPC; 254 } 255 } 256 257 258 /** 259 * Creates an integer array of the given length, initialized with 260 * Integer.MAX_VALUE. 261 */ 262 private int[] createMaxArray(int length) 263 { 264 int[] startPCs = new int[length]; 265 for (int index = 0; index < length; index++) 266 { 267 startPCs[index] = Integer.MAX_VALUE; 268 } 269 return startPCs; 270 } 271 }