/* * ProGuard -- shrinking, optimization, obfuscation, and preverification * of Java bytecode. * * Copyright (c) 2002-2010 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.optimize.info; import proguard.classfile.*; import proguard.classfile.instruction.*; import proguard.classfile.instruction.visitor.InstructionVisitor; import proguard.classfile.attribute.visitor.AttributeVisitor; import proguard.classfile.attribute.*; import proguard.classfile.util.*; import proguard.classfile.visitor.MemberVisitor; import proguard.optimize.evaluation.PartialEvaluator; import proguard.evaluation.value.*; /** * This MemberVisitor counts the parameters and marks the used parameters * of the methods that it visits. It also marks the 'this' parameters of * methods that have hierarchies. * * @author Eric Lafortune */ public class ParameterUsageMarker extends SimplifiedVisitor implements MemberVisitor, AttributeVisitor, InstructionVisitor { private static final boolean DEBUG = false; private final boolean markThisParameter; private final boolean markAllParameters; private final PartialEvaluator partialEvaluator = new PartialEvaluator(); /** * Creates a new ParameterUsageMarker. */ public ParameterUsageMarker() { this(false, false); } /** * Creates a new ParameterUsageMarker that optionally marks all parameters. * @param markThisParameter specifies whether all 'this' parameters should * be marked as being used. * @param markAllParameters specifies whether all other parameters should * be marked as being used. */ public ParameterUsageMarker(boolean markThisParameter, boolean markAllParameters) { this.markThisParameter = markThisParameter; this.markAllParameters = markAllParameters; } // Implementations for MemberVisitor. public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) { int parameterSize = ClassUtil.internalMethodParameterSize(programMethod.getDescriptor(programClass), programMethod.getAccessFlags()); if (parameterSize > 0) { int accessFlags = programMethod.getAccessFlags(); // Must we mark the 'this' parameter? if (markThisParameter && (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0) { // Mark the 'this' parameter. markParameterUsed(programMethod, 0); } // Must we mark all other parameters? if (markAllParameters) { // Mark all parameters, without the 'this' parameter. markUsedParameters(programMethod, (accessFlags & ClassConstants.INTERNAL_ACC_STATIC) != 0 ? -1L : -2L); } // Is it a native method? if ((accessFlags & ClassConstants.INTERNAL_ACC_NATIVE) != 0) { // Mark all parameters. markUsedParameters(programMethod, -1L); } // Is it an abstract method? else if ((accessFlags & ClassConstants.INTERNAL_ACC_ABSTRACT) != 0) { // Mark the 'this' parameter. markParameterUsed(programMethod, 0); } // Is it a non-native, concrete method? else { // Is the method not static, but synchronized, or can it have // other implementations, or is it a class instance initializer? if ((accessFlags & ClassConstants.INTERNAL_ACC_STATIC) == 0 && ((accessFlags & ClassConstants.INTERNAL_ACC_SYNCHRONIZED) != 0 || programClass.mayHaveImplementations(programMethod) || programMethod.getName(programClass).equals(ClassConstants.INTERNAL_METHOD_NAME_INIT))) { // Mark the 'this' parameter. markParameterUsed(programMethod, 0); } // Mark the parameters that are used by the code. programMethod.attributesAccept(programClass, this); } if (DEBUG) { System.out.print("ParameterUsageMarker: ["+programClass.getName() +"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]: "); for (int index = 0; index < parameterSize; index++) { System.out.print(isParameterUsed(programMethod, index) ? '+' : '-'); } System.out.println(); } } // Set the parameter size. setParameterSize(programMethod, parameterSize); } public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) { // Can the method have other implementations? if (libraryClass.mayHaveImplementations(libraryMethod)) { // All implementations must keep all parameters of this method, // including the 'this' parameter. markUsedParameters(libraryMethod, -1L); } } // Implementations for AttributeVisitor. public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) { // Evaluate the code. partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); // Mark the parameters that are used by the code. codeAttribute.instructionsAccept(clazz, method, this); } // Implementations for InstructionVisitor. public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) { if (partialEvaluator.isTraced(offset) && variableInstruction.isLoad()) { int parameterIndex = variableInstruction.variableIndex; if (parameterIndex < codeAttribute.u2maxLocals) { Value producer = partialEvaluator.getVariablesBefore(offset).getProducerValue(parameterIndex); if (producer != null && producer.instructionOffsetValue().contains(PartialEvaluator.AT_METHOD_ENTRY)) { // Mark the variable. markParameterUsed(method, parameterIndex); // Account for Category 2 instructions, which take up two entries. if (variableInstruction.isCategory2()) { markParameterUsed(method, parameterIndex + 1); } } } } } // Small utility methods. /** * Sets the total size of the parameters. */ private static void setParameterSize(Method method, int parameterSize) { MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); if (info != null) { info.setParameterSize(parameterSize); } } /** * Returns the total size of the parameters. */ public static int getParameterSize(Method method) { MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); return info != null ? info.getParameterSize() : 0; } /** * Marks the given parameter as being used. */ public static void markParameterUsed(Method method, int variableIndex) { MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); if (info != null) { info.setParameterUsed(variableIndex); } } /** * Marks the given parameters as being used. */ public static void markUsedParameters(Method method, long usedParameters) { MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); if (info != null) { info.setUsedParameters(info.getUsedParameters() | usedParameters); } } /** * Returns whether the given parameter is being used. */ public static boolean isParameterUsed(Method method, int variableIndex) { MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); return info == null || info.isParameterUsed(variableIndex); } /** * Returns which parameters are being used. */ public static long getUsedParameters(Method method) { MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); return info != null ? info.getUsedParameters() : -1L; } }