diff options
Diffstat (limited to 'src/proguard/optimize/evaluation/EvaluationShrinker.java')
-rw-r--r-- | src/proguard/optimize/evaluation/EvaluationShrinker.java | 1912 |
1 files changed, 1912 insertions, 0 deletions
diff --git a/src/proguard/optimize/evaluation/EvaluationShrinker.java b/src/proguard/optimize/evaluation/EvaluationShrinker.java new file mode 100644 index 0000000..1463feb --- /dev/null +++ b/src/proguard/optimize/evaluation/EvaluationShrinker.java @@ -0,0 +1,1912 @@ +/* + * ProGuard -- shrinking, optimization, obfuscation, and preverification + * of Java bytecode. + * + * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +package proguard.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.*; +import proguard.evaluation.value.*; +import proguard.optimize.info.*; + +/** + * 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_RESULTS = true; + private static boolean DEBUG = true; + //*/ + + 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); + private final MyUnusedParameterSimplifier unusedParameterSimplifier = new MyUnusedParameterSimplifier(); + private final MyProducerMarker producerMarker = new MyProducerMarker(); + private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer(); + private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false); + + private boolean[][] variablesNecessaryAfter = new boolean[ClassConstants.TYPICAL_CODE_LENGTH][ClassConstants.TYPICAL_VARIABLES_SIZE]; + 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("Class "+ClassUtil.externalClassName(clazz.getName())); + System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(), + 0, + method.getName(clazz), + method.getDescriptor(clazz))); + } + + // Initialize the necessary array. + initializeNecessary(codeAttribute); + + // Evaluate the method. + partialEvaluator.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++) + { + // Is it a variable initialization that hasn't been marked yet? + if (partialEvaluator.isTraced(offset) && + !isInstructionNecessary(offset)) + { + // Is the corresponding variable necessary anywhere in the code, + // accoriding to a simple partial evaluation? + int variableIndex = partialEvaluator.initializedVariable(offset); + if (variableIndex >= 0 && + isVariableInitializationNecessary(clazz, + method, + codeAttribute, + offset, + variableIndex)) + { + markInstruction(offset); + } + } + } + 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.deleteInstruction(offset); + + codeAttributeEditor.insertBeforeInstruction(offset, (Instruction)null); + codeAttributeEditor.replaceInstruction(offset, (Instruction)null); + codeAttributeEditor.insertAfterInstruction(offset, (Instruction)null); + + // 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.INTERNAL_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.opcode < InstructionConstants.OP_ISTORE) + { + 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 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. + // 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 top = tracedStack.size() - 1; + + int requiredPushCount = 0; + for (int stackIndex = 0; stackIndex < popCount; stackIndex++) + { + // Is the stack entry required by other consumers? + if (!isStackSimplifiedBefore(offset, top - stackIndex) && + !isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - stackIndex)) + { + // Remember to push it. + requiredPushCount++; + } + } + + // 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+"]"); + } + + insertPushInstructions(offset, false, tracedStack.getTop(0).computationalType()); + } + } + + // 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 top = tracedStack.size() - 1; + + int requiredPopCount = 0; + for (int stackIndex = 0; stackIndex < pushCount; stackIndex++) + { + // Is the stack entry required by consumers? + if (!isStackEntryNecessaryAfter(offset, top - 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, 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 top = tracedStack.size() - 1; + + int expectedPopCount = 0; + for (int stackIndex = 0; stackIndex < popCount; stackIndex++) + { + // Is the stack entry required by other consumers? + if (isAnyStackEntryNecessaryAfter(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), top - 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, 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 top = tracedStack.size() - 1; + + int expectedPushCount = 0; + for (int stackIndex = 0; stackIndex < pushCount; stackIndex++) + { + // Is the stack entry required by consumers? + if (isStackEntryNecessaryAfter(offset, top - 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, tracedStack.getTop(0).computationalType()); + } + } + } + } + + + public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction) + { + if (isInstructionNecessary(offset) && + isDupOrSwap(simpleInstruction)) + { + fixDupInstruction(clazz, codeAttribute, offset, simpleInstruction); + } + else + { + visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction); + } + } + } + + + // Small utility methods. + + /** + * Marks the variable and the corresponding producing instructions + * of the consumer at the given offset. + * @param consumerOffset the offset of the consumer. + * @param variableIndex the index of the variable to be marked. + */ + private void markVariableProducers(int consumerOffset, + int variableIndex) + { + TracedVariables tracedVariables = + partialEvaluator.getVariablesBefore(consumerOffset); + + // Mark the producer of the loaded value. + markVariableProducers(tracedVariables.getProducerValue(variableIndex).instructionOffsetValue(), + variableIndex); + } + + + /** + * Marks the variable and its producing instructions at the given offsets. + * @param producerOffsets the offsets of the producers to be marked. + * @param variableIndex the index of the variable to be marked. + */ + private void markVariableProducers(InstructionOffsetValue producerOffsets, + int variableIndex) + { + 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); + + markVariableAfter(offset, variableIndex); + markInstruction(offset); + } + } + } + + + /** + * 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) + { + // Mark the producers of the popped values. + int popCount = consumer.stackPopCount(clazz); + for (int stackIndex = 0; stackIndex < popCount; 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 consumerStackIndex the index of the stack entry to be checked + * (counting from the top). + * @param producerStackIndex the index of the stack entry to be marked + * (counting from the top). + */ + private void conditionallyMarkStackEntryProducers(int consumerOffset, + int consumerStackIndex, + int producerStackIndex) + { + int top = partialEvaluator.getStackAfter(consumerOffset).size() - 1; + + if (isStackEntryNecessaryAfter(consumerOffset, top - consumerStackIndex)) + { + markStackEntryProducers(consumerOffset, producerStackIndex); + } + } + + + /** + * 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 top). + */ + private void markStackEntryProducers(int consumerOffset, + int stackIndex) + { + TracedStack tracedStack = + partialEvaluator.getStackBefore(consumerOffset); + + int stackBottomIndex = tracedStack.size() - 1 - stackIndex; + + if (!isStackSimplifiedBefore(consumerOffset, stackBottomIndex)) + { + markStackEntryProducers(tracedStack.getTopProducerValue(stackIndex).instructionOffsetValue(), + stackBottomIndex); + } + } + + + /** + * 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); + } + } + + + /** + * Marks the specified instruction if it is a required dup/swap instruction, + * replacing it by an appropriate variant if necessary. + * @param clazz the class that is being checked. + * @param codeAttribute the code that is being checked. + * @param dupOffset the offset of the dup/swap instruction. + * @param instruction the dup/swap instruction. + */ + private void fixDupInstruction(Clazz clazz, + CodeAttribute codeAttribute, + int dupOffset, + Instruction instruction) + { + int top = partialEvaluator.getStackAfter(dupOffset).size() - 1; + + byte oldOpcode = instruction.opcode; + byte newOpcode = 0; + + // Simplify the popping instruction if possible. + switch (oldOpcode) + { + case InstructionConstants.OP_DUP: + { + boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); + boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); + + // Should either the original element or the copy be present? + if (stackEntryPresent0 || + stackEntryPresent1) + { + // Should both the original element and the copy be present? + if (stackEntryPresent0 && + stackEntryPresent1) + { + newOpcode = InstructionConstants.OP_DUP; + } + } + break; + } + case InstructionConstants.OP_DUP_X1: + { + boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); + boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); + boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); + + // Should either the original element or the copy be present? + if (stackEntryPresent0 || + stackEntryPresent2) + { + // Should the copy be present? + if (stackEntryPresent2) + { + // Compute the number of elements to be skipped. + int skipCount = stackEntryPresent1 ? 1 : 0; + + // Should the original element be present? + if (stackEntryPresent0) + { + // Copy the original element. + newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount); + } + else if (skipCount == 1) + { + // Move the original element. + newOpcode = InstructionConstants.OP_SWAP; + } + } + } + break; + } + case InstructionConstants.OP_DUP_X2: + { + boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); + boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); + boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); + boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3); + + // Should either the original element or the copy be present? + if (stackEntryPresent0 || + stackEntryPresent3) + { + // Should the copy be present? + if (stackEntryPresent3) + { + int skipCount = (stackEntryPresent1 ? 1 : 0) + + (stackEntryPresent2 ? 1 : 0); + + // Should the original element be present? + if (stackEntryPresent0) + { + // Copy the original element. + newOpcode = (byte)(InstructionConstants.OP_DUP + skipCount); + } + else if (skipCount == 1) + { + // Move the original element. + newOpcode = InstructionConstants.OP_SWAP; + } + else if (skipCount == 2) + { + // We can't easily move the original element. + throw new UnsupportedOperationException("Can't handle dup_x2 instruction moving original element across two elements at ["+dupOffset +"]"); + } + } + } + break; + } + case InstructionConstants.OP_DUP2: + { + boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); + boolean stackEntriesPresent23 = isStackEntriesNecessaryAfter(dupOffset, top - 2, top - 3); + + // Should either the original element or the copy be present? + if (stackEntriesPresent01 || + stackEntriesPresent23) + { + // Should both the original element and the copy be present? + if (stackEntriesPresent01 && + stackEntriesPresent23) + { + newOpcode = InstructionConstants.OP_DUP2; + } + } + break; + } + case InstructionConstants.OP_DUP2_X1: + { + boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); + boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); + boolean stackEntriesPresent34 = isStackEntriesNecessaryAfter(dupOffset, top - 3, top - 4); + + // Should either the original element or the copy be present? + if (stackEntriesPresent01 || + stackEntriesPresent34) + { + // Should the copy be present? + if (stackEntriesPresent34) + { + int skipCount = stackEntryPresent2 ? 1 : 0; + + // Should the original element be present? + if (stackEntriesPresent01) + { + // Copy the original element. + newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount); + } + else if (skipCount > 0) + { + // We can't easily move the original element. + throw new UnsupportedOperationException("Can't handle dup2_x1 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]"); + } + } + } + break; + } + case InstructionConstants.OP_DUP2_X2: + { + boolean stackEntriesPresent01 = isStackEntriesNecessaryAfter(dupOffset, top - 0, top - 1); + boolean stackEntryPresent2 = isStackEntryNecessaryAfter(dupOffset, top - 2); + boolean stackEntryPresent3 = isStackEntryNecessaryAfter(dupOffset, top - 3); + boolean stackEntriesPresent45 = isStackEntriesNecessaryAfter(dupOffset, top - 4, top - 5); + + // Should either the original element or the copy be present? + if (stackEntriesPresent01 || + stackEntriesPresent45) + { + // Should the copy be present? + if (stackEntriesPresent45) + { + int skipCount = (stackEntryPresent2 ? 1 : 0) + + (stackEntryPresent3 ? 1 : 0); + + // Should the original element be present? + if (stackEntriesPresent01) + { + // Copy the original element. + newOpcode = (byte)(InstructionConstants.OP_DUP2 + skipCount); + } + else if (skipCount > 0) + { + // We can't easily move the original element. + throw new UnsupportedOperationException("Can't handle dup2_x2 instruction moving original element across "+skipCount+" elements at ["+dupOffset +"]"); + } + } + } + break; + } + case InstructionConstants.OP_SWAP: + { + boolean stackEntryPresent0 = isStackEntryNecessaryAfter(dupOffset, top - 0); + boolean stackEntryPresent1 = isStackEntryNecessaryAfter(dupOffset, top - 1); + + // Will either element be present? + if (stackEntryPresent0 || + stackEntryPresent1) + { + // Will both elements be present? + if (stackEntryPresent0 && + stackEntryPresent1) + { + newOpcode = InstructionConstants.OP_SWAP; + } + } + break; + } + } + + if (newOpcode == 0) + { + // Delete the instruction. + codeAttributeEditor.deleteInstruction(dupOffset); + + if (extraDeletedInstructionVisitor != null) + { + extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, dupOffset, null); + } + + if (DEBUG) System.out.println(" Marking but deleting instruction "+instruction.toString(dupOffset)); + } + else if (newOpcode == oldOpcode) + { + // Leave the instruction unchanged. + codeAttributeEditor.undeleteInstruction(dupOffset); + + if (DEBUG) System.out.println(" Marking unchanged instruction "+instruction.toString(dupOffset)); + } + else + { + // Replace the instruction. + Instruction replacementInstruction = new SimpleInstruction(newOpcode); + codeAttributeEditor.replaceInstruction(dupOffset, + replacementInstruction); + + if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(dupOffset)+" by "+replacementInstruction.toString()); + } + } + + + /** + * 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, + 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. + if (replace) + { + // Replace the push instruction. + codeAttributeEditor.replaceInstruction(offset, replacementInstruction); + } + else + { + // Insert the push instruction. + codeAttributeEditor.insertBeforeInstruction(offset, replacementInstruction); + + if (extraAddedInstructionVisitor != null) + { + replacementInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + + + /** + * 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, 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); + + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, popInstruction); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, popInstruction); + + if (extraAddedInstructionVisitor != null) + { + popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + break; + } + case 2: + { + // Replace or insert a single pop2 instruction. + Instruction popInstruction = + new SimpleInstruction(InstructionConstants.OP_POP2); + + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, popInstruction); + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, popInstruction); + + if (extraAddedInstructionVisitor != null) + { + popInstruction.accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + 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; + } + + if (replace) + { + codeAttributeEditor.replaceInstruction(offset, popInstructions); + + for (int index = 1; index < popInstructions.length; index++) + { + if (extraAddedInstructionVisitor != null) + { + popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + else + { + codeAttributeEditor.insertAfterInstruction(offset, popInstructions); + + for (int index = 0; index < popInstructions.length; index++) + { + if (extraAddedInstructionVisitor != null) + { + popInstructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor); + } + } + } + break; + } + } + } + + + /** + * 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).shrink(); + + 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 (variablesNecessaryAfter.length < codeLength || + variablesNecessaryAfter[0].length < maxLocals) + { + variablesNecessaryAfter = new boolean[codeLength][maxLocals]; + } + else + { + for (int offset = 0; offset < codeLength; offset++) + { + for (int index = 0; index < maxLocals; index++) + { + variablesNecessaryAfter[offset][index] = false; + } + } + } + + if (stacksNecessaryAfter.length < codeLength || + stacksNecessaryAfter[0].length < maxStack) + { + stacksNecessaryAfter = new boolean[codeLength][maxStack]; + } + else + { + for (int offset = 0; offset < codeLength; offset++) + { + for (int index = 0; index < maxStack; index++) + { + stacksNecessaryAfter[offset][index] = false; + } + } + } + + if (stacksSimplifiedBefore.length < codeLength || + stacksSimplifiedBefore[0].length < maxStack) + { + stacksSimplifiedBefore = new boolean[codeLength][maxStack]; + } + else + { + for (int offset = 0; offset < codeLength; offset++) + { + for (int index = 0; index < maxStack; index++) + { + stacksSimplifiedBefore[offset][index] = false; + } + } + } + + if (instructionsNecessary.length < codeLength) + { + instructionsNecessary = new boolean[codeLength]; + } + else + { + for (int index = 0; index < codeLength; index++) + { + instructionsNecessary[index] = false; + } + } + } + + + /** + * Returns whether the given stack entry is present after execution of the + * instruction at the given offset. + */ + 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 the specified variable must be initialized at the + * specified offset, according to the verifier of the JVM. + */ + private boolean isVariableInitializationNecessary(Clazz clazz, + Method method, + CodeAttribute codeAttribute, + int initializationOffset, + int variableIndex) + { + int codeLength = codeAttribute.u4codeLength; + + // Is the variable necessary anywhere at all? + if (isVariableNecessaryAfterAny(0, codeLength, variableIndex)) + { + if (DEBUG) System.out.println("Simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]"); + + // Lazily compute perform simple partial evaluation, the way the + // JVM preverifier would do it. + simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute); + + if (DEBUG) System.out.println("End of simple partial evaluation for initialization of variable v"+variableIndex+" at ["+initializationOffset+"]"); + + // Check if the variable is necessary elsewhere. + for (int offset = 0; offset < codeLength; offset++) + { + if (isInstructionNecessary(offset)) + { + Value producer = partialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex); + if (producer != null) + { + Value simpleProducer = simplePartialEvaluator.getVariablesBefore(offset).getProducerValue(variableIndex); + if (simpleProducer != null) + { + InstructionOffsetValue producerOffsets = + producer.instructionOffsetValue(); + InstructionOffsetValue simpleProducerOffsets = + simpleProducer.instructionOffsetValue(); + + // Does the sophisticated partial evaluation have fewer + // producers than the simple one? + // And does the simple partial evaluation point to an + // initialization of the variable? + if (producerOffsets.instructionOffsetCount() < + simpleProducerOffsets.instructionOffsetCount() && + isVariableNecessaryAfterAny(producerOffsets, variableIndex) && + simpleProducerOffsets.contains(initializationOffset)) + { + // Then the initialization is necessary. + return true; + } + } + } + } + } + } + + return false; + } + + + private void markVariableAfter(int instructionOffset, + int variableIndex) + { + if (!isVariableNecessaryAfter(instructionOffset, variableIndex)) + { + if (DEBUG) System.out.print("["+instructionOffset+".v"+variableIndex+"],"); + + variablesNecessaryAfter[instructionOffset][variableIndex] = true; + + if (maxMarkedOffset < instructionOffset) + { + maxMarkedOffset = instructionOffset; + } + } + } + + + /** + * Returns whether the specified variable is ever necessary after any + * instruction in the specified block. + */ + private boolean isVariableNecessaryAfterAny(int startOffset, + int endOffset, + int variableIndex) + { + for (int offset = startOffset; offset < endOffset; offset++) + { + if (isVariableNecessaryAfter(offset, variableIndex)) + { + return true; + } + } + + return false; + } + + + /** + * Returns whether the specified variable is ever necessary after any + * instruction in the specified set of instructions offsets. + */ + private boolean isVariableNecessaryAfterAny(InstructionOffsetValue instructionOffsetValue, + int variableIndex) + { + int count = instructionOffsetValue.instructionOffsetCount(); + + for (int index = 0; index < count; index++) + { + if (isVariableNecessaryAfter(instructionOffsetValue.instructionOffset(index), + variableIndex)) + { + return true; + } + } + + return false; + } + + + private boolean isVariableNecessaryAfter(int instructionOffset, + int variableIndex) + { + return instructionOffset == PartialEvaluator.AT_METHOD_ENTRY || + variablesNecessaryAfter[instructionOffset][variableIndex]; + } + + + 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; + } + } + } + + + 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; + } + + + 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]; + } +}
\ No newline at end of file |