1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2009 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.optimize.info; 22 23 import proguard.classfile.*; 24 import proguard.classfile.instruction.*; 25 import proguard.classfile.instruction.visitor.InstructionVisitor; 26 import proguard.classfile.attribute.visitor.AttributeVisitor; 27 import proguard.classfile.attribute.*; 28 import proguard.classfile.util.*; 29 import proguard.classfile.visitor.MemberVisitor; 30 import proguard.optimize.evaluation.PartialEvaluator; 31 import proguard.evaluation.value.*; 32 33 /** 34 * This MemberVisitor counts the parameters and marks the used parameters 35 * of the methods that it visits. It also marks the 'this' parameters of 36 * methods that have hierarchies. 37 * 38 * @author Eric Lafortune 39 */ 40 public class ParameterUsageMarker 41 extends SimplifiedVisitor 42 implements MemberVisitor, 43 AttributeVisitor, 44 InstructionVisitor 45 { 46 private static final boolean DEBUG = false; 47 48 49 private final boolean markThisParameter; 50 private final boolean markAllParameters; 51 private final PartialEvaluator partialEvaluator = new PartialEvaluator(); 52 53 54 /** 55 * Creates a new ParameterUsageMarker. 56 */ 57 public ParameterUsageMarker() 58 { 59 this(false, false); 60 } 61 62 63 /** 64 * Creates a new ParameterUsageMarker that optionally marks all parameters. 65 * @param markThisParameter specifies whether all 'this' parameters should 66 * be marked as being used. 67 * @param markAllParameters specifies whether all other parameters should 68 * be marked as being used. 69 */ 70 public ParameterUsageMarker(boolean markThisParameter, 71 boolean markAllParameters) 72 { 73 this.markThisParameter = markThisParameter; 74 this.markAllParameters = markAllParameters; 75 } 76 77 78 // Implementations for MemberVisitor. 79 80 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 81 { 82 int parameterSize = 83 ClassUtil.internalMethodParameterSize(programMethod.getDescriptor(programClass), 84 programMethod.getAccessFlags()); 85 86 if (parameterSize > 0) 87 { 88 int accessFlags = programMethod.getAccessFlags(); 89 90 // Must we mark the 'this' parameter? 91 if (markThisParameter && 92 (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0) 93 { 94 // Mark the 'this' parameter. 95 markParameterUsed(programMethod, 0); 96 } 97 98 // Must we mark all other parameters? 99 if (markAllParameters) 100 { 101 // Mark all parameters, without the 'this' parameter. 102 markUsedParameters(programMethod, 103 (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? 104 -1L : -2L); 105 } 106 107 // Is it a native method? 108 if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) 109 { 110 // Mark all parameters. 111 markUsedParameters(programMethod, -1L); 112 } 113 114 // Is it an abstract method? 115 else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) 116 { 117 // Mark the 'this' parameter. 118 markParameterUsed(programMethod, 0); 119 } 120 121 // Is it a non-native, concrete method? 122 else 123 { 124 // Is the method not static, but synchronized, or can it have 125 // other implementations, or is it a class instance initializer? 126 if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0 && 127 ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0 || 128 programClass.mayHaveImplementations(programMethod) || 129 programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))) 130 { 131 // Mark the 'this' parameter. 132 markParameterUsed(programMethod, 0); 133 } 134 135 // Mark the parameters that are used by the code. 136 programMethod.attributesAccept(programClass, this); 137 } 138 139 if (DEBUG) 140 { 141 System.out.print("ParameterUsageMarker: ["+programClass.getName() +"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]: "); 142 for (int index = 0; index < parameterSize; index++) 143 { 144 System.out.print(isParameterUsed(programMethod, index) ? '+' : '-'); 145 } 146 System.out.println(); 147 } 148 149 } 150 151 // Set the parameter size. 152 setParameterSize(programMethod, parameterSize); 153 } 154 155 156 public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) 157 { 158 // Can the method have other implementations? 159 if (libraryClass.mayHaveImplementations(libraryMethod)) 160 { 161 // All implementations must keep all parameters of this method, 162 // including the 'this' parameter. 163 markUsedParameters(libraryMethod, -1L); 164 } 165 } 166 167 168 // Implementations for AttributeVisitor. 169 170 public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} 171 172 173 public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) 174 { 175 // Evaluate the code. 176 partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); 177 178 // Mark the parameters that are used by the code. 179 codeAttribute.instructionsAccept(clazz, method, this); 180 } 181 182 183 // Implementations for InstructionVisitor. 184 185 public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} 186 187 188 public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) 189 { 190 if (partialEvaluator.isTraced(offset) && 191 variableInstruction.isLoad()) 192 { 193 int parameterIndex = variableInstruction.variableIndex; 194 if (parameterIndex < codeAttribute.u2maxLocals) 195 { 196 Value producer = 197 partialEvaluator.getVariablesBefore(offset).getProducerValue(parameterIndex); 198 if (producer != null && 199 producer.instructionOffsetValue().contains(PartialEvaluator.AT_METHOD_ENTRY)) 200 { 201 // Mark the variable. 202 markParameterUsed(method, parameterIndex); 203 204 // Account for Category 2 instructions, which take up two entries. 205 if (variableInstruction.isCategory2()) 206 { 207 markParameterUsed(method, parameterIndex + 1); 208 } 209 } 210 } 211 } 212 } 213 214 215 // Small utility methods. 216 217 /** 218 * Sets the total size of the parameters. 219 */ 220 private static void setParameterSize(Method method, int parameterSize) 221 { 222 MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); 223 if (info != null) 224 { 225 info.setParameterSize(parameterSize); 226 } 227 } 228 229 230 /** 231 * Returns the total size of the parameters. 232 */ 233 public static int getParameterSize(Method method) 234 { 235 MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); 236 return info != null ? info.getParameterSize() : 0; 237 } 238 239 240 /** 241 * Marks the given parameter as being used. 242 */ 243 public static void markParameterUsed(Method method, int variableIndex) 244 { 245 MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); 246 if (info != null) 247 { 248 info.setParameterUsed(variableIndex); 249 } 250 } 251 252 253 /** 254 * Marks the given parameters as being used. 255 */ 256 public static void markUsedParameters(Method method, long usedParameters) 257 { 258 MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); 259 if (info != null) 260 { 261 info.setUsedParameters(info.getUsedParameters() | usedParameters); 262 } 263 } 264 265 266 /** 267 * Returns whether the given parameter is being used. 268 */ 269 public static boolean isParameterUsed(Method method, int variableIndex) 270 { 271 MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); 272 return info == null || 273 info.isParameterUsed(variableIndex); 274 } 275 276 277 /** 278 * Returns which parameters are being used. 279 */ 280 public static long getUsedParameters(Method method) 281 { 282 MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); 283 return info != null ? info.getUsedParameters() : -1L; 284 } 285 } 286