/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ package proguard.evaluation; import proguard.classfile.*; import proguard.classfile.attribute.CodeAttribute; import proguard.classfile.constant.*; import proguard.classfile.constant.visitor.ConstantVisitor; import proguard.classfile.instruction.*; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; import proguard.evaluation.value.*; /** * This InvocationUnit sets up the variables for entering a method, * and it updates the stack for the invocation of a class member, * using simple values. * * @author Eric Lafortune */ public class BasicInvocationUnit extends SimplifiedVisitor implements InvocationUnit, ConstantVisitor, MemberVisitor { protected final ValueFactory valueFactory; // Fields acting as parameters between the visitor methods. private boolean isStatic; private boolean isLoad; private Stack stack; private Clazz returnTypeClass; /** * Creates a new BasicInvocationUnit with the given value factory. */ public BasicInvocationUnit(ValueFactory valueFactory) { this.valueFactory = valueFactory; } // Implementations for InvocationUnit. public void enterMethod(Clazz clazz, Method method, Variables variables) { String descriptor = method.getDescriptor(clazz); // Initialize the parameters. boolean isStatic = (method.getAccessFlags() & ClassConstants.INTERNAL_ACC_STATIC) != 0; // Count the number of parameters, taking into account their categories. int parameterSize = ClassUtil.internalMethodParameterSize(descriptor, isStatic); // Reuse the existing parameters object, ensuring the right size. variables.reset(parameterSize); // Go over the parameters again. InternalTypeEnumeration internalTypeEnumeration = new InternalTypeEnumeration(descriptor); int parameterIndex = 0; int variableIndex = 0; // Put the 'this' reference in variable 0. if (!isStatic) { // Get the reference value. Value value = getMethodParameterValue(clazz, method, parameterIndex++, ClassUtil.internalTypeFromClassName(clazz.getName()), clazz); // Store the value in variable 0. variables.store(variableIndex++, value); } Clazz[] referencedClasses = ((ProgramMethod)method).referencedClasses; int referencedClassIndex = 0; // Set up the variables corresponding to the parameter types and values. while (internalTypeEnumeration.hasMoreTypes()) { String type = internalTypeEnumeration.nextType(); Clazz referencedClass = referencedClasses != null && ClassUtil.isInternalClassType(type) ? referencedClasses[referencedClassIndex++] : null; // Get the parameter value. Value value = getMethodParameterValue(clazz, method, parameterIndex++, type, referencedClass); // Store the value in the corresponding variable. variables.store(variableIndex++, value); // Increment the variable index again for Category 2 values. if (value.isCategory2()) { variableIndex++; } } } public void exitMethod(Clazz clazz, Method method, Value returnValue) { setMethodReturnValue(clazz, method, returnValue); } public void invokeMember(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction, Stack stack) { int constantIndex = constantInstruction.constantIndex; switch (constantInstruction.opcode) { case InstructionConstants.OP_GETSTATIC: isStatic = true; isLoad = true; break; case InstructionConstants.OP_PUTSTATIC: isStatic = true; isLoad = false; break; case InstructionConstants.OP_GETFIELD: isStatic = false; isLoad = true; break; case InstructionConstants.OP_PUTFIELD: isStatic = false; isLoad = false; break; case InstructionConstants.OP_INVOKESTATIC: isStatic = true; break; case InstructionConstants.OP_INVOKEVIRTUAL: case InstructionConstants.OP_INVOKESPECIAL: case InstructionConstants.OP_INVOKEINTERFACE: isStatic = false; break; } // Pop the parameters and push the return value. this.stack = stack; clazz.constantPoolEntryAccept(constantIndex, this); this.stack = null; } // Implementations for ConstantVisitor. public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant) { // Pop the field value, if applicable. if (!isLoad) { setFieldValue(clazz, fieldrefConstant, stack.pop()); } // Pop the reference value, if applicable. if (!isStatic) { setFieldClassValue(clazz, fieldrefConstant, stack.apop()); } // Push the field value, if applicable. if (isLoad) { String type = fieldrefConstant.getType(clazz); stack.push(getFieldValue(clazz, fieldrefConstant, type)); } } public void visitAnyMethodrefConstant(Clazz clazz, RefConstant methodrefConstant) { String type = methodrefConstant.getType(clazz); // Count the number of parameters. int parameterCount = ClassUtil.internalMethodParameterCount(type); if (!isStatic) { parameterCount++; } // Pop the parameters and the class reference, in reverse order. for (int parameterIndex = parameterCount-1; parameterIndex >= 0; parameterIndex--) { setMethodParameterValue(clazz, methodrefConstant, parameterIndex, stack.pop()); } // Push the return value, if applicable. String returnType = ClassUtil.internalMethodReturnType(type); if (returnType.charAt(0) != ClassConstants.INTERNAL_TYPE_VOID) { stack.push(getMethodReturnValue(clazz, methodrefConstant, returnType)); } } /** * Sets the class through which the specified field is accessed. */ protected void setFieldClassValue(Clazz clazz, RefConstant refConstant, ReferenceValue value) { // We don't care about the new value. } /** * Returns the class though which the specified field is accessed. */ protected Value getFieldClassValue(Clazz clazz, RefConstant refConstant, String type) { // Try to figure out the class of the return type. returnTypeClass = null; refConstant.referencedMemberAccept(this); return valueFactory.createValue(type, returnTypeClass, true); } /** * Sets the value of the specified field. */ protected void setFieldValue(Clazz clazz, RefConstant refConstant, Value value) { // We don't care about the new field value. } /** * Returns the value of the specified field. */ protected Value getFieldValue(Clazz clazz, RefConstant refConstant, String type) { // Try to figure out the class of the return type. returnTypeClass = null; refConstant.referencedMemberAccept(this); return valueFactory.createValue(type, returnTypeClass, true); } /** * Sets the value of the specified method parameter. */ protected void setMethodParameterValue(Clazz clazz, RefConstant refConstant, int parameterIndex, Value value) { // We don't care about the parameter value. } /** * Returns the value of the specified method parameter. */ protected Value getMethodParameterValue(Clazz clazz, Method method, int parameterIndex, String type, Clazz referencedClass) { return valueFactory.createValue(type, referencedClass, true); } /** * Sets the return value of the specified method. */ protected void setMethodReturnValue(Clazz clazz, Method method, Value value) { // We don't care about the return value. } /** * Returns the return value of the specified method. */ protected Value getMethodReturnValue(Clazz clazz, RefConstant refConstant, String type) { // Try to figure out the class of the return type. returnTypeClass = null; refConstant.referencedMemberAccept(this); return valueFactory.createValue(type, returnTypeClass, true); } // Implementations for MemberVisitor. public void visitProgramField(ProgramClass programClass, ProgramField programField) { returnTypeClass = programField.referencedClass; } public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { Clazz[] referencedClasses = programMethod.referencedClasses; if (referencedClasses != null) { returnTypeClass = referencedClasses[referencedClasses.length - 1]; } } public void visitLibraryField(LibraryClass programClass, LibraryField programField) { returnTypeClass = programField.referencedClass; } public void visitLibraryMethod(LibraryClass programClass, LibraryMethod programMethod) { Clazz[] referencedClasses = programMethod.referencedClasses; if (referencedClasses != null) { returnTypeClass = referencedClasses[referencedClasses.length - 1]; } } // public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) // { // } }