proguard.optimize.evaluation.EvaluationShrinker Maven / Gradle / Ivy
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2018 GuardSquare NV
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package proguard.optimize.evaluation;
import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.*;
import proguard.classfile.constant.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.SimplifiedVisitor;
import proguard.classfile.visitor.*;
import proguard.evaluation.TracedStack;
import proguard.evaluation.value.*;
import proguard.optimize.info.ParameterUsageMarker;
/**
* This AttributeVisitor shrinks the code attributes that it visits, based
* on partial evaluation.
*
* @author Eric Lafortune
*/
public class EvaluationShrinker
extends SimplifiedVisitor
implements AttributeVisitor,
ExceptionInfoVisitor
{
//*
private static final boolean DEBUG = false;
private static final boolean DEBUG_RESULTS = false;
/*/
private static boolean DEBUG = System.getProperty("es") != null;
private static boolean DEBUG_RESULTS = DEBUG;
//*/
private static final int UNSUPPORTED = -1;
private static final int NOP = InstructionConstants.OP_NOP & 0xff;
private static final int POP = InstructionConstants.OP_POP & 0xff;
private static final int POP2 = InstructionConstants.OP_POP2 & 0xff;
private static final int DUP = InstructionConstants.OP_DUP & 0xff;
private static final int DUP_X1 = InstructionConstants.OP_DUP_X1 & 0xff;
private static final int DUP_X2 = InstructionConstants.OP_DUP_X2 & 0xff;
private static final int DUP2 = InstructionConstants.OP_DUP2 & 0xff;
private static final int DUP2_X1 = InstructionConstants.OP_DUP2_X1 & 0xff;
private static final int DUP2_X2 = InstructionConstants.OP_DUP2_X2 & 0xff;
private static final int SWAP = InstructionConstants.OP_SWAP & 0xff;
private static final int MOV_X2 = DUP_X2 | (POP << 8);
private static final int MOV2_X1 = DUP2_X1 | (POP2 << 8);
private static final int MOV2_X2 = DUP2_X2 | (POP2 << 8);
private static final int POP_X1 = SWAP | (POP << 8);
private static final int POP_X2 = DUP2_X1 | (POP2 << 8) | (POP << 16);
private static final int POP_X3 = UNSUPPORTED;
private static final int POP2_X1 = DUP_X2 | (POP << 8) | (POP2 << 16);
private static final int POP2_X2 = DUP2_X2 | (POP2 << 8) | (POP2 << 16);
private static final int POP3 = POP2 | (POP << 8);
private static final int POP4 = POP2 | (POP2 << 8);
private static final int POP_DUP = POP | (DUP << 8);
private static final int POP_SWAP_POP = POP | (SWAP << 8) | (POP << 16);
private static final int POP2_SWAP_POP = POP2 | (SWAP << 8) | (POP << 16);
private static final int SWAP_DUP_X1 = SWAP | (DUP_X1 << 8);
private static final int SWAP_DUP_X1_SWAP = SWAP | (DUP_X1 << 8) | (SWAP << 16);
private static final int SWAP_POP_DUP = SWAP | (POP << 8) | (DUP << 16);
private static final int SWAP_POP_DUP_X1 = SWAP | (POP << 8) | (DUP_X1 << 16);
private static final int DUP_X2_POP2 = DUP_X2 | (POP2 << 8);
private static final int DUP2_X1_POP3 = DUP2_X1 | (POP2 << 8) | (POP << 16);
private static final int DUP2_X2_POP3 = DUP2_X2 | (POP2 << 8) | (POP << 16);
private static final int DUP2_X2_SWAP_POP = DUP2_X2 | (SWAP << 8) | (POP << 16);
private final InstructionUsageMarker instructionUsageMarker;
private final boolean runInstructionUsageMarker;
private final InstructionVisitor extraDeletedInstructionVisitor;
private final InstructionVisitor extraAddedInstructionVisitor;
private final MyStaticInvocationFixer staticInvocationFixer = new MyStaticInvocationFixer();
private final MyBackwardBranchFixer backwardBranchFixer = new MyBackwardBranchFixer();
private final MyNonReturningSubroutineFixer nonReturningSubroutineFixer = new MyNonReturningSubroutineFixer();
private final MyStackConsistencyFixer stackConsistencyFixer = new MyStackConsistencyFixer();
private final MyInstructionDeleter instructionDeleter = new MyInstructionDeleter();
private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true);
/**
* Creates a new EvaluationShrinker.
*/
public EvaluationShrinker()
{
this(new PartialEvaluator(), true, null, null);
}
/**
* Creates a new EvaluationShrinker.
* @param partialEvaluator the partial evaluator that will
* analyze the code.
* @param runPartialEvaluator specifies whether the partial
* evaluator should be run for each
* method, or if some other class is
* already doing this.
* @param extraDeletedInstructionVisitor an optional extra visitor for all
* deleted instructions.
* @param extraAddedInstructionVisitor an optional extra visitor for all
* added instructions.
*/
public EvaluationShrinker(PartialEvaluator partialEvaluator,
boolean runPartialEvaluator,
InstructionVisitor extraDeletedInstructionVisitor,
InstructionVisitor extraAddedInstructionVisitor)
{
this(new InstructionUsageMarker(partialEvaluator, runPartialEvaluator),
true,
extraDeletedInstructionVisitor,
extraAddedInstructionVisitor);
}
/**
* Creates a new EvaluationShrinker.
* @param instructionUsageMarker the instruction usage marker that
* will analyze the code.
* @param runInstructionUsageMarker specifies whether the usage
* marker should be run for each
* method, or if some other class is
* already doing this.
* @param extraDeletedInstructionVisitor an optional extra visitor for all
* deleted instructions.
* @param extraAddedInstructionVisitor an optional extra visitor for all
* added instructions.
*/
public EvaluationShrinker(InstructionUsageMarker instructionUsageMarker,
boolean runInstructionUsageMarker,
InstructionVisitor extraDeletedInstructionVisitor,
InstructionVisitor extraAddedInstructionVisitor)
{
this.instructionUsageMarker = instructionUsageMarker;
this.runInstructionUsageMarker = runInstructionUsageMarker;
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()+")");
ex.printStackTrace();
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("EvaluationShrinker ["+clazz.getName()+"."+method.getName(clazz)+method.getDescriptor(clazz)+"]");
}
// Analyze the method.
if (runInstructionUsageMarker)
{
instructionUsageMarker.visitCodeAttribute(clazz, method, codeAttribute);
}
int codeLength = codeAttribute.u4codeLength;
if (DEBUG) System.out.println();
// Reset the code changes.
codeAttributeEditor.reset(codeLength);
// Replace virtual invocations by static invocations, where neceesary.
if (DEBUG) System.out.println("Static invocation fixing:");
codeAttribute.instructionsAccept(clazz, method,
instructionUsageMarker.necessaryInstructionFilter(true,
staticInvocationFixer));
if (DEBUG) System.out.println();
// Replace traced but unnecessary 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("Backward branch fixing:");
codeAttribute.instructionsAccept(clazz, method,
instructionUsageMarker.tracedInstructionFilter(true,
instructionUsageMarker.necessaryInstructionFilter(false,
backwardBranchFixer)));
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:");
codeAttribute.instructionsAccept(clazz, method,
instructionUsageMarker.necessaryInstructionFilter(true,
nonReturningSubroutineFixer));
if (DEBUG) System.out.println();
// Locally fix instructions, in order to keep the stack consistent.
if (DEBUG) System.out.println("Stack consistency fixing:");
codeAttribute.instructionsAccept(clazz, method,
instructionUsageMarker.tracedInstructionFilter(true,
stackConsistencyFixer));
if (DEBUG) System.out.println();
// Delete all instructions that are not used.
if (DEBUG) System.out.println("Deleting unused instructions");
codeAttribute.instructionsAccept(clazz, method,
instructionUsageMarker.necessaryInstructionFilter(false,
instructionDeleter));
if (DEBUG) System.out.println();
if (DEBUG_RESULTS)
{
System.out.println("Simplification results:");
int offset = 0;
do
{
Instruction instruction = InstructionFactory.create(codeAttribute.code,
offset);
System.out.println((instructionUsageMarker.isInstructionNecessary(offset) ? " + " :
instructionUsageMarker.isExtraPushPopInstructionNecessary(offset) ? " ~ " :
" - ") +
instruction.toString(offset));
if (instructionUsageMarker.isTraced(offset))
{
InstructionOffsetValue branchTargets = instructionUsageMarker.branchTargets(offset);
if (branchTargets != null)
{
System.out.println(" has overall been branching to "+branchTargets);
}
boolean deleted = codeAttributeEditor.deleted[offset];
if (instructionUsageMarker.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);
}
// Clear exception handlers that are not necessary.
codeAttribute.exceptionsAccept(clazz, method, this);
// Apply all accumulated changes to the code.
codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
}
/**
* This MemberVisitor converts virtual method invocations into static
* method invocations if the 'this' parameter isn't used.
*/
private class MyStaticInvocationFixer
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_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)
{
// Make the method invocation static, if possible.
if ((programMethod.getAccessFlags() & ClassConstants.ACC_STATIC) == 0 &&
!ParameterUsageMarker.isParameterUsed(programMethod, 0))
{
replaceByStaticInvocation(programClass,
invocationOffset,
invocationInstruction);
}
}
}
/**
* This InstructionVisitor replaces all backward branches by
* infinite loops.
*/
private class MyBackwardBranchFixer
extends SimplifiedVisitor
implements InstructionVisitor
{
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
{
// Is it a traced but unmarked backward branch, without an unmarked
// straddling forward branch? Note that this is still a heuristic.
if (isAllSmallerThanOrEqual(instructionUsageMarker.branchTargets(offset),
offset) &&
!isAnyUnnecessaryInstructionBranchingOver(lastNecessaryInstructionOffset(offset),
offset))
{
replaceByInfiniteLoop(clazz, offset);
if (DEBUG) System.out.println(" Setting infinite loop instead of "+instruction.toString(offset));
}
}
/**
* 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 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 (instructionUsageMarker.isInstructionNecessary(instructionOffset))
{
return offset;
}
}
return 0;
}
}
/**
* This InstructionVisitor appends infinite loops after all visited
* non-returning subroutine invocations.
*/
private class MyNonReturningSubroutineFixer
extends SimplifiedVisitor
implements InstructionVisitor
{
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
{
// Is it a necessary subroutine invocation?
if (branchInstruction.canonicalOpcode() == InstructionConstants.OP_JSR)
{
int nextOffset = offset + branchInstruction.length(offset);
if (!instructionUsageMarker.isInstructionNecessary(nextOffset))
{
replaceByInfiniteLoop(clazz, nextOffset);
if (DEBUG) System.out.println(" Adding infinite loop at ["+nextOffset+"] after "+branchInstruction.toString(offset));
}
}
}
}
/**
* 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 (instructionUsageMarker.isInstructionNecessary(offset))
{
// Check all stack entries that are popped.
// Unusual case: an exception handler with an exception that is
// no longer consumed directly by a method.
// Typical case: a freshly marked variable initialization that
// requires some value on the stack.
int popCount = instruction.stackPopCount(clazz);
if (popCount > 0)
{
TracedStack tracedStack =
instructionUsageMarker.getStackBefore(offset);
int stackSize = tracedStack.size();
int requiredPopCount = 0;
int requiredPushCount = 0;
for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++)
{
boolean stackEntryUnwantedBefore =
instructionUsageMarker.isStackEntryUnwantedBefore( offset, stackIndex);
boolean stackEntryPresentBefore =
instructionUsageMarker.isStackEntryPresentBefore( offset, stackIndex);
if (stackEntryUnwantedBefore)
{
if (stackEntryPresentBefore)
{
// Remember to pop it.
requiredPopCount++;
}
}
else
{
if (!stackEntryPresentBefore)
{
// Remember to push some value.
requiredPushCount++;
}
}
}
// Pop some unnecessary stack entries.
if (requiredPopCount > 0)
{
if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset));
insertPopInstructions(offset, false, true, popCount);
}
// Push some necessary stack entries.
if (requiredPushCount > 0)
{
Value value = tracedStack.getTop(0);
if (DEBUG) System.out.println(" Inserting before marked consumer "+instruction.toString(offset));
if (requiredPushCount > (value.isCategory2() ? 2 : 1))
{
throw new IllegalArgumentException("Unsupported stack size increment ["+requiredPushCount+"] at ["+offset+"]");
}
insertPushInstructions(offset, false, true, value.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 =
instructionUsageMarker.getStackAfter(offset);
int stackSize = tracedStack.size();
int requiredPopCount = 0;
for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++)
{
// Is the stack entry required by consumers?
if (!instructionUsageMarker.isStackEntryNecessaryAfter(offset, stackIndex))
{
// Remember to pop it.
requiredPopCount++;
}
}
// Pop the unnecessary stack entries.
if (requiredPopCount > 0)
{
if (DEBUG) System.out.println(" Inserting after marked producer "+instruction.toString(offset));
insertPopInstructions(offset, false, false, requiredPopCount);
}
}
}
else if (instructionUsageMarker.isExtraPushPopInstructionNecessary(offset))
{
// 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 =
instructionUsageMarker.getStackBefore(offset);
int stackSize = tracedStack.size();
int expectedPopCount = 0;
for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++)
{
// Is this stack entry pushed by any producer
// (because it is required by other consumers)?
if (instructionUsageMarker.isStackEntryPresentBefore(offset, stackIndex))
{
// Remember to pop it.
expectedPopCount++;
}
}
// Pop the unnecessary stack entries.
if (expectedPopCount > 0)
{
if (DEBUG) System.out.println(" Replacing unmarked consumer "+instruction.toString(offset));
insertPopInstructions(offset, true, false, expectedPopCount);
}
}
// Check all stack entries that would be pushed.
// Typical case: a corresponding stack entry is pushed
// elsewhere so it still has to be pushed here.
int pushCount = instruction.stackPushCount(clazz);
if (pushCount > 0)
{
TracedStack tracedStack =
instructionUsageMarker.getStackAfter(offset);
int stackSize = tracedStack.size();
int expectedPushCount = 0;
for (int stackIndex = stackSize - pushCount; stackIndex < stackSize; stackIndex++)
{
// Is the stack entry required by consumers?
if (instructionUsageMarker.isStackEntryNecessaryAfter(offset, stackIndex))
{
// Remember to push it.
expectedPushCount++;
}
}
// Push some necessary stack entries.
if (expectedPushCount > 0)
{
if (DEBUG) System.out.println(" Replacing unmarked producer "+instruction.toString(offset));
insertPushInstructions(offset, true, false, tracedStack.getTop(0).computationalType());
}
}
}
}
public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
{
if (instructionUsageMarker.isInstructionNecessary(offset) &&
isDupOrSwap(simpleInstruction))
{
int topBefore = instructionUsageMarker.getStackBefore(offset).size() - 1;
int topAfter = instructionUsageMarker.getStackAfter(offset).size() - 1;
byte oldOpcode = simpleInstruction.opcode;
// Simplify the dup/swap instruction if possible.
int newOpcodes = fixDupSwap(offset, oldOpcode, topBefore, topAfter);
// Did we find a suitable (extended) opcode?
if (newOpcodes == UNSUPPORTED)
{
// We can't easily emulate some constructs.
throw new UnsupportedOperationException("Can't handle "+simpleInstruction.toString()+" instruction at ["+offset +"]");
}
// Is there a single replacement opcode?
if ((newOpcodes & ~0xff) == 0)
{
byte newOpcode = (byte)newOpcodes;
if (newOpcode == InstructionConstants.OP_NOP)
{
// Delete the instruction.
codeAttributeEditor.deleteInstruction(offset);
if (extraDeletedInstructionVisitor != null)
{
extraDeletedInstructionVisitor.visitSimpleInstruction(null, null, null, offset, null);
}
if (DEBUG) System.out.println(" Deleting marked instruction "+simpleInstruction.toString(offset));
}
else if (newOpcode == oldOpcode)
{
// Leave the instruction unchanged.
codeAttributeEditor.undeleteInstruction(offset);
if (DEBUG) System.out.println(" Marking unchanged instruction "+simpleInstruction.toString(offset));
}
else
{
// Replace the instruction.
Instruction replacementInstruction = new SimpleInstruction(newOpcode);
codeAttributeEditor.replaceInstruction(offset,
replacementInstruction);
if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by "+replacementInstruction.toString());
}
}
else
{
// Collect the replacement instructions.
Instruction[] replacementInstructions = new Instruction[4];
if (DEBUG) System.out.println(" Replacing instruction "+simpleInstruction.toString(offset)+" by");
int count = 0;
while (newOpcodes != 0)
{
SimpleInstruction replacementInstruction = new SimpleInstruction((byte)newOpcodes);
replacementInstructions[count++] = replacementInstruction;
if (DEBUG) System.out.println(" "+replacementInstruction.toString());
newOpcodes >>>= 8;
}
// Create a properly sized array.
if (count < 4)
{
Instruction[] newInstructions = new Instruction[count];
System.arraycopy(replacementInstructions, 0, newInstructions, 0, count);
replacementInstructions = newInstructions;
}
codeAttributeEditor.replaceInstruction(offset,
replacementInstructions);
}
}
else
{
visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
}
}
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
{
if (instructionUsageMarker.isInstructionNecessary(offset))
{
if (branchInstruction.stackPopCount(clazz) > 0 &&
!instructionUsageMarker.isStackEntryPresentBefore(offset, instructionUsageMarker.getStackBefore(offset).size() - 1))
{
// Replace the branch instruction by a simple goto.
Instruction replacementInstruction = new BranchInstruction(InstructionConstants.OP_GOTO,
branchInstruction.branchOffset);
codeAttributeEditor.replaceInstruction(offset,
replacementInstruction);
if (DEBUG) System.out.println(" Replacing branch instruction "+branchInstruction.toString(offset)+" by "+replacementInstruction.toString());
}
}
else
{
visitAnyInstruction(clazz, method, codeAttribute, offset, branchInstruction);
}
}
public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
{
if (instructionUsageMarker.isInstructionNecessary(offset))
{
if (switchInstruction.stackPopCount(clazz) > 0 &&
!instructionUsageMarker.isStackEntryPresentBefore(offset, instructionUsageMarker.getStackBefore(offset).size() - 1))
{
// Replace the switch instruction by a simple goto.
Instruction replacementInstruction = new BranchInstruction(InstructionConstants.OP_GOTO,
switchInstruction.defaultOffset);
codeAttributeEditor.replaceInstruction(offset,
replacementInstruction);
if (DEBUG) System.out.println(" Replacing switch instruction "+switchInstruction.toString(offset)+" by "+replacementInstruction.toString());
}
}
else
{
visitAnyInstruction(clazz, method, codeAttribute, offset, switchInstruction);
}
}
/**
* 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 a dup/swap opcode that is corrected for the stack entries
* that are present before the instruction and necessary after the
* instruction. The returned integer opcode may contain multiple byte
* opcodes (least significant byte first).
* @param instructionOffset the offset of the dup/swap instruction.
* @param dupSwapOpcode the original dup/swap opcode.
* @param topBefore the index of the top stack entry before
* the instruction (counting from the bottom).
* @param topAfter the index of the top stack entry after
* the instruction (counting from the bottom).
* @return the corrected opcode.
*/
private int fixDupSwap(int instructionOffset,
byte dupSwapOpcode,
int topBefore,
int topAfter)
{
switch (dupSwapOpcode)
{
case InstructionConstants.OP_DUP: return fixedDup (instructionOffset, topBefore, topAfter);
case InstructionConstants.OP_DUP_X1: return fixedDup_x1 (instructionOffset, topBefore, topAfter);
case InstructionConstants.OP_DUP_X2: return fixedDup_x2 (instructionOffset, topBefore, topAfter);
case InstructionConstants.OP_DUP2: return fixedDup2 (instructionOffset, topBefore, topAfter);
case InstructionConstants.OP_DUP2_X1: return fixedDup2_x1(instructionOffset, topBefore, topAfter);
case InstructionConstants.OP_DUP2_X2: return fixedDup2_x2(instructionOffset, topBefore, topAfter);
case InstructionConstants.OP_SWAP: return fixedSwap (instructionOffset, topBefore, topAfter);
default: throw new IllegalArgumentException("Not a dup/swap opcode ["+dupSwapOpcode+"]");
}
}
private int fixedDup(int instructionOffset, int topBefore, int topAfter)
{
boolean stackEntryPresent0 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore);
boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter);
boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1);
// Figure out which stack entries should be moved,
// copied, or removed.
return
stackEntryNecessary0 ?
stackEntryNecessary1 ? DUP : // ...O -> ...OO
NOP : // ...O -> ...O
stackEntryNecessary1 ? NOP : // ...O -> ...O
stackEntryPresent0 ? POP : // ...O -> ...
NOP; // ... -> ...
}
private int fixedDup_x1(int instructionOffset, int topBefore, int topAfter)
{
boolean stackEntryPresent0 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore);
boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 1);
boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter);
boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1);
boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 2);
// Figure out which stack entries should be moved,
// copied, or removed.
return
stackEntryNecessary1 ?
stackEntryNecessary2 ?
stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO
SWAP : // ...XO -> ...OX
// !stackEntryNecessary2
stackEntryNecessary0 ? NOP : // ...XO -> ...XO
stackEntryPresent0 ? POP : // ...XO -> ...X
NOP : // ...X -> ...X
stackEntryPresent1 ?
stackEntryNecessary2 ?
stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO
POP_X1 : // ...XO -> ...O
// !stackEntryNecessary2
stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O
stackEntryPresent0 ? POP2 : // ...XO -> ...
POP : // ...X -> ...
// !stackEntryPresent1
stackEntryNecessary2 ?
stackEntryNecessary0 ? DUP : // ...O -> ...OO
NOP : // ...O -> ...O
// !stackEntryNecessary2
stackEntryNecessary0 ? NOP : // ...O -> ...O
stackEntryPresent0 ? POP : // ...O -> ...
NOP; // ... -> ...
}
private int fixedDup_x2(int instructionOffset, int topBefore, int topAfter)
{
boolean stackEntryPresent0 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore);
boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 1);
boolean stackEntryPresent2 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 2);
boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter);
boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1);
boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 2);
boolean stackEntryNecessary3 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 3);
// Figure out which stack entries should be moved,
// copied, or removed.
return
stackEntryNecessary1 ?
stackEntryNecessary2 ?
stackEntryNecessary3 ?
stackEntryNecessary0 ? DUP_X2 : // ...XYO -> ...OXYO
MOV_X2 : // ...XYO -> ...OXY
// !stackEntryNecessary3
stackEntryNecessary0 ? NOP : // ...XYO -> ...XYO
stackEntryPresent0 ? POP : // ...XYO -> ...XY
NOP : // ...XY -> ...XY
stackEntryPresent2 ?
stackEntryNecessary3 ?
// stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OYO
UNSUPPORTED : // ...XYO -> ...OY
// !stackEntryNecessary3
stackEntryNecessary0 ? POP_X2 : // ...XYO -> ...YO
stackEntryPresent0 ? POP_SWAP_POP : // ...XYO -> ...Y
POP_X1 : // ...XY -> ...Y
// !stackEntryPresent2
stackEntryNecessary3 ?
stackEntryNecessary0 ? DUP_X1 : // ...YO -> ...OYO
SWAP : // ...YO -> ...OY
// !stackEntryNecessary3
stackEntryNecessary0 ? NOP : // ...YO -> ...YO
stackEntryPresent0 ? POP : // ...YO -> ...Y
NOP : // ...Y -> ...Y
stackEntryPresent1 ?
stackEntryNecessary2 ?
stackEntryNecessary3 ?
stackEntryNecessary0 ? SWAP_POP_DUP_X1 : // ...XYO -> ...OXO
DUP_X2_POP2 : // ...XYO -> ...OX
// !stackEntryNecessary3
stackEntryNecessary0 ? POP_X1 : // ...XYO -> ...XO
stackEntryPresent0 ? POP2 : // ...XYO -> ...X
POP : // ...XY -> ...X
stackEntryPresent2 ?
stackEntryNecessary3 ?
stackEntryNecessary0 ? UNSUPPORTED : // ...XYO -> ...OO
POP2_X1 : // ...XYO -> ...O
// !stackEntryNecessary3
stackEntryNecessary0 ? POP2_X1 : // ...XYO -> ...O
stackEntryPresent0 ? POP3 : // ...XYO -> ...
POP2 : // ...XY -> ...
// !stackEntryPresent2
stackEntryNecessary3 ?
stackEntryNecessary0 ? SWAP_POP_DUP : // ...YO -> ...OO
POP_X1 : // ...YO -> ...O
// !stackEntryNecessary3
stackEntryNecessary0 ? POP_X1 : // ...YO -> ...O
stackEntryPresent0 ? POP2 : // ...YO -> ...
POP : // ...Y -> ...
// !stackEntryPresent1
stackEntryNecessary2 ?
stackEntryNecessary3 ?
stackEntryNecessary0 ? DUP_X1 : // ...XO -> ...OXO
SWAP : // ...XO -> ...OX
// !stackEntryNecessary3
stackEntryNecessary0 ? NOP : // ...XO -> ...XO
stackEntryPresent0 ? POP : // ...XO -> ...X
NOP : // ...X -> ...X
stackEntryPresent2 ?
stackEntryNecessary3 ?
stackEntryNecessary0 ? SWAP_POP_DUP : // ...XO -> ...OO
POP_X1 : // ...XO -> ...O
// !stackEntryNecessary3
stackEntryNecessary0 ? POP_X1 : // ...XO -> ...O
stackEntryPresent0 ? POP2 : // ...XO -> ...
POP : // ...X -> ...
// !stackEntryPresent2
stackEntryNecessary3 ?
stackEntryNecessary0 ? DUP : // ...O -> ...OO
NOP : // ...O -> ...O
// !stackEntryNecessary3
stackEntryNecessary0 ? NOP : // ...O -> ...O
stackEntryPresent0 ? POP : // ...O -> ...
NOP; // ... -> ...
}
private int fixedDup2(int instructionOffset, int topBefore, int topAfter)
{
boolean stackEntryPresent0 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore);
boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset,
topBefore -
1);
boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter);
boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1);
boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 2);
boolean stackEntryNecessary3 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 3);
return
stackEntryNecessary3 ?
stackEntryNecessary2 ?
stackEntryNecessary1 ?
stackEntryNecessary0 ? DUP2 : // ...AB -> ...ABAB
SWAP_DUP_X1 : // ...AB -> ...ABA
// !stackEntryNecessary1
stackEntryNecessary0 ? DUP : // ...AB -> ...ABB
NOP : // ...AB -> ...AB
// !stackEntryNecessary2
stackEntryNecessary1 ?
stackEntryNecessary0 ? SWAP_DUP_X1_SWAP : // ...AB -> ...AAB
stackEntryPresent0 ? POP_DUP : // ...AB -> ...AA
DUP : // ...A -> ...AA
// !stackEntryNecessary1
stackEntryNecessary0 ? NOP : // ...AB -> ...AB
stackEntryPresent0 ? POP : // ...AB -> ...A
NOP : // ...A -> ...A
// !stackEntryNecessary3
stackEntryNecessary2 ?
stackEntryNecessary1 ?
stackEntryNecessary0 ? DUP_X1 : // ...AB -> ...BAB
SWAP : // ...AB -> ...BA
stackEntryPresent1 ?
stackEntryNecessary0 ? SWAP_POP_DUP : // ...AB -> ...BB
POP_X1 : // ...AB -> ...B
// !stackEntryPresent1
stackEntryNecessary0 ? POP : // ...B -> ...BB
NOP : // ...B -> ...B
// !stackEntryNecessary2
stackEntryNecessary1 ?
stackEntryNecessary0 ? NOP : // ...AB -> ...AB
stackEntryPresent0 ? POP : // ...AB -> ...A
NOP : // ...A -> ...A
stackEntryPresent1 ?
stackEntryNecessary0 ? POP_X1 : // ...AB -> ...B
stackEntryPresent0 ? POP2 : // ...AB -> ...
POP : // ...A -> ...
// !stackEntryPresent1
stackEntryNecessary0 ? NOP : // ...B -> ...B
stackEntryPresent0 ? POP : // ...B -> ...
NOP; // ... -> ...
}
private int fixedDup2_x1(int instructionOffset, int topBefore, int topAfter)
{
// We're currently assuming the value to be duplicated
// is a long or a double, taking up two slots, or at
// least consistent.
boolean stackEntriesPresent01 = instructionUsageMarker.isStackEntriesPresentBefore(instructionOffset, topBefore, topBefore - 1);
boolean stackEntryPresent2 = instructionUsageMarker.isStackEntryPresentBefore( instructionOffset, topBefore - 2);
boolean stackEntriesNecessary01 = instructionUsageMarker.isStackEntriesNecessaryAfter(instructionOffset, topAfter, topAfter - 1);
boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter( instructionOffset, topAfter - 2);
boolean stackEntriesNecessary34 = instructionUsageMarker.isStackEntriesNecessaryAfter(instructionOffset, topAfter - 3, topAfter - 4);
// Figure out which stack entries should be moved,
// copied, or removed.
return
stackEntryNecessary2 ?
stackEntriesNecessary34 ?
stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB
MOV2_X1 : // ...XAB -> ...ABX
// !stackEntriesNecessary34
stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB
stackEntriesPresent01 ? POP2 : // ...XAB -> ...X
NOP : // ...X -> ...X
stackEntryPresent2 ?
stackEntriesNecessary34 ?
stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB
POP_X2 : // ...XAB -> ...AB
// !stackEntriesNecessary34
stackEntriesNecessary01 ? DUP2_X1_POP3 : // ...XAB -> ...AB
stackEntriesPresent01 ? POP3 : // ...XAB -> ...
POP : // ...X -> ...
// !stackEntryPresent2
stackEntriesNecessary34 ?
stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB
NOP : // ...AB -> ...AB
// !stackEntriesNecessary34
stackEntriesNecessary01 ? NOP : // ...AB -> ...AB
stackEntriesPresent01 ? POP2 : // ...AB -> ...
NOP; // ... -> ...
}
private int fixedDup2_x2(int instructionOffset, int topBefore, int topAfter)
{
// We're currently assuming the value to be duplicated
// is a long or a double, taking up two slots, or at
// least consistent.
boolean stackEntriesPresent01 = instructionUsageMarker.isStackEntriesPresentBefore(instructionOffset, topBefore, topBefore - 1);
boolean stackEntryPresent2 = instructionUsageMarker.isStackEntryPresentBefore( instructionOffset, topBefore - 2);
boolean stackEntryPresent3 = instructionUsageMarker.isStackEntryPresentBefore( instructionOffset, topBefore - 3);
boolean stackEntriesNecessary01 = instructionUsageMarker.isStackEntriesNecessaryAfter(instructionOffset, topAfter, topAfter - 1);
boolean stackEntryNecessary2 = instructionUsageMarker.isStackEntryNecessaryAfter( instructionOffset, topAfter - 2);
boolean stackEntryNecessary3 = instructionUsageMarker.isStackEntryNecessaryAfter( instructionOffset, topAfter - 3);
boolean stackEntriesNecessary45 = instructionUsageMarker.isStackEntriesNecessaryAfter(instructionOffset, topAfter - 4, topAfter - 5);
// Figure out which stack entries should be moved,
// copied, or removed.
return
stackEntryNecessary2 ?
stackEntryNecessary3 ?
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? DUP2_X2 : // ...XYAB -> ...ABXYAB
MOV2_X2 : // ...XYAB -> ...ABXY
// !stackEntriesNecessary45
stackEntriesNecessary01 ? NOP : // ...XYAB -> ...XYAB
stackEntriesPresent01 ? POP2 : // ...XYAB -> ...XY
NOP : // ...XY -> ...XY
stackEntryPresent3 ?
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABYAB
DUP2_X2_SWAP_POP : // ...XYAB -> ...ABY
// !stackEntriesNecessary45
stackEntriesNecessary01 ? POP_X3 : // ...XYAB -> ...YAB
stackEntriesPresent01 ? POP2_SWAP_POP : // ...XYAB -> ...Y
POP_X1 : // ...XY -> ...Y
// !stackEntryPresent3
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? DUP2_X1 : // ...YAB -> ...ABYAB
MOV2_X1 : // ...YAB -> ...ABY
// !stackEntriesNecessary45
stackEntriesNecessary01 ? NOP : // ...YAB -> ...YAB
stackEntriesPresent01 ? POP2 : // ...YAB -> ...Y
NOP : // ...Y -> ...Y
stackEntryPresent2 ?
stackEntryNecessary3 ?
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABXAB
DUP2_X2_POP3 : // ...XYAB -> ...ABX
// !stackEntriesNecessary45
stackEntriesNecessary01 ? POP_X2 : // ...XYAB -> ...XAB
stackEntriesPresent01 ? POP3 : // ...XYAB -> ...X
POP : // ...XY -> ...X
stackEntryPresent3 ?
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? UNSUPPORTED : // ...XYAB -> ...ABAB
POP2_X2 : // ...XYAB -> ...AB
// !stackEntriesNecessary45
stackEntriesNecessary01 ? POP2_X2 : // ...XYAB -> ...AB
stackEntriesPresent01 ? POP4 : // ...XYAB -> ...
POP2 : // ...XY -> ...
// !stackEntryPresent3
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? UNSUPPORTED : // ...YAB -> ...ABAB
POP_X2 : // ...YAB -> ...AB
// !stackEntriesNecessary45
stackEntriesNecessary01 ? POP_X2 : // ...YAB -> ...AB
stackEntriesPresent01 ? POP3 : // ...YAB -> ...
POP : // ...Y -> ...
// !stackEntryPresent2
stackEntryNecessary3 ?
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? DUP2_X1 : // ...XAB -> ...ABXAB
MOV2_X1 : // ...XAB -> ...ABX
// !stackEntriesNecessary45
stackEntriesNecessary01 ? NOP : // ...XAB -> ...XAB
stackEntriesPresent01 ? POP2 : // ...XAB -> ...X
NOP : // ...X -> ...X
stackEntryPresent3 ?
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? UNSUPPORTED : // ...XAB -> ...ABAB
POP_X2 : // ...XAB -> ...AB
// !stackEntriesNecessary45
stackEntriesNecessary01 ? POP_X2 : // ...XAB -> ...AB
stackEntriesPresent01 ? POP3 : // ...XAB -> ...
POP : // ...X -> ...
// !stackEntryPresent3
stackEntriesNecessary45 ?
stackEntriesNecessary01 ? DUP2 : // ...AB -> ...ABAB
NOP : // ...AB -> ...AB
// !stackEntriesNecessary45
stackEntriesNecessary01 ? NOP : // ...AB -> ...AB
stackEntriesPresent01 ? POP2 : // ...AB -> ...
NOP; // ... -> ...
}
private int fixedSwap(int instructionOffset, int topBefore, int topAfter)
{
boolean stackEntryPresent0 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore);
boolean stackEntryPresent1 = instructionUsageMarker.isStackEntryPresentBefore(instructionOffset, topBefore - 1);
boolean stackEntryNecessary0 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter);
boolean stackEntryNecessary1 = instructionUsageMarker.isStackEntryNecessaryAfter(instructionOffset, topAfter - 1);
// Figure out which stack entries should be moved
// or removed.
return
stackEntryNecessary0 ?
stackEntryNecessary1 ? SWAP : // ...AB -> ...BA
stackEntryPresent0 ? POP : // ...AB -> ...A
NOP : // ...A -> ...A
stackEntryPresent1 ? POP_X1 : // ...AB -> ...B
NOP; // ...B -> ...B
}
}
/**
* This InstructionVisitor deletes all visited instructions.
*/
private class MyInstructionDeleter
extends SimplifiedVisitor
implements InstructionVisitor
{
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
{
codeAttributeEditor.deleteInstruction(offset);
// We're allowing edits on deleted instructions.
//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);
}
}
}
// Implementations for ExceptionInfoVisitor.
public void visitExceptionInfo(Clazz clazz, Method method, CodeAttribute codeAttribute, ExceptionInfo exceptionInfo)
{
// Is the catch handler necessary?
if (!instructionUsageMarker.isTraced(exceptionInfo.u2handlerPC))
{
// Make the code block empty, so the code editor can remove it.
exceptionInfo.u2endPC = exceptionInfo.u2startPC;
}
}
// Small utility methods.
/**
* 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 (instructionUsageMarker.isTraced(offset) &&
!instructionUsageMarker.isInstructionNecessary(offset) &&
isAnyLargerThan(instructionUsageMarker.branchTargets(offset),
instructionOffset2))
{
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;
}
/**
* Pushes a specified type of stack entry before or at the given offset.
* The instruction is marked as necessary.
*/
private void insertPushInstructions(int offset,
boolean replace,
boolean before,
int computationalType)
{
// We can edit an instruction without marking it.
//markInstruction(offset);
// Create a simple push instrucion.
Instruction replacementInstruction =
new SimpleInstruction(pushOpcode(computationalType));
if (DEBUG) System.out.println(": "+replacementInstruction.toString(offset));
// Replace or insert the push instruction.
insertInstruction(offset, replace, before, replacementInstruction);
}
/**
* Returns the opcode of a push instruction corresponding to the given
* computational type.
* @param computationalType the computational type to be pushed on the stack.
*/
private byte pushOpcode(int computationalType)
{
switch (computationalType)
{
case Value.TYPE_INTEGER: return InstructionConstants.OP_ICONST_0;
case Value.TYPE_LONG: return InstructionConstants.OP_LCONST_0;
case Value.TYPE_FLOAT: return InstructionConstants.OP_FCONST_0;
case Value.TYPE_DOUBLE: return InstructionConstants.OP_DCONST_0;
case Value.TYPE_REFERENCE:
case Value.TYPE_INSTRUCTION_OFFSET: return InstructionConstants.OP_ACONST_NULL;
}
throw new IllegalArgumentException("No push opcode for computational type ["+computationalType+"]");
}
/**
* Pops the given number of stack entries at or after the given offset.
* The instructions are marked as necessary.
*/
private void insertPopInstructions(int offset,
boolean replace,
boolean before,
int popCount)
{
// We can edit an instruction without marking it.
//markInstruction(offset);
switch (popCount)
{
case 1:
{
// Replace or insert a single pop instruction.
Instruction popInstruction =
new SimpleInstruction(InstructionConstants.OP_POP);
insertInstruction(offset, replace, before, popInstruction);
break;
}
case 2:
{
// Replace or insert a single pop2 instruction.
Instruction popInstruction =
new SimpleInstruction(InstructionConstants.OP_POP2);
insertInstruction(offset, replace, before, popInstruction);
break;
}
default:
{
// Replace or insert the specified number of pop instructions.
Instruction[] popInstructions =
new Instruction[popCount / 2 + popCount % 2];
Instruction popInstruction =
new SimpleInstruction(InstructionConstants.OP_POP2);
for (int index = 0; index < popCount / 2; index++)
{
popInstructions[index] = popInstruction;
}
if (popCount % 2 == 1)
{
popInstruction =
new SimpleInstruction(InstructionConstants.OP_POP);
popInstructions[popCount / 2] = popInstruction;
}
insertInstructions(offset,
replace,
before,
popInstruction,
popInstructions);
break;
}
}
}
/**
* Inserts or replaces the given instruction at the given offset.
*/
private void insertInstruction(int offset,
boolean replace,
boolean before,
Instruction instruction)
{
if (replace)
{
codeAttributeEditor.replaceInstruction(offset, instruction);
if (extraAddedInstructionVisitor != null &&
!instructionUsageMarker.isInstructionNecessary(offset))
{
instruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
}
}
else
{
if (before)
{
codeAttributeEditor.insertBeforeInstruction(offset, instruction);
}
else
{
codeAttributeEditor.insertAfterInstruction(offset, instruction);
}
if (extraAddedInstructionVisitor != null)
{
instruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
}
}
}
/**
* Inserts or replaces the given instruction at the given offset.
*/
private void insertInstructions(int offset,
boolean replace,
boolean before,
Instruction instruction,
Instruction[] instructions)
{
if (replace)
{
codeAttributeEditor.replaceInstruction(offset, instructions);
if (extraAddedInstructionVisitor != null)
{
if (!instructionUsageMarker.isInstructionNecessary(offset))
{
instruction.accept(null, null, null, offset, extraAddedInstructionVisitor);
}
for (int index = 1; index < instructions.length; index++)
{
instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor);
}
}
}
else
{
if (before)
{
codeAttributeEditor.insertBeforeInstruction(offset, instructions);
}
else
{
codeAttributeEditor.insertAfterInstruction(offset, instructions);
}
for (int index = 0; index < instructions.length; index++)
{
if (extraAddedInstructionVisitor != null)
{
instructions[index].accept(null, null, null, offset, extraAddedInstructionVisitor);
}
}
}
}
/**
* Replaces the instruction at a given offset by a static invocation.
*/
private void replaceByStaticInvocation(Clazz clazz,
int offset,
ConstantInstruction constantInstruction)
{
// Remember the replacement instruction.
Instruction replacementInstruction =
new ConstantInstruction(InstructionConstants.OP_INVOKESTATIC,
constantInstruction.constantIndex);
if (DEBUG) System.out.println(" Replacing by static invocation "+constantInstruction.toString(offset)+" -> "+replacementInstruction.toString());
codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
}
/**
* Replaces the given instruction by an infinite loop.
*/
private void replaceByInfiniteLoop(Clazz clazz,
int offset)
{
if (DEBUG) System.out.println(" Inserting infinite loop at ["+offset+"]");
// We can edit an instruction without marking it.
//markInstruction(offset);
// Replace the instruction by an infinite loop.
Instruction replacementInstruction =
new BranchInstruction(InstructionConstants.OP_GOTO, 0);
codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy