diff options
Diffstat (limited to 'src/proguard/optimize/evaluation')
15 files changed, 0 insertions, 8752 deletions
diff --git a/src/proguard/optimize/evaluation/EvaluationShrinker.java b/src/proguard/optimize/evaluation/EvaluationShrinker.java deleted file mode 100644 index daccec1..0000000 --- a/src/proguard/optimize/evaluation/EvaluationShrinker.java +++ /dev/null @@ -1,2298 +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.RefConstant; -import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.editor.CodeAttributeEditor; -import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.evaluation.TracedStack; -import proguard.evaluation.value.*; -import proguard.optimize.info.*; - -import java.util.Arrays; - -/** - * This AttributeVisitor simplifies the code attributes that it visits, based - * on partial evaluation. - * - * @author Eric Lafortune - */ -public class EvaluationShrinker -extends SimplifiedVisitor -implements AttributeVisitor -{ - //* - private static final boolean DEBUG_RESULTS = false; - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = System.getProperty("es") != null; - private static boolean DEBUG_RESULTS = DEBUG; - //*/ - - private static final int UNSUPPORTED = -1; - private static final int NOP = InstructionConstants.OP_NOP & 0xff; - private static final int POP = InstructionConstants.OP_POP & 0xff; - private static final int POP2 = InstructionConstants.OP_POP2 & 0xff; - private static final int DUP = InstructionConstants.OP_DUP & 0xff; - private static final int DUP_X1 = InstructionConstants.OP_DUP_X1 & 0xff; - private static final int DUP_X2 = InstructionConstants.OP_DUP_X2 & 0xff; - private static final int DUP2 = InstructionConstants.OP_DUP2 & 0xff; - private static final int DUP2_X1 = InstructionConstants.OP_DUP2_X1 & 0xff; - private static final int DUP2_X2 = InstructionConstants.OP_DUP2_X2 & 0xff; - private static final int SWAP = InstructionConstants.OP_SWAP & 0xff; - private static final int MOV_X2 = DUP_X2 | (POP << 8); - private static final int MOV2_X1 = DUP2_X1 | (POP2 << 8); - private static final int MOV2_X2 = DUP2_X2 | (POP2 << 8); - private static final int POP_X1 = SWAP | (POP << 8); - private static final int POP_X2 = DUP2_X1 | (POP2 << 8) | (POP << 16); - private static final int POP_X3 = UNSUPPORTED; - private static final int POP2_X1 = DUP_X2 | (POP << 8) | (POP2 << 16); - private static final int POP2_X2 = DUP2_X2 | (POP2 << 8) | (POP2 << 16); - private static final int POP3 = POP2 | (POP << 8); - private static final int POP4 = POP2 | (POP2 << 8); - private static final int POP_DUP = POP | (DUP << 8); - private static final int POP_SWAP_POP = POP | (SWAP << 8) | (POP << 16); - private static final int POP2_SWAP_POP = POP2 | (SWAP << 8) | (POP << 16); - private static final int SWAP_DUP_X1 = SWAP | (DUP_X1 << 8); - private static final int SWAP_DUP_X1_SWAP = SWAP | (DUP_X1 << 8) | (SWAP << 16); - private static final int SWAP_POP_DUP = SWAP | (POP << 8) | (DUP << 16); - private static final int SWAP_POP_DUP_X1 = SWAP | (POP << 8) | (DUP_X1 << 16); - private static final int DUP_X2_POP2 = DUP_X2 | (POP2 << 8); - private static final int DUP2_X1_POP3 = DUP2_X1 | (POP2 << 8) | (POP << 16); - private static final int DUP2_X2_POP3 = DUP2_X2 | (POP2 << 8) | (POP << 16); - private static final int DUP2_X2_SWAP_POP = DUP2_X2 | (SWAP << 8) | (POP << 16); - - - private final InstructionVisitor extraDeletedInstructionVisitor; - private final InstructionVisitor extraAddedInstructionVisitor; - - private final PartialEvaluator partialEvaluator; - private final PartialEvaluator simplePartialEvaluator = new PartialEvaluator(); - private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); - private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier(); - private final MyProducerMarker producerMarker = new MyProducerMarker(); - private final MyVariableInitializationMarker variableInitializationMarker = new MyVariableInitializationMarker(); - private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer(); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, false); - - private boolean[][] stacksNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; - private boolean[][] stacksSimplifiedBefore = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_STACK_SIZE]; - private boolean[] instructionsNecessary = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; - - private int maxMarkedOffset; - - - /** - * Creates a new EvaluationShrinker. - */ - public EvaluationShrinker() - { - this(new PartialEvaluator(), null, null); - } - - - /** - * Creates a new EvaluationShrinker. - * @param partialEvaluator the partial evaluator that will - * execute the code and provide - * information about the results. - * @param extraDeletedInstructionVisitor an optional extra visitor for all - * deleted instructions. - * @param extraAddedInstructionVisitor an optional extra visitor for all - * added instructions. - */ - public EvaluationShrinker(PartialEvaluator partialEvaluator, - InstructionVisitor extraDeletedInstructionVisitor, - InstructionVisitor extraAddedInstructionVisitor) - { - this.partialEvaluator = partialEvaluator; - this.extraDeletedInstructionVisitor = extraDeletedInstructionVisitor; - this.extraAddedInstructionVisitor = extraAddedInstructionVisitor; - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { -// DEBUG = DEBUG_RESULTS = -// clazz.getName().equals("abc/Def") && -// method.getName(clazz).equals("abc"); - - // TODO: Remove this when the evaluation shrinker has stabilized. - // Catch any unexpected exceptions from the actual visiting method. - try - { - // Process the code. - visitCodeAttribute0(clazz, method, codeAttribute); - } - catch (RuntimeException ex) - { - System.err.println("Unexpected error while shrinking instructions after partial evaluation:"); - System.err.println(" Class = ["+clazz.getName()+"]"); - System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); - System.err.println("Not optimizing this method"); - - if (DEBUG) - { - method.accept(clazz, new ClassPrinter()); - - throw ex; - } - } - } - - - public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - if (DEBUG_RESULTS) - { - System.out.println(); - System.out.println("EvaluationShrinker ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - } - - // Initialize the necessary array. - initializeNecessary(codeAttribute); - - // Evaluate the method. - partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); - - // Evaluate the method the way the JVM verifier would do it. - simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); - - int codeLength = codeAttribute.u4codeLength; - - // Reset the code changes. - codeAttributeEditor.reset(codeLength); - - // Mark any unused method parameters on the stack. - if (DEBUG) System.out.println("Invocation simplification:"); - - for (int offset = 0; offset < codeLength; offset++) - { - if (partialEvaluator.isTraced(offset)) - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - - instruction.accept(clazz, method, codeAttribute, offset, unusedParameterSimplifier); - } - } - - // Mark all essential instructions that have been encountered as used. - if (DEBUG) System.out.println("Usage initialization: "); - - maxMarkedOffset = -1; - - // The invocation of the "super" or "this" <init> method inside a - // constructor is always necessary. - int superInitializationOffset = partialEvaluator.superInitializationOffset(); - if (superInitializationOffset != PartialEvaluator.NONE) - { - if (DEBUG) System.out.print("(super.<init>)"); - - markInstruction(superInitializationOffset); - } - - // Also mark infinite loops and instructions that cause side effects. - for (int offset = 0; offset < codeLength; offset++) - { - if (partialEvaluator.isTraced(offset)) - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - - // Mark that the instruction is necessary if it is an infinite loop. - if (instruction.opcode == InstructionConstants.OP_GOTO && - ((BranchInstruction)instruction).branchOffset == 0) - { - if (DEBUG) System.out.print("(infinite loop)"); - markInstruction(offset); - } - - // Mark that the instruction is necessary if it has side effects. - else if (sideEffectInstructionChecker.hasSideEffects(clazz, - method, - codeAttribute, - offset, - instruction)) - { - markInstruction(offset); - } - } - } - if (DEBUG) System.out.println(); - - - // Globally mark instructions and their produced variables and stack - // entries on which necessary instructions depend. - // Instead of doing this recursively, we loop across all instructions, - // starting at the highest previously unmarked instruction that has - // been been marked. - if (DEBUG) System.out.println("Usage marking:"); - - while (maxMarkedOffset >= 0) - { - int offset = maxMarkedOffset; - - maxMarkedOffset = offset - 1; - - if (partialEvaluator.isTraced(offset)) - { - if (isInstructionNecessary(offset)) - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - - instruction.accept(clazz, method, codeAttribute, offset, producerMarker); - } - - // Check if this instruction is a branch origin from a branch - // that straddles some marked code. - markStraddlingBranches(offset, - partialEvaluator.branchTargets(offset), - true); - - // Check if this instruction is a branch target from a branch - // that straddles some marked code. - markStraddlingBranches(offset, - partialEvaluator.branchOrigins(offset), - false); - } - - if (DEBUG) - { - if (maxMarkedOffset > offset) - { - System.out.println(" -> "+maxMarkedOffset); - } - } - } - if (DEBUG) System.out.println(); - - - // Mark variable initializations, even if they aren't strictly necessary. - // The virtual machine's verification step is not smart enough to see - // this, and may complain otherwise. - if (DEBUG) System.out.println("Initialization marking: "); - - for (int offset = 0; offset < codeLength; offset++) - { - if (isInstructionNecessary(offset)) - { - // Mark initializations of the required instruction. - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - - instruction.accept(clazz, method, codeAttribute, offset, variableInitializationMarker); - } - } - if (DEBUG) System.out.println(); - - - // Locally fix instructions, in order to keep the stack consistent. - if (DEBUG) System.out.println("Stack consistency fixing:"); - - maxMarkedOffset = codeLength - 1; - - while (maxMarkedOffset >= 0) - { - int offset = maxMarkedOffset; - - maxMarkedOffset = offset - 1; - - if (partialEvaluator.isTraced(offset)) - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - - instruction.accept(clazz, method, codeAttribute, offset, stackConsistencyFixer); - - // Check if this instruction is a branch origin from a branch - // that straddles some marked code. - markStraddlingBranches(offset, - partialEvaluator.branchTargets(offset), - true); - - // Check if this instruction is a branch target from a branch - // that straddles some marked code. - markStraddlingBranches(offset, - partialEvaluator.branchOrigins(offset), - false); - } - } - if (DEBUG) System.out.println(); - - - // Replace traced but unmarked backward branches by infinite loops. - // The virtual machine's verification step is not smart enough to see - // the code isn't reachable, and may complain otherwise. - // Any clearly unreachable code will still be removed elsewhere. - if (DEBUG) System.out.println("Infinite loop fixing:"); - - for (int offset = 0; offset < codeLength; offset++) - { - // Is it a traced but unmarked backward branch, without an unmarked - // straddling forward branch? Note that this is still a heuristic. - if (partialEvaluator.isTraced(offset) && - !isInstructionNecessary(offset) && - isAllSmallerThanOrEqual(partialEvaluator.branchTargets(offset), - offset) && - !isAnyUnnecessaryInstructionBranchingOver(lastNecessaryInstructionOffset(offset), - offset)) - { - replaceByInfiniteLoop(clazz, offset); - } - } - if (DEBUG) System.out.println(); - - - // Insert infinite loops after jumps to subroutines that don't return. - // The virtual machine's verification step is not smart enough to see - // the code isn't reachable, and may complain otherwise. - if (DEBUG) System.out.println("Non-returning subroutine fixing:"); - - for (int offset = 0; offset < codeLength; offset++) - { - // Is it a traced but unmarked backward branch, without an unmarked - // straddling forward branch? Note that this is still a heuristic. - if (isInstructionNecessary(offset) && - partialEvaluator.isSubroutineInvocation(offset)) - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - - int nextOffset = offset + instruction.length(offset); - if (!isInstructionNecessary(nextOffset)) - { - replaceByInfiniteLoop(clazz, nextOffset); - } - } - } - if (DEBUG) System.out.println(); - - - // Delete all instructions that are not used. - int offset = 0; - do - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - if (!isInstructionNecessary(offset)) - { - codeAttributeEditor.clearModifications(offset); - codeAttributeEditor.deleteInstruction(offset); - - // Visit the instruction, if required. - if (extraDeletedInstructionVisitor != null) - { - instruction.accept(clazz, method, codeAttribute, offset, extraDeletedInstructionVisitor); - } - } - - offset += instruction.length(offset); - } - while (offset < codeLength); - - - if (DEBUG_RESULTS) - { - System.out.println("Simplification results:"); - - offset = 0; - do - { - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - System.out.println((isInstructionNecessary(offset) ? " + " : " - ")+instruction.toString(offset)); - - if (partialEvaluator.isTraced(offset)) - { - int initializationOffset = partialEvaluator.initializationOffset(offset); - if (initializationOffset != PartialEvaluator.NONE) - { - System.out.println(" is to be initialized at ["+initializationOffset+"]"); - } - - InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); - if (branchTargets != null) - { - System.out.println(" has overall been branching to "+branchTargets); - } - - boolean deleted = codeAttributeEditor.deleted[offset]; - if (isInstructionNecessary(offset) && deleted) - { - System.out.println(" is deleted"); - } - - Instruction preInsertion = codeAttributeEditor.preInsertions[offset]; - if (preInsertion != null) - { - System.out.println(" is preceded by: "+preInsertion); - } - - Instruction replacement = codeAttributeEditor.replacements[offset]; - if (replacement != null) - { - System.out.println(" is replaced by: "+replacement); - } - - Instruction postInsertion = codeAttributeEditor.postInsertions[offset]; - if (postInsertion != null) - { - System.out.println(" is followed by: "+postInsertion); - } - } - - offset += instruction.length(offset); - } - while (offset < codeLength); - } - - // Apply all accumulated changes to the code. - codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); - } - - - /** - * This MemberVisitor marks stack entries that aren't necessary because - * parameters aren't used in the methods that are visited. - */ - private class MyUnusedParameterSimplifier - extends SimplifiedVisitor - implements InstructionVisitor, - ConstantVisitor, - MemberVisitor - { - private int invocationOffset; - private ConstantInstruction invocationInstruction; - - - // Implementations for InstructionVisitor. - - public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {} - - - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) - { - switch (constantInstruction.opcode) - { - case InstructionConstants.OP_INVOKEVIRTUAL: - case InstructionConstants.OP_INVOKESPECIAL: - case InstructionConstants.OP_INVOKESTATIC: - case InstructionConstants.OP_INVOKEINTERFACE: - this.invocationOffset = offset; - this.invocationInstruction = constantInstruction; - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this); - break; - } - } - - - // Implementations for ConstantVisitor. - - public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) - { - refConstant.referencedMemberAccept(this); - } - - - // Implementations for MemberVisitor. - - public void visitAnyMember(Clazz clazz, Member member) {} - - - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) - { - // Get the total size of the parameters. - int parameterSize = ParameterUsageMarker.getParameterSize(programMethod); - - // Make the method invocation static, if possible. - if ((programMethod.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 && - !ParameterUsageMarker.isParameterUsed(programMethod, 0)) - { - replaceByStaticInvocation(programClass, - invocationOffset, - invocationInstruction); - } - - // Remove unused parameters. - for (int index = 0; index < parameterSize; index++) - { - if (!ParameterUsageMarker.isParameterUsed(programMethod, index)) - { - TracedStack stack = - partialEvaluator.getStackBefore(invocationOffset); - - int stackIndex = stack.size() - parameterSize + index; - - if (DEBUG) - { - System.out.println(" ["+invocationOffset+"] Ignoring parameter #"+index+" of "+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"] (stack entry #"+stackIndex+" ["+stack.getBottom(stackIndex)+"])"); - System.out.println(" Full stack: "+stack); - } - - markStackSimplificationBefore(invocationOffset, stackIndex); - } - } - } - } - - - /** - * This InstructionVisitor marks the producing instructions and produced - * variables and stack entries of the instructions that it visits. - * Simplified method arguments are ignored. - */ - private class MyProducerMarker - extends SimplifiedVisitor - implements InstructionVisitor - { - // Implementations for InstructionVisitor. - - public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) - { - markStackProducers(clazz, offset, instruction); - } - - - public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) - { - switch (simpleInstruction.opcode) - { - case InstructionConstants.OP_DUP: - conditionallyMarkStackEntryProducers(offset, 0, 0); - conditionallyMarkStackEntryProducers(offset, 1, 0); - break; - case InstructionConstants.OP_DUP_X1: - conditionallyMarkStackEntryProducers(offset, 0, 0); - conditionallyMarkStackEntryProducers(offset, 1, 1); - conditionallyMarkStackEntryProducers(offset, 2, 0); - break; - case InstructionConstants.OP_DUP_X2: - conditionallyMarkStackEntryProducers(offset, 0, 0); - conditionallyMarkStackEntryProducers(offset, 1, 1); - conditionallyMarkStackEntryProducers(offset, 2, 2); - conditionallyMarkStackEntryProducers(offset, 3, 0); - break; - case InstructionConstants.OP_DUP2: - conditionallyMarkStackEntryProducers(offset, 0, 0); - conditionallyMarkStackEntryProducers(offset, 1, 1); - conditionallyMarkStackEntryProducers(offset, 2, 0); - conditionallyMarkStackEntryProducers(offset, 3, 1); - break; - case InstructionConstants.OP_DUP2_X1: - conditionallyMarkStackEntryProducers(offset, 0, 0); - conditionallyMarkStackEntryProducers(offset, 1, 1); - conditionallyMarkStackEntryProducers(offset, 2, 2); - conditionallyMarkStackEntryProducers(offset, 3, 0); - conditionallyMarkStackEntryProducers(offset, 4, 1); - break; - case InstructionConstants.OP_DUP2_X2: - conditionallyMarkStackEntryProducers(offset, 0, 0); - conditionallyMarkStackEntryProducers(offset, 1, 1); - conditionallyMarkStackEntryProducers(offset, 2, 2); - conditionallyMarkStackEntryProducers(offset, 3, 3); - conditionallyMarkStackEntryProducers(offset, 4, 0); - conditionallyMarkStackEntryProducers(offset, 5, 1); - break; - case InstructionConstants.OP_SWAP: - conditionallyMarkStackEntryProducers(offset, 0, 1); - conditionallyMarkStackEntryProducers(offset, 1, 0); - break; - default: - markStackProducers(clazz, offset, simpleInstruction); - break; - } - } - - - public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) - { - // Is the variable being loaded or incremented? - if (variableInstruction.isLoad()) - { - markVariableProducers(offset, variableInstruction.variableIndex); - } - else - { - markStackProducers(clazz, offset, variableInstruction); - } - } - - - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) - { - // Mark the initializer invocation, if this is a 'new' instruction. - if (constantInstruction.opcode == InstructionConstants.OP_NEW) - { - markInitialization(offset); - } - - markStackProducers(clazz, offset, constantInstruction); - } - - - public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) - { - // Explicitly mark the produced stack entry of a 'jsr' instruction, - // because the consuming 'astore' instruction of the subroutine is - // cleared every time it is traced. - if (branchInstruction.opcode == InstructionConstants.OP_JSR || - branchInstruction.opcode == InstructionConstants.OP_JSR_W) - { - markStackEntryAfter(offset, 0); - } - else - { - markStackProducers(clazz, offset, branchInstruction); - } - } - } - - - /** - * This InstructionVisitor marks variable initializations that are - * necessary to appease the JVM. - */ - private class MyVariableInitializationMarker - extends SimplifiedVisitor - implements InstructionVisitor - { - // 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) - { - // Is the variable being loaded or incremented? - if (variableInstruction.isLoad()) - { - // Mark any variable initializations for this variable load that - // are required according to the JVM. - markVariableInitializersBefore(offset, variableInstruction.variableIndex); - } - } - } - - - /** - * This InstructionVisitor fixes instructions locally, popping any unused - * produced stack entries after marked instructions, and popping produced - * stack entries and pushing missing stack entries instead of unmarked - * instructions. - */ - private class MyStackConsistencyFixer - extends SimplifiedVisitor - implements InstructionVisitor - { - // Implementations for InstructionVisitor. - - public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) - { - // Has the instruction been marked? - if (isInstructionNecessary(offset)) - { - // Check all stack entries that are popped. - // Unusual case: an exception handler with an exception that is - // no longer consumed directly by a method. - // Typical case: a freshly marked variable initialization that - // requires some value on the stack. - int popCount = instruction.stackPopCount(clazz); - if (popCount > 0) - { - TracedStack tracedStack = - partialEvaluator.getStackBefore(offset); - - int stackSize = tracedStack.size(); - - int requiredPopCount = 0; - int requiredPushCount = 0; - for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) - { - boolean stackSimplifiedBefore = - isStackSimplifiedBefore(offset, stackIndex); - boolean stackEntryPresentBefore = - isStackEntryPresentBefore(offset, stackIndex); - - if (stackSimplifiedBefore) - { - // Is this stack entry pushed by any producer - // (maybe an exception in an exception handler)? - if (isStackEntryPresentBefore(offset, stackIndex)) - { - // Mark all produced stack entries. - markStackEntryProducers(offset, stackIndex); - - // Remember to pop it. - requiredPopCount++; - } - } - else - { - // Is this stack entry pushed by any producer - // (because it is required by other consumers)? - if (stackEntryPresentBefore) - { - // Mark all produced stack entries. - markStackEntryProducers(offset, stackIndex); - } - else - { - // Remember to push it. - requiredPushCount++; - } - } - } - - // Pop some unnecessary stack entries. - if (requiredPopCount > 0) - { - if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset)); - - insertPopInstructions(offset, false, true, popCount); - } - - // Push some necessary stack entries. - if (requiredPushCount > 0) - { - if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset)); - - if (requiredPushCount > (instruction.isCategory2() ? 2 : 1)) - { - throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"] at ["+offset+"]"); - } - - insertPushInstructions(offset, false, true, tracedStack.getTop(0).computationalType()); - } - } - - // Check all other stack entries, if this is a return - // instruction. - // Typical case: the code returns, but there are still other - // entries left on the stack. These have to be consistent. - InstructionOffsetValue branchTargets = - partialEvaluator.branchTargets(offset); - if (branchTargets != null && - branchTargets.instructionOffsetCount() == 0) - { - TracedStack tracedStack = - partialEvaluator.getStackBefore(offset); - - int unpoppedStackSize = tracedStack.size() - popCount; - - for (int stackIndex = 0; stackIndex < unpoppedStackSize; stackIndex++) - { - // Is this stack entry pushed by any producer - // (because it is required by other consumers)? - if (isStackEntryPresentBefore(offset, stackIndex)) - { - // Mark all produced stack entries. - markStackEntryProducers(offset, stackIndex); - } - } - } - - // Check all stack entries that are pushed. - // Typical case: a return value that wasn't really required and - // that should be popped. - int pushCount = instruction.stackPushCount(clazz); - if (pushCount > 0) - { - TracedStack tracedStack = - partialEvaluator.getStackAfter(offset); - - int stackSize = tracedStack.size(); - - int requiredPopCount = 0; - for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++) - { - // Is the stack entry required by consumers? - if (!isStackEntryNecessaryAfter(offset, stackIndex)) - { - // Remember to pop it. - requiredPopCount++; - } - } - - // Pop the unnecessary stack entries. - if (requiredPopCount > 0) - { - if (DEBUG) System.out.println(" Inserting after marked producer "+instruction.toString(offset)); - - insertPopInstructions(offset, false, false, requiredPopCount); - } - } - } - else - { - // Check all stack entries that would be popped. - // Typical case: a stack value that is required elsewhere and - // that still has to be popped. - int popCount = instruction.stackPopCount(clazz); - if (popCount > 0) - { - TracedStack tracedStack = - partialEvaluator.getStackBefore(offset); - - int stackSize = tracedStack.size(); - - int expectedPopCount = 0; - for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) - { - // Is this stack entry pushed by any producer - // (because it is required by other consumers)? - if (isStackEntryPresentBefore(offset, stackIndex)) - { - // Mark all produced stack entries. - markStackEntryProducers(offset, stackIndex); - - // Remember to pop it. - expectedPopCount++; - } - } - - // Pop the unnecessary stack entries. - if (expectedPopCount > 0) - { - if (DEBUG) System.out.println(" Replacing unmarked consumer "+instruction.toString(offset)); - - insertPopInstructions(offset, true, false, expectedPopCount); - } - } - - // Check all stack entries that would be pushed. - // Typical case: never. - int pushCount = instruction.stackPushCount(clazz); - if (pushCount > 0) - { - TracedStack tracedStack = - partialEvaluator.getStackAfter(offset); - - int stackSize = tracedStack.size(); - - int expectedPushCount = 0; - for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++) - { - // Is the stack entry required by consumers? - if (isStackEntryNecessaryAfter(offset, stackIndex)) - { - // Remember to push it. - expectedPushCount++; - } - } - - // Push some necessary stack entries. - if (expectedPushCount > 0) - { - if (DEBUG) System.out.println(" Replacing unmarked producer "+instruction.toString(offset)); - - insertPushInstructions(offset, true, false, tracedStack.getTop(0).computationalType()); - } - } - } - } - - - public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) - { - if (isInstructionNecessary(offset) && - isDupOrSwap(simpleInstruction)) - { - int stackSizeBefore = partialEvaluator.getStackBefore(offset).size(); - - // Check all stack entries that are popped. - // Typical case: a freshly marked variable initialization that - // requires some value on the stack. - int popCount = simpleInstruction.stackPopCount(clazz); - if (popCount > 0) - { - for (int stackIndex = stackSizeBefore - popCount; stackIndex < stackSizeBefore; stackIndex++) - { - // Is this stack entry pushed by any producer - // (because it is required by other consumers)? - if (isStackEntryPresentBefore(offset, stackIndex)) - { - // Mark all produced stack entries. - markStackEntryProducers(offset, stackIndex); - } - } - } - - int topBefore = stackSizeBefore - 1; - int topAfter = partialEvaluator.getStackAfter(offset).size() - 1; - - byte oldOpcode = simpleInstruction.opcode; - - // Simplify the dup/swap instruction if possible. - int newOpcodes = fixDupSwap(offset, oldOpcode, topBefore, topAfter); - - // Did we find a suitabe (extended) opcode? - if (newOpcodes == UNSUPPORTED) - { - // We can't easily emulate some constructs. - throw new UnsupportedOperationException("Can't handle "+simpleInstruction.toString()+" instruction at ["+offset +"]"); - } - - // Is there a single replacement opcode? - if ((newOpcodes & ~0xff) == 0) - { - byte newOpcode = (byte)newOpcodes; - - if (newOpcode == InstructionConstants.OP_NOP) - { - // Delete the instruction. - codeAttributeEditor.deleteInstruction(offset); - - if (extraDeletedInstructionVisitor != null) - { - extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, offset, null); - } - - if (DEBUG) System.out.println(" Deleting marked instruction "+simpleInstruction.toString(offset)); - } - else if (newOpcode == oldOpcode) - { - // Leave the instruction unchanged. - codeAttributeEditor.undeleteInstruction(offset); - - if (DEBUG) System.out.println(" Marking unchanged instruction "+simpleInstruction.toString(offset)); - } - else - { - // Replace the instruction. - Instruction replacementInstruction = new SimpleInstruction(newOpcode); - codeAttributeEditor.replaceInstruction(offset, - replacementInstruction); - - if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by "+replacementInstruction.toString()); - } - } - else - { - // Collect the replacement instructions. - Instruction[] replacementInstructions = new Instruction[4]; - - if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by"); - int count = 0; - while (newOpcodes != 0) - { - SimpleInstruction replacementInstruction = new SimpleInstruction((byte)newOpcodes); - replacementInstructions[count++] = replacementInstruction; - - if (DEBUG) System.out.println(" "+replacementInstruction.toString()); - newOpcodes >>>= 8; - } - - // Create a properly sized array. - if (count < 4) - { - Instruction[] newInstructions = new Instruction[count]; - System.arraycopy(replacementInstructions, 0, newInstructions, 0, count); - replacementInstructions = newInstructions; - } - - codeAttributeEditor.replaceInstruction(offset, - replacementInstructions); - } - } - else - { - visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); - } - } - - - /** - * Returns a dup/swap opcode that is corrected for the stack entries - * that are present before the instruction and necessary after the - * instruction. The returned integer opcode may contain multiple byte - * opcodes (least significant byte first). - * @param instructionOffset the offset of the dup/swap instruction. - * @param dupSwapOpcode the original dup/swap opcode. - * @param topBefore the index of the top stack entry before - * the instruction (counting from the bottom). - * @param topAfter the index of the top stack entry after - * the instruction (counting from the bottom). - * @return the corrected opcode. - */ - private int fixDupSwap(int instructionOffset, - byte dupSwapOpcode, - int topBefore, - int topAfter) - { - switch (dupSwapOpcode) - { - case InstructionConstants.OP_DUP: return fixedDup (instructionOffset, topBefore, topAfter); - case InstructionConstants.OP_DUP_X1: return fixedDup_x1 (instructionOffset, topBefore, topAfter); - case InstructionConstants.OP_DUP_X2: return fixedDup_x2 (instructionOffset, topBefore, topAfter); - case InstructionConstants.OP_DUP2: return fixedDup2 (instructionOffset, topBefore, topAfter); - case InstructionConstants.OP_DUP2_X1: return fixedDup2_x1(instructionOffset, topBefore, topAfter); - case InstructionConstants.OP_DUP2_X2: return fixedDup2_x2(instructionOffset, topBefore, topAfter); - case InstructionConstants.OP_SWAP: return fixedSwap (instructionOffset, topBefore, topAfter); - default: throw new IllegalArgumentException("Not a dup/swap opcode ["+dupSwapOpcode+"]"); - } - } - - - private int fixedDup(int instructionOffset, int topBefore, int topAfter) - { - boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); - - boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); - boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); - - // Figure out which stack entries should be moved, - // copied, or removed. - return - stackEntryNecessary0 ? - stackEntryNecessary1 ? DUP : // ...O -> ...OO - NOP : // ...O -> ...O - stackEntryNecessary1 ? NOP : // ...O -> ...O - stackEntryPresent0 ? POP : // ...O -> ... - NOP; // ... -> ... - } - - - private int fixedDup_x1(int instructionOffset, int topBefore, int topAfter) - { - boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); - boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); - - boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); - boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); - boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); - - // Figure out which stack entries should be moved, - // copied, or removed. - return - stackEntryNecessary1 ? - stackEntryNecessary2 ? - stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO - SWAP : // ...XO -> ...OX - // !stackEntryNecessary2 - stackEntryNecessary0 ? NOP : // ...XO -> ...XO - stackEntryPresent0 ? POP : // ...XO -> ...X - NOP : // ...X -> ...X - stackEntryPresent1 ? - stackEntryNecessary2 ? - stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO - POP_X1 : // ...XO -> ...O - // !stackEntryNecessary2 - stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O - stackEntryPresent0 ? POP2 : // ...XO -> ... - POP : // ...X -> ... - // !stackEntryPresent1 - stackEntryNecessary2 ? - stackEntryNecessary0 ? DUP : // ...O -> ...OO - NOP : // ...O -> ...O - // !stackEntryNecessary2 - stackEntryNecessary0 ? NOP : // ...O -> ...O - stackEntryPresent0 ? POP : // ...O -> ... - NOP; // ... -> ... - } - - - private int fixedDup_x2(int instructionOffset, int topBefore, int topAfter) - { - boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); - boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); - boolean stackEntryPresent2 = isStackEntryPresentBefore(instructionOffset, topBefore - 2); - - boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); - boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); - boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); - boolean stackEntryNecessary3 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 3); - - // Figure out which stack entries should be moved, - // copied, or removed. - return - stackEntryNecessary1 ? - stackEntryNecessary2 ? - stackEntryNecessary3 ? - stackEntryNecessary0 ? DUP_X2 : // ...XYO -> ...OXYO - MOV_X2 : // ...XYO -> ...OXY - // !stackEntryNecessary3 - stackEntryNecessary0 ? NOP : // ...XYO -> ...XYO - stackEntryPresent0 ? POP : // ...XYO -> ...XY - NOP : // ...XY -> ...XY - stackEntryPresent2 ? - stackEntryNecessary3 ? - // stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OYO - UNSUPPORTED : // ...XYO -> ...OY - // !stackEntryNecessary3 - stackEntryNecessary0 ? POP_X2 : // ...XYO -> ...YO - stackEntryPresent0 ? POP_SWAP_POP : // ...XYO -> ...Y - POP_X1 : // ...XY -> ...Y - // !stackEntryPresent2 - stackEntryNecessary3 ? - stackEntryNecessary0 ? DUP_X1 : // ...YO -> ...OYO - SWAP : // ...YO -> ...OY - // !stackEntryNecessary3 - stackEntryNecessary0 ? NOP : // ...YO -> ...YO - stackEntryPresent0 ? POP : // ...YO -> ...Y - NOP : // ...Y -> ...Y - stackEntryPresent1 ? - stackEntryNecessary2 ? - stackEntryNecessary3 ? - stackEntryNecessary0 ? SWAP_POP_DUP_X1 : // ...XYO -> ...OXO - DUP_X2_POP2 : // ...XYO -> ...OX - // !stackEntryNecessary3 - stackEntryNecessary0 ? POP_X1 : // ...XYO -> ...XO - stackEntryPresent0 ? POP2 : // ...XYO -> ...X - POP : // ...XY -> ...X - stackEntryPresent2 ? - stackEntryNecessary3 ? - stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OO - POP2_X1 : // ...XYO -> ...O - // !stackEntryNecessary3 - stackEntryNecessary0 ? POP2_X1 : // ...XYO -> ...O - stackEntryPresent0 ? POP3 : // ...XYO -> ... - POP2 : // ...XY -> ... - // !stackEntryPresent2 - stackEntryNecessary3 ? - stackEntryNecessary0 ? SWAP_POP_DUP : // ...YO -> ...OO - POP_X1 : // ...YO -> ...O - // !stackEntryNecessary3 - stackEntryNecessary0 ? POP_X1 : // ...YO -> ...O - stackEntryPresent0 ? POP2 : // ...YO -> ... - POP : // ...Y -> ... - // !stackEntryPresent1 - stackEntryNecessary2 ? - stackEntryNecessary3 ? - stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO - SWAP : // ...XO -> ...OX - // !stackEntryNecessary3 - stackEntryNecessary0 ? NOP : // ...XO -> ...XO - stackEntryPresent0 ? POP : // ...XO -> ...X - NOP : // ...X -> ...X - stackEntryPresent2 ? - stackEntryNecessary3 ? - stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO - POP_X1 : // ...XO -> ...O - // !stackEntryNecessary3 - stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O - stackEntryPresent0 ? POP2 : // ...XO -> ... - POP : // ...X -> ... - // !stackEntryPresent2 - stackEntryNecessary3 ? - stackEntryNecessary0 ? DUP : // ...O -> ...OO - NOP : // ...O -> ...O - // !stackEntryNecessary3 - stackEntryNecessary0 ? NOP : // ...O -> ...O - stackEntryPresent0 ? POP : // ...O -> ... - NOP; // ... -> ... - } - - - private int fixedDup2(int instructionOffset, int topBefore, int topAfter) - { - boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); - boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); - - boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); - boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); - boolean stackEntryNecessary2 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 2); - boolean stackEntryNecessary3 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 3); - - return - stackEntryNecessary3 ? - stackEntryNecessary2 ? - stackEntryNecessary1 ? - stackEntryNecessary0 ? DUP2 : // ...AB -> ...ABAB - SWAP_DUP_X1 : // ...AB -> ...ABA - // !stackEntryNecessary1 - stackEntryNecessary0 ? DUP : // ...AB -> ...ABB - NOP : // ...AB -> ...AB - // !stackEntryNecessary2 - stackEntryNecessary1 ? - stackEntryNecessary0 ? SWAP_DUP_X1_SWAP : // ...AB -> ...AAB - stackEntryPresent0 ? POP_DUP : // ...AB -> ...AA - DUP : // ...A -> ...AA - // !stackEntryNecessary1 - stackEntryNecessary0 ? NOP : // ...AB -> ...AB - stackEntryPresent0 ? POP : // ...AB -> ...A - NOP : // ...A -> ...A - // !stackEntryNecessary3 - stackEntryNecessary2 ? - stackEntryNecessary1 ? - stackEntryNecessary0 ? DUP_X1 : // ...AB -> ...BAB - SWAP : // ...AB -> ...BA - stackEntryPresent1 ? - stackEntryNecessary0 ? SWAP_POP_DUP : // ...AB -> ...BB - POP_X1 : // ...AB -> ...B - // !stackEntryPresent1 - stackEntryNecessary0 ? POP : // ...B -> ...BB - NOP : // ...B -> ...B - // !stackEntryNecessary2 - stackEntryNecessary1 ? - stackEntryNecessary0 ? NOP : // ...AB -> ...AB - stackEntryPresent0 ? POP : // ...AB -> ...A - NOP : // ...A -> ...A - stackEntryPresent1 ? - stackEntryNecessary0 ? POP_X1 : // ...AB -> ...B - stackEntryPresent0 ? POP2 : // ...AB -> ... - POP : // ...A -> ... - // !stackEntryPresent1 - stackEntryNecessary0 ? NOP : // ...B -> ...B - stackEntryPresent0 ? POP : // ...B -> ... - NOP; // ... -> ... - } - - - private int fixedDup2_x1(int instructionOffset, int topBefore, int topAfter) - { - // We're currently assuming the value to be duplicated - // is a long or a double, taking up two slots, or at - // least consistent. - boolean stackEntriesPresent01 = isStackEntriesPresentBefore(instructionOffset, topBefore - 0, topBefore - 1); - boolean stackEntryPresent2 = isStackEntryPresentBefore( instructionOffset, topBefore - 2); - - boolean stackEntriesNecessary01 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 0, topAfter - 1); - boolean stackEntryNecessary2 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); - boolean stackEntriesNecessary34 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 3, topAfter - 4); - - // Figure out which stack entries should be moved, - // copied, or removed. - return - stackEntryNecessary2 ? - stackEntriesNecessary34 ? - stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB - MOV2_X1 : // ...XAB -> ...ABX - // !stackEntriesNecessary34 - stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB - stackEntriesPresent01 ? POP2 : // ...XAB -> ...X - NOP : // ...X -> ...X - stackEntryPresent2 ? - stackEntriesNecessary34 ? - stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB - POP_X2 : // ...XAB -> ...AB - // !stackEntriesNecessary34 - stackEntriesNecessary01 ? DUP2_X1_POP3 : // ...XAB -> ...AB - stackEntriesPresent01 ? POP3 : // ...XAB -> ... - POP : // ...X -> ... - // !stackEntryPresent2 - stackEntriesNecessary34 ? - stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB - NOP : // ...AB -> ...AB - // !stackEntriesNecessary34 - stackEntriesNecessary01 ? NOP : // ...AB -> ...AB - stackEntriesPresent01 ? POP2 : // ...AB -> ... - NOP; // ... -> ... - } - - - private int fixedDup2_x2(int instructionOffset, int topBefore, int topAfter) - { - // We're currently assuming the value to be duplicated - // is a long or a double, taking up two slots, or at - // least consistent. - boolean stackEntriesPresent01 = isStackEntriesPresentBefore(instructionOffset, topBefore - 0, topBefore - 1); - boolean stackEntryPresent2 = isStackEntryPresentBefore( instructionOffset, topBefore - 2); - boolean stackEntryPresent3 = isStackEntryPresentBefore( instructionOffset, topBefore - 3); - - boolean stackEntriesNecessary01 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 0, topAfter - 1); - boolean stackEntryNecessary2 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 2); - boolean stackEntryNecessary3 = isStackEntryNecessaryAfter( instructionOffset, topAfter - 3); - boolean stackEntriesNecessary45 = isStackEntriesNecessaryAfter(instructionOffset, topAfter - 4, topAfter - 5); - - // Figure out which stack entries should be moved, - // copied, or removed. - return - stackEntryNecessary2 ? - stackEntryNecessary3 ? - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? DUP2_X2 : // ...XYAB -> ...ABXYAB - MOV2_X2 : // ...XYAB -> ...ABXY - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? NOP : // ...XYAB -> ...XYAB - stackEntriesPresent01 ? POP2 : // ...XYAB -> ...XY - NOP : // ...XY -> ...XY - stackEntryPresent3 ? - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABYAB - DUP2_X2_SWAP_POP : // ...XYAB -> ...ABY - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? POP_X3 : // ...XYAB -> ...YAB - stackEntriesPresent01 ? POP2_SWAP_POP : // ...XYAB -> ...Y - POP_X1 : // ...XY -> ...Y - // !stackEntryPresent3 - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? DUP2_X1 : // ...YAB -> ...ABYAB - MOV2_X1 : // ...YAB -> ...ABY - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? NOP : // ...YAB -> ...YAB - stackEntriesPresent01 ? POP2 : // ...YAB -> ...Y - NOP : // ...Y -> ...Y - stackEntryPresent2 ? - stackEntryNecessary3 ? - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABXAB - DUP2_X2_POP3 : // ...XYAB -> ...ABX - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? POP_X2 : // ...XYAB -> ...XAB - stackEntriesPresent01 ? POP3 : // ...XYAB -> ...X - POP : // ...XY -> ...X - stackEntryPresent3 ? - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABAB - POP2_X2 : // ...XYAB -> ...AB - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? POP2_X2 : // ...XYAB -> ...AB - stackEntriesPresent01 ? POP4 : // ...XYAB -> ... - POP2 : // ...XY -> ... - // !stackEntryPresent3 - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? UNSUPPORTED : // ...YAB -> ...ABAB - POP_X2 : // ...YAB -> ...AB - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? POP_X2 : // ...YAB -> ...AB - stackEntriesPresent01 ? POP3 : // ...YAB -> ... - POP : // ...Y -> ... - // !stackEntryPresent2 - stackEntryNecessary3 ? - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB - MOV2_X1 : // ...XAB -> ...ABX - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB - stackEntriesPresent01 ? POP2 : // ...XAB -> ...X - NOP : // ...X -> ...X - stackEntryPresent3 ? - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB - POP_X2 : // ...XAB -> ...AB - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? POP_X2 : // ...XAB -> ...AB - stackEntriesPresent01 ? POP3 : // ...XAB -> ... - POP : // ...X -> ... - // !stackEntryPresent3 - stackEntriesNecessary45 ? - stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB - NOP : // ...AB -> ...AB - // !stackEntriesNecessary45 - stackEntriesNecessary01 ? NOP : // ...AB -> ...AB - stackEntriesPresent01 ? POP2 : // ...AB -> ... - NOP; // ... -> ... - } - - - private int fixedSwap(int instructionOffset, int topBefore, int topAfter) - { - boolean stackEntryPresent0 = isStackEntryPresentBefore(instructionOffset, topBefore - 0); - boolean stackEntryPresent1 = isStackEntryPresentBefore(instructionOffset, topBefore - 1); - - boolean stackEntryNecessary0 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 0); - boolean stackEntryNecessary1 = isStackEntryNecessaryAfter(instructionOffset, topAfter - 1); - - // Figure out which stack entries should be moved - // or removed. - return - stackEntryNecessary0 ? - stackEntryNecessary1 ? SWAP : // ...AB -> ...BA - stackEntryPresent0 ? POP : // ...AB -> ...A - NOP : // ...A -> ...A - stackEntryPresent1 ? POP_X1 : // ...AB -> ...B - NOP; // ...B -> ...B - } - } - - - // Small utility methods. - - /** - * Marks the producing instructions of the variable consumer at the given - * offset. - * @param consumerOffset the offset of the variable consumer. - * @param variableIndex the index of the variable that is loaded. - */ - private void markVariableProducers(int consumerOffset, - int variableIndex) - { - InstructionOffsetValue producerOffsets = - partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); - - if (producerOffsets != null) - { - int offsetCount = producerOffsets.instructionOffsetCount(); - for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) - { - // Make sure the variable and the instruction are marked - // at the producing offset. - int offset = producerOffsets.instructionOffset(offsetIndex); - - markInstruction(offset); - } - } - } - - - /** - * Ensures that the given variable is initialized before the specified - * consumer of that variable, in the JVM's view. - * @param consumerOffset the instruction offset before which the variable - * needs to be initialized. - * @param variableIndex the index of the variable. - */ - private void markVariableInitializersBefore(int consumerOffset, - int variableIndex) - { - // Make sure the variable is initialized after all producers. - // Use the simple evaluator, to get the JVM's view of what is - // initialized. - InstructionOffsetValue producerOffsets = - simplePartialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue(); - - int offsetCount = producerOffsets.instructionOffsetCount(); - for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) - { - // Avoid infinite loops by only looking at producers before - // the consumer. - int producerOffset = - producerOffsets.instructionOffset(offsetIndex); - if (producerOffset < consumerOffset) - { - markVariableInitializersAfter(producerOffset, variableIndex); - } - } - } - - - /** - * Ensures that the given variable is initialized after the specified - * producer of that variable, in the JVM's view. - * @param producerOffset the instruction offset after which the variable - * needs to be initialized. - * @param variableIndex the index of the variable. - */ - private void markVariableInitializersAfter(int producerOffset, - int variableIndex) - { - // No problem if the producer has already been marked. - if (!isInstructionNecessary(producerOffset)) - { - // Is the unmarked producer a variable initialization? - if (isVariableInitialization(producerOffset, variableIndex)) - { - // Mark the producer. - if (DEBUG) System.out.print(" Marking initialization of v"+variableIndex+" at "); - - markInstruction(producerOffset); - - if (DEBUG) System.out.println(); - } - else - { - // Don't mark the producer, but recursively look at the - // preceding producers of the same variable. Their values - // will fall through, replacing this producer. - markVariableInitializersBefore(producerOffset, variableIndex); - } - } - } - - - /** - * Marks the stack entries and their producing instructions of the - * consumer at the given offset. - * @param clazz the containing class. - * @param consumerOffset the offset of the consumer. - * @param consumer the consumer of the stack entries. - */ - private void markStackProducers(Clazz clazz, - int consumerOffset, - Instruction consumer) - { - TracedStack tracedStack = - partialEvaluator.getStackBefore(consumerOffset); - - int stackSize = tracedStack.size(); - - // Mark the producers of the popped values. - int popCount = consumer.stackPopCount(clazz); - for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++) - { - markStackEntryProducers(consumerOffset, stackIndex); - } - } - - - /** - * Marks the stack entry and the corresponding producing instructions - * of the consumer at the given offset, if the stack entry of the - * consumer is marked. - * @param consumerOffset the offset of the consumer. - * @param consumerTopStackIndex the index of the stack entry to be checked - * (counting from the top). - * @param producerTopStackIndex the index of the stack entry to be marked - * (counting from the top). - */ - private void conditionallyMarkStackEntryProducers(int consumerOffset, - int consumerTopStackIndex, - int producerTopStackIndex) - { - int consumerBottomStackIndex = partialEvaluator.getStackAfter(consumerOffset).size() - consumerTopStackIndex - 1; - - if (isStackEntryNecessaryAfter(consumerOffset, consumerBottomStackIndex)) - { - int producerBottomStackIndex = partialEvaluator.getStackBefore(consumerOffset).size() - producerTopStackIndex - 1; - - markStackEntryProducers(consumerOffset, producerBottomStackIndex); - } - } - - - /** - * Marks the stack entry and the corresponding producing instructions - * of the consumer at the given offset. - * @param consumerOffset the offset of the consumer. - * @param stackIndex the index of the stack entry to be marked - * (counting from the bottom). - */ - private void markStackEntryProducers(int consumerOffset, - int stackIndex) - { - if (!isStackSimplifiedBefore(consumerOffset, stackIndex)) - { - markStackEntryProducers(partialEvaluator.getStackBefore(consumerOffset).getBottomProducerValue(stackIndex).instructionOffsetValue(), - stackIndex); - } - } - - - /** - * Marks the stack entry and its producing instructions at the given - * offsets. - * @param producerOffsets the offsets of the producers to be marked. - * @param stackIndex the index of the stack entry to be marked - * (counting from the bottom). - */ - private void markStackEntryProducers(InstructionOffsetValue producerOffsets, - int stackIndex) - { - if (producerOffsets != null) - { - int offsetCount = producerOffsets.instructionOffsetCount(); - for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) - { - // Make sure the stack entry and the instruction are marked - // at the producing offset. - int offset = producerOffsets.instructionOffset(offsetIndex); - - markStackEntryAfter(offset, stackIndex); - markInstruction(offset); - } - } - } - - - /** - * Marks the stack entry and its initializing instruction - * ('invokespecial *.<init>') for the given 'new' instruction offset. - * @param newInstructionOffset the offset of the 'new' instruction. - */ - private void markInitialization(int newInstructionOffset) - { - int initializationOffset = - partialEvaluator.initializationOffset(newInstructionOffset); - - TracedStack tracedStack = - partialEvaluator.getStackAfter(newInstructionOffset); - - markStackEntryAfter(initializationOffset, tracedStack.size() - 1); - markInstruction(initializationOffset); - } - - - /** - * Marks the branch instructions of straddling branches, if they straddle - * some code that has been marked. - * @param instructionOffset the offset of the branch origin or branch target. - * @param branchOffsets the offsets of the straddling branch targets - * or branch origins. - * @param isPointingToTargets <code>true</code> if the above offsets are - * branch targets, <code>false</code> if they - * are branch origins. - */ - private void markStraddlingBranches(int instructionOffset, - InstructionOffsetValue branchOffsets, - boolean isPointingToTargets) - { - if (branchOffsets != null) - { - // Loop over all branch offsets. - int branchCount = branchOffsets.instructionOffsetCount(); - for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) - { - // Is the branch straddling forward any necessary instructions? - int branchOffset = branchOffsets.instructionOffset(branchIndex); - - // Is the offset pointing to a branch origin or to a branch target? - if (isPointingToTargets) - { - markStraddlingBranch(instructionOffset, - branchOffset, - instructionOffset, - branchOffset); - } - else - { - markStraddlingBranch(instructionOffset, - branchOffset, - branchOffset, - instructionOffset); - } - } - } - } - - - private void markStraddlingBranch(int instructionOffsetStart, - int instructionOffsetEnd, - int branchOrigin, - int branchTarget) - { - if (!isInstructionNecessary(branchOrigin) && - isAnyInstructionNecessary(instructionOffsetStart, instructionOffsetEnd)) - { - if (DEBUG) System.out.print("["+branchOrigin+"->"+branchTarget+"]"); - - // Mark the branch instruction. - markInstruction(branchOrigin); - } - } - - - /** - * Pushes a specified type of stack entry before or at the given offset. - * The instruction is marked as necessary. - */ - private void insertPushInstructions(int offset, - boolean replace, - boolean before, - int computationalType) - { - // Mark this instruction. - markInstruction(offset); - - // Create a simple push instrucion. - Instruction replacementInstruction = - new SimpleInstruction(pushOpcode(computationalType)); - - if (DEBUG) System.out.println(": "+replacementInstruction.toString(offset)); - - // Replace or insert the push instruction. - insertInstruction(offset, replace, before, replacementInstruction); - } - - - /** - * Returns the opcode of a push instruction corresponding to the given - * computational type. - * @param computationalType the computational type to be pushed on the stack. - */ - private byte pushOpcode(int computationalType) - { - switch (computationalType) - { - case Value.TYPE_INTEGER: return InstructionConstants.OP_ICONST_0; - case Value.TYPE_LONG: return InstructionConstants.OP_LCONST_0; - case Value.TYPE_FLOAT: return InstructionConstants.OP_FCONST_0; - case Value.TYPE_DOUBLE: return InstructionConstants.OP_DCONST_0; - case Value.TYPE_REFERENCE: - case Value.TYPE_INSTRUCTION_OFFSET: return InstructionConstants.OP_ACONST_NULL; - } - - throw new IllegalArgumentException("No push opcode for computational type ["+computationalType+"]"); - } - - - /** - * Pops the given number of stack entries at or after the given offset. - * The instructions are marked as necessary. - */ - private void insertPopInstructions(int offset, - boolean replace, - boolean before, - int popCount) - { - // Mark this instruction. - markInstruction(offset); - - switch (popCount) - { - case 1: - { - // Replace or insert a single pop instruction. - Instruction popInstruction = - new SimpleInstruction(InstructionConstants.OP_POP); - - insertInstruction(offset, replace, before, popInstruction); - break; - } - case 2: - { - // Replace or insert a single pop2 instruction. - Instruction popInstruction = - new SimpleInstruction(InstructionConstants.OP_POP2); - - insertInstruction(offset, replace, before, popInstruction); - break; - } - default: - { - // Replace or 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; - } - - insertInstructions(offset, - replace, - before, - popInstruction, - popInstructions); - break; - } - } - } - - - /** - * Inserts or replaces the given instruction at the given offset. - */ - private void insertInstruction(int offset, - boolean replace, - boolean before, - Instruction instruction) - { - if (replace) - { - codeAttributeEditor.replaceInstruction(offset, instruction); - } - else - { - if (before) - { - codeAttributeEditor.insertBeforeInstruction(offset, instruction); - } - else - { - codeAttributeEditor.insertAfterInstruction(offset, instruction); - } - - if (extraAddedInstructionVisitor != null) - { - instruction.accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } - } - - - /** - * Inserts or replaces the given instruction at the given offset. - */ - private void insertInstructions(int offset, - boolean replace, - boolean before, - Instruction instruction, - Instruction[] instructions) - { - if (replace) - { - codeAttributeEditor.replaceInstruction(offset, instructions); - - if (extraAddedInstructionVisitor != null) - { - for (int index = 1; index < instructions.length; index++) - { - instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } - } - else - { - if (before) - { - codeAttributeEditor.insertBeforeInstruction(offset, instructions); - } - else - { - codeAttributeEditor.insertAfterInstruction(offset, instructions); - } - - for (int index = 0; index < instructions.length; index++) - { - if (extraAddedInstructionVisitor != null) - { - instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); - } - } - } - } - - - /** - * Replaces the instruction at a given offset by a static invocation. - */ - private void replaceByStaticInvocation(Clazz clazz, - int offset, - ConstantInstruction constantInstruction) - { - // Remember the replacement instruction. - Instruction replacementInstruction = - new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC, - constantInstruction.constantIndex); - - if (DEBUG) System.out.println(" Replacing by static invocation "+constantInstruction.toString(offset)+" -> "+replacementInstruction.toString()); - - codeAttributeEditor.replaceInstruction(offset, replacementInstruction); - } - - - /** - * Replaces the given instruction by an infinite loop. - */ - private void replaceByInfiniteLoop(Clazz clazz, - int offset) - { - if (DEBUG) System.out.println(" Inserting infinite loop at ["+offset+"]"); - - // Mark the instruction. - markInstruction(offset); - - // Replace the instruction by an infinite loop. - Instruction replacementInstruction = - new BranchInstruction(InstructionConstants.OP_GOTO, 0); - - codeAttributeEditor.replaceInstruction(offset, replacementInstruction); - } - - - // Small utility methods. - - /** - * Returns whether the given instruction is a dup or swap instruction - * (dup, dup_x1, dup_x2, dup2, dup2_x1, dup2_x2, swap). - */ - private boolean isDupOrSwap(Instruction instruction) - { - return instruction.opcode >= InstructionConstants.OP_DUP && - instruction.opcode <= InstructionConstants.OP_SWAP; - } - - - /** - * Returns whether the given instruction is a pop instruction - * (pop, pop2). - */ - private boolean isPop(Instruction instruction) - { - return instruction.opcode == InstructionConstants.OP_POP || - instruction.opcode == InstructionConstants.OP_POP2; - } - - - /** - * Returns whether any traced but unnecessary instruction between the two - * given offsets is branching over the second given offset. - */ - private boolean isAnyUnnecessaryInstructionBranchingOver(int instructionOffset1, - int instructionOffset2) - { - for (int offset = instructionOffset1; offset < instructionOffset2; offset++) - { - // Is it a traced but unmarked straddling branch? - if (partialEvaluator.isTraced(offset) && - !isInstructionNecessary(offset) && - isAnyLargerThan(partialEvaluator.branchTargets(offset), - instructionOffset2)) - { - return true; - } - } - - return false; - } - - - /** - * Returns whether all of the given instruction offsets (at least one) - * are smaller than or equal to the given offset. - */ - private boolean isAllSmallerThanOrEqual(InstructionOffsetValue instructionOffsets, - int instructionOffset) - { - if (instructionOffsets != null) - { - // Loop over all instruction offsets. - int branchCount = instructionOffsets.instructionOffsetCount(); - if (branchCount > 0) - { - for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) - { - // Is the offset larger than the reference offset? - if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset) - { - return false; - } - } - - return true; - } - } - - return false; - } - - - /** - * Returns whether any of the given instruction offsets (at least one) - * is larger than the given offset. - */ - private boolean isAnyLargerThan(InstructionOffsetValue instructionOffsets, - int instructionOffset) - { - if (instructionOffsets != null) - { - // Loop over all instruction offsets. - int branchCount = instructionOffsets.instructionOffsetCount(); - if (branchCount > 0) - { - for (int branchIndex = 0; branchIndex < branchCount; branchIndex++) - { - // Is the offset larger than the reference offset? - if (instructionOffsets.instructionOffset(branchIndex) > instructionOffset) - { - return true; - } - } - } - } - - return false; - } - - - /** - * Initializes the necessary data structure. - */ - private void initializeNecessary(CodeAttribute codeAttribute) - { - int codeLength = codeAttribute.u4codeLength; - int maxLocals = codeAttribute.u2maxLocals; - int maxStack = codeAttribute.u2maxStack; - - // Create new arrays for storing information at each instruction offset. - if (stacksNecessaryAfter.length < codeLength || - stacksNecessaryAfter[0].length < maxStack) - { - stacksNecessaryAfter = new boolean[codeLength][maxStack]; - } - else - { - for (int offset = 0; offset < codeLength; offset++) - { - Arrays.fill(stacksNecessaryAfter[offset], 0, maxStack, false); - } - } - - if (stacksSimplifiedBefore.length < codeLength || - stacksSimplifiedBefore[0].length < maxStack) - { - stacksSimplifiedBefore = new boolean[codeLength][maxStack]; - } - else - { - for (int offset = 0; offset < codeLength; offset++) - { - Arrays.fill(stacksSimplifiedBefore[offset], 0, maxStack, false); - } - } - - if (instructionsNecessary.length < codeLength) - { - instructionsNecessary = new boolean[codeLength]; - } - else - { - Arrays.fill(instructionsNecessary, 0, codeLength, false); - } - } - - - /** - * Returns whether the specified variable is initialized at the specified - * offset. - */ - private boolean isVariableInitialization(int instructionOffset, - int variableIndex) - { - // Wasn't the variable set yet? - Value valueBefore = simplePartialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex); - if (valueBefore == null) - { - return true; - } - - // Is the computational type different now? - Value valueAfter = simplePartialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex); - if (valueAfter.computationalType() != valueBefore.computationalType()) - { - return true; - } - - // Is the reference type different now? - if (valueAfter.computationalType() == Value.TYPE_REFERENCE && - (valueAfter.referenceValue().isNull() == Value.ALWAYS || - !valueAfter.referenceValue().getType().equals(valueBefore.referenceValue().getType()))) - { - return true; - } - - // Was the producer an argument (which may be removed)? - Value producersBefore = simplePartialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex); - return producersBefore.instructionOffsetValue().instructionOffsetCount() == 1 && - producersBefore.instructionOffsetValue().instructionOffset(0) == PartialEvaluator.AT_METHOD_ENTRY; - } - - - /** - * Marks the stack entry after the given offset. - * @param instructionOffset the offset of the stack entry to be marked. - * @param stackIndex the index of the stack entry to be marked - * (counting from the bottom). - */ - private void markStackEntryAfter(int instructionOffset, - int stackIndex) - { - if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex)) - { - if (DEBUG) System.out.print("["+instructionOffset+".s"+stackIndex+"],"); - - stacksNecessaryAfter[instructionOffset][stackIndex] = true; - - if (maxMarkedOffset < instructionOffset) - { - maxMarkedOffset = instructionOffset; - } - } - } - - - - /** - * Returns whether the stack specified entries before the given offset are - * present. - */ - private boolean isStackEntriesPresentBefore(int instructionOffset, - int stackIndex1, - int stackIndex2) - { - boolean present1 = isStackEntryPresentBefore(instructionOffset, stackIndex1); - boolean present2 = isStackEntryPresentBefore(instructionOffset, stackIndex2); - -// if (present1 ^ present2) -// { -// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); -// } - - return present1 || present2; - } - - - /** - * Returns whether the specified stack entry before the given offset is - * present. - * @param instructionOffset the offset of the stack entry to be checked. - * @param stackIndex the index of the stack entry to be checked - * (counting from the bottom). - */ - private boolean isStackEntryPresentBefore(int instructionOffset, - int stackIndex) - { - TracedStack tracedStack = - partialEvaluator.getStackBefore(instructionOffset); - - InstructionOffsetValue producerOffsets = - tracedStack.getBottomProducerValue(stackIndex).instructionOffsetValue(); - - return isAnyStackEntryNecessaryAfter(producerOffsets, stackIndex); - } - - - /** - * Returns whether the stack specified entries after the given offset are - * necessary. - */ - private boolean isStackEntriesNecessaryAfter(int instructionOffset, - int stackIndex1, - int stackIndex2) - { - boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1); - boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2); - -// if (present1 ^ present2) -// { -// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions"); -// } - - return present1 || present2; - } - - - /** - * Returns whether any of the stack entries after the given offsets are - * necessary. - * @param instructionOffsets the offsets of the stack entries to be checked. - * @param stackIndex the index of the stack entries to be checked - * (counting from the bottom). - */ - private boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets, - int stackIndex) - { - int offsetCount = instructionOffsets.instructionOffsetCount(); - - for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++) - { - if (isStackEntryNecessaryAfter(instructionOffsets.instructionOffset(offsetIndex), stackIndex)) - { - return true; - } - } - - return false; - } - - - /** - * Returns whether the specified stack entry after the given offset is - * necessary. - * @param instructionOffset the offset of the stack entry to be checked. - * @param stackIndex the index of the stack entry to be checked - * (counting from the bottom). - */ - private boolean isStackEntryNecessaryAfter(int instructionOffset, - int stackIndex) - { - return instructionOffset == PartialEvaluator.AT_CATCH_ENTRY || - stacksNecessaryAfter[instructionOffset][stackIndex]; - } - - - private void markStackSimplificationBefore(int instructionOffset, - int stackIndex) - { - stacksSimplifiedBefore[instructionOffset][stackIndex] = true; - } - - - private boolean isStackSimplifiedBefore(int instructionOffset, - int stackIndex) - { - return stacksSimplifiedBefore[instructionOffset][stackIndex]; - } - - - private void markInstruction(int instructionOffset) - { - if (!isInstructionNecessary(instructionOffset)) - { - if (DEBUG) System.out.print(instructionOffset+","); - - instructionsNecessary[instructionOffset] = true; - - if (maxMarkedOffset < instructionOffset) - { - maxMarkedOffset = instructionOffset; - } - } - } - - - private boolean isAnyInstructionNecessary(int instructionOffset1, - int instructionOffset2) - { - for (int instructionOffset = instructionOffset1; - instructionOffset < instructionOffset2; - instructionOffset++) - { - if (isInstructionNecessary(instructionOffset)) - { - return true; - } - } - - return false; - } - - - /** - * Returns the highest offset of an instruction that has been marked as - * necessary, before the given offset. - */ - private int lastNecessaryInstructionOffset(int instructionOffset) - { - for (int offset = instructionOffset-1; offset >= 0; offset--) - { - if (isInstructionNecessary(instructionOffset)) - { - return offset; - } - } - - return 0; - } - - - private boolean isInstructionNecessary(int instructionOffset) - { - return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY || - instructionsNecessary[instructionOffset]; - } -} diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java deleted file mode 100644 index 8187342..0000000 --- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java +++ /dev/null @@ -1,1360 +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.editor.*; -import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.*; -import proguard.classfile.visitor.ClassPrinter; -import proguard.evaluation.TracedVariables; -import proguard.evaluation.value.*; -import proguard.optimize.info.SideEffectInstructionChecker; - -import java.util.Arrays; - -/** - * This AttributeVisitor simplifies the code attributes that it visits, based - * on partial evaluation. - * - * @author Eric Lafortune - */ -public class EvaluationSimplifier -extends SimplifiedVisitor -implements AttributeVisitor, - InstructionVisitor -{ - private static final int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f); - private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0); - - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = System.getProperty("es") != null; - //*/ - - private final InstructionVisitor extraInstructionVisitor; - - private final PartialEvaluator partialEvaluator; - private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true); - private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true); - - - /** - * Creates a new EvaluationSimplifier. - */ - public EvaluationSimplifier() - { - this(new PartialEvaluator(), null); - } - - - /** - * Creates a new EvaluationSimplifier. - * @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 EvaluationSimplifier(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) - { -// DEBUG = -// clazz.getName().equals("abc/Def") && -// method.getName(clazz).equals("abc"); - - // TODO: Remove this when the evaluation simplifier has stabilized. - // Catch any unexpected exceptions from the actual visiting method. - try - { - // Process the code. - visitCodeAttribute0(clazz, method, codeAttribute); - } - catch (RuntimeException ex) - { - System.err.println("Unexpected error while simplifying instructions after partial evaluation:"); - System.err.println(" Class = ["+clazz.getName()+"]"); - System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); - System.err.println("Not optimizing this method"); - - if (DEBUG) - { - method.accept(clazz, new ClassPrinter()); - - throw ex; - } - } - } - - - public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - if (DEBUG) - { - System.out.println(); - System.out.println("EvaluationSimplifier ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - } - - // 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_IALOAD: - case InstructionConstants.OP_BALOAD: - case InstructionConstants.OP_CALOAD: - case InstructionConstants.OP_SALOAD: - case InstructionConstants.OP_IADD: - case InstructionConstants.OP_ISUB: - case InstructionConstants.OP_IMUL: - case InstructionConstants.OP_IDIV: - case InstructionConstants.OP_IREM: - case InstructionConstants.OP_INEG: - case InstructionConstants.OP_ISHL: - case InstructionConstants.OP_ISHR: - case InstructionConstants.OP_IUSHR: - case InstructionConstants.OP_IAND: - case InstructionConstants.OP_IOR: - case InstructionConstants.OP_IXOR: - case InstructionConstants.OP_L2I: - case InstructionConstants.OP_F2I: - case InstructionConstants.OP_D2I: - case InstructionConstants.OP_I2B: - case InstructionConstants.OP_I2C: - case InstructionConstants.OP_I2S: - case InstructionConstants.OP_ARRAYLENGTH: - replaceIntegerPushInstruction(clazz, offset, simpleInstruction); - break; - - case InstructionConstants.OP_LALOAD: - case InstructionConstants.OP_LADD: - case InstructionConstants.OP_LSUB: - case InstructionConstants.OP_LMUL: - case InstructionConstants.OP_LDIV: - case InstructionConstants.OP_LREM: - case InstructionConstants.OP_LNEG: - case InstructionConstants.OP_LSHL: - case InstructionConstants.OP_LSHR: - case InstructionConstants.OP_LUSHR: - case InstructionConstants.OP_LAND: - case InstructionConstants.OP_LOR: - case InstructionConstants.OP_LXOR: - case InstructionConstants.OP_I2L: - case InstructionConstants.OP_F2L: - case InstructionConstants.OP_D2L: - replaceLongPushInstruction(clazz, offset, simpleInstruction); - break; - - case InstructionConstants.OP_FALOAD: - case InstructionConstants.OP_FADD: - case InstructionConstants.OP_FSUB: - case InstructionConstants.OP_FMUL: - case InstructionConstants.OP_FDIV: - case InstructionConstants.OP_FREM: - case InstructionConstants.OP_FNEG: - case InstructionConstants.OP_I2F: - case InstructionConstants.OP_L2F: - case InstructionConstants.OP_D2F: - replaceFloatPushInstruction(clazz, offset, simpleInstruction); - break; - - case InstructionConstants.OP_DALOAD: - case InstructionConstants.OP_DADD: - case InstructionConstants.OP_DSUB: - case InstructionConstants.OP_DMUL: - case InstructionConstants.OP_DDIV: - case InstructionConstants.OP_DREM: - case InstructionConstants.OP_DNEG: - case InstructionConstants.OP_I2D: - case InstructionConstants.OP_L2D: - case InstructionConstants.OP_F2D: - replaceDoublePushInstruction(clazz, offset, simpleInstruction); - break; - - case InstructionConstants.OP_AALOAD: - replaceReferencePushInstruction(clazz, offset, simpleInstruction); - break; - } - } - - - public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) - { - int variableIndex = variableInstruction.variableIndex; - - switch (variableInstruction.opcode) - { - case InstructionConstants.OP_ILOAD: - case InstructionConstants.OP_ILOAD_0: - case InstructionConstants.OP_ILOAD_1: - case InstructionConstants.OP_ILOAD_2: - case InstructionConstants.OP_ILOAD_3: - replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex); - break; - - case InstructionConstants.OP_LLOAD: - case InstructionConstants.OP_LLOAD_0: - case InstructionConstants.OP_LLOAD_1: - case InstructionConstants.OP_LLOAD_2: - case InstructionConstants.OP_LLOAD_3: - replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex); - break; - - case InstructionConstants.OP_FLOAD: - case InstructionConstants.OP_FLOAD_0: - case InstructionConstants.OP_FLOAD_1: - case InstructionConstants.OP_FLOAD_2: - case InstructionConstants.OP_FLOAD_3: - replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex); - break; - - case InstructionConstants.OP_DLOAD: - case InstructionConstants.OP_DLOAD_0: - case InstructionConstants.OP_DLOAD_1: - case InstructionConstants.OP_DLOAD_2: - case InstructionConstants.OP_DLOAD_3: - replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex); - break; - - case InstructionConstants.OP_ALOAD: - case InstructionConstants.OP_ALOAD_0: - case InstructionConstants.OP_ALOAD_1: - case InstructionConstants.OP_ALOAD_2: - case InstructionConstants.OP_ALOAD_3: - replaceReferencePushInstruction(clazz, offset, variableInstruction); - 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: - deleteReferencePopInstruction(clazz, offset, variableInstruction); - break; - - case InstructionConstants.OP_RET: - replaceBranchInstruction(clazz, offset, variableInstruction); - break; - } - } - - - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) - { - switch (constantInstruction.opcode) - { - case InstructionConstants.OP_GETSTATIC: - case InstructionConstants.OP_GETFIELD: - replaceAnyPushInstruction(clazz, offset, constantInstruction); - break; - - case InstructionConstants.OP_INVOKEVIRTUAL: - case InstructionConstants.OP_INVOKESPECIAL: - case InstructionConstants.OP_INVOKESTATIC: - case InstructionConstants.OP_INVOKEINTERFACE: - if (constantInstruction.stackPushCount(clazz) > 0 && - !sideEffectInstructionChecker.hasSideEffects(clazz, - method, - codeAttribute, - offset, - constantInstruction)) - { - replaceAnyPushInstruction(clazz, offset, constantInstruction); - } - - break; - - case InstructionConstants.OP_CHECKCAST: - replaceReferencePushInstruction(clazz, offset, constantInstruction); - break; - } - } - - - public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) - { - switch (branchInstruction.opcode) - { - case InstructionConstants.OP_GOTO: - case InstructionConstants.OP_GOTO_W: - // Don't replace unconditional branches. - break; - - case InstructionConstants.OP_JSR: - case InstructionConstants.OP_JSR_W: - replaceJsrInstruction(clazz, offset, branchInstruction); - break; - - default: - replaceBranchInstruction(clazz, offset, branchInstruction); - break; - } - } - - - public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction) - { - // First try to simplify it to a simple branch. - replaceBranchInstruction(clazz, offset, tableSwitchInstruction); - - // Otherwise try to simplify simple enum switches. - if (!codeAttributeEditor.isModified(offset)) - { - replaceSimpleEnumSwitchInstruction(clazz, - codeAttribute, - offset, - tableSwitchInstruction); - - // Otherwise make sure all branch targets are valid. - if (!codeAttributeEditor.isModified(offset)) - { - cleanUpSwitchInstruction(clazz, offset, tableSwitchInstruction); - - trimSwitchInstruction(clazz, offset, tableSwitchInstruction); - } - } - } - - - public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction) - { - // First try to simplify it to a simple branch. - replaceBranchInstruction(clazz, offset, lookUpSwitchInstruction); - - // Otherwise try to simplify simple enum switches. - if (!codeAttributeEditor.isModified(offset)) - { - replaceSimpleEnumSwitchInstruction(clazz, - codeAttribute, - offset, - lookUpSwitchInstruction); - - // Otherwise make sure all branch targets are valid. - if (!codeAttributeEditor.isModified(offset)) - { - cleanUpSwitchInstruction(clazz, offset, lookUpSwitchInstruction); - - trimSwitchInstruction(clazz, offset, lookUpSwitchInstruction); - } - } - } - - - // Small utility methods. - - /** - * Replaces the push instruction at the given offset by a simpler push - * instruction, if possible. - */ - private void replaceAnyPushInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); - if (pushedValue.isParticular()) - { - switch (pushedValue.computationalType()) - { - case Value.TYPE_INTEGER: - replaceIntegerPushInstruction(clazz, offset, instruction); - break; - case Value.TYPE_LONG: - replaceLongPushInstruction(clazz, offset, instruction); - break; - case Value.TYPE_FLOAT: - replaceFloatPushInstruction(clazz, offset, instruction); - break; - case Value.TYPE_DOUBLE: - replaceDoublePushInstruction(clazz, offset, instruction); - break; - case Value.TYPE_REFERENCE: - replaceReferencePushInstruction(clazz, offset, instruction); - break; - } - } - } - - - /** - * Replaces the integer pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceIntegerPushInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - replaceIntegerPushInstruction(clazz, - offset, - instruction, - partialEvaluator.getVariablesBefore(offset).size()); - } - - - /** - * Replaces the integer pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceIntegerPushInstruction(Clazz clazz, - int offset, - Instruction instruction, - int maxVariableIndex) - { - Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); - if (pushedValue.isParticular()) - { - // Push a constant instead. - int value = pushedValue.integerValue().value(); - if ((short)value == value) - { - replaceConstantPushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_SIPUSH, - value); - } - else - { - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor((ProgramClass)clazz); - - Instruction replacementInstruction = - new ConstantInstruction(InstructionConstants.OP_LDC, - constantPoolEditor.addIntegerConstant(value)); - - replaceInstruction(clazz, offset, instruction, replacementInstruction); - } - } - else if (pushedValue.isSpecific()) - { - // Load an equivalent lower-numbered variable instead, if any. - TracedVariables variables = partialEvaluator.getVariablesBefore(offset); - for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) - { - if (pushedValue.equals(variables.load(variableIndex))) - { - replaceVariablePushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_ILOAD, - variableIndex); - break; - } - } - } - } - - - /** - * Replaces the long pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceLongPushInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - replaceLongPushInstruction(clazz, - offset, - instruction, - partialEvaluator.getVariablesBefore(offset).size()); - } - - - /** - * Replaces the long pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceLongPushInstruction(Clazz clazz, - int offset, - Instruction instruction, - int maxVariableIndex) - { - Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); - if (pushedValue.isParticular()) - { - // Push a constant instead. - long value = pushedValue.longValue().value(); - if (value == 0L || - value == 1L) - { - replaceConstantPushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_LCONST_0, - (int)value); - } - else - { - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor((ProgramClass)clazz); - - Instruction replacementInstruction = - new ConstantInstruction(InstructionConstants.OP_LDC2_W, - constantPoolEditor.addLongConstant(value)); - - replaceInstruction(clazz, offset, instruction, replacementInstruction); - } - } - else if (pushedValue.isSpecific()) - { - // Load an equivalent lower-numbered variable instead, if any. - TracedVariables variables = partialEvaluator.getVariablesBefore(offset); - for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) - { - // Note that we have to check the second part as well. - if (pushedValue.equals(variables.load(variableIndex)) && - variables.load(variableIndex + 1) != null && - variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) - { - replaceVariablePushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_LLOAD, - variableIndex); - } - } - } - } - - - /** - * Replaces the float pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceFloatPushInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - replaceFloatPushInstruction(clazz, - offset, - instruction, - partialEvaluator.getVariablesBefore(offset).size()); - } - - - /** - * Replaces the float pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceFloatPushInstruction(Clazz clazz, - int offset, - Instruction instruction, - int maxVariableIndex) - { - Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); - if (pushedValue.isParticular()) - { - // Push a constant instead. - // Make sure to distinguish between +0.0 and -0.0. - float value = pushedValue.floatValue().value(); - if (value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS || - value == 1.0f || - value == 2.0f) - { - replaceConstantPushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_FCONST_0, - (int)value); - } - else - { - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor((ProgramClass)clazz); - - Instruction replacementInstruction = - new ConstantInstruction(InstructionConstants.OP_LDC, - constantPoolEditor.addFloatConstant(value)); - - replaceInstruction(clazz, offset, instruction, replacementInstruction); - } - } - else if (pushedValue.isSpecific()) - { - // Load an equivalent lower-numbered variable instead, if any. - TracedVariables variables = partialEvaluator.getVariablesBefore(offset); - for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) - { - if (pushedValue.equals(variables.load(variableIndex))) - { - replaceVariablePushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_FLOAD, - variableIndex); - } - } - } - } - - - /** - * Replaces the double pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceDoublePushInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - replaceDoublePushInstruction(clazz, - offset, - instruction, - partialEvaluator.getVariablesBefore(offset).size()); - } - - - /** - * Replaces the double pushing instruction at the given offset by a simpler - * push instruction, if possible. - */ - private void replaceDoublePushInstruction(Clazz clazz, - int offset, - Instruction instruction, - int maxVariableIndex) - { - Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); - if (pushedValue.isParticular()) - { - // Push a constant instead. - // Make sure to distinguish between +0.0 and -0.0. - double value = pushedValue.doubleValue().value(); - if (value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS || - value == 1.0) - { - replaceConstantPushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_DCONST_0, - (int)value); - } - else - { - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor((ProgramClass)clazz); - - Instruction replacementInstruction = - new ConstantInstruction(InstructionConstants.OP_LDC2_W, - constantPoolEditor.addDoubleConstant(value)); - - replaceInstruction(clazz, offset, instruction, replacementInstruction); - } - } - else if (pushedValue.isSpecific()) - { - // Load an equivalent lower-numbered variable instead, if any. - TracedVariables variables = partialEvaluator.getVariablesBefore(offset); - for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++) - { - // Note that we have to check the second part as well. - if (pushedValue.equals(variables.load(variableIndex)) && - variables.load(variableIndex + 1) != null && - variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP) - { - replaceVariablePushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_DLOAD, - variableIndex); - } - } - } - } - - - /** - * Replaces the reference pushing instruction at the given offset by a - * simpler push instruction, if possible. - */ - private void replaceReferencePushInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0); - if (pushedValue.isParticular()) - { - // A reference value can only be specific if it is null. - replaceConstantPushInstruction(clazz, - offset, - instruction, - InstructionConstants.OP_ACONST_NULL, - 0); - } - } - - - /** - * Replaces the instruction at a given offset by a given push instruction - * of a constant. - */ - private void replaceConstantPushInstruction(Clazz clazz, - int offset, - Instruction instruction, - byte replacementOpcode, - int value) - { - Instruction replacementInstruction = - new SimpleInstruction(replacementOpcode, value); - - replaceInstruction(clazz, offset, instruction, replacementInstruction); - } - - - /** - * Replaces the instruction at a given offset by a given push instruction - * of a variable. - */ - private void replaceVariablePushInstruction(Clazz clazz, - int offset, - Instruction instruction, - byte replacementOpcode, - int variableIndex) - { - Instruction replacementInstruction = - new VariableInstruction(replacementOpcode, variableIndex); - - replaceInstruction(clazz, offset, instruction, replacementInstruction); - } - - - /** - * Replaces the given 'jsr' instruction by a simpler branch instruction, - * if it jumps to a subroutine that doesn't return or a subroutine that - * is only called from one place. - */ - private void replaceJsrInstruction(Clazz clazz, - int offset, - BranchInstruction branchInstruction) - { - // Is the subroutine ever returning? - int subroutineStart = offset + branchInstruction.branchOffset; - if (!partialEvaluator.isSubroutineReturning(subroutineStart) || - partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1) - { - // All 'jsr' instructions to this subroutine can be replaced - // by unconditional branch instructions. - replaceBranchInstruction(clazz, offset, branchInstruction); - } - else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset))) - { - // We have to make sure the instruction after this 'jsr' - // instruction is valid, even if it is never reached. - replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction); - } - } - - - /** - * Deletes the reference popping instruction at the given offset, if - * it is at the start of a subroutine that doesn't return or a subroutine - * that is only called from one place. - */ - private void deleteReferencePopInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - if (partialEvaluator.isSubroutineStart(offset) && - (!partialEvaluator.isSubroutineReturning(offset) || - partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1)) - { - if (DEBUG) System.out.println(" Deleting store of subroutine return address "+instruction.toString(offset)); - - // A reference value can only be specific if it is null. - codeAttributeEditor.deleteInstruction(offset); - } - } - - - /** - * Deletes the given branch instruction, or replaces it by a simpler branch - * instruction, if possible. - */ - private void replaceBranchInstruction(Clazz clazz, - int offset, - Instruction instruction) - { - InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); - - // Is there exactly one branch target (not from a goto or jsr)? - if (branchTargets != null && - branchTargets.instructionOffsetCount() == 1) - { - // Is it branching to the next instruction? - int branchOffset = branchTargets.instructionOffset(0) - offset; - if (branchOffset == instruction.length(offset)) - { - if (DEBUG) System.out.println(" Ignoring zero branch instruction at ["+offset+"]"); - } - else - { - // Replace the branch instruction by a simple branch instruction. - Instruction replacementInstruction = - new BranchInstruction(InstructionConstants.OP_GOTO, - branchOffset); - - replaceInstruction(clazz, offset, instruction, replacementInstruction); - } - } - } - - - /** - * Replaces the given table switch instruction, if it is based on the value - * of a fixed array. This is typical for switches on simple enums. - */ - private void replaceSimpleEnumSwitchInstruction(Clazz clazz, - CodeAttribute codeAttribute, - int offset, - TableSwitchInstruction tableSwitchInstruction) - { - // Check if the switch instruction is consuming a single value loaded - // from a fully specified array. - InstructionOffsetValue producerOffsets = - partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue(); - - if (producerOffsets.instructionOffsetCount() == 1) - { - int producerOffset = producerOffsets.instructionOffset(0); - - if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD && - !codeAttributeEditor.isModified(producerOffset)) - { - ReferenceValue referenceValue = - partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue(); - - if (referenceValue.isParticular()) - { - // Simplify the entire construct. - replaceSimpleEnumSwitchInstruction(clazz, - codeAttribute, - producerOffset, - offset, - tableSwitchInstruction, - referenceValue); - } - } - } - } - - - /** - * Replaces the given table switch instruction that is based on a value of - * the given fixed array. - */ - private void replaceSimpleEnumSwitchInstruction(Clazz clazz, - CodeAttribute codeAttribute, - int loadOffset, - int switchOffset, - TableSwitchInstruction tableSwitchInstruction, - ReferenceValue mappingValue) - { - ValueFactory valueFactory = new ParticularValueFactory(); - - // Transform the jump offsets. - int[] jumpOffsets = tableSwitchInstruction.jumpOffsets; - int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()]; - - for (int index = 0; index < newJumpOffsets.length; index++) - { - int switchCase = - mappingValue.integerArrayLoad(valueFactory.createIntegerValue( - index), - valueFactory).value(); - - newJumpOffsets[index] = - switchCase >= tableSwitchInstruction.lowCase && - switchCase <= tableSwitchInstruction.highCase ? - jumpOffsets[switchCase - tableSwitchInstruction.lowCase] : - tableSwitchInstruction.defaultOffset; - } - - // Update the instruction. - tableSwitchInstruction.lowCase = 0; - tableSwitchInstruction.highCase = newJumpOffsets.length - 1; - tableSwitchInstruction.jumpOffsets = newJumpOffsets; - - // Replace the original one with the new version. - replaceSimpleEnumSwitchInstruction(clazz, - loadOffset, - switchOffset, - tableSwitchInstruction); - - cleanUpSwitchInstruction(clazz, switchOffset, tableSwitchInstruction); - - trimSwitchInstruction(clazz, switchOffset, tableSwitchInstruction); - } - - - /** - * Replaces the given look up switch instruction, if it is based on the - * value of a fixed array. This is typical for switches on simple enums. - */ - private void replaceSimpleEnumSwitchInstruction(Clazz clazz, - CodeAttribute codeAttribute, - int offset, - LookUpSwitchInstruction lookupSwitchInstruction) - { - // Check if the switch instruction is consuming a single value loaded - // from a fully specified array. - InstructionOffsetValue producerOffsets = - partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue(); - - if (producerOffsets.instructionOffsetCount() == 1) - { - int producerOffset = producerOffsets.instructionOffset(0); - - if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD && - !codeAttributeEditor.isModified(producerOffset)) - { - ReferenceValue referenceValue = - partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue(); - - if (referenceValue.isParticular()) - { - // Simplify the entire construct. - replaceSimpleEnumSwitchInstruction(clazz, - codeAttribute, - producerOffset, - offset, - lookupSwitchInstruction, - referenceValue); - } - } - } - } - - - /** - * Replaces the given look up switch instruction that is based on a value of - * the given fixed array. This is typical for switches on simple enums. - */ - private void replaceSimpleEnumSwitchInstruction(Clazz clazz, - CodeAttribute codeAttribute, - int loadOffset, - int switchOffset, - LookUpSwitchInstruction lookupSwitchInstruction, - ReferenceValue mappingValue) - { - ValueFactory valueFactory = new ParticularValueFactory(); - - // Transform the jump offsets. - int[] cases = lookupSwitchInstruction.cases; - int[] jumpOffsets = lookupSwitchInstruction.jumpOffsets; - int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()]; - - for (int index = 0; index < newJumpOffsets.length; index++) - { - int switchCase = - mappingValue.integerArrayLoad(valueFactory.createIntegerValue(index), - valueFactory).value(); - - int caseIndex = Arrays.binarySearch(cases, switchCase); - - newJumpOffsets[index] = caseIndex >= 0 ? - jumpOffsets[caseIndex] : - lookupSwitchInstruction.defaultOffset; - } - - // Replace the original lookup switch with a table switch. - TableSwitchInstruction replacementSwitchInstruction = - new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH, - lookupSwitchInstruction.defaultOffset, - 0, - newJumpOffsets.length - 1, - newJumpOffsets); - - replaceSimpleEnumSwitchInstruction(clazz, - loadOffset, - switchOffset, - replacementSwitchInstruction); - - cleanUpSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction); - - trimSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction); - } - - - /** - * Makes sure all branch targets of the given switch instruction are valid. - */ - private void cleanUpSwitchInstruction(Clazz clazz, - int offset, - SwitchInstruction switchInstruction) - { - // Get the actual branch targets. - InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); - - // Get an offset that can serve as a valid default offset. - int defaultOffset = - branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) - - offset; - - Instruction replacementInstruction = null; - - // Check the jump offsets. - int[] jumpOffsets = switchInstruction.jumpOffsets; - for (int index = 0; index < jumpOffsets.length; index++) - { - if (!branchTargets.contains(offset + jumpOffsets[index])) - { - // Replace the unused offset. - jumpOffsets[index] = defaultOffset; - - // Remember to replace the instruction. - replacementInstruction = switchInstruction; - } - } - - // Check the default offset. - if (!branchTargets.contains(offset + switchInstruction.defaultOffset)) - { - // Replace the unused offset. - switchInstruction.defaultOffset = defaultOffset; - - // Remember to replace the instruction. - replacementInstruction = switchInstruction; - } - - if (replacementInstruction != null) - { - replaceInstruction(clazz, offset, switchInstruction, replacementInstruction); - } - } - - - /** - * Trims redundant offsets from the given switch instruction. - */ - private void trimSwitchInstruction(Clazz clazz, - int offset, - TableSwitchInstruction tableSwitchInstruction) - { - // Get an offset that can serve as a valid default offset. - int defaultOffset = tableSwitchInstruction.defaultOffset; - int[] jumpOffsets = tableSwitchInstruction.jumpOffsets; - int length = jumpOffsets.length; - - // Find the lowest index with a non-default jump offset. - int lowIndex = 0; - while (lowIndex < length && - jumpOffsets[lowIndex] == defaultOffset) - { - lowIndex++; - } - - // Find the highest index with a non-default jump offset. - int highIndex = length - 1; - while (highIndex >= 0 && - jumpOffsets[highIndex] == defaultOffset) - { - highIndex--; - } - - // Can we use a shorter array? - int newLength = highIndex - lowIndex + 1; - if (newLength < length) - { - if (newLength <= 0) - { - // Replace the switch instruction by a simple branch instruction. - Instruction replacementInstruction = - new BranchInstruction(InstructionConstants.OP_GOTO, - defaultOffset); - - replaceInstruction(clazz, offset, tableSwitchInstruction, - replacementInstruction); - } - else - { - // Trim the array. - int[] newJumpOffsets = new int[newLength]; - - System.arraycopy(jumpOffsets, lowIndex, newJumpOffsets, 0, newLength); - - tableSwitchInstruction.jumpOffsets = newJumpOffsets; - tableSwitchInstruction.lowCase += lowIndex; - tableSwitchInstruction.highCase -= length - newLength - lowIndex; - - replaceInstruction(clazz, offset, tableSwitchInstruction, - tableSwitchInstruction); - } - } - } - - - /** - * Trims redundant offsets from the given switch instruction. - */ - private void trimSwitchInstruction(Clazz clazz, - int offset, - LookUpSwitchInstruction lookUpSwitchInstruction) - { - // Get an offset that can serve as a valid default offset. - int defaultOffset = lookUpSwitchInstruction.defaultOffset; - int[] jumpOffsets = lookUpSwitchInstruction.jumpOffsets; - int length = jumpOffsets.length; - int newLength = length; - - // Count the default jump offsets. - for (int index = 0; index < length; index++) - { - if (jumpOffsets[index] == defaultOffset) - { - newLength--; - } - } - - // Can we use shorter arrays? - if (newLength < length) - { - if (newLength <= 0) - { - // Replace the switch instruction by a simple branch instruction. - Instruction replacementInstruction = - new BranchInstruction(InstructionConstants.OP_GOTO, - defaultOffset); - - replaceInstruction(clazz, offset, lookUpSwitchInstruction, - replacementInstruction); - } - else - { - // Remove redundant entries from the arrays. - int[] cases = lookUpSwitchInstruction.cases; - int[] newJumpOffsets = new int[newLength]; - int[] newCases = new int[newLength]; - - int newIndex = 0; - - for (int index = 0; index < length; index++) - { - if (jumpOffsets[index] != defaultOffset) - { - newJumpOffsets[newIndex] = jumpOffsets[index]; - newCases[newIndex++] = cases[index]; - } - } - - lookUpSwitchInstruction.jumpOffsets = newJumpOffsets; - lookUpSwitchInstruction.cases = newCases; - - replaceInstruction(clazz, offset, lookUpSwitchInstruction, - lookUpSwitchInstruction); - } - } - } - - - /** - * Replaces the given instruction by an infinite loop. - */ - private void replaceByInfiniteLoop(Clazz clazz, - int offset, - Instruction instruction) - { - // Replace the instruction by an infinite loop. - Instruction replacementInstruction = - new BranchInstruction(InstructionConstants.OP_GOTO, 0); - - if (DEBUG) System.out.println(" Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset)); - - 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); - } - } - - - /** - * Replaces the instruction at a given offset by a given push instruction. - */ - 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); - } - } - - - /** - * 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 the simple enum switch instructions at a given offsets by a - * given replacement instruction. - */ - private void replaceSimpleEnumSwitchInstruction(Clazz clazz, - int loadOffset, - int switchOffset, - SwitchInstruction replacementSwitchInstruction) - { - if (DEBUG) System.out.println(" Replacing switch instruction at ["+switchOffset+"] -> ["+loadOffset+"] swap + pop, "+replacementSwitchInstruction.toString(switchOffset)+")"); - - // Remove the array load instruction. - codeAttributeEditor.replaceInstruction(loadOffset, new Instruction[] - { - new SimpleInstruction(InstructionConstants.OP_SWAP), - new SimpleInstruction(InstructionConstants.OP_POP), - }); - - // Replace the switch instruction. - codeAttributeEditor.replaceInstruction(switchOffset, replacementSwitchInstruction); - - // 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. - replacementSwitchInstruction.accept(clazz, - null, - null, - switchOffset, - extraInstructionVisitor); - } - } -} diff --git a/src/proguard/optimize/evaluation/LivenessAnalyzer.java b/src/proguard/optimize/evaluation/LivenessAnalyzer.java deleted file mode 100644 index 87bd4e5..0000000 --- a/src/proguard/optimize/evaluation/LivenessAnalyzer.java +++ /dev/null @@ -1,526 +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.*; -import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.SimplifiedVisitor; -import proguard.evaluation.value.*; - -/** - * This AttributeVisitor analyzes the liveness of the variables in the code - * attributes that it visits, based on partial evaluation. - * - * @author Eric Lafortune - */ -public class LivenessAnalyzer -extends SimplifiedVisitor -implements AttributeVisitor, - InstructionVisitor, - ExceptionInfoVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = true; - //*/ - - private static final int MAX_VARIABLES_SIZE = 64; - - private final PartialEvaluator partialEvaluator; - - private long[] isAliveBefore = new long[ClassConstants.TYPICAL_CODE_LENGTH]; - private long[] isAliveAfter = new long[ClassConstants.TYPICAL_CODE_LENGTH]; - private long[] isCategory2 = new long[ClassConstants.TYPICAL_CODE_LENGTH]; - - // Fields acting as global temporary variables. - private boolean checkAgain; - private long alive; - - - /** - * Creates a new LivenessAnalyzer. - */ - public LivenessAnalyzer() - { - this(new PartialEvaluator()); - } - - - /** - * Creates a new LivenessAnalyzer that will use the given partial evaluator. - * It will run this evaluator on every code attribute that it visits. - */ - public LivenessAnalyzer(PartialEvaluator partialEvaluator) - { - this.partialEvaluator = partialEvaluator; - } - - - /** - * Returns whether the instruction at the given offset has ever been - * executed during the partial evaluation. - */ - public boolean isTraced(int instructionOffset) - { - return partialEvaluator.isTraced(instructionOffset); - } - - - /** - * Returns whether the specified variable is alive before the instruction - * at the given offset. - */ - public boolean isAliveBefore(int instructionOffset, int variableIndex) - { - return variableIndex >= MAX_VARIABLES_SIZE || - (isAliveBefore[instructionOffset] & (1L << variableIndex)) != 0; - } - - - /** - * Sets whether the specified variable is alive before the instruction - * at the given offset. - */ - public void setAliveBefore(int instructionOffset, int variableIndex, boolean alive) - { - if (variableIndex < MAX_VARIABLES_SIZE) - { - if (alive) - { - isAliveBefore[instructionOffset] |= 1L << variableIndex; - } - else - { - isAliveBefore[instructionOffset] &= ~(1L << variableIndex); - } - } - } - - - /** - * Returns whether the specified variable is alive after the instruction - * at the given offset. - */ - public boolean isAliveAfter(int instructionOffset, int variableIndex) - { - return variableIndex >= MAX_VARIABLES_SIZE || - (isAliveAfter[instructionOffset] & (1L << variableIndex)) != 0; - } - - - /** - * Sets whether the specified variable is alive after the instruction - * at the given offset. - */ - public void setAliveAfter(int instructionOffset, int variableIndex, boolean alive) - { - if (variableIndex < MAX_VARIABLES_SIZE) - { - if (alive) - { - isAliveAfter[instructionOffset] |= 1L << variableIndex; - } - else - { - isAliveAfter[instructionOffset] &= ~(1L << variableIndex); - } - } - } - - - /** - * Returns whether the specified variable takes up two entries after the - * instruction at the given offset. - */ - public boolean isCategory2(int instructionOffset, int variableIndex) - { - return variableIndex < MAX_VARIABLES_SIZE && - (isCategory2[instructionOffset] & (1L << variableIndex)) != 0; - } - - - /** - * Sets whether the specified variable takes up two entries after the - * instruction at the given offset. - */ - public void setCategory2(int instructionOffset, int variableIndex, boolean category2) - { - if (variableIndex < MAX_VARIABLES_SIZE) - { - if (category2) - { - isCategory2[instructionOffset] |= 1L << variableIndex; - } - else - { - isCategory2[instructionOffset] &= ~(1L << variableIndex); - } - } - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { -// DEBUG = -// clazz.getName().equals("abc/Def") && -// method.getName(clazz).equals("abc"); - - if (DEBUG) - { - System.out.println(); - System.out.println("Liveness analysis: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); - } - - // Initialize the global arrays. - initializeArrays(codeAttribute); - - // Evaluate the method. - partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); - - int codeLength = codeAttribute.u4codeLength; - int variablesSize = codeAttribute.u2maxLocals; - - // We'll only really analyze the first 64 variables. - if (variablesSize > MAX_VARIABLES_SIZE) - { - variablesSize = MAX_VARIABLES_SIZE; - } - - // Mark liveness blocks, as many times as necessary. - do - { - checkAgain = false; - alive = 0L; - - // Loop over all traced instructions, backward. - for (int offset = codeLength - 1; offset >= 0; offset--) - { - if (partialEvaluator.isTraced(offset)) - { - // Update the liveness based on the branch targets. - InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset); - if (branchTargets != null) - { - // Update the liveness right after the branch instruction. - alive = combinedLiveness(branchTargets); - } - - // Merge the current liveness. - alive |= isAliveAfter[offset]; - - // Update the liveness after the instruction. - isAliveAfter[offset] = alive; - - // Update the current liveness based on the instruction. - codeAttribute.instructionAccept(clazz, method, offset, this); - - // Merge the current liveness. - alive |= isAliveBefore[offset]; - - // Update the liveness before the instruction. - if ((~isAliveBefore[offset] & alive) != 0L) - { - isAliveBefore[offset] = alive; - - // Do we have to check again after this loop? - checkAgain |= offset < maxOffset(partialEvaluator.branchOrigins(offset)); - } - } - } - - // Account for the liveness at the start of the exception handlers. - codeAttribute.exceptionsAccept(clazz, method, this); - } - while (checkAgain); - - // Loop over all instructions, to mark variables that take up two entries. - for (int offset = 0; offset < codeLength; offset++) - { - if (partialEvaluator.isTraced(offset)) - { - // Loop over all variables. - for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) - { - // Is the variable alive and a category 2 type? - if (isAliveBefore(offset, variableIndex)) - { - Value value = partialEvaluator.getVariablesBefore(offset).getValue(variableIndex); - if (value != null && value.isCategory2()) - { - // Mark it as such. - setCategory2(offset, variableIndex, true); - - // Mark the next variable as well. - setAliveBefore(offset, variableIndex + 1, true); - setCategory2( offset, variableIndex + 1, true); - } - } - - // Is the variable alive and a category 2 type? - if (isAliveAfter(offset, variableIndex)) - { - Value value = partialEvaluator.getVariablesAfter(offset).getValue(variableIndex); - if (value != null && value.isCategory2()) - { - // Mark it as such. - setCategory2(offset, variableIndex, true); - - // Mark the next variable as well. - setAliveAfter(offset, variableIndex + 1, true); - setCategory2( offset, variableIndex + 1, true); - } - } - } - } - } - - if (DEBUG) - { - // Loop over all instructions. - for (int offset = 0; offset < codeLength; offset++) - { - if (partialEvaluator.isTraced(offset)) - { - long aliveBefore = isAliveBefore[offset]; - long aliveAfter = isAliveAfter[offset]; - long category2 = isCategory2[offset]; - - // Print out the liveness of all variables before the instruction. - for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) - { - long variableMask = (1L << variableIndex); - System.out.print((aliveBefore & variableMask) == 0L ? '.' : - (category2 & variableMask) == 0L ? 'x' : - '*'); - } - - // Print out the instruction itself. - System.out.println(" "+ InstructionFactory.create(codeAttribute.code, offset).toString(offset)); - - // Print out the liveness of all variables after the instruction. - for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) - { - long variableMask = (1L << variableIndex); - System.out.print((aliveAfter & variableMask) == 0L ? '.' : - (category2 & variableMask) == 0L ? 'x' : - '='); - } - - System.out.println(); - } - } - } - } - - - // 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) - { - int variableIndex = variableInstruction.variableIndex; - if (variableIndex < MAX_VARIABLES_SIZE) - { - long livenessMask = 1L << variableIndex; - - // Is it a load instruction or a store instruction? - if (variableInstruction.isLoad()) - { - // Start marking the variable before the load instruction. - alive |= livenessMask; - } - else - { - // Stop marking the variable before the store instruction. - alive &= ~livenessMask; - - // But do mark the variable right after the store instruction. - isAliveAfter[offset] |= livenessMask; - } - } - } - - - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) - { - // Special case: variable 0 ('this') in an initializer has to be alive - // as long as it hasn't been initialized. - if (offset == partialEvaluator.superInitializationOffset()) - { - alive |= 1L; - } - } - - - // Implementations for ExceptionInfoVisitor. - - public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) - { - // Are any variables alive at the start of the handler? - long alive = isAliveBefore[exceptionInfo.u2handlerPC]; - if (alive != 0L) - { - // Set the same liveness flags for the entire try block. - int startOffset = exceptionInfo.u2startPC; - int endOffset = exceptionInfo.u2endPC; - - for (int offset = startOffset; offset < endOffset; offset++) - { - if (partialEvaluator.isTraced(offset)) - { - if ((~(isAliveBefore[offset] & isAliveAfter[offset]) & alive) != 0L) - { - isAliveBefore[offset] |= alive; - isAliveAfter[offset] |= alive; - - // Check again after having marked this try block. - checkAgain = true; - } - } - } - } - } - - - // Small utility methods. - - /** - * Initializes the global arrays. - */ - private void initializeArrays(CodeAttribute codeAttribute) - { - int codeLength = codeAttribute.u4codeLength; - - // Create new arrays for storing information at each instruction offset. - if (isAliveBefore.length < codeLength) - { - isAliveBefore = new long[codeLength]; - isAliveAfter = new long[codeLength]; - isCategory2 = new long[codeLength]; - } - else - { - for (int index = 0; index < codeLength; index++) - { - isAliveBefore[index] = 0L; - isAliveAfter[index] = 0L; - isCategory2[index] = 0L; - } - } - } - - - /** - * Returns the combined liveness mask of the variables right before the - * specified instruction offsets. - */ - private long combinedLiveness(InstructionOffsetValue instructionOffsetValue) - { - long alive = 0L; - - int count = instructionOffsetValue.instructionOffsetCount(); - for (int index = 0; index < count; index++) - { - alive |= isAliveBefore[instructionOffsetValue.instructionOffset(index)]; - } - - return alive; - } - - - /** - * Returns the minimum offset from the given instruction offsets. - */ - private int minOffset(Value instructionOffsets) - { - return minOffset(instructionOffsets, Integer.MAX_VALUE); - } - - - /** - * Returns the minimum offset from the given instruction offsets. - */ - private int minOffset(Value instructionOffsets, int minOffset) - { - if (instructionOffsets != null) - { - InstructionOffsetValue instructionOffsetValue = - instructionOffsets.instructionOffsetValue(); - - int count = instructionOffsetValue.instructionOffsetCount(); - for (int index = 0; index < count; index++) - { - int offset = instructionOffsetValue.instructionOffset(index); - if (minOffset > offset) - { - minOffset = offset; - } - } - } - - return minOffset; - } - - - /** - * Returns the maximum offset from the given instruction offsets. - */ - private int maxOffset(Value instructionOffsets) - { - return maxOffset(instructionOffsets, Integer.MIN_VALUE); - } - - - /** - * Returns the maximum offset from the given instruction offsets. - */ - private int maxOffset(Value instructionOffsets, int maxOffset) - { - if (instructionOffsets != null) - { - InstructionOffsetValue instructionOffsetValue = - instructionOffsets.instructionOffsetValue(); - - int count = instructionOffsetValue.instructionOffsetCount(); - for (int index = 0; index < count; index++) - { - int offset = instructionOffsetValue.instructionOffset(index); - if (maxOffset < offset) - { - maxOffset = offset; - } - } - } - - return maxOffset; - } -} diff --git a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java b/src/proguard/optimize/evaluation/LoadingInvocationUnit.java deleted file mode 100644 index 80b4b84..0000000 --- a/src/proguard/optimize/evaluation/LoadingInvocationUnit.java +++ /dev/null @@ -1,164 +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.constant.RefConstant; -import proguard.evaluation.BasicInvocationUnit; -import proguard.evaluation.value.*; - -/** - * This InvocationUnit loads parameter values and return values that were - * previously stored with the methods that are invoked. - * - * @see StoringInvocationUnit - * @author Eric Lafortune - */ -public class LoadingInvocationUnit -extends BasicInvocationUnit -{ - private final boolean loadFieldValues; - private final boolean loadMethodParameterValues; - private final boolean loadMethodReturnValues; - - - /** - * Creates a new LoadingInvocationUnit with the given value factory. - */ - public LoadingInvocationUnit(ValueFactory valueFactory) - { - this(valueFactory, true, true, true); - } - - - /** - * Creates a new LoadingInvocationUnit with the given value factory, for - * loading the specified values. - */ - public LoadingInvocationUnit(ValueFactory valueFactory, - boolean loadFieldValues, - boolean loadMethodParameterValues, - boolean loadMethodReturnValues) - { - super(valueFactory); - - this.loadFieldValues = loadFieldValues; - this.loadMethodParameterValues = loadMethodParameterValues; - this.loadMethodReturnValues = loadMethodReturnValues; - } - - - // Implementations for BasicInvocationUnit. - - protected Value getFieldClassValue(Clazz clazz, - RefConstant refConstant, - String type) - { - if (loadFieldValues) - { - // Do we know this field? - Member referencedMember = refConstant.referencedMember; - if (referencedMember != null) - { - // Retrieve the stored field class value. - ReferenceValue value = StoringInvocationUnit.getFieldClassValue((Field)referencedMember); - if (value != null) - { - return value; - } - } - } - - return super.getFieldClassValue(clazz, refConstant, type); - } - - - protected Value getFieldValue(Clazz clazz, - RefConstant refConstant, - String type) - { - if (loadFieldValues) - { - // Do we know this field? - Member referencedMember = refConstant.referencedMember; - if (referencedMember != null) - { - // Retrieve the stored field value. - Value value = StoringInvocationUnit.getFieldValue((Field)referencedMember); - if (value != null) - { - return value; - } - } - } - - return super.getFieldValue(clazz, refConstant, type); - } - - - protected Value getMethodParameterValue(Clazz clazz, - Method method, - int parameterIndex, - String type, - Clazz referencedClass) - { - if (loadMethodParameterValues) - { - // Retrieve the stored method parameter value. - Value value = StoringInvocationUnit.getMethodParameterValue(method, parameterIndex); - if (value != null) - { - return value; - } - } - - return super.getMethodParameterValue(clazz, - method, - parameterIndex, - type, - referencedClass); - } - - - protected Value getMethodReturnValue(Clazz clazz, - RefConstant refConstant, - String type) - { - if (loadMethodReturnValues) - { - // Do we know this method? - Member referencedMember = refConstant.referencedMember; - if (referencedMember != null) - { - // Retrieve the stored method return value. - Value value = StoringInvocationUnit.getMethodReturnValue((Method)referencedMember); - if (value != null) - { - return value; - } - } - } - - return super.getMethodReturnValue(clazz, - refConstant, - type); - } -} diff --git a/src/proguard/optimize/evaluation/PartialEvaluator.java b/src/proguard/optimize/evaluation/PartialEvaluator.java deleted file mode 100644 index 0301f12..0000000 --- a/src/proguard/optimize/evaluation/PartialEvaluator.java +++ /dev/null @@ -1,1282 +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.*; -import proguard.classfile.constant.ClassConstant; -import proguard.classfile.instruction.*; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.evaluation.*; -import proguard.evaluation.value.*; -import proguard.optimize.peephole.BranchTargetFinder; - -import java.util.Arrays; - -/** - * This AttributeVisitor performs partial evaluation on the code attributes - * that it visits. - * - * @author Eric Lafortune - */ -public class PartialEvaluator -extends SimplifiedVisitor -implements AttributeVisitor, - ExceptionInfoVisitor -{ - //* - private static final boolean DEBUG = false; - private static final boolean DEBUG_RESULTS = false; - /*/ - private static boolean DEBUG = true; - private static boolean DEBUG_RESULTS = true; - //*/ - - private static final int MAXIMUM_EVALUATION_COUNT = 5; - - public static final int NONE = -2; - public static final int AT_METHOD_ENTRY = -1; - public static final int AT_CATCH_ENTRY = -1; - - private final ValueFactory valueFactory; - private final InvocationUnit invocationUnit; - private final boolean evaluateAllCode; - - private InstructionOffsetValue[] branchOriginValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; - private InstructionOffsetValue[] branchTargetValues = new InstructionOffsetValue[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedVariables[] variablesBefore = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedStack[] stacksBefore = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedVariables[] variablesAfter = new TracedVariables[ClassConstants.TYPICAL_CODE_LENGTH]; - private TracedStack[] stacksAfter = new TracedStack[ClassConstants.TYPICAL_CODE_LENGTH]; - private boolean[] generalizedContexts = new boolean[ClassConstants.TYPICAL_CODE_LENGTH]; - private int[] evaluationCounts = new int[ClassConstants.TYPICAL_CODE_LENGTH]; - private boolean evaluateExceptions; - - private final BasicBranchUnit branchUnit; - private final BranchTargetFinder branchTargetFinder; - - private final java.util.Stack callingInstructionBlockStack; - private final java.util.Stack instructionBlockStack = new java.util.Stack(); - - - /** - * Creates a simple PartialEvaluator. - */ - public PartialEvaluator() - { - this(new ValueFactory(), - new BasicInvocationUnit(new ValueFactory()), - true); - } - - - /** - * Creates a new PartialEvaluator. - * @param valueFactory the value factory that will create all values - * during evaluation. - * @param invocationUnit the invocation unit that will handle all - * communication with other fields and methods. - * @param evaluateAllCode a flag that specifies whether all casts, branch - * targets, and exception handlers should be - * evaluated, even if they are unnecessary or - * unreachable. - */ - public PartialEvaluator(ValueFactory valueFactory, - InvocationUnit invocationUnit, - boolean evaluateAllCode) - { - this(valueFactory, - invocationUnit, - evaluateAllCode, - evaluateAllCode ? - new BasicBranchUnit() : - new TracedBranchUnit(), - new BranchTargetFinder(), - null); - } - - - /** - * Creates a new PartialEvaluator, based on an existing one. - * @param partialEvaluator the subroutine calling partial evaluator. - */ - private PartialEvaluator(PartialEvaluator partialEvaluator) - { - this(partialEvaluator.valueFactory, - partialEvaluator.invocationUnit, - partialEvaluator.evaluateAllCode, - partialEvaluator.branchUnit, - partialEvaluator.branchTargetFinder, - partialEvaluator.instructionBlockStack); - } - - - /** - * Creates a new PartialEvaluator. - * @param valueFactory the value factory that will create - * all values during evaluation. - * @param invocationUnit the invocation unit that will handle - * all communication with other fields - * and methods. - * @param evaluateAllCode a flag that specifies whether all - * casts, branch targets, and exception - * handlers should be evaluated, even - * if they are unnecessary or - * unreachable. - * @param branchUnit the branch unit that will handle all - * branches. - * @param branchTargetFinder the utility class that will find all - * branches. - * @param callingInstructionBlockStack the stack of instruction blocks to - * be evaluated - */ - private PartialEvaluator(ValueFactory valueFactory, - InvocationUnit invocationUnit, - boolean evaluateAllCode, - BasicBranchUnit branchUnit, - BranchTargetFinder branchTargetFinder, - java.util.Stack callingInstructionBlockStack) - { - this.valueFactory = valueFactory; - this.invocationUnit = invocationUnit; - this.evaluateAllCode = evaluateAllCode; - this.branchUnit = branchUnit; - this.branchTargetFinder = branchTargetFinder; - this.callingInstructionBlockStack = callingInstructionBlockStack == null ? - this.instructionBlockStack : - callingInstructionBlockStack; - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { -// DEBUG = DEBUG_RESULTS = -// clazz.getName().equals("abc/Def") && -// method.getName(clazz).equals("abc"); - - // TODO: Remove this when the partial evaluator has stabilized. - // Catch any unexpected exceptions from the actual visiting method. - try - { - // Process the code. - visitCodeAttribute0(clazz, method, codeAttribute); - } - catch (RuntimeException ex) - { - System.err.println("Unexpected error while performing partial evaluation:"); - System.err.println(" Class = ["+clazz.getName()+"]"); - System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); - - if (DEBUG) - { - method.accept(clazz, new ClassPrinter()); - - System.out.println("Evaluation results:"); - - int offset = 0; - do - { - if (isBranchOrExceptionTarget(offset)) - { - System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); - if (isTraced(offset)) - { - System.out.println(" Vars: "+variablesBefore[offset]); - System.out.println(" Stack: "+stacksBefore[offset]); - } - } - - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - System.out.println(instruction.toString(offset)); - - if (isTraced(offset)) - { - int initializationOffset = branchTargetFinder.initializationOffset(offset); - if (initializationOffset != NONE) - { - System.out.println(" is to be initialized at ["+initializationOffset+"]"); - } - - InstructionOffsetValue branchTargets = branchTargets(offset); - if (branchTargets != null) - { - System.out.println(" has overall been branching to "+branchTargets); - } - - System.out.println(" Vars: "+variablesAfter[offset]); - System.out.println(" Stack: "+stacksAfter[offset]); - } - - offset += instruction.length(offset); - } - while (offset < codeAttribute.u4codeLength); - } - - throw ex; - } - } - - - public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - // Evaluate the instructions, starting at the entry point. - if (DEBUG) - { - System.out.println(); - System.out.println("Partial evaluation: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); - System.out.println(" Max locals = "+codeAttribute.u2maxLocals); - System.out.println(" Max stack = "+codeAttribute.u2maxStack); - } - - // Reuse the existing variables and stack objects, ensuring the right size. - TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals); - TracedStack stack = new TracedStack(codeAttribute.u2maxStack); - - // Initialize the reusable arrays and variables. - initializeArrays(codeAttribute); - initializeParameters(clazz, method, codeAttribute, variables); - - // Find all instruction offsets,... - codeAttribute.accept(clazz, method, branchTargetFinder); - - // Start executing the first instruction block. - evaluateInstructionBlockAndExceptionHandlers(clazz, - method, - codeAttribute, - variables, - stack, - 0, - codeAttribute.u4codeLength); - - if (DEBUG_RESULTS) - { - System.out.println("Evaluation results:"); - - int offset = 0; - do - { - if (isBranchOrExceptionTarget(offset)) - { - System.out.println("Branch target from ["+branchOriginValues[offset]+"]:"); - if (isTraced(offset)) - { - System.out.println(" Vars: "+variablesBefore[offset]); - System.out.println(" Stack: "+stacksBefore[offset]); - } - } - - Instruction instruction = InstructionFactory.create(codeAttribute.code, - offset); - System.out.println(instruction.toString(offset)); - - if (isTraced(offset)) - { - int initializationOffset = branchTargetFinder.initializationOffset(offset); - if (initializationOffset >= 0) - { - System.out.println(" is to be initialized at ["+initializationOffset+"]"); - } - - InstructionOffsetValue branchTargets = branchTargets(offset); - if (branchTargets != null) - { - System.out.println(" has overall been branching to "+branchTargets); - } - - System.out.println(" Vars: "+variablesAfter[offset]); - System.out.println(" Stack: "+stacksAfter[offset]); - } - - offset += instruction.length(offset); - } - while (offset < codeAttribute.u4codeLength); - } - } - - - /** - * Returns whether a block of instructions is ever used. - */ - public boolean isTraced(int startOffset, int endOffset) - { - for (int index = startOffset; index < endOffset; index++) - { - if (isTraced(index)) - { - return true; - } - } - - return false; - } - - - /** - * Returns whether the instruction at the given offset has ever been - * executed during the partial evaluation. - */ - public boolean isTraced(int instructionOffset) - { - return evaluationCounts[instructionOffset] > 0; - } - - - /** - * Returns whether there is an instruction at the given offset. - */ - public boolean isInstruction(int instructionOffset) - { - return branchTargetFinder.isInstruction(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is the target of a - * branch instruction or an exception. - */ - public boolean isBranchOrExceptionTarget(int instructionOffset) - { - return branchTargetFinder.isBranchTarget(instructionOffset) || - branchTargetFinder.isExceptionHandler(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is the start of a - * subroutine. - */ - public boolean isSubroutineStart(int instructionOffset) - { - return branchTargetFinder.isSubroutineStart(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is a subroutine - * invocation. - */ - public boolean isSubroutineInvocation(int instructionOffset) - { - return branchTargetFinder.isSubroutineInvocation(instructionOffset); - } - - - /** - * Returns whether the instruction at the given offset is part of a - * subroutine. - */ - public boolean isSubroutine(int instructionOffset) - { - return branchTargetFinder.isSubroutine(instructionOffset); - } - - - /** - * Returns whether the subroutine at the given offset is ever returning - * by means of a regular 'ret' instruction. - */ - public boolean isSubroutineReturning(int instructionOffset) - { - return branchTargetFinder.isSubroutineReturning(instructionOffset); - } - - - /** - * Returns the offset after the subroutine that starts at the given - * offset. - */ - public int subroutineEnd(int instructionOffset) - { - return branchTargetFinder.subroutineEnd(instructionOffset); - } - - - /** - * Returns the instruction offset at which the object instance that is - * created at the given 'new' instruction offset is initialized, or - * <code>NONE</code> if it is not being created. - */ - public int initializationOffset(int instructionOffset) - { - return branchTargetFinder.initializationOffset(instructionOffset); - } - - - /** - * Returns whether the method is an instance initializer. - */ - public boolean isInitializer() - { - return branchTargetFinder.isInitializer(); - } - - - /** - * Returns the instruction offset at which this initializer is calling - * the "super" or "this" initializer method, or <code>NONE</code> if it is - * not an initializer. - */ - public int superInitializationOffset() - { - return branchTargetFinder.superInitializationOffset(); - } - - - /** - * Returns the offset of the 'new' instruction that corresponds to the - * invocation of the instance initializer at the given offset, or - * <code>AT_METHOD_ENTRY</code> if the invocation is calling the "super" or - * "this" initializer method, , or <code>NONE</code> if it is not a 'new' - * instruction. - */ - public int creationOffset(int offset) - { - return branchTargetFinder.creationOffset(offset); - } - - - /** - * Returns the variables before execution of the instruction at the given - * offset. - */ - public TracedVariables getVariablesBefore(int instructionOffset) - { - return variablesBefore[instructionOffset]; - } - - - /** - * Returns the variables after execution of the instruction at the given - * offset. - */ - public TracedVariables getVariablesAfter(int instructionOffset) - { - return variablesAfter[instructionOffset]; - } - - - /** - * Returns the stack before execution of the instruction at the given - * offset. - */ - public TracedStack getStackBefore(int instructionOffset) - { - return stacksBefore[instructionOffset]; - } - - - /** - * Returns the stack after execution of the instruction at the given - * offset. - */ - public TracedStack getStackAfter(int instructionOffset) - { - return stacksAfter[instructionOffset]; - } - - - /** - * Returns the instruction offsets that branch to the given instruction - * offset. - */ - public InstructionOffsetValue branchOrigins(int instructionOffset) - { - return branchOriginValues[instructionOffset]; - } - - - /** - * Returns the instruction offsets to which the given instruction offset - * branches. - */ - public InstructionOffsetValue branchTargets(int instructionOffset) - { - return branchTargetValues[instructionOffset]; - } - - - // Utility methods to evaluate instruction blocks. - - /** - * Pushes block of instructions to be executed in the calling partial - * evaluator. - */ - private void pushCallingInstructionBlock(TracedVariables variables, - TracedStack stack, - int startOffset) - { - callingInstructionBlockStack.push(new MyInstructionBlock(variables, - stack, - startOffset)); - } - - - /** - * Pushes block of instructions to be executed in this partial evaluator. - */ - private void pushInstructionBlock(TracedVariables variables, - TracedStack stack, - int startOffset) - { - instructionBlockStack.push(new MyInstructionBlock(variables, - stack, - startOffset)); - } - - - /** - * Evaluates the instruction block and the exception handlers covering the - * given instruction range in the given code. - */ - private void evaluateInstructionBlockAndExceptionHandlers(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int startOffset, - int endOffset) - { - evaluateInstructionBlock(clazz, - method, - codeAttribute, - variables, - stack, - startOffset); - - evaluateExceptionHandlers(clazz, - method, - codeAttribute, - startOffset, - endOffset); - } - - - /** - * Evaluates a block of instructions, starting at the given offset and ending - * at a branch instruction, a return instruction, or a throw instruction. - */ - private void evaluateInstructionBlock(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int startOffset) - { - // Execute the initial instruction block. - evaluateSingleInstructionBlock(clazz, - method, - codeAttribute, - variables, - stack, - startOffset); - - // Execute all resulting instruction blocks on the execution stack. - while (!instructionBlockStack.empty()) - { - if (DEBUG) System.out.println("Popping alternative branch out of "+instructionBlockStack.size()+" blocks"); - - MyInstructionBlock instructionBlock = - (MyInstructionBlock)instructionBlockStack.pop(); - - evaluateSingleInstructionBlock(clazz, - method, - codeAttribute, - instructionBlock.variables, - instructionBlock.stack, - instructionBlock.startOffset); - } - } - - - /** - * Evaluates a block of instructions, starting at the given offset and ending - * at a branch instruction, a return instruction, or a throw instruction. - * Instruction blocks that are to be evaluated as a result are pushed on - * the given stack. - */ - private void evaluateSingleInstructionBlock(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int startOffset) - { - byte[] code = codeAttribute.code; - - if (DEBUG) - { - System.out.println("Instruction block starting at ["+startOffset+"] in "+ - ClassUtil.externalFullMethodDescription(clazz.getName(), - 0, - method.getName(clazz), - method.getDescriptor(clazz))); - System.out.println("Init vars: "+variables); - System.out.println("Init stack: "+stack); - } - - Processor processor = new Processor(variables, - stack, - valueFactory, - branchUnit, - invocationUnit, - evaluateAllCode); - - int instructionOffset = startOffset; - - int maxOffset = startOffset; - - // Evaluate the subsequent instructions. - while (true) - { - if (maxOffset < instructionOffset) - { - maxOffset = instructionOffset; - } - - // Maintain a generalized local variable frame and stack at this - // instruction offset, before execution. - int evaluationCount = evaluationCounts[instructionOffset]; - if (evaluationCount == 0) - { - // First time we're passing by this instruction. - if (variablesBefore[instructionOffset] == null) - { - // There's not even a context at this index yet. - variablesBefore[instructionOffset] = new TracedVariables(variables); - stacksBefore[instructionOffset] = new TracedStack(stack); - } - else - { - // Reuse the context objects at this index. - variablesBefore[instructionOffset].initialize(variables); - stacksBefore[instructionOffset].copy(stack); - } - - // We'll execute in the generalized context, because it is - // the same as the current context. - generalizedContexts[instructionOffset] = true; - } - else - { - // Merge in the current context. - boolean variablesChanged = variablesBefore[instructionOffset].generalize(variables, true); - boolean stackChanged = stacksBefore[instructionOffset].generalize(stack); - - //System.out.println("GVars: "+variablesBefore[instructionOffset]); - //System.out.println("GStack: "+stacksBefore[instructionOffset]); - - // Bail out if the current context is the same as last time. - if (!variablesChanged && - !stackChanged && - generalizedContexts[instructionOffset]) - { - if (DEBUG) System.out.println("Repeated variables, stack, and branch targets"); - - break; - } - - // See if this instruction has been evaluated an excessive number - // of times. - if (evaluationCount >= MAXIMUM_EVALUATION_COUNT) - { - if (DEBUG) System.out.println("Generalizing current context after "+evaluationCount+" evaluations"); - - // Continue, but generalize the current context. - // Note that the most recent variable values have to remain - // last in the generalizations, for the sake of the ret - // instruction. - variables.generalize(variablesBefore[instructionOffset], false); - stack.generalize(stacksBefore[instructionOffset]); - - // We'll execute in the generalized context. - generalizedContexts[instructionOffset] = true; - } - else - { - // We'll execute in the current context. - generalizedContexts[instructionOffset] = false; - } - } - - // We'll evaluate this instruction. - evaluationCounts[instructionOffset]++; - - // Remember this instruction's offset with any stored value. - Value storeValue = new InstructionOffsetValue(instructionOffset); - variables.setProducerValue(storeValue); - stack.setProducerValue(storeValue); - - // Reset the trace value. - InstructionOffsetValue traceValue = InstructionOffsetValue.EMPTY_VALUE; - - // Note that the instruction is only volatile. - Instruction instruction = InstructionFactory.create(code, instructionOffset); - - // By default, the next instruction will be the one after this - // instruction. - int nextInstructionOffset = instructionOffset + - instruction.length(instructionOffset); - InstructionOffsetValue nextInstructionOffsetValue = new InstructionOffsetValue(nextInstructionOffset); - branchUnit.resetCalled(); - branchUnit.setTraceBranchTargets(nextInstructionOffsetValue); - - if (DEBUG) - { - System.out.println(instruction.toString(instructionOffset)); - } - - try - { - // Process the instruction. The processor may modify the - // variables and the stack, and it may call the branch unit - // and the invocation unit. - instruction.accept(clazz, - method, - codeAttribute, - instructionOffset, - processor); - } - catch (RuntimeException ex) - { - System.err.println("Unexpected error while evaluating instruction:"); - System.err.println(" Class = ["+clazz.getName()+"]"); - System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]"); - System.err.println(" Instruction = "+instruction.toString(instructionOffset)); - System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")"); - - throw ex; - } - - // Collect the branch targets from the branch unit. - InstructionOffsetValue branchTargets = branchUnit.getTraceBranchTargets(); - int branchTargetCount = branchTargets.instructionOffsetCount(); - - // Stop tracing. - branchUnit.setTraceBranchTargets(traceValue); - - if (DEBUG) - { - if (branchUnit.wasCalled()) - { - System.out.println(" is branching to "+branchTargets); - } - if (branchTargetValues[instructionOffset] != null) - { - System.out.println(" has up till now been branching to "+branchTargetValues[instructionOffset]); - } - - System.out.println(" Vars: "+variables); - System.out.println(" Stack: "+stack); - } - - // Maintain a generalized local variable frame and stack at this - // instruction offset, after execution. - if (evaluationCount == 0) - { - // First time we're passing by this instruction. - if (variablesAfter[instructionOffset] == null) - { - // There's not even a context at this index yet. - variablesAfter[instructionOffset] = new TracedVariables(variables); - stacksAfter[instructionOffset] = new TracedStack(stack); - } - else - { - // Reuse the context objects at this index. - variablesAfter[instructionOffset].initialize(variables); - stacksAfter[instructionOffset].copy(stack); - } - } - else - { - // Merge in the current context. - variablesAfter[instructionOffset].generalize(variables, true); - stacksAfter[instructionOffset].generalize(stack); - } - - // Did the branch unit get called? - if (branchUnit.wasCalled()) - { - // Accumulate the branch targets at this offset. - branchTargetValues[instructionOffset] = branchTargetValues[instructionOffset] == null ? - branchTargets : - branchTargetValues[instructionOffset].generalize(branchTargets).instructionOffsetValue(); - - // Are there no branch targets at all? - if (branchTargetCount == 0) - { - // Exit from this code block. - break; - } - - // Accumulate the branch origins at the branch target offsets. - InstructionOffsetValue instructionOffsetValue = new InstructionOffsetValue(instructionOffset); - for (int index = 0; index < branchTargetCount; index++) - { - int branchTarget = branchTargets.instructionOffset(index); - branchOriginValues[branchTarget] = branchOriginValues[branchTarget] == null ? - instructionOffsetValue: - branchOriginValues[branchTarget].generalize(instructionOffsetValue).instructionOffsetValue(); - } - - // Are there multiple branch targets? - if (branchTargetCount > 1) - { - // Push them on the execution stack and exit from this block. - for (int index = 0; index < branchTargetCount; index++) - { - if (DEBUG) System.out.println("Pushing alternative branch #"+index+" out of "+branchTargetCount+", from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(index)+"]"); - - pushInstructionBlock(new TracedVariables(variables), - new TracedStack(stack), - branchTargets.instructionOffset(index)); - } - - break; - } - - if (DEBUG) System.out.println("Definite branch from ["+instructionOffset+"] to ["+branchTargets.instructionOffset(0)+"]"); - } - - // Just continue with the next instruction. - instructionOffset = branchTargets.instructionOffset(0); - - // Is this a subroutine invocation? - if (instruction.opcode == InstructionConstants.OP_JSR || - instruction.opcode == InstructionConstants.OP_JSR_W) - { - // Evaluate the subroutine in another partial evaluator. - evaluateSubroutine(clazz, - method, - codeAttribute, - variables, - stack, - instructionOffset, - instructionBlockStack); - - break; - } - else if (instruction.opcode == InstructionConstants.OP_RET) - { - // Let the partial evaluator that has called the subroutine - // handle the evaluation after the return. - pushCallingInstructionBlock(new TracedVariables(variables), - new TracedStack(stack), - instructionOffset); - break; - } - } - - if (DEBUG) System.out.println("Ending processing of instruction block starting at ["+startOffset+"]"); - } - - - /** - * Evaluates a subroutine and its exception handlers, starting at the given - * offset and ending at a subroutine return instruction. - */ - private void evaluateSubroutine(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables, - TracedStack stack, - int subroutineStart, - java.util.Stack instructionBlockStack) - { - int subroutineEnd = branchTargetFinder.subroutineEnd(subroutineStart); - - if (DEBUG) System.out.println("Evaluating subroutine from "+subroutineStart+" to "+subroutineEnd); - - // Create a temporary partial evaluator, so there are no conflicts - // with variables that are alive across subroutine invocations, between - // different invocations. - PartialEvaluator subroutinePartialEvaluator = - new PartialEvaluator(this); - - subroutinePartialEvaluator.initializeArrays(codeAttribute); - - // Evaluate the subroutine. - subroutinePartialEvaluator.evaluateInstructionBlockAndExceptionHandlers(clazz, - method, - codeAttribute, - variables, - stack, - subroutineStart, - subroutineEnd); - - // Merge back the temporary partial evaluator. This way, we'll get - // the lowest common denominator of stacks and variables. - generalize(subroutinePartialEvaluator, 0, codeAttribute.u4codeLength); - - if (DEBUG) System.out.println("Ending subroutine from "+subroutineStart+" to "+subroutineEnd); - } - - - /** - * Generalizes the results of this partial evaluator with those of another - * given partial evaluator, over a given range of instructions. - */ - private void generalize(PartialEvaluator other, - int codeStart, - int codeEnd) - { - if (DEBUG) System.out.println("Generalizing with temporary partial evaluation"); - - for (int offset = codeStart; offset < codeEnd; offset++) - { - if (other.branchOriginValues[offset] != null) - { - branchOriginValues[offset] = branchOriginValues[offset] == null ? - other.branchOriginValues[offset] : - branchOriginValues[offset].generalize(other.branchOriginValues[offset]).instructionOffsetValue(); - } - - if (other.isTraced(offset)) - { - if (other.branchTargetValues[offset] != null) - { - branchTargetValues[offset] = branchTargetValues[offset] == null ? - other.branchTargetValues[offset] : - branchTargetValues[offset].generalize(other.branchTargetValues[offset]).instructionOffsetValue(); - } - - if (evaluationCounts[offset] == 0) - { - variablesBefore[offset] = other.variablesBefore[offset]; - stacksBefore[offset] = other.stacksBefore[offset]; - variablesAfter[offset] = other.variablesAfter[offset]; - stacksAfter[offset] = other.stacksAfter[offset]; - generalizedContexts[offset] = other.generalizedContexts[offset]; - evaluationCounts[offset] = other.evaluationCounts[offset]; - } - else - { - variablesBefore[offset].generalize(other.variablesBefore[offset], false); - stacksBefore[offset] .generalize(other.stacksBefore[offset]); - variablesAfter[offset] .generalize(other.variablesAfter[offset], false); - stacksAfter[offset] .generalize(other.stacksAfter[offset]); - //generalizedContexts[offset] - evaluationCounts[offset] += other.evaluationCounts[offset]; - } - } - } - } - - - /** - * Evaluates the exception handlers covering and targeting the given - * instruction range in the given code. - */ - private void evaluateExceptionHandlers(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - int startOffset, - int endOffset) - { - if (DEBUG) System.out.println("Evaluating exceptions covering ["+startOffset+" -> "+endOffset+"]:"); - - ExceptionHandlerFilter exceptionEvaluator = - new ExceptionHandlerFilter(startOffset, - endOffset, - this); - - // Evaluate the exception catch blocks, until their entry variables - // have stabilized. - do - { - // Reset the flag to stop evaluating. - evaluateExceptions = false; - - // Evaluate all relevant exception catch blocks once. - codeAttribute.exceptionsAccept(clazz, - method, - startOffset, - endOffset, - exceptionEvaluator); - } - while (evaluateExceptions); - } - - - // Implementations for ExceptionInfoVisitor. - - public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo) - { - int startPC = exceptionInfo.u2startPC; - int endPC = exceptionInfo.u2endPC; - - // Do we have to evaluate this exception catch block? - if (isTraced(startPC, endPC)) - { - int handlerPC = exceptionInfo.u2handlerPC; - int catchType = exceptionInfo.u2catchType; - - if (DEBUG) System.out.println("Evaluating exception ["+startPC +" -> "+endPC +": "+handlerPC+"]:"); - - // Reuse the existing variables and stack objects, ensuring the - // right size. - TracedVariables variables = new TracedVariables(codeAttribute.u2maxLocals); - TracedStack stack = new TracedStack(codeAttribute.u2maxStack); - - // Initialize the trace values. - Value storeValue = new InstructionOffsetValue(AT_CATCH_ENTRY); - variables.setProducerValue(storeValue); - stack.setProducerValue(storeValue); - - // Initialize the variables by generalizing the variables of the - // try block. Make sure to include the results of the last - // instruction for preverification. - generalizeVariables(startPC, - endPC, - evaluateAllCode, - variables); - - // Initialize the the stack. - //stack.push(valueFactory.createReference((ClassConstant)((ProgramClass)clazz).getConstant(exceptionInfo.u2catchType), false)); - String catchClassName = catchType != 0 ? - clazz.getClassName(catchType) : - ClassConstants.NAME_JAVA_LANG_THROWABLE; - - Clazz catchClass = catchType != 0 ? - ((ClassConstant)((ProgramClass)clazz).getConstant(catchType)).referencedClass : - null; - - stack.push(valueFactory.createReferenceValue(catchClassName, - catchClass, - false)); - - int evaluationCount = evaluationCounts[handlerPC]; - - // Evaluate the instructions, starting at the entry point. - evaluateInstructionBlock(clazz, - method, - codeAttribute, - variables, - stack, - handlerPC); - - // Remember to evaluate all exception handlers once more. - if (!evaluateExceptions) - { - evaluateExceptions = evaluationCount < evaluationCounts[handlerPC]; - } - } -// else if (evaluateAllCode) -// { -// if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"] yet"); -// -// // We don't have any information on the try block yet, but we do -// // have to evaluate the exception handler. -// // Remember to evaluate all exception handlers once more. -// evaluateExceptions = true; -// } - else - { - if (DEBUG) System.out.println("No information for partial evaluation of exception ["+startPC +" -> "+endPC +": "+exceptionInfo.u2handlerPC+"]"); - } - } - - - // Small utility methods. - - /** - * Initializes the data structures for the variables, stack, etc. - */ - private void initializeArrays(CodeAttribute codeAttribute) - { - int codeLength = codeAttribute.u4codeLength; - - // Create new arrays for storing information at each instruction offset. - if (variablesAfter.length < codeLength) - { - // Create new arrays. - branchOriginValues = new InstructionOffsetValue[codeLength]; - branchTargetValues = new InstructionOffsetValue[codeLength]; - variablesBefore = new TracedVariables[codeLength]; - stacksBefore = new TracedStack[codeLength]; - variablesAfter = new TracedVariables[codeLength]; - stacksAfter = new TracedStack[codeLength]; - generalizedContexts = new boolean[codeLength]; - evaluationCounts = new int[codeLength]; - } - else - { - // Reset the arrays. - Arrays.fill(branchOriginValues, null); - Arrays.fill(branchTargetValues, null); - Arrays.fill(generalizedContexts, false); - Arrays.fill(evaluationCounts, 0); - - for (int index = 0; index < codeLength; index++) - { - if (variablesBefore[index] != null) - { - variablesBefore[index].reset(codeAttribute.u2maxLocals); - } - - if (stacksBefore[index] != null) - { - stacksBefore[index].reset(codeAttribute.u2maxStack); - } - - if (variablesAfter[index] != null) - { - variablesAfter[index].reset(codeAttribute.u2maxLocals); - } - - if (stacksAfter[index] != null) - { - stacksAfter[index].reset(codeAttribute.u2maxStack); - } - } - } - } - - - /** - * Initializes the data structures for the variables, stack, etc. - */ - private void initializeParameters(Clazz clazz, - Method method, - CodeAttribute codeAttribute, - TracedVariables variables) - { - // Create the method parameters. - TracedVariables parameters = new TracedVariables(codeAttribute.u2maxLocals); - - // Remember this instruction's offset with any stored value. - Value storeValue = new InstructionOffsetValue(AT_METHOD_ENTRY); - parameters.setProducerValue(storeValue); - - // Initialize the method parameters. - invocationUnit.enterMethod(clazz, method, parameters); - - if (DEBUG) - { - System.out.println(" Params: "+parameters); - } - - // Initialize the variables with the parameters. - variables.initialize(parameters); - - // Set the store value of each parameter variable. - InstructionOffsetValue atMethodEntry = new InstructionOffsetValue(AT_METHOD_ENTRY); - - for (int index = 0; index < parameters.size(); index++) - { - variables.setProducerValue(index, atMethodEntry); - } - } - - - /** - * Generalize the local variable frames of a block of instructions. - */ - private void generalizeVariables(int startOffset, - int endOffset, - boolean includeAfterLastInstruction, - TracedVariables generalizedVariables) - { - boolean first = true; - int lastIndex = -1; - - // Generalize the variables before each of the instructions in the block. - for (int index = startOffset; index < endOffset; index++) - { - if (isTraced(index)) - { - TracedVariables tracedVariables = variablesBefore[index]; - - if (first) - { - // Initialize the variables with the first traced local - // variable frame. - generalizedVariables.initialize(tracedVariables); - - first = false; - } - else - { - // Generalize the variables with the traced local variable - // frame. We can't use the return value, because local - // generalization can be different a couple of times, - // with the global generalization being the same. - generalizedVariables.generalize(tracedVariables, false); - } - - lastIndex = index; - } - } - - // Generalize the variables after the last instruction in the block, - // if required. - if (includeAfterLastInstruction && - lastIndex >= 0) - { - TracedVariables tracedVariables = variablesAfter[lastIndex]; - - if (first) - { - // Initialize the variables with the local variable frame. - generalizedVariables.initialize(tracedVariables); - } - else - { - // Generalize the variables with the local variable frame. - generalizedVariables.generalize(tracedVariables, false); - } - } - - // Just clear the variables if there aren't any traced instructions - // in the block. - if (first) - { - generalizedVariables.reset(generalizedVariables.size()); - } - } - - - private static class MyInstructionBlock - { - private TracedVariables variables; - private TracedStack stack; - private int startOffset; - - - private MyInstructionBlock(TracedVariables variables, - TracedStack stack, - int startOffset) - { - this.variables = variables; - this.stack = stack; - this.startOffset = startOffset; - } - } -} diff --git a/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java b/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java deleted file mode 100644 index d6aaf7d..0000000 --- a/src/proguard/optimize/evaluation/SimpleEnumArrayPropagator.java +++ /dev/null @@ -1,94 +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.util.SimplifiedVisitor; -import proguard.classfile.visitor.*; -import proguard.evaluation.value.*; -import proguard.optimize.info.*; - -/** - * This ClassVisitor propagates the value of the $VALUES field to the values() - * method in the simple enum classes that it visits. - * - * @see SimpleEnumMarker - * @author Eric Lafortune - */ -public class SimpleEnumArrayPropagator -extends SimplifiedVisitor -implements ClassVisitor, - MemberVisitor -{ - private final ValueFactory valueFactory = new ParticularValueFactory(); - - private Value array; - - - // Implementations for ClassVisitor. - - public void visitProgramClass(ProgramClass programClass) - { - // Update the return value of the "int[] values()" method. - programClass.methodsAccept(new MemberDescriptorFilter("()[I", this)); - } - - - // Implementations for MemberVisitor. - - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) - { - // Find the array length of the "int[] $VALUES" field. - programClass.fieldsAccept(new MemberDescriptorFilter("[I", this)); - - if (array != null) - { - // Set the array value with the found array length. We can't use - // the original array, because its elements might get overwritten. - Value propagatedArray = - valueFactory.createArrayReferenceValue("I", - null, - array.referenceValue().arrayLength( - valueFactory)); - - setMethodReturnValue(programMethod, propagatedArray); - - array = null; - } - } - - public void visitProgramField(ProgramClass programClass, ProgramField programField) - { - array = StoringInvocationUnit.getFieldValue(programField); - } - - - // Small utility methods. - - private static void setMethodReturnValue(Method method, Value value) - { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.setReturnValue(value); - } - } -} diff --git a/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java b/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java deleted file mode 100644 index 1bd5008..0000000 --- a/src/proguard/optimize/evaluation/SimpleEnumClassChecker.java +++ /dev/null @@ -1,74 +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.visitor.*; -import proguard.optimize.info.SimpleEnumMarker; - -/** - * This ClassVisitor marks all program classes that it visits as simple enums, - * if their methods qualify. - * - * @author Eric Lafortune - */ -public class SimpleEnumClassChecker -implements ClassVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = System.getProperty("enum") != null; - //*/ - - - private final SimpleEnumMarker simpleEnumMarker = new SimpleEnumMarker(true); - private final MemberVisitor virtualMethodChecker = new MemberAccessFilter(0, - ClassConstants.ACC_PRIVATE | - ClassConstants.ACC_STATIC, - new MemberToClassVisitor( - new SimpleEnumMarker(false))); - - - // Implementations for ClassVisitor. - - public void visitLibraryClass(LibraryClass libraryClass) {} - - public void visitProgramClass(ProgramClass programClass) - { - // Does the class have the simple enum constructor? - if (programClass.findMethod(ClassConstants.METHOD_NAME_INIT, - ClassConstants.METHOD_TYPE_INIT_ENUM) != null) - { - if (DEBUG) - { - System.out.println("SimpleEnumClassChecker: ["+programClass.getName()+"] is a candidate simple enum, without extra fields"); - } - - // Mark it. - simpleEnumMarker.visitProgramClass(programClass); - - // However, unmark it again if it has any non-private, non-static - // methods. - programClass.methodsAccept(virtualMethodChecker); - } - } -}
\ No newline at end of file diff --git a/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java b/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java deleted file mode 100644 index 33f775f..0000000 --- a/src/proguard/optimize/evaluation/SimpleEnumClassSimplifier.java +++ /dev/null @@ -1,164 +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.*; -import proguard.classfile.constant.*; -import proguard.classfile.editor.*; -import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.SimplifiedVisitor; -import proguard.classfile.visitor.*; -import proguard.optimize.info.SimpleEnumMarker; -import proguard.optimize.peephole.*; - -/** - * This ClassVisitor simplifies the classes that it visits to simple enums. - * - * @see SimpleEnumMarker - * @see MemberReferenceFixer - * @author Eric Lafortune - */ -public class SimpleEnumClassSimplifier -extends SimplifiedVisitor -implements ClassVisitor, - AttributeVisitor, - InstructionVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = System.getProperty("enum") != null; - //*/ - - - private static final int ENUM_CLASS_NAME = InstructionSequenceReplacer.A; - private static final int ENUM_TYPE_NAME = InstructionSequenceReplacer.B; - private static final int ENUM_CONSTANT_NAME = InstructionSequenceReplacer.X; - private static final int ENUM_CONSTANT_ORDINAL = InstructionSequenceReplacer.Y; - private static final int ENUM_CONSTANT_FIELD_NAME = InstructionSequenceReplacer.Z; - - private static final int STRING_ENUM_CONSTANT_NAME = 0; - - private static final int METHOD_ENUM_INIT = 1; - private static final int FIELD_ENUM_CONSTANT = 2; - - private static final int CLASS_ENUM = 3; - - private static final int NAME_AND_TYPE_ENUM_INIT = 4; - private static final int NAME_AND_TYPE_ENUM_CONSTANT = 5; - - private static final int UTF8_INIT = 6; - private static final int UTF8_STRING_I = 7; - - - private static final Constant[] CONSTANTS = new Constant[] - { - new StringConstant(ENUM_CONSTANT_NAME, null, null), - - new MethodrefConstant(CLASS_ENUM, NAME_AND_TYPE_ENUM_INIT, null, null), - new FieldrefConstant( CLASS_ENUM, NAME_AND_TYPE_ENUM_CONSTANT, null, null), - - new ClassConstant(ENUM_CLASS_NAME, null), - - new NameAndTypeConstant(UTF8_INIT, UTF8_STRING_I), - new NameAndTypeConstant(ENUM_CONSTANT_FIELD_NAME, ENUM_TYPE_NAME), - - new Utf8Constant(ClassConstants.METHOD_NAME_INIT), - new Utf8Constant(ClassConstants.METHOD_TYPE_INIT_ENUM), - }; - - private static final Instruction[] INSTRUCTIONS = new Instruction[] - { - new ConstantInstruction(InstructionConstants.OP_NEW, CLASS_ENUM), - new SimpleInstruction(InstructionConstants.OP_DUP), - new ConstantInstruction(InstructionConstants.OP_LDC, STRING_ENUM_CONSTANT_NAME), - new SimpleInstruction(InstructionConstants.OP_ICONST_0, ENUM_CONSTANT_ORDINAL), - new ConstantInstruction(InstructionConstants.OP_INVOKESPECIAL, METHOD_ENUM_INIT), - }; - - private static final Instruction[] REPLACEMENT_INSTRUCTIONS = new Instruction[] - { - new SimpleInstruction(InstructionConstants.OP_SIPUSH, ENUM_CONSTANT_ORDINAL), - new SimpleInstruction(InstructionConstants.OP_ICONST_1), - new SimpleInstruction(InstructionConstants.OP_IADD), - }; - - - private final CodeAttributeEditor codeAttributeEditor = - new CodeAttributeEditor(true, true); - - private final InstructionSequenceReplacer instructionSequenceReplacer = - new InstructionSequenceReplacer(CONSTANTS, - INSTRUCTIONS, - REPLACEMENT_INSTRUCTIONS, - null, - codeAttributeEditor); - - private final MemberVisitor initializerSimplifier = new AllAttributeVisitor(this); - - - // Implementations for ClassVisitor. - - public void visitProgramClass(ProgramClass programClass) - { - if (DEBUG) - { - System.out.println("SimpleEnumClassSimplifier: ["+programClass.getName()+"]"); - } - - // Unmark the class as an enum. - programClass.u2accessFlags &= ~ClassConstants.ACC_ENUM; - - // Remove the valueOf method, if present. - Method valueOfMethod = - programClass.findMethod(ClassConstants.METHOD_NAME_VALUEOF, null); - if (valueOfMethod != null) - { - new ClassEditor(programClass).removeMethod(valueOfMethod); - } - - // Simplify the static initializer. - programClass.methodAccept(ClassConstants.METHOD_NAME_CLINIT, - ClassConstants.METHOD_TYPE_CLINIT, - initializerSimplifier); - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - // Set up the code attribute editor. - codeAttributeEditor.reset(codeAttribute.u4codeLength); - - // Find the peephole changes. - codeAttribute.instructionsAccept(clazz, method, instructionSequenceReplacer); - - // Apply the peephole changes. - codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute); - } -} diff --git a/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java b/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java deleted file mode 100644 index f1323ea..0000000 --- a/src/proguard/optimize/evaluation/SimpleEnumDescriptorSimplifier.java +++ /dev/null @@ -1,583 +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.*; -import proguard.classfile.constant.*; -import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.editor.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.optimize.info.*; - -/** - * This ClassVisitor simplifies the descriptors that contain simple enums in - * the program classes that it visits. - * - * @see SimpleEnumMarker - * @see MemberReferenceFixer - * @author Eric Lafortune - */ -public class SimpleEnumDescriptorSimplifier -extends SimplifiedVisitor -implements ClassVisitor, - ConstantVisitor, - MemberVisitor, - AttributeVisitor, - LocalVariableInfoVisitor, - LocalVariableTypeInfoVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = System.getProperty("enum") != null; - //*/ - - - // Implementations for ClassVisitor. - - public void visitProgramClass(ProgramClass programClass) - { - if (DEBUG) - { - System.out.println("SimpleEnumDescriptorSimplifier: "+programClass.getName()); - } - - // Simplify the class members. - programClass.fieldsAccept(this); - programClass.methodsAccept(this); - - // Simplify the attributes. - //programClass.attributesAccept(this); - - // Simplify the simple enum array constants. - programClass.constantPoolEntriesAccept(this); - } - - - // 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? - Clazz referencedClass = stringConstant.referencedClass; - if (isSimpleEnum(referencedClass)) - { - // Is it an array type? - String name = stringConstant.getString(clazz); - if (ClassUtil.isInternalArrayType(name)) - { - // Update the type. - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor((ProgramClass)clazz); - - String newName = simplifyDescriptor(name, referencedClass); - - stringConstant.u2stringIndex = - constantPoolEditor.addUtf8Constant(newName); - - // Clear the referenced class. - stringConstant.referencedClass = null; - } - } - } - - - public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) - { - // Update the descriptor if it has any simple enum classes. - String descriptor = invokeDynamicConstant.getType(clazz); - String newDescriptor = simplifyDescriptor(descriptor, invokeDynamicConstant.referencedClasses); - - if (!descriptor.equals(newDescriptor)) - { - // Update the descriptor. - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor((ProgramClass)clazz); - - invokeDynamicConstant.u2nameAndTypeIndex = - constantPoolEditor.addNameAndTypeConstant(invokeDynamicConstant.getName(clazz), - newDescriptor); - - // Update the referenced classes. - invokeDynamicConstant.referencedClasses = - simplifyReferencedClasses(descriptor, invokeDynamicConstant.referencedClasses); - } - } - - - public void visitClassConstant(Clazz clazz, ClassConstant classConstant) - { - // Does the constant refer to a simple enum type? - Clazz referencedClass = classConstant.referencedClass; - if (isSimpleEnum(referencedClass)) - { - // Is it an array type? - String name = classConstant.getName(clazz); - if (ClassUtil.isInternalArrayType(name)) - { - // Update the type. - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor((ProgramClass)clazz); - - String newName = simplifyDescriptor(name, referencedClass); - - classConstant.u2nameIndex = - constantPoolEditor.addUtf8Constant(newName); - - // Clear the referenced class. - classConstant.referencedClass = null; - } - } - } - - - // Implementations for MemberVisitor. - - public void visitProgramField(ProgramClass programClass, ProgramField programField) - { - // Update the descriptor if it has a simple enum class. - String descriptor = programField.getDescriptor(programClass); - String newDescriptor = simplifyDescriptor(descriptor, programField.referencedClass); - - if (!descriptor.equals(newDescriptor)) - { - String name = programField.getName(programClass); - String newName = name + ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); - - if (DEBUG) - { - System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+" "+descriptor + "] -> ["+newName+" "+newDescriptor+"]"); - } - - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor(programClass); - - // Update the name. - programField.u2nameIndex = - constantPoolEditor.addUtf8Constant(newName); - - // Update the descriptor itself. - programField.u2descriptorIndex = - constantPoolEditor.addUtf8Constant(newDescriptor); - - // Clear the referenced class. - programField.referencedClass = null; - - // Clear the field value. - FieldOptimizationInfo fieldOptimizationInfo = - FieldOptimizationInfo.getFieldOptimizationInfo(programField); - if (fieldOptimizationInfo != null) - { - fieldOptimizationInfo.resetValue(programClass, programField); - } - - // Simplify the signature. - programField.attributesAccept(programClass, this); - } - } - - - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) - { -// // Skip the valueOf method. -// if (programMethod.getName(programClass).equals(ClassConstants.METHOD_NAME_VALUEOF)) -// { -// return; -// } - - // Simplify the code, the signature, and the parameter annotations, - // before simplifying the descriptor. - programMethod.attributesAccept(programClass, this); - - // Update the descriptor if it has any simple enum classes. - String descriptor = programMethod.getDescriptor(programClass); - String newDescriptor = simplifyDescriptor(descriptor, programMethod.referencedClasses); - - if (!descriptor.equals(newDescriptor)) - { - String name = programMethod.getName(programClass); - String newName = name; - - // Append a code, if the method isn't a class instance initializer. - if (!name.equals(ClassConstants.METHOD_NAME_INIT)) - { - newName += ClassConstants.SPECIAL_MEMBER_SEPARATOR + Long.toHexString(Math.abs((descriptor).hashCode())); - } - - if (DEBUG) - { - System.out.println("SimpleEnumDescriptorSimplifier: ["+programClass.getName()+"."+name+descriptor+"] -> ["+newName+newDescriptor+"]"); - } - - ConstantPoolEditor constantPoolEditor = - new ConstantPoolEditor(programClass); - - // Update the name, if necessary. - if (!newName.equals(name)) - { - programMethod.u2nameIndex = - constantPoolEditor.addUtf8Constant(newName); - } - - // Update the descriptor itself. - programMethod.u2descriptorIndex = - constantPoolEditor.addUtf8Constant(newDescriptor); - - // Update the referenced classes. - programMethod.referencedClasses = - simplifyReferencedClasses(descriptor, programMethod.referencedClasses); - } - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - // Simplify the local variable descriptors. - codeAttribute.attributesAccept(clazz, method, this); - } - - - public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) - { - // Change the references of the local variables. - localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); - } - - - public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) - { - // Change the references of the local variables. - localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); - } - - - public void visitSignatureAttribute(Clazz clazz, Field field, SignatureAttribute signatureAttribute) - { - // We're only looking at the base type for now. - if (signatureAttribute.referencedClasses != null && - signatureAttribute.referencedClasses.length > 0) - { - // Update the signature if it has any simple enum classes. - String signature = signatureAttribute.getSignature(clazz); - String newSignature = simplifyDescriptor(signature, - signatureAttribute.referencedClasses[0]); - - if (!signature.equals(newSignature)) - { - // Update the signature. - signatureAttribute.u2signatureIndex = - new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); - - // Clear the referenced class. - signatureAttribute.referencedClasses[0] = null; - } - } - } - - - public void visitSignatureAttribute(Clazz clazz, Method method, SignatureAttribute signatureAttribute) - { - // Compute the new signature. - String signature = signatureAttribute.getSignature(clazz); - String newSignature = simplifyDescriptor(signature, - signatureAttribute.referencedClasses); - - if (!signature.equals(newSignature)) - { - // Update the signature. - signatureAttribute.u2signatureIndex = - new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); - } - } - - - // Implementations for LocalVariableInfoVisitor. - - public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) - { - // Update the descriptor if it has a simple enum class. - String descriptor = localVariableInfo.getDescriptor(clazz); - String newDescriptor = simplifyDescriptor(descriptor, localVariableInfo.referencedClass); - - if (!descriptor.equals(newDescriptor)) - { - // Update the descriptor. - localVariableInfo.u2descriptorIndex = - new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newDescriptor); - - // Clear the referenced class. - localVariableInfo.referencedClass = null; - } - } - - - // Implementations for LocalVariableTypeInfoVisitor. - - public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) - { - // We're only looking at the base type for now. - if (localVariableTypeInfo.referencedClasses != null && - localVariableTypeInfo.referencedClasses.length > 0) - { - // Update the signature if it has any simple enum classes. - String signature = localVariableTypeInfo.getSignature(clazz); - String newSignature = simplifyDescriptor(signature, - localVariableTypeInfo.referencedClasses[0]); - - if (!signature.equals(newSignature)) - { - // Update the signature. - localVariableTypeInfo.u2signatureIndex = - new ConstantPoolEditor((ProgramClass)clazz).addUtf8Constant(newSignature); - - // Clear the referenced class. - localVariableTypeInfo.referencedClasses[0] = null; - } - } - } - - - // Small utility methods. - - /** - * Returns the descriptor with simplified enum type. - */ - private String simplifyDescriptor(String descriptor, - Clazz referencedClass) - { - return isSimpleEnum(referencedClass) ? - descriptor.substring(0, ClassUtil.internalArrayTypeDimensionCount(descriptor)) + ClassConstants.TYPE_INT : - descriptor; - } - - - /** - * Returns the descriptor with simplified enum types. - */ - private String simplifyDescriptor(String descriptor, - Clazz[] referencedClasses) - { - if (referencedClasses != null) - { - InternalTypeEnumeration internalTypeEnumeration = - new InternalTypeEnumeration(descriptor); - - int referencedClassIndex = 0; - - StringBuffer newDescriptorBuffer = - new StringBuffer(descriptor.length()); - - // Go over the formal type parameters. - { - // Consider the classes referenced by this formal type - // parameter. - String type = internalTypeEnumeration.formalTypeParameters(); - int count = new DescriptorClassEnumeration(type).classCount(); - - // Replace simple enum types. - for (int counter = 0; counter < count; counter++) - { - Clazz referencedClass = - referencedClasses[referencedClassIndex++]; - - if (isSimpleEnum(referencedClass)) - { - // Let's replace the simple enum type by - // java.lang.Integer. - type = - type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + - ClassConstants.NAME_JAVA_LANG_INTEGER; - } - } - - newDescriptorBuffer.append(type); - } - - newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_OPEN); - - // Go over the parameters. - while (internalTypeEnumeration.hasMoreTypes()) - { - // Consider the classes referenced by this parameter type. - String type = internalTypeEnumeration.nextType(); - int count = new DescriptorClassEnumeration(type).classCount(); - - // Replace simple enum types. - for (int counter = 0; counter < count; counter++) - { - Clazz referencedClass = - referencedClasses[referencedClassIndex++]; - - if (isSimpleEnum(referencedClass)) - { - type = - type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + - ClassConstants.TYPE_INT; - } - } - - newDescriptorBuffer.append(type); - } - - newDescriptorBuffer.append(ClassConstants.METHOD_ARGUMENTS_CLOSE); - - // Go over the return value. - { - String type = internalTypeEnumeration.returnType(); - int count = new DescriptorClassEnumeration(type).classCount(); - - // Replace simple enum types. - for (int counter = 0; counter < count; counter++) - { - Clazz referencedClass = - referencedClasses[referencedClassIndex++]; - - if (isSimpleEnum(referencedClass)) - { - type = - type.substring(0, ClassUtil.internalArrayTypeDimensionCount(type)) + - ClassConstants.TYPE_INT; - } - } - - newDescriptorBuffer.append(type); - } - - descriptor = newDescriptorBuffer.toString(); - } - - return descriptor; - } - - - /** - * Returns the simplified and shrunk array of referenced classes for the - * given descriptor. - */ - private Clazz[] simplifyReferencedClasses(String descriptor, - Clazz[] referencedClasses) - { - if (referencedClasses != null) - { - InternalTypeEnumeration internalTypeEnumeration = - new InternalTypeEnumeration(descriptor); - - int referencedClassIndex = 0; - int newReferencedClassIndex = 0; - - // Go over the formal type parameters. - { - String type = internalTypeEnumeration.formalTypeParameters(); - int count = new DescriptorClassEnumeration(type).classCount(); - - // Clear all non-simple enum classes - // (now java.lang.Integer). - for (int counter = 0; counter < count; counter++) - { - Clazz referencedClass = - referencedClasses[referencedClassIndex++]; - - referencedClasses[newReferencedClassIndex++] = - isSimpleEnum(referencedClass) ? null : referencedClass; - } - } - - // Go over the parameters. - while (internalTypeEnumeration.hasMoreTypes()) - { - // Consider the classes referenced by this parameter type. - String type = internalTypeEnumeration.nextType(); - int count = new DescriptorClassEnumeration(type).classCount(); - - // Copy all non-simple enum classes. - for (int counter = 0; counter < count; counter++) - { - Clazz referencedClass = - referencedClasses[referencedClassIndex++]; - - if (!isSimpleEnum(referencedClass)) - { - referencedClasses[newReferencedClassIndex++] = - referencedClass; - } - } - } - - // Go over the return type. - { - String type = internalTypeEnumeration.returnType(); - int count = new DescriptorClassEnumeration(type).classCount(); - - // Copy all non-simple enum classes. - for (int counter = 0; counter < count; counter++) - { - Clazz referencedClass = - referencedClasses[referencedClassIndex++]; - - if (!isSimpleEnum(referencedClass)) - { - referencedClasses[newReferencedClassIndex++] = - referencedClass; - } - } - } - - // Shrink the array to the proper size. - if (newReferencedClassIndex == 0) - { - referencedClasses = null; - } - else if (newReferencedClassIndex < referencedClassIndex) - { - Clazz[] newReferencedClasses = new Clazz[newReferencedClassIndex]; - System.arraycopy(referencedClasses, 0, - newReferencedClasses, 0, - newReferencedClassIndex); - - referencedClasses = newReferencedClasses; - } - } - - return referencedClasses; - } - - - /** - * Returns whether the given class is not null and a simple enum class. - */ - private boolean isSimpleEnum(Clazz clazz) - { - return clazz != null && - SimpleEnumMarker.isSimpleEnum(clazz); - } -} diff --git a/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java b/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java deleted file mode 100644 index b748c68..0000000 --- a/src/proguard/optimize/evaluation/SimpleEnumUseChecker.java +++ /dev/null @@ -1,760 +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.*; -import proguard.classfile.constant.ClassConstant; -import proguard.classfile.constant.visitor.ConstantVisitor; -import proguard.classfile.instruction.*; -import proguard.classfile.instruction.visitor.InstructionVisitor; -import proguard.classfile.util.*; -import proguard.classfile.visitor.*; -import proguard.evaluation.*; -import proguard.evaluation.value.*; -import proguard.optimize.info.SimpleEnumMarker; - -/** - * This ClassVisitor marks enums that can't be simplified due to the way they - * are used in the classes that it visits. - * - * @see SimpleEnumMarker - * @author Eric Lafortune - */ -public class SimpleEnumUseChecker -extends SimplifiedVisitor -implements ClassVisitor, - MemberVisitor, - AttributeVisitor, - InstructionVisitor, - ConstantVisitor, - ParameterVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = System.getProperty("enum") != null; - //*/ - - private final PartialEvaluator partialEvaluator; - private final MemberVisitor methodCodeChecker = new AllAttributeVisitor(this); - private final ConstantVisitor invokedMethodChecker = new ReferencedMemberVisitor(this); - private final ConstantVisitor parameterChecker = new ReferencedMemberVisitor(new AllParameterVisitor(this)); - private final ClassVisitor complexEnumMarker = new SimpleEnumMarker(false); - private final ReferencedClassVisitor referencedComplexEnumMarker = new ReferencedClassVisitor(complexEnumMarker); - - - // Fields acting as parameters and return values for the visitor methods. - private int invocationOffset; - - - /** - * Creates a new SimpleEnumUseSimplifier. - */ - public SimpleEnumUseChecker() - { - this(new PartialEvaluator()); - } - - - /** - * Creates a new SimpleEnumUseChecker. - * @param partialEvaluator the partial evaluator that will execute the code - * and provide information about the results. - */ - public SimpleEnumUseChecker(PartialEvaluator partialEvaluator) - { - this.partialEvaluator = partialEvaluator; - } - - - // Implementations for ClassVisitor. - - public void visitProgramClass(ProgramClass programClass) - { - if ((programClass.getAccessFlags() & ClassConstants.ACC_ANNOTATTION) != 0) - { - // Unmark the simple enum classes in annotations. - programClass.methodsAccept(referencedComplexEnumMarker); - } - else - { - // Unmark the simple enum classes that are used in a complex way. - programClass.methodsAccept(methodCodeChecker); - } - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { - // Evaluate the method. - partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); - - int codeLength = codeAttribute.u4codeLength; - - // Check all traced instructions. - 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); - - // Check generalized stacks and variables at branch targets. - if (partialEvaluator.isBranchOrExceptionTarget(offset)) - { - checkMixedStackEntriesBefore(offset); - - checkMixedVariablesBefore(offset); - } - } - } - } - - - // Implementations for InstructionVisitor. - - public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) - { - switch (simpleInstruction.opcode) - { - case InstructionConstants.OP_AASTORE: - { - // Check if the instruction is storing a simple enum in a - // more general array. - if (!isPoppingSimpleEnumType(offset, 2)) - { - if (DEBUG) - { - if (isPoppingSimpleEnumType(offset)) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] stores enum ["+ - partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] in more general array ["+ - partialEvaluator.getStackBefore(offset).getTop(2).referenceValue().getType()+"]"); - } - } - - markPoppedComplexEnumType(offset); - } - break; - } - case InstructionConstants.OP_ARETURN: - { - // Check if the instruction is returning a simple enum as a - // more general type. - if (!isReturningSimpleEnumType(clazz, method)) - { - if (DEBUG) - { - if (isPoppingSimpleEnumType(offset)) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] returns enum [" + - partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as more general type"); - } - } - - markPoppedComplexEnumType(offset); - } - break; - } - case InstructionConstants.OP_MONITORENTER: - case InstructionConstants.OP_MONITOREXIT: - { - // Make sure the popped type is not a simple enum type. - if (DEBUG) - { - if (isPoppingSimpleEnumType(offset)) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] uses enum ["+ - partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as monitor"); - } - } - - markPoppedComplexEnumType(offset); - - break; - } - } - } - - - public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction) - { - } - - - public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction) - { - switch (constantInstruction.opcode) - { - case InstructionConstants.OP_PUTSTATIC: - case InstructionConstants.OP_PUTFIELD: - { - // Check if the instruction is generalizing a simple enum to a - // different type. - invocationOffset = offset; - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, - parameterChecker); - 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 (isPoppingSimpleEnumType(offset, stackEntryIndex) && - !isSupportedMethod(invokedMethodName, - invokedMethodType)) - { - if (DEBUG) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] calls ["+partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue().getType()+"."+invokedMethodName+"]"); - } - - markPoppedComplexEnumType(offset, stackEntryIndex); - } - - // Check if any of the parameters is generalizing a simple - // enum to a different type. - invocationOffset = offset; - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, - parameterChecker); - break; - } - case InstructionConstants.OP_INVOKESPECIAL: - case InstructionConstants.OP_INVOKESTATIC: - case InstructionConstants.OP_INVOKEINTERFACE: - { - // Check if it is calling a method that we can't simplify. - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, - invokedMethodChecker); - - // Check if any of the parameters is generalizing a simple - // enum to a different type. - invocationOffset = offset; - clazz.constantPoolEntryAccept(constantInstruction.constantIndex, - parameterChecker); - break; - } - case InstructionConstants.OP_CHECKCAST: - case InstructionConstants.OP_INSTANCEOF: - { - // Check if the instruction is popping a different type. - if (!isPoppingExpectedType(offset, - clazz, - constantInstruction.constantIndex)) - { - if (DEBUG) - { - if (isPoppingSimpleEnumType(offset)) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+ - partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+ - clazz.getClassName(constantInstruction.constantIndex)+"]"); - } - } - - // Make sure the popped type is not a simple enum type. - markPoppedComplexEnumType(offset); - - // Make sure the checked type is not a simple enum type. - // We're somewhat arbitrarily skipping casts in static - // methods of simple enum classes, because they do occur - // in values() and valueOf(String), without obstructing - // simplification. - if (!isSimpleEnum(clazz) || - (method.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 || - constantInstruction.opcode != InstructionConstants.OP_CHECKCAST) - { - if (DEBUG) - { - if (isSimpleEnum(((ClassConstant)((ProgramClass)clazz).getConstant(constantInstruction.constantIndex)).referencedClass)) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] is casting or checking ["+ - partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] as ["+ - clazz.getClassName(constantInstruction.constantIndex)+"]"); - } - } - - markConstantComplexEnumType(clazz, constantInstruction.constantIndex); - } - } - break; - } - } - } - - - public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction) - { - switch (branchInstruction.opcode) - { - case InstructionConstants.OP_IFACMPEQ: - case InstructionConstants.OP_IFACMPNE: - { - // Check if the instruction is comparing different types. - if (!isPoppingIdenticalTypes(offset, 0, 1)) - { - if (DEBUG) - { - if (isPoppingSimpleEnumType(offset, 0)) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(0).referenceValue().getType()+"] to plain type"); - } - - if (isPoppingSimpleEnumType(offset, 1)) - { - System.out.println("SimpleEnumUseChecker: ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"] compares ["+partialEvaluator.getStackBefore(offset).getTop(1).referenceValue().getType()+"] to plain type"); - } - } - - // Make sure the first popped type is not a simple enum type. - markPoppedComplexEnumType(offset, 0); - - // Make sure the second popped type is not a simple enum type. - markPoppedComplexEnumType(offset, 1); - } - break; - } - } - } - - - public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction) - { - } - - - // Implementations for MemberVisitor. - - public void visitLibraryMethod(LibraryClass libraryClass, LibraryMethod libraryMethod) {} - - - public void visitProgramMethod(ProgramClass programClass, ProgramMethod programMethod) - { - if (isSimpleEnum(programClass) && - isUnsupportedMethod(programMethod.getName(programClass), - programMethod.getDescriptor(programClass))) - { - if (DEBUG) - { - System.out.println("SimpleEnumUseChecker: invocation of ["+programClass.getName()+"."+programMethod.getName(programClass)+programMethod.getDescriptor(programClass)+"]"); - } - - complexEnumMarker.visitProgramClass(programClass); - } - } - - - // 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. - int stackEntryIndex = parameterSize - parameterOffset - 1; - if (ClassUtil.isInternalClassType(parameterType) && - !isPoppingExpectedType(invocationOffset, stackEntryIndex, - ClassUtil.isInternalArrayType(parameterType) ? - parameterType : - ClassUtil.internalClassNameFromClassType(parameterType))) - { - if (DEBUG) - { - ReferenceValue poppedValue = - partialEvaluator.getStackBefore(invocationOffset).getTop(stackEntryIndex).referenceValue(); - if (isSimpleEnumType(poppedValue)) - { - System.out.println("SimpleEnumUseChecker: ["+poppedValue.getType()+"] "+ - (member instanceof Field ? - ("is stored as more general type ["+parameterType+"] in field ["+clazz.getName()+"."+member.getName(clazz)+"]") : - ("is passed as more general argument #"+parameterIndex+" ["+parameterType+"] to ["+clazz.getName()+"."+member.getName(clazz)+"]"))); - } - } - - // Make sure the popped type is not a simple enum type. - markPoppedComplexEnumType(invocationOffset, stackEntryIndex); - } - } - - - // Small utility methods. - - /** - * Returns whether the specified enum method is supported for simple enums. - */ - private boolean isSupportedMethod(String name, String type) - { - return - name.equals(ClassConstants.METHOD_NAME_ORDINAL) && - type.equals(ClassConstants.METHOD_TYPE_ORDINAL) || - - name.equals(ClassConstants.METHOD_NAME_CLONE) && - type.equals(ClassConstants.METHOD_TYPE_CLONE); - } - - - /** - * Returns whether the specified enum method is unsupported for simple enums. - */ - private boolean isUnsupportedMethod(String name, String type) - { - return - name.equals(ClassConstants.METHOD_NAME_VALUEOF); - } - - - /** - * Unmarks simple enum classes that are mixed with incompatible reference - * types in the stack before the given instruction offset. - */ - private void checkMixedStackEntriesBefore(int offset) - { - TracedStack stackBefore = partialEvaluator.getStackBefore(offset); - - // Check all stack entries. - int stackSize = stackBefore.size(); - - for (int stackEntryIndex = 0; stackEntryIndex < stackSize; stackEntryIndex++) - { - // Check reference entries. - Value stackEntry = stackBefore.getBottom(stackEntryIndex); - if (stackEntry.computationalType() == Value.TYPE_REFERENCE) - { - // Check reference entries with multiple producers. - InstructionOffsetValue producerOffsets = - stackBefore.getBottomActualProducerValue(stackEntryIndex).instructionOffsetValue(); - - int producerCount = producerOffsets.instructionOffsetCount(); - if (producerCount > 1) - { - // Is the consumed stack entry not a simple enum? - ReferenceValue consumedStackEntry = - stackEntry.referenceValue(); - - if (!isSimpleEnumType(consumedStackEntry)) - { - // Check all producers. - for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) - { - int producerOffset = - producerOffsets.instructionOffset(producerIndex); - - if (producerOffset >= 0) - { - if (DEBUG) - { - ReferenceValue producedValue = - partialEvaluator.getStackAfter(producerOffset).getTop(0).referenceValue(); - if (isSimpleEnumType(producedValue)) - { - System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type on stack"); - } - } - - // Make sure the produced stack entry isn't a - // simple enum either. - markPushedComplexEnumType(producerOffset); - } - } - } - } - } - } - } - - - /** - * Unmarks simple enum classes that are mixed with incompatible reference - * types in the variables before the given instruction offset. - */ - private void checkMixedVariablesBefore(int offset) - { - TracedVariables variablesBefore = - partialEvaluator.getVariablesBefore(offset); - - // Check all variables. - int variablesSize = variablesBefore.size(); - - for (int variableIndex = 0; variableIndex < variablesSize; variableIndex++) - { - // Check reference variables. - Value variable = variablesBefore.getValue(variableIndex); - if (variable != null && - variable.computationalType() == Value.TYPE_REFERENCE) - { - // Check reference variables with multiple producers. - InstructionOffsetValue producerOffsets = - variablesBefore.getProducerValue(variableIndex).instructionOffsetValue(); - - int producerCount = producerOffsets.instructionOffsetCount(); - if (producerCount > 1) - { - // Is the consumed variable not a simple enum? - ReferenceValue consumedVariable = - variable.referenceValue(); - - if (!isSimpleEnumType(consumedVariable)) - { - // Check all producers. - for (int producerIndex = 0; producerIndex < producerCount; producerIndex++) - { - int producerOffset = - producerOffsets.instructionOffset(producerIndex); - - if (producerOffset >= 0) - { - if (DEBUG) - { - ReferenceValue producedValue = - partialEvaluator.getVariablesAfter(producerOffset).getValue(variableIndex).referenceValue(); - if (isSimpleEnumType(producedValue)) - { - System.out.println("SimpleEnumUseChecker: ["+producedValue.getType()+"] mixed with general type in variables"); - } - } - - // Make sure the stored variable entry isn't a - // simple enum either. - markStoredComplexEnumType(producerOffset, variableIndex); - } - } - } - } - } - } - } - - - /** - * Returns whether the instruction at the given offset is popping two - * identical reference types. - */ - private boolean isPoppingIdenticalTypes(int offset, - int stackEntryIndex1, - int stackEntryIndex2) - { - TracedStack stackBefore = partialEvaluator.getStackBefore(offset); - - String type1 = - stackBefore.getTop(stackEntryIndex1).referenceValue().getType(); - String type2 = - stackBefore.getTop(stackEntryIndex2).referenceValue().getType(); - - return type1 == null ? type2 == null : type1.equals(type2); - } - - - /** - * Returns whether the instruction at the given offset is popping exactly - * the reference type of the specified class constant. - */ - private boolean isPoppingExpectedType(int offset, - Clazz clazz, - int constantIndex) - { - return isPoppingExpectedType(offset, 0, clazz, constantIndex); - } - - - /** - * Returns whether the instruction at the given offset is popping exactly - * the reference type of the specified class constant. - */ - private boolean isPoppingExpectedType(int offset, - int stackEntryIndex, - Clazz clazz, - int constantIndex) - { - return isPoppingExpectedType(offset, - stackEntryIndex, - clazz.getClassName(constantIndex)); - } - - - /** - * Returns whether the instruction at the given offset is popping exactly - * the given reference type. - */ - private boolean isPoppingExpectedType(int offset, - int stackEntryIndex, - String expectedType) - { - TracedStack stackBefore = partialEvaluator.getStackBefore(offset); - - String poppedType = - stackBefore.getTop(stackEntryIndex).referenceValue().getType(); - - return expectedType.equals(poppedType); - } - - - /** - * Returns whether the given method is returning a simple enum type. - * This includes simple enum arrays. - */ - private boolean isReturningSimpleEnumType(Clazz clazz, Method method) - { - String descriptor = method.getDescriptor(clazz); - String returnType = ClassUtil.internalMethodReturnType(descriptor); - - if (ClassUtil.isInternalClassType(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 popping a type - * with a simple enum class. This includes simple enum arrays. - */ - private boolean isPoppingSimpleEnumType(int offset) - { - return isPoppingSimpleEnumType(offset, 0); - } - - - /** - * Returns whether the instruction at the given offset is popping a type - * with a simple enum class. This includes simple enum arrays. - */ - private boolean isPoppingSimpleEnumType(int offset, int stackEntryIndex) - { - ReferenceValue referenceValue = - partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); - - return isSimpleEnumType(referenceValue); - } - - - /** - * Returns whether the given value is a simple enum type. This includes - * simple enum arrays. - */ - private boolean isSimpleEnumType(ReferenceValue referenceValue) - { - return isSimpleEnum(referenceValue.getReferencedClass()); - } - - - /** - * Returns whether the given class is not null and a simple enum class. - */ - private boolean isSimpleEnum(Clazz clazz) - { - return clazz != null && - SimpleEnumMarker.isSimpleEnum(clazz); - } - - - /** - * Marks the enum class of the popped type as complex. - */ - private void markConstantComplexEnumType(Clazz clazz, int constantIndex) - { - clazz.constantPoolEntryAccept(constantIndex, - referencedComplexEnumMarker); - } - - - /** - * Marks the enum class of the popped type as complex. - */ - private void markPoppedComplexEnumType(int offset) - { - markPoppedComplexEnumType(offset, 0); - } - - - /** - * Marks the enum class of the specified popped type as complex. - */ - private void markPoppedComplexEnumType(int offset, int stackEntryIndex) - { - ReferenceValue referenceValue = - partialEvaluator.getStackBefore(offset).getTop(stackEntryIndex).referenceValue(); - - markComplexEnumType(referenceValue); - } - - - /** - * Marks the enum class of the specified pushed type as complex. - */ - private void markPushedComplexEnumType(int offset) - { - ReferenceValue referenceValue = - partialEvaluator.getStackAfter(offset).getTop(0).referenceValue(); - - markComplexEnumType(referenceValue); - } - - - /** - * Marks the enum class of the specified stored type as complex. - */ - private void markStoredComplexEnumType(int offset, int variableIndex) - { - ReferenceValue referenceValue = - partialEvaluator.getVariablesAfter(offset).getValue(variableIndex).referenceValue(); - - markComplexEnumType(referenceValue); - } - - - /** - * Marks the enum class of the specified value as complex. - */ - private void markComplexEnumType(ReferenceValue referenceValue) - { - Clazz clazz = referenceValue.getReferencedClass(); - if (clazz != null) - { - clazz.accept(complexEnumMarker); - } - } -} 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); - } - } - } -} diff --git a/src/proguard/optimize/evaluation/StoringInvocationUnit.java b/src/proguard/optimize/evaluation/StoringInvocationUnit.java deleted file mode 100644 index 271b654..0000000 --- a/src/proguard/optimize/evaluation/StoringInvocationUnit.java +++ /dev/null @@ -1,207 +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.constant.RefConstant; -import proguard.evaluation.BasicInvocationUnit; -import proguard.evaluation.value.*; -import proguard.optimize.info.*; - -/** - * This InvocationUnit stores parameter values and return values with the - * methods that are invoked. - * - * @see LoadingInvocationUnit - * @author Eric Lafortune - */ -public class StoringInvocationUnit -extends BasicInvocationUnit -{ - private boolean storeFieldValues; - private boolean storeMethodParameterValues; - private boolean storeMethodReturnValues; - - - /** - * Creates a new StoringInvocationUnit with the given value factory. - */ - public StoringInvocationUnit(ValueFactory valueFactory) - { - this(valueFactory, true, true, true); - } - - - /** - * Creates a new StoringInvocationUnit with the given value factory, for - * storing the specified values. - */ - public StoringInvocationUnit(ValueFactory valueFactory, - boolean storeFieldValues, - boolean storeMethodParameterValues, - boolean storeMethodReturnValues) - { - super(valueFactory); - - this.storeFieldValues = storeFieldValues; - this.storeMethodParameterValues = storeMethodParameterValues; - this.storeMethodReturnValues = storeMethodReturnValues; - } - - - // Implementations for BasicInvocationUnit. - - protected void setFieldClassValue(Clazz clazz, - RefConstant refConstant, - ReferenceValue value) - { - if (storeFieldValues) - { - Member referencedMember = refConstant.referencedMember; - if (referencedMember != null) - { - generalizeFieldClassValue((Field)referencedMember, value); - } - } - } - - - protected void setFieldValue(Clazz clazz, - RefConstant refConstant, - Value value) - { - if (storeFieldValues) - { - Member referencedMember = refConstant.referencedMember; - if (referencedMember != null) - { - generalizeFieldValue((Field)referencedMember, value); - } - } - } - - - protected void setMethodParameterValue(Clazz clazz, - RefConstant refConstant, - int parameterIndex, - Value value) - { - if (storeMethodParameterValues) - { - Member referencedMember = refConstant.referencedMember; - if (referencedMember != null) - { - generalizeMethodParameterValue((Method)referencedMember, - parameterIndex, - value); - } - } - } - - - protected void setMethodReturnValue(Clazz clazz, - Method method, - Value value) - { - if (storeMethodReturnValues) - { - generalizeMethodReturnValue(method, value); - } - } - - - // Small utility methods. - - private static void generalizeFieldClassValue(Field field, ReferenceValue value) - { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - if (info != null) - { - info.generalizeReferencedClass(value); - } - } - - - public static ReferenceValue getFieldClassValue(Field field) - { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - return info != null ? - info.getReferencedClass() : - null; - } - - - private static void generalizeFieldValue(Field field, Value value) - { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - if (info != null) - { - info.generalizeValue(value); - } - } - - - public static Value getFieldValue(Field field) - { - FieldOptimizationInfo info = FieldOptimizationInfo.getFieldOptimizationInfo(field); - return info != null ? - info.getValue() : - null; - } - - - private static void generalizeMethodParameterValue(Method method, int parameterIndex, Value value) - { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.generalizeParameter(parameterIndex, value); - } - } - - - public static Value getMethodParameterValue(Method method, int parameterIndex) - { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null ? - info.getParameter(parameterIndex) : - null; - } - - - private static void generalizeMethodReturnValue(Method method, Value value) - { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - if (info != null) - { - info.generalizeReturnValue(value); - } - } - - - public static Value getMethodReturnValue(Method method) - { - MethodOptimizationInfo info = MethodOptimizationInfo.getMethodOptimizationInfo(method); - return info != null ? - info.getReturnValue() : - null; - } -} diff --git a/src/proguard/optimize/evaluation/TracedBranchUnit.java b/src/proguard/optimize/evaluation/TracedBranchUnit.java deleted file mode 100644 index 9e55275..0000000 --- a/src/proguard/optimize/evaluation/TracedBranchUnit.java +++ /dev/null @@ -1,59 +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.Clazz; -import proguard.classfile.attribute.CodeAttribute; -import proguard.evaluation.BasicBranchUnit; -import proguard.evaluation.value.Value; - -/** - * This BranchUnit remembers the branch unit commands that are invoked on it. - * - * @author Eric Lafortune - */ -class TracedBranchUnit -extends BasicBranchUnit -{ - // Implementations for BranchUnit. - - public void branchConditionally(Clazz clazz, - CodeAttribute codeAttribute, - int offset, - int branchTarget, - int conditional) - { - if (conditional == Value.ALWAYS) - { - // Always branch. - super.branch(clazz, codeAttribute, offset, branchTarget); - } - else if (conditional != Value.NEVER) - { - // Maybe branch. - super.branchConditionally(clazz, codeAttribute, offset, branchTarget, conditional); - } - else - { - super.setCalled(); - } - } -} diff --git a/src/proguard/optimize/evaluation/VariableOptimizer.java b/src/proguard/optimize/evaluation/VariableOptimizer.java deleted file mode 100644 index bef1445..0000000 --- a/src/proguard/optimize/evaluation/VariableOptimizer.java +++ /dev/null @@ -1,357 +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.*; -import proguard.classfile.editor.*; -import proguard.classfile.util.*; -import proguard.classfile.visitor.MemberVisitor; - -/** - * This AttributeVisitor optimizes variable allocation based on their the liveness, - * in the code attributes that it visits. - * - * @author Eric Lafortune - */ -public class VariableOptimizer -extends SimplifiedVisitor -implements AttributeVisitor, - LocalVariableInfoVisitor, - LocalVariableTypeInfoVisitor -{ - //* - private static final boolean DEBUG = false; - /*/ - private static boolean DEBUG = true; - //*/ - - private static final int MAX_VARIABLES_SIZE = 64; - - - private final boolean reuseThis; - private final MemberVisitor extraVariableMemberVisitor; - - private final LivenessAnalyzer livenessAnalyzer = new LivenessAnalyzer(); - private final VariableRemapper variableRemapper = new VariableRemapper(); - private VariableCleaner variableCleaner = new VariableCleaner(); - - private int[] variableMap = new int[ClassConstants.TYPICAL_VARIABLES_SIZE]; - - - /** - * Creates a new VariableOptimizer. - * @param reuseThis specifies whether the 'this' variable can be reused. - * Many JVMs for JME and IBM's JVMs for JSE can't handle - * such reuse. - */ - public VariableOptimizer(boolean reuseThis) - { - this(reuseThis, null); - } - - - /** - * Creates a new VariableOptimizer with an extra visitor. - * @param reuseThis specifies whether the 'this' variable - * can be reused. Many JVMs for JME and - * IBM's JVMs for JSE can't handle such - * reuse. - * @param extraVariableMemberVisitor an optional extra visitor for all - * removed variables. - */ - public VariableOptimizer(boolean reuseThis, - MemberVisitor extraVariableMemberVisitor) - { - this.reuseThis = reuseThis; - this.extraVariableMemberVisitor = extraVariableMemberVisitor; - } - - - // Implementations for AttributeVisitor. - - public void visitAnyAttribute(Clazz clazz, Attribute attribute) {} - - - public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute) - { -// DEBUG = -// clazz.getName().equals("abc/Def") && -// method.getName(clazz).equals("abc"); - - // Initialize the global arrays. - initializeArrays(codeAttribute); - - // Analyze the liveness of the variables in the code. - livenessAnalyzer.visitCodeAttribute(clazz, method, codeAttribute); - - // Trim the variables in the local variable tables, because even - // clipping the tables individually may leave some inconsistencies - // between them. - codeAttribute.attributesAccept(clazz, method, this); - - int startIndex = - (method.getAccessFlags() & ClassConstants.ACC_STATIC) != 0 || - reuseThis ? 0 : 1; - - int parameterSize = - ClassUtil.internalMethodParameterSize(method.getDescriptor(clazz), - method.getAccessFlags()); - - int variableSize = codeAttribute.u2maxLocals; - int codeLength = codeAttribute.u4codeLength; - - boolean remapping = false; - - // Loop over all variables. - for (int oldIndex = 0; oldIndex < variableSize; oldIndex++) - { - // By default, the variable will be mapped onto itself. - variableMap[oldIndex] = oldIndex; - - // Only try remapping the variable if it's not a parameter. - if (oldIndex >= parameterSize && - oldIndex < MAX_VARIABLES_SIZE) - { - // Try to remap the variable to a variable with a smaller index. - for (int newIndex = startIndex; newIndex < oldIndex; newIndex++) - { - if (areNonOverlapping(oldIndex, newIndex, codeLength)) - { - variableMap[oldIndex] = newIndex; - - updateLiveness(oldIndex, newIndex, codeLength); - - remapping = true; - - // This variable has been remapped. Go to the next one. - break; - } - } - } - } - - // Have we been able to remap any variables? - if (remapping) - { - if (DEBUG) - { - System.out.println("VariableOptimizer: "+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)); - for (int index= 0; index < variableSize; index++) - { - System.out.println(" v"+index+" -> "+variableMap[index]); - } - } - - // Remap the variables. - variableRemapper.setVariableMap(variableMap); - variableRemapper.visitCodeAttribute(clazz, method, codeAttribute); - - // Visit the method, if required. - if (extraVariableMemberVisitor != null) - { - method.accept(clazz, extraVariableMemberVisitor); - } - } - else - { - // Just clean up any empty variables. - variableCleaner.visitCodeAttribute(clazz, method, codeAttribute); - } - } - - - public void visitLocalVariableTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTableAttribute localVariableTableAttribute) - { - // Trim the variables in the local variable table. - localVariableTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); - } - - - public void visitLocalVariableTypeTableAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeTableAttribute localVariableTypeTableAttribute) - { - // Trim the variables in the local variable type table. - localVariableTypeTableAttribute.localVariablesAccept(clazz, method, codeAttribute, this); - } - - - // Implementations for LocalVariableInfoVisitor. - - public void visitLocalVariableInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableInfo localVariableInfo) - { - // Trim the local variable to the instructions at which it is alive. - int variable = localVariableInfo.u2index; - int startPC = localVariableInfo.u2startPC; - int endPC = startPC + localVariableInfo.u2length; - - startPC = firstLiveness(startPC, endPC, variable); - endPC = lastLiveness(startPC, endPC, variable); - - // Leave the start address of unused variables unchanged. - int length = endPC - startPC; - if (length > 0) - { - localVariableInfo.u2startPC = startPC; - } - - localVariableInfo.u2length = length; - } - - - // Implementations for LocalVariableTypeInfoVisitor. - - public void visitLocalVariableTypeInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, LocalVariableTypeInfo localVariableTypeInfo) - { - // Trim the local variable type to the instructions at which it is alive. - int variable = localVariableTypeInfo.u2index; - int startPC = localVariableTypeInfo.u2startPC; - int endPC = startPC + localVariableTypeInfo.u2length; - - startPC = firstLiveness(startPC, endPC, variable); - endPC = lastLiveness(startPC, endPC, variable); - - // Leave the start address of unused variables unchanged. - int length = endPC - startPC; - if (length > 0) - { - localVariableTypeInfo.u2startPC = startPC; - } - - localVariableTypeInfo.u2length = length; - } - - - // Small utility methods. - - /** - * Initializes the global arrays. - */ - private void initializeArrays(CodeAttribute codeAttribute) - { - int codeLength = codeAttribute.u4codeLength; - - // Create new arrays for storing information at each instruction offset. - if (variableMap.length < codeLength) - { - variableMap = new int[codeLength]; - } - } - - - /** - * Returns whether the given variables are never alive at the same time. - */ - private boolean areNonOverlapping(int variableIndex1, - int variableIndex2, - int codeLength) - { - // Loop over all instructions. - for (int offset = 0; offset < codeLength; offset++) - { - if ((livenessAnalyzer.isAliveBefore(offset, variableIndex1) && - livenessAnalyzer.isAliveBefore(offset, variableIndex2)) || - - (livenessAnalyzer.isAliveAfter(offset, variableIndex1) && - livenessAnalyzer.isAliveAfter(offset, variableIndex2)) || - - // For now, exclude Category 2 variables. - livenessAnalyzer.isCategory2(offset, variableIndex1)) - { - return false; - } - } - - return true; - } - - - /** - * Updates the liveness resulting from mapping the given old variable on - * the given new variable. - */ - private void updateLiveness(int oldVariableIndex, - int newVariableIndex, - int codeLength) - { - // Loop over all instructions. - for (int offset = 0; offset < codeLength; offset++) - { - // Update the liveness before the instruction. - if (livenessAnalyzer.isAliveBefore(offset, oldVariableIndex)) - { - livenessAnalyzer.setAliveBefore(offset, oldVariableIndex, false); - livenessAnalyzer.setAliveBefore(offset, newVariableIndex, true); - } - - // Update the liveness after the instruction. - if (livenessAnalyzer.isAliveAfter(offset, oldVariableIndex)) - { - livenessAnalyzer.setAliveAfter(offset, oldVariableIndex, false); - livenessAnalyzer.setAliveAfter(offset, newVariableIndex, true); - } - } - } - - - /** - * Returns the first instruction offset between the given offsets at which - * the given variable goes alive. - */ - private int firstLiveness(int startOffset, int endOffset, int variableIndex) - { - for (int offset = startOffset; offset < endOffset; offset++) - { - if (livenessAnalyzer.isTraced(offset) && - livenessAnalyzer.isAliveBefore(offset, variableIndex)) - { - return offset; - } - } - - return endOffset; - } - - - /** - * Returns the last instruction offset between the given offsets before - * which the given variable is still alive. - */ - private int lastLiveness(int startOffset, int endOffset, int variableIndex) - { - int previousOffset = endOffset; - - for (int offset = endOffset-1; offset >= startOffset; offset--) - { - if (livenessAnalyzer.isTraced(offset)) - { - if (livenessAnalyzer.isAliveBefore(offset, variableIndex)) - { - return previousOffset; - } - - previousOffset = offset; - } - } - - return endOffset; - } -} diff --git a/src/proguard/optimize/evaluation/package.html b/src/proguard/optimize/evaluation/package.html deleted file mode 100644 index 5341f9f..0000000 --- a/src/proguard/optimize/evaluation/package.html +++ /dev/null @@ -1,4 +0,0 @@ -<body> -This package contains visitors that perform partial evaluation and subsequent -optimizations on byte code. -</body> |