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 */ 21 package proguard.evaluation; 22 23 import proguard.classfile.*; 24 import proguard.classfile.attribute.CodeAttribute; 25 import proguard.classfile.constant.*; 26 import proguard.classfile.constant.visitor.ConstantVisitor; 27 import proguard.classfile.instruction.*; 28 import proguard.classfile.util.*; 29 import proguard.classfile.visitor.MemberVisitor; 30 import proguard.evaluation.value.*; 31 32 /** 33 * This InvocationUnit sets up the variables for entering a method, 34 * and it updates the stack for the invocation of a class member, 35 * using simple values. 36 * 37 * @author Eric Lafortune 38 */ 39 public class BasicInvocationUnit 40 extends SimplifiedVisitor 41 implements InvocationUnit, 42 ConstantVisitor, 43 MemberVisitor 44 { 45 protected final ValueFactory valueFactory; 46 47 // Fields acting as parameters between the visitor methods. 48 private boolean isStatic; 49 private boolean isLoad; 50 private Stack stack; 51 private Clazz returnTypeClass; 52 53 54 /** 55 * Creates a new BasicInvocationUnit with the given value factory. 56 */ BasicInvocationUnit(ValueFactory valueFactory)57 public BasicInvocationUnit(ValueFactory valueFactory) 58 { 59 this.valueFactory = valueFactory; 60 } 61 62 63 // Implementations for InvocationUnit. 64 enterMethod(Clazz clazz, Method method, Variables variables)65 public void enterMethod(Clazz clazz, Method method, Variables variables) 66 { 67 String descriptor = method.getDescriptor(clazz); 68 69 // Initialize the parameters. 70 boolean isStatic = 71 (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0; 72 73 // Count the number of parameters, taking into account their categories. 74 int parameterSize = ClassUtil.internalMethodParameterSize(descriptor, isStatic); 75 76 // Reuse the existing parameters object, ensuring the right size. 77 variables.reset(parameterSize); 78 79 // Go over the parameters again. 80 InternalTypeEnumeration internalTypeEnumeration = 81 new InternalTypeEnumeration(descriptor); 82 83 int parameterIndex = 0; 84 int variableIndex = 0; 85 86 // Put the 'this' reference in variable 0. 87 if (!isStatic) 88 { 89 // Get the reference value. 90 Value value = getMethodParameterValue(clazz, 91 method, 92 parameterIndex++, 93 ClassUtil.internalTypeFromClassName(clazz.getName()), 94 clazz); 95 96 // Store the value in variable 0. 97 variables.store(variableIndex++, value); 98 } 99 100 Clazz[] referencedClasses = ((ProgramMethod)method).referencedClasses; 101 int referencedClassIndex = 0; 102 103 // Set up the variables corresponding to the parameter types and values. 104 while (internalTypeEnumeration.hasMoreTypes()) 105 { 106 String type = internalTypeEnumeration.nextType(); 107 108 Clazz referencedClass = referencedClasses != null && 109 ClassUtil.isInternalClassType(type) ? 110 referencedClasses[referencedClassIndex++] : 111 null; 112 113 // Get the parameter value. 114 Value value = getMethodParameterValue(clazz, 115 method, 116 parameterIndex++, 117 type, 118 referencedClass); 119 120 // Store the value in the corresponding variable. 121 variables.store(variableIndex++, value); 122 123 // Increment the variable index again for Category 2 values. 124 if (value.isCategory2()) 125 { 126 variableIndex++; 127 } 128 } 129 } 130 131 exitMethod(Clazz clazz, Method method, Value returnValue)132 public void exitMethod(Clazz clazz, Method method, Value returnValue) 133 { 134 setMethodReturnValue(clazz, method, returnValue); 135 } 136 137 invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack)138 public void invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack) 139 { 140 int constantIndex = constantInstruction.constantIndex; 141 142 switch (constantInstruction.opcode) 143 { 144 case InstructionConstants.OP_GETSTATIC: 145 isStatic = true; 146 isLoad = true; 147 break; 148 149 case InstructionConstants.OP_PUTSTATIC: 150 isStatic = true; 151 isLoad = false; 152 break; 153 154 case InstructionConstants.OP_GETFIELD: 155 isStatic = false; 156 isLoad = true; 157 break; 158 159 case InstructionConstants.OP_PUTFIELD: 160 isStatic = false; 161 isLoad = false; 162 break; 163 164 case InstructionConstants.OP_INVOKESTATIC: 165 case InstructionConstants.OP_INVOKEDYNAMIC: 166 isStatic = true; 167 break; 168 169 case InstructionConstants.OP_INVOKEVIRTUAL: 170 case InstructionConstants.OP_INVOKESPECIAL: 171 case InstructionConstants.OP_INVOKEINTERFACE: 172 isStatic = false; 173 break; 174 } 175 176 // Pop the parameters and push the return value. 177 this.stack = stack; 178 clazz.constantPoolEntryAccept(constantIndex, this); 179 this.stack = null; 180 } 181 182 183 // Implementations for ConstantVisitor. 184 visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)185 public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) 186 { 187 // Pop the field value, if applicable. 188 if (!isLoad) 189 { 190 setFieldValue(clazz, fieldrefConstant, stack.pop()); 191 } 192 193 // Pop the reference value, if applicable. 194 if (!isStatic) 195 { 196 setFieldClassValue(clazz, fieldrefConstant, stack.apop()); 197 } 198 199 // Push the field value, if applicable. 200 if (isLoad) 201 { 202 String type = fieldrefConstant.getType(clazz); 203 204 stack.push(getFieldValue(clazz, fieldrefConstant, type)); 205 } 206 } 207 208 visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant)209 public void visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant) 210 { 211 String type = methodrefConstant.getType(clazz); 212 213 // Count the number of parameters. 214 int parameterCount = ClassUtil.internalMethodParameterCount(type); 215 if (!isStatic) 216 { 217 parameterCount++; 218 } 219 220 // Pop the parameters and the class reference, in reverse order. 221 for (int parameterIndex = parameterCount-1; parameterIndex >= 0; parameterIndex--) 222 { 223 setMethodParameterValue(clazz, methodrefConstant, parameterIndex, stack.pop()); 224 } 225 226 // Push the return value, if applicable. 227 String returnType = ClassUtil.internalMethodReturnType(type); 228 if (returnType.charAt(0) != ClassConstants.TYPE_VOID) 229 { 230 stack.push(getMethodReturnValue(clazz, methodrefConstant, returnType)); 231 } 232 } 233 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)234 public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) 235 { 236 String type = invokeDynamicConstant.getType(clazz); 237 238 // Count the number of parameters. 239 int parameterCount = ClassUtil.internalMethodParameterCount(type); 240 if (!isStatic) 241 { 242 parameterCount++; 243 } 244 245 // Pop the parameters and the class reference, in reverse order. 246 for (int parameterIndex = parameterCount-1; parameterIndex >= 0; parameterIndex--) 247 { 248 stack.pop(); 249 } 250 251 // Push the return value, if applicable. 252 String returnType = ClassUtil.internalMethodReturnType(type); 253 if (returnType.charAt(0) != ClassConstants.TYPE_VOID) 254 { 255 stack.push(getMethodReturnValue(clazz, invokeDynamicConstant, returnType)); 256 } 257 } 258 259 260 /** 261 * Sets the class through which the specified field is accessed. 262 */ setFieldClassValue(Clazz clazz, RefConstant refConstant, ReferenceValue value)263 protected void setFieldClassValue(Clazz clazz, 264 RefConstant refConstant, 265 ReferenceValue value) 266 { 267 // We don't care about the new value. 268 } 269 270 271 /** 272 * Returns the class though which the specified field is accessed. 273 */ getFieldClassValue(Clazz clazz, RefConstant refConstant, String type)274 protected Value getFieldClassValue(Clazz clazz, 275 RefConstant refConstant, 276 String type) 277 { 278 // Try to figure out the class of the return type. 279 returnTypeClass = null; 280 refConstant.referencedMemberAccept(this); 281 282 return valueFactory.createValue(type, 283 returnTypeClass, 284 true); 285 } 286 287 288 /** 289 * Sets the value of the specified field. 290 */ setFieldValue(Clazz clazz, RefConstant refConstant, Value value)291 protected void setFieldValue(Clazz clazz, 292 RefConstant refConstant, 293 Value value) 294 { 295 // We don't care about the new field value. 296 } 297 298 299 /** 300 * Returns the value of the specified field. 301 */ getFieldValue(Clazz clazz, RefConstant refConstant, String type)302 protected Value getFieldValue(Clazz clazz, 303 RefConstant refConstant, 304 String type) 305 { 306 // Try to figure out the class of the return type. 307 returnTypeClass = null; 308 refConstant.referencedMemberAccept(this); 309 310 return valueFactory.createValue(type, 311 returnTypeClass, 312 true); 313 } 314 315 316 /** 317 * Sets the value of the specified method parameter. 318 */ setMethodParameterValue(Clazz clazz, RefConstant refConstant, int parameterIndex, Value value)319 protected void setMethodParameterValue(Clazz clazz, 320 RefConstant refConstant, 321 int parameterIndex, 322 Value value) 323 { 324 // We don't care about the parameter value. 325 } 326 327 328 /** 329 * Returns the value of the specified method parameter. 330 */ getMethodParameterValue(Clazz clazz, Method method, int parameterIndex, String type, Clazz referencedClass)331 protected Value getMethodParameterValue(Clazz clazz, 332 Method method, 333 int parameterIndex, 334 String type, 335 Clazz referencedClass) 336 { 337 return valueFactory.createValue(type, referencedClass, true); 338 } 339 340 341 /** 342 * Sets the return value of the specified method. 343 */ setMethodReturnValue(Clazz clazz, Method method, Value value)344 protected void setMethodReturnValue(Clazz clazz, 345 Method method, 346 Value value) 347 { 348 // We don't care about the return value. 349 } 350 351 352 /** 353 * Returns the return value of the specified method. 354 */ getMethodReturnValue(Clazz clazz, RefConstant refConstant, String type)355 protected Value getMethodReturnValue(Clazz clazz, 356 RefConstant refConstant, 357 String type) 358 { 359 // Try to figure out the class of the return type. 360 returnTypeClass = null; 361 refConstant.referencedMemberAccept(this); 362 363 return valueFactory.createValue(type, 364 returnTypeClass, 365 true); 366 } 367 368 369 /** 370 * Returns the return value of the specified method. 371 */ getMethodReturnValue(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant, String type)372 protected Value getMethodReturnValue(Clazz clazz, 373 InvokeDynamicConstant invokeDynamicConstant, 374 String type) 375 { 376 // Try to figure out the class of the return type. 377 Clazz[] referencedClasses = invokeDynamicConstant.referencedClasses; 378 379 Clazz returnTypeClass = referencedClasses == null ? null : 380 referencedClasses[referencedClasses.length - 1]; 381 382 return valueFactory.createValue(type, 383 returnTypeClass, 384 true); 385 } 386 387 388 // Implementations for MemberVisitor. 389 visitProgramField(ProgramClass programClass, ProgramField programField)390 public void visitProgramField(ProgramClass programClass, ProgramField programField) 391 { 392 returnTypeClass = programField.referencedClass; 393 } 394 395 visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod)396 public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) 397 { 398 Clazz[] referencedClasses = programMethod.referencedClasses; 399 if (referencedClasses != null) 400 { 401 returnTypeClass = referencedClasses[referencedClasses.length - 1]; 402 } 403 } 404 405 visitLibraryField(LibraryClass programClass, LibraryField programField)406 public void visitLibraryField(LibraryClass programClass, LibraryField programField) 407 { 408 returnTypeClass = programField.referencedClass; 409 } 410 411 visitLibraryMethod(LibraryClass programClass, LibraryMethod programMethod)412 public void visitLibraryMethod(LibraryClass programClass, LibraryMethod programMethod) 413 { 414 Clazz[] referencedClasses = programMethod.referencedClasses; 415 if (referencedClasses != null) 416 { 417 returnTypeClass = referencedClasses[referencedClasses.length - 1]; 418 } 419 } 420 } 421