diff options
Diffstat (limited to 'src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java')
-rw-r--r-- | src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java | 820 |
1 files changed, 0 insertions, 820 deletions
diff --git a/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java b/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java deleted file mode 100644 index b5a2396..0000000 --- a/src/proguard/optimize/evaluation/SimpleEnumUseSimplifier.java +++ /dev/null @@ -1,820 +0,0 @@ -/* - * ProGuard -- shrinking, optimization, obfuscation, and preverification - * of Java bytecode. - * - * Copyright (c) 2002-2014 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.evaluation; - -import proguard.classfile.*; -import proguard.classfile.attribute.*; -import proguard.classfile.attribute.visitor.AttributeVisitor; -import proguard.classfile.constant.*; -import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.editor.*; -import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.evaluation.value.*; -import proguard.optimize.info.SimpleEnumMarker; - -/** - * This AttributeVisitor simplifies the use of enums in the code attributes that - * it visits. - * - * @see SimpleEnumMarker - * @see MemberReferenceFixer - * @author Eric Lafortune - */ -public class SimpleEnumUseSimplifier -extends SimplifiedVisitor -implements AttributeVisitor, - InstructionVisitor, - ConstantVisitor, - ParameterVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = System.getProperty("enum") != null; - //*/ - - private final InstructionVisitor extraInstructionVisitor; - - private final PartialEvaluator partialEvaluator; - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(true, true); - private final ConstantVisitor nullParameterFixer = new ReferencedMemberVisitor(new AllParameterVisitor(this)); - - // Fields acting as parameters and return values for the visitor methods. - private Clazz invocationClazz; - private Method invocationMethod; - private CodeAttribute invocationCodeAttribute; - private int invocationOffset; - private boolean isSimpleEnum; - - - /** - * Creates a new SimpleEnumUseSimplifier. - */ - public SimpleEnumUseSimplifier() - { - this(new PartialEvaluator(), null); - } - - - /** - * Creates a new SimpleEnumDescriptorSimplifier. - * @param partialEvaluator the partial evaluator that will - * execute the code and provide - * information about the results. - * @param extraInstructionVisitor an optional extra visitor for all - * simplified instructions. - */ - public SimpleEnumUseSimplifier(PartialEvaluator partialEvaluator, - InstructionVisitor extraInstructionVisitor) - { - this.partialEvaluator = partialEvaluator; - this.extraInstructionVisitor = extraInstructionVisitor; - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - if (DEBUG) - { - System.out.println("SimpleEnumUseSimplifier: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); - } - - // Skip the non-static methods of simple enum classes. - if (SimpleEnumMarker.isSimpleEnum(clazz) && - (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0) - { - return; - } - - // Evaluate the method. - partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); - - int codeLength = codeAttribute.u4codeLength; - - // Reset the code changes. - codeAttributeEditor.reset(codeLength); - - // Replace any instructions that can be simplified. - for (int offset = 0; offset < codeLength; offset++) - { - if (partialEvaluator.isTraced(offset)) - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - - instruction.accept(clazz, method, codeAttribute, offset, this); - } - } - - // Apply all accumulated changes to the code. - codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); - } - - - // Implementations for InstructionVisitor. - - public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) - { - switch (simpleInstruction.opcode) - { - case InstructionConstants.OP_AALOAD: - { - if (isPushingSimpleEnum(offset)) - { - // Load a simple enum integer from an integer array. - replaceInstruction(clazz, - offset, - simpleInstruction, - new SimpleInstruction( - InstructionConstants.OP_IALOAD)); - } - break; - } - case InstructionConstants.OP_AASTORE: - { - if (isPoppingSimpleEnumArray(offset, 2)) - { - // Store a simple enum integer in an integer array. - replaceInstruction(clazz, - offset, - simpleInstruction, - new SimpleInstruction(InstructionConstants.OP_IASTORE)); - - // Replace any producers of null constants. - replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); - } - break; - } - case InstructionConstants.OP_ARETURN: - { - if (isReturningSimpleEnum(clazz, method)) - { - // Return a simple enum integer instead of an enum. - replaceInstruction(clazz, - offset, - simpleInstruction, - new SimpleInstruction(InstructionConstants.OP_IRETURN)); - - // Replace any producers of null constants. - replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); - } - break; - } - } - } - - - public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) - { - int variableIndex = variableInstruction.variableIndex; - - switch (variableInstruction.opcode) - { - case InstructionConstants.OP_ALOAD: - case InstructionConstants.OP_ALOAD_0: - case InstructionConstants.OP_ALOAD_1: - case InstructionConstants.OP_ALOAD_2: - case InstructionConstants.OP_ALOAD_3: - { - if (isPushingSimpleEnum(offset)) - { - // Load a simple enum integer instead of an enum. - replaceInstruction(clazz, - offset, - variableInstruction, - new VariableInstruction(InstructionConstants.OP_ILOAD, - variableIndex)); - - // Replace any producers of null constants. - replaceNullVariableProducers(clazz, - method, - codeAttribute, - offset, - variableIndex); - } - break; - } - case InstructionConstants.OP_ASTORE: - case InstructionConstants.OP_ASTORE_0: - case InstructionConstants.OP_ASTORE_1: - case InstructionConstants.OP_ASTORE_2: - case InstructionConstants.OP_ASTORE_3: - { - if (!partialEvaluator.isSubroutineStart(offset) && - isPoppingSimpleEnum(offset)) - { - // Store a simple enum integer instead of an enum. - replaceInstruction(clazz, - offset, - variableInstruction, - new VariableInstruction(InstructionConstants.OP_ISTORE, - variableIndex)); - - // Replace any producers of null constants. - replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); - } - break; - } - } - } - - - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) - { - switch (constantInstruction.opcode) - { - case InstructionConstants.OP_PUTSTATIC: - case InstructionConstants.OP_PUTFIELD: - { - // Replace any producers of null constants. - invocationClazz = clazz; - invocationMethod = method; - invocationCodeAttribute = codeAttribute; - invocationOffset = offset; - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, - nullParameterFixer); - break; - } - case InstructionConstants.OP_INVOKEVIRTUAL: - { - // Check if the instruction is calling a simple enum. - String invokedMethodName = - clazz.getRefName(constantInstruction.constantIndex); - String invokedMethodType = - clazz.getRefType(constantInstruction.constantIndex); - int stackEntryIndex = - ClassUtil.internalMethodParameterSize(invokedMethodType); - if (isPoppingSimpleEnum(offset, stackEntryIndex)) - { - replaceSupportedMethod(clazz, - offset, - constantInstruction, - invokedMethodName, - invokedMethodType); - } - - // Fall through to check the parameters. - } - case InstructionConstants.OP_INVOKESPECIAL: - case InstructionConstants.OP_INVOKESTATIC: - case InstructionConstants.OP_INVOKEINTERFACE: - { - // Replace any producers of null constants. - invocationClazz = clazz; - invocationMethod = method; - invocationCodeAttribute = codeAttribute; - invocationOffset = offset; - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, - nullParameterFixer); - break; - } - case InstructionConstants.OP_ANEWARRAY: - { - int constantIndex = constantInstruction.constantIndex; - - if (isReferencingSimpleEnum(clazz, constantIndex) && - !ClassUtil.isInternalArrayType(clazz.getClassName(constantIndex))) - { - // Create an integer array instead of an enum array. - replaceInstruction(clazz, - offset, - constantInstruction, - new SimpleInstruction(InstructionConstants.OP_NEWARRAY, - InstructionConstants.ARRAY_T_INT)); - } - break; - } - case InstructionConstants.OP_CHECKCAST: - { - if (isPoppingSimpleEnum(offset)) - { - // Enum classes can only be simple if the checkcast - // succeeds, so we can delete it. - deleteInstruction(clazz, - offset, - constantInstruction); - - // Replace any producers of null constants. - replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); - } - break; - } - case InstructionConstants.OP_INSTANCEOF: - { - if (isPoppingSimpleEnum(offset)) - { - // Enum classes can only be simple if the instanceof - // succeeds, so we can push a constant result. - replaceInstruction(clazz, - offset, - constantInstruction, - new SimpleInstruction(InstructionConstants.OP_ICONST_1)); - - // Replace any producers of null constants. - replaceNullStackEntryProducers(clazz, method, codeAttribute, offset); - } - break; - } - } - } - - - public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) - { - switch (branchInstruction.opcode) - { - case InstructionConstants.OP_IFACMPEQ: - { - if (isPoppingSimpleEnum(offset)) - { - // Compare simple enum integers instead of enums. - replaceInstruction(clazz, - offset, - branchInstruction, - new BranchInstruction(InstructionConstants.OP_IFICMPEQ, - branchInstruction.branchOffset)); - } - break; - } - case InstructionConstants.OP_IFACMPNE: - { - if (isPoppingSimpleEnum(offset)) - { - // Compare simple enum integers instead of enums. - replaceInstruction(clazz, - offset, - branchInstruction, - new BranchInstruction(InstructionConstants.OP_IFICMPNE, - branchInstruction.branchOffset)); - } - break; - } - case InstructionConstants.OP_IFNULL: - { - if (isPoppingSimpleEnum(offset)) - { - // Compare with 0 instead of null. - replaceInstruction(clazz, - offset, - branchInstruction, - new BranchInstruction( - InstructionConstants.OP_IFEQ, - branchInstruction.branchOffset)); - } - break; - } - case InstructionConstants.OP_IFNONNULL: - { - if (isPoppingSimpleEnum(offset)) - { - // Compare with 0 instead of null. - replaceInstruction(clazz, - offset, - branchInstruction, - new BranchInstruction(InstructionConstants.OP_IFNE, - branchInstruction.branchOffset)); - } - break; - } - } - } - - - public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) - { - } - - - // Implementations for ConstantVisitor. - - public void visitAnyConstant(Clazz clazz, Constant constant) {} - - - public void visitStringConstant(Clazz clazz, StringConstant stringConstant) - { - // Does the constant refer to a simple enum type? - isSimpleEnum = isSimpleEnum(stringConstant.referencedClass); - } - - - public void visitClassConstant(Clazz clazz, ClassConstant classConstant) - { - // Does the constant refer to a simple enum type? - isSimpleEnum = isSimpleEnum(classConstant.referencedClass); - } - - - // Implementations for ParameterVisitor. - - public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass) - { - // Check if the parameter is passing a simple enum as a more general - // type. - if (!ClassUtil.isInternalPrimitiveType(parameterType.charAt(0)) && - isSimpleEnum(referencedClass)) - { - // Replace any producers of null constants for this parameter. - int stackEntryIndex = parameterSize - parameterOffset - 1; - - replaceNullStackEntryProducers(invocationClazz, - invocationMethod, - invocationCodeAttribute, - invocationOffset, - stackEntryIndex); - } - } - - - // Small utility methods. - - /** - * Returns whether the constant at the given offset is referencing a - * simple enum class. - */ - private boolean isReferencingSimpleEnum(Clazz clazz, int constantIndex) - { - isSimpleEnum = false; - - clazz.constantPoolEntryAccept(constantIndex, this); - - return isSimpleEnum; - } - - - /** - * Returns whether the given method is returning a simple enum class. - */ - private boolean isReturningSimpleEnum(Clazz clazz, Method method) - { - String descriptor = method.getDescriptor(clazz); - String returnType = ClassUtil.internalMethodReturnType(descriptor); - - if (ClassUtil.isInternalClassType(returnType) && - !ClassUtil.isInternalArrayType(returnType)) - { - Clazz[] referencedClasses = - ((ProgramMethod)method).referencedClasses; - - if (referencedClasses != null) - { - int returnedClassIndex = - new DescriptorClassEnumeration(descriptor).classCount() - 1; - - Clazz returnedClass = referencedClasses[returnedClassIndex]; - - return isSimpleEnum(returnedClass); - } - } - - return false; - } - - - /** - * Returns whether the instruction at the given offset is pushing a simple - * enum class. - */ - private boolean isPushingSimpleEnum(int offset) - { - ReferenceValue referenceValue = - partialEvaluator.getStackAfter(offset).getTop(0).referenceValue(); - - Clazz referencedClass = referenceValue.getReferencedClass(); - - return isSimpleEnum(referencedClass) && - !ClassUtil.isInternalArrayType(referenceValue.getType()); - } - - - /** - * Returns whether the instruction at the given offset is popping a simple - * enum class. - */ - private boolean isPoppingSimpleEnum(int offset) - { - return isPoppingSimpleEnum(offset, 0); - } - - - /** - * Returns whether the instruction at the given offset is popping a simple - * enum class. - */ - private boolean isPoppingSimpleEnum(int offset, int stackEntryIndex) - { - ReferenceValue referenceValue = - partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); - - return isSimpleEnum(referenceValue.getReferencedClass()) && - !ClassUtil.isInternalArrayType(referenceValue.getType()); - } - - - /** - * Returns whether the instruction at the given offset is popping a simple - * enum type. This includes simple enum arrays. - */ - private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex) - { - ReferenceValue referenceValue = - partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); - - return isSimpleEnum(referenceValue.getReferencedClass()); - } - - - /** - * Returns whether the instruction at the given offset is popping a - * one-dimensional simple enum array. - */ - private boolean isPoppingSimpleEnumArray(int offset, int stackEntryIndex) - { - ReferenceValue referenceValue = - partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); - - return isSimpleEnum(referenceValue.getReferencedClass()) && - ClassUtil.internalArrayTypeDimensionCount(referenceValue.getType()) == 1; - } - - - /** - * Returns whether the given class is not null and a simple enum class. - */ - private boolean isSimpleEnum(Clazz clazz) - { - return clazz != null && - SimpleEnumMarker.isSimpleEnum(clazz); - } - - - /** - * Returns whether the specified enum method is supported for simple enums. - */ - private void replaceSupportedMethod(Clazz clazz, - int offset, - Instruction instruction, - String name, - String type) - { - if (name.equals(ClassConstants.METHOD_NAME_ORDINAL) && - type.equals(ClassConstants.METHOD_TYPE_ORDINAL)) - { - Instruction[] replacementInstructions = new Instruction[] - { - new SimpleInstruction(InstructionConstants.OP_ICONST_1), - new SimpleInstruction(InstructionConstants.OP_ISUB), - }; - - replaceInstructions(clazz, - offset, - instruction, - replacementInstructions); - } - } - - - /** - * Replaces the instruction at the given offset by the given instructions. - */ - private void replaceInstructions(Clazz clazz, - int offset, - Instruction instruction, - Instruction[] replacementInstructions) - { - if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstructions.length+" instructions"); - - codeAttributeEditor.replaceInstruction(offset, replacementInstructions); - - // Visit the instruction, if required. - if (extraInstructionVisitor != null) - { - // Note: we're not passing the right arguments for now, knowing that - // they aren't used anyway. - instruction.accept(clazz, null, null, offset, extraInstructionVisitor); - } - } - - - /** - * Replaces the instruction at the given offset by the given instruction, - * popping any now unused stack entries. - */ - private void replaceInstruction(Clazz clazz, - int offset, - Instruction instruction, - Instruction replacementInstruction) - { - // Pop unneeded stack entries if necessary. - int popCount = - instruction.stackPopCount(clazz) - - replacementInstruction.stackPopCount(clazz); - - insertPopInstructions(offset, popCount); - - if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)")); - - codeAttributeEditor.replaceInstruction(offset, replacementInstruction); - - // Visit the instruction, if required. - if (extraInstructionVisitor != null) - { - // Note: we're not passing the right arguments for now, knowing that - // they aren't used anyway. - instruction.accept(clazz, null, null, offset, extraInstructionVisitor); - } - } - - - /** - * Deletes the instruction at the given offset, popping any now unused - * stack entries. - */ - private void deleteInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - // Pop unneeded stack entries if necessary. - //int popCount = instruction.stackPopCount(clazz); - // - //insertPopInstructions(offset, popCount); - // - //if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset)+(popCount == 0 ? "" : " ("+popCount+" pops)")); - - if (DEBUG) System.out.println(" Deleting instruction "+instruction.toString(offset)); - - codeAttributeEditor.deleteInstruction(offset); - - // Visit the instruction, if required. - if (extraInstructionVisitor != null) - { - // Note: we're not passing the right arguments for now, knowing that - // they aren't used anyway. - instruction.accept(clazz, null, null, offset, extraInstructionVisitor); - } - } - - - /** - * Pops the given number of stack entries before the instruction at the - * given offset. - */ - private void insertPopInstructions(int offset, int popCount) - { - switch (popCount) - { - case 0: - { - break; - } - case 1: - { - // Insert a single pop instruction. - Instruction popInstruction = - new SimpleInstruction(InstructionConstants.OP_POP); - - codeAttributeEditor.insertBeforeInstruction(offset, - popInstruction); - break; - } - case 2: - { - // Insert a single pop2 instruction. - Instruction popInstruction = - new SimpleInstruction(InstructionConstants.OP_POP2); - - codeAttributeEditor.insertBeforeInstruction(offset, - popInstruction); - break; - } - default: - { - // Insert the specified number of pop instructions. - Instruction[] popInstructions = - new Instruction[popCount / 2 + popCount % 2]; - - Instruction popInstruction = - new SimpleInstruction(InstructionConstants.OP_POP2); - - for (int index = 0; index < popCount / 2; index++) - { - popInstructions[index] = popInstruction; - } - - if (popCount % 2 == 1) - { - popInstruction = - new SimpleInstruction(InstructionConstants.OP_POP); - - popInstructions[popCount / 2] = popInstruction; - } - - codeAttributeEditor.insertBeforeInstruction(offset, - popInstructions); - break; - } - } - } - - - /** - * Replaces aconst_null producers of the consumer of the top stack entry - * at the given offset by iconst_0. - */ - private void replaceNullStackEntryProducers(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - int consumerOffset) - { - replaceNullStackEntryProducers(clazz, method, codeAttribute, consumerOffset, 0); - } - - - /** - * Replaces aconst_null producers of the specified stack entry by - * iconst_0. - */ - private void replaceNullStackEntryProducers(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - int consumerOffset, - int stackEntryIndex) - { - InstructionOffsetValue producerOffsets = - partialEvaluator.getStackBefore(consumerOffset).getTopActualProducerValue(stackEntryIndex).instructionOffsetValue(); - - for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++) - { - int producerOffset = producerOffsets.instructionOffset(index); - - // TODO: A method might be pushing the null constant. - if (producerOffset >= 0 && - codeAttribute.code[producerOffset] == InstructionConstants.OP_ACONST_NULL) - { - // Replace pushing null by pushing 0. - replaceInstruction(clazz, - producerOffset, - new SimpleInstruction(InstructionConstants.OP_ACONST_NULL), - new SimpleInstruction(InstructionConstants.OP_ICONST_0)); - } - } - } - - - /** - * Replaces aconst_null/astore producers of the specified reference variable by - * iconst_0/istore. - */ - private void replaceNullVariableProducers(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - int consumerOffset, - int variableIndex) - { - InstructionOffsetValue producerOffsets = - partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); - - for (int index = 0; index < producerOffsets.instructionOffsetCount(); index++) - { - int producerOffset = producerOffsets.instructionOffset(index); - - if (producerOffset >= 0 && - partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue().isNull() == Value.ALWAYS) - { - // Replace loading null by loading 0. - replaceInstruction(clazz, - producerOffset, - new VariableInstruction(InstructionConstants.OP_ASTORE, variableIndex), - new VariableInstruction(InstructionConstants.OP_ISTORE, variableIndex)); - - // Replace pushing null by pushing 0. - replaceNullStackEntryProducers(clazz, method, codeAttribute, producerOffset); - } - } - } -} |