blob: 1463feb5e9aa933358f8eca7a9a5130f58346503 [file] [log] [blame]
/*
* 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];
}
}