proguard.optimize.evaluation.InstructionUsageMarker Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of proguard-base Show documentation
Show all versions of proguard-base Show documentation
ProGuard is a free shrinker, optimizer, obfuscator, and preverifier for Java bytecode
/*
* ProGuard -- shrinking, optimization, obfuscation, and preverification
* of Java bytecode.
*
* Copyright (c) 2002-2021 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 org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import proguard.classfile.*;
import proguard.classfile.attribute.*;
import proguard.classfile.attribute.visitor.AttributeVisitor;
import proguard.classfile.constant.*;
import proguard.classfile.constant.visitor.ConstantVisitor;
import proguard.classfile.editor.ClassEstimates;
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.*;
import proguard.util.ArrayUtil;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
/**
* This AttributeVisitor marks necessary instructions in the code attributes
* that it visits, based on partial evaluation.
*
* @see NoSideEffectClassMarker
* @see SideEffectClassMarker
* @see ReadWriteFieldMarker
* @see NoSideEffectMethodMarker
* @see NoExternalSideEffectMethodMarker
* @see SideEffectMethodMarker
* @see ParameterEscapeMarker
*
* @author Eric Lafortune
*/
public class InstructionUsageMarker
implements AttributeVisitor
{
private static final Logger logger = LogManager.getLogger(InstructionUsageMarker.class);
private final PartialEvaluator partialEvaluator;
private final boolean runPartialEvaluator;
private final boolean ensureSafetyForVerifier;
private final boolean markExternalSideEffects;
private final PartialEvaluator simplePartialEvaluator;
private final SideEffectInstructionChecker sideEffectInstructionChecker;
private final MyParameterUsageMarker parameterUsageMarker = new MyParameterUsageMarker();
private final MyInitialUsageMarker initialUsageMarker = new MyInitialUsageMarker();
private final MyProducerMarker producerMarker = new MyProducerMarker();
private final MyVariableInitializationMarker variableInitializationMarker = new MyVariableInitializationMarker();
private final MyStackConsistencyMarker stackConsistencyMarker = new MyStackConsistencyMarker();
private final MyExtraPushPopInstructionMarker extraPushPopInstructionMarker = new MyExtraPushPopInstructionMarker();
private InstructionOffsetValue[] reverseDependencies = new InstructionOffsetValue[ClassEstimates.TYPICAL_CODE_LENGTH];
private boolean[][] stacksNecessaryAfter = new boolean[ClassEstimates.TYPICAL_CODE_LENGTH][ClassEstimates.TYPICAL_STACK_SIZE];
private boolean[][] stacksUnwantedBefore = new boolean[ClassEstimates.TYPICAL_CODE_LENGTH][ClassEstimates.TYPICAL_STACK_SIZE];
private boolean[] instructionsNecessary = new boolean[ClassEstimates.TYPICAL_CODE_LENGTH];
private boolean[] extraPushPopInstructionsNecessary = new boolean[ClassEstimates.TYPICAL_CODE_LENGTH];
private int maxMarkedOffset;
/**
* Creates a new InstructionUsageMarker.
*
* @param markExternalSideEffects specifies whether instructions with external side effects should be marked
*/
public InstructionUsageMarker(boolean markExternalSideEffects)
{
this(new PartialEvaluator(), true, true, markExternalSideEffects);
}
/**
* Creates a new InstructionUsageMarker.
* @param partialEvaluator the evaluator to be used for the analysis.
* @param runPartialEvaluator specifies whether to run this evaluator on
* every code attribute that is visited.
* @param markExternalSideEffects specifies whether instructions with external side effects should be marked
*/
public InstructionUsageMarker(PartialEvaluator partialEvaluator,
boolean runPartialEvaluator,
boolean markExternalSideEffects)
{
this(partialEvaluator, runPartialEvaluator, true, markExternalSideEffects);
}
/**
* Creates a new InstructionUsageMarker.
* @param partialEvaluator the evaluator to be used for the analysis.
* @param runPartialEvaluator specifies whether to run this evaluator on
* every code attribute that is visited.
* @param ensureSafetyForVerifier some instructions are not necessary for
* code correctness but are necessary for the
* java verifier. This flag determines whether
* these instructions are visited (default: true).
* @param markExternalSideEffects specifies whether instructions with external side effects should be marked
*/
public InstructionUsageMarker(PartialEvaluator partialEvaluator,
boolean runPartialEvaluator,
boolean ensureSafetyForVerifier,
boolean markExternalSideEffects)
{
this.partialEvaluator = partialEvaluator;
this.runPartialEvaluator = runPartialEvaluator;
this.ensureSafetyForVerifier = ensureSafetyForVerifier;
this.markExternalSideEffects = markExternalSideEffects;
this.sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true, markExternalSideEffects);
if (ensureSafetyForVerifier)
{
this.simplePartialEvaluator = new PartialEvaluator(new TypedReferenceValueFactory());
}
else
{
this.simplePartialEvaluator = partialEvaluator;
}
}
/**
* Returns whether the specified instruction was traced in the most
* recently analyzed code attribute.
*/
public boolean isTraced(int instructionOffset)
{
return partialEvaluator.isTraced(instructionOffset);
}
/**
* Returns a filtering version of the given instruction visitor that only
* visits traced instructions.
*/
public InstructionVisitor tracedInstructionFilter(InstructionVisitor instructionVisitor)
{
return partialEvaluator.tracedInstructionFilter(instructionVisitor);
}
/**
* Returns a filtering version of the given instruction visitor that only
* visits traced or untraced instructions.
*/
public InstructionVisitor tracedInstructionFilter(boolean traced,
InstructionVisitor instructionVisitor)
{
return partialEvaluator.tracedInstructionFilter(traced, instructionVisitor);
}
/**
* Returns whether the specified instruction is necessary in the most
* recently analyzed code attribute.
*/
public boolean isInstructionNecessary(int instructionOffset)
{
return instructionsNecessary[instructionOffset];
}
/**
* Returns whether an extra push/pop instruction is required at the given
* offset in the most recently analyzed code attribute.
*/
public boolean isExtraPushPopInstructionNecessary(int instructionOffset)
{
return extraPushPopInstructionsNecessary[instructionOffset];
}
/**
* Returns a filtering version of the given instruction visitor that only
* visits necessary instructions.
*/
public InstructionVisitor necessaryInstructionFilter(InstructionVisitor instructionVisitor)
{
return necessaryInstructionFilter(true, instructionVisitor);
}
/**
* Returns a filtering version of the given instruction visitor that only
* visits necessary or unnecessary instructions.
*/
public InstructionVisitor necessaryInstructionFilter(boolean necessary,
InstructionVisitor instructionVisitor)
{
return new MyNecessaryInstructionFilter(necessary, instructionVisitor);
}
/**
* Returns the stack before execution of the instruction at the given
* offset.
*/
public TracedStack getStackBefore(int instructionOffset)
{
return partialEvaluator.getStackBefore(instructionOffset);
}
/**
* Returns the stack after execution of the instruction at the given
* offset.
*/
public TracedStack getStackAfter(int instructionOffset)
{
return partialEvaluator.getStackAfter(instructionOffset);
}
/**
* Returns whether the specified stack entry before the given offset is
* unwanted, e.g. because it was intended as a method parameter that has
* been removed.
*/
public boolean isStackEntryUnwantedBefore(int instructionOffset,
int stackIndex)
{
return stacksUnwantedBefore[instructionOffset][stackIndex];
}
/**
* Returns whether the stack specified entries before the given offset are
* present.
*/
public boolean isStackEntriesPresentBefore(int instructionOffset,
int stackIndex1,
int stackIndex2)
{
boolean present1 = isStackEntryPresentBefore(instructionOffset, stackIndex1);
boolean present2 = isStackEntryPresentBefore(instructionOffset, stackIndex2);
//if (present1 ^ present2)
//{
// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions");
//}
return present1 || present2;
}
/**
* Returns whether the specified stack entry before the given offset is
* present.
* @param instructionOffset the offset of the stack entry to be checked.
* @param stackIndex the index of the stack entry to be checked
* (counting from the bottom).
*/
public boolean isStackEntryPresentBefore(int instructionOffset,
int stackIndex)
{
TracedStack tracedStack =
partialEvaluator.getStackBefore(instructionOffset);
InstructionOffsetValue producerOffsets =
tracedStack.getBottomProducerValue(stackIndex).instructionOffsetValue();
return isAnyStackEntryNecessaryAfter(producerOffsets, stackIndex);
}
/**
* Returns whether the stack specified entries after the given offset are
* necessary.
*/
public boolean isStackEntriesNecessaryAfter(int instructionOffset,
int stackIndex1,
int stackIndex2)
{
boolean present1 = isStackEntryNecessaryAfter(instructionOffset, stackIndex1);
boolean present2 = isStackEntryNecessaryAfter(instructionOffset, stackIndex2);
//if (present1 ^ present2)
//{
// throw new UnsupportedOperationException("Can't handle partial use of dup2 instructions");
//}
return present1 || present2;
}
/**
* Returns whether any of the stack entries after the given offsets are
* necessary.
* @param instructionOffsets the offsets of the stack entries to be checked.
* @param stackIndex the index of the stack entries to be checked
* (counting from the bottom).
*/
public boolean isAnyStackEntryNecessaryAfter(InstructionOffsetValue instructionOffsets,
int stackIndex)
{
int offsetCount = instructionOffsets.instructionOffsetCount();
for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
if (instructionOffsets.isExceptionHandler(offsetIndex) ||
isStackEntryNecessaryAfter(instructionOffsets.instructionOffset(offsetIndex), stackIndex))
{
return true;
}
}
return false;
}
/**
* Returns whether the specified stack entry after the given offset is
* necessary.
* @param instructionOffset the offset of the stack entry to be checked.
* @param stackIndex the index of the stack entry to be checked
* (counting from the bottom).
*/
public boolean isStackEntryNecessaryAfter(int instructionOffset,
int stackIndex)
{
return
(instructionOffset & InstructionOffsetValue.EXCEPTION_HANDLER) != 0 ||
stacksNecessaryAfter[instructionOffset][stackIndex];
}
/**
* Returns the instruction offsets to which the given instruction offset
* branches in the most recently analyzed code attribute.
*/
public InstructionOffsetValue branchTargets(int instructionOffset)
{
return partialEvaluator.branchTargets(instructionOffset);
}
// 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 instruction usage marker has stabilized.
// Catch any unexpected exceptions from the actual visiting method.
try
{
// Process the code.
visitCodeAttribute0(clazz, method, codeAttribute);
}
catch (RuntimeException ex)
{
logger.error("Unexpected error while marking instruction usage after partial evaluation:");
logger.error(" Class = [{}]", clazz.getName());
logger.error(" Method = [{}{}]", method.getName(clazz), method.getDescriptor(clazz));
logger.error(" Exception = [{}] ({})", ex.getClass().getName(), ex.getMessage());
logger.debug("{}", () -> {
StringWriter sw = new StringWriter();
method.accept(clazz, new ClassPrinter(new PrintWriter(sw)));
return sw.toString();
});
throw ex;
}
}
public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
{
logger.debug("InstructionUsageMarker [{}.{}{}]", clazz.getName(), method.getName(clazz), method.getDescriptor(clazz));
// Initialize the necessary arrays.
initializeNecessary(codeAttribute);
// Evaluate the method.
if (runPartialEvaluator)
{
partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
}
if (ensureSafetyForVerifier)
{
// Evaluate the method the way the JVM verifier would do it.
simplePartialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
}
int codeLength = codeAttribute.u4codeLength;
maxMarkedOffset = -1;
// Mark any unused method parameters on the stack.
logger.debug("Invocation simplification:");
codeAttribute.instructionsAccept(clazz, method,
partialEvaluator.tracedInstructionFilter(parameterUsageMarker));
// Mark all essential instructions that have been encountered as used.
// Also mark infinite loops and instructions that can have side effects.
logger.debug("Usage initialization: ");
codeAttribute.instructionsAccept(clazz, method,
partialEvaluator.tracedInstructionFilter(initialUsageMarker));
// 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.
logger.debug("Usage marking:");
while (maxMarkedOffset >= 0)
{
int offset = maxMarkedOffset;
maxMarkedOffset = offset - 1;
if (partialEvaluator.isTraced(offset))
{
if (isInstructionNecessary(offset))
{
// Mark the stack/variable producers of this instruction/
Instruction instruction = InstructionFactory.create(codeAttribute.code,
offset);
instruction.accept(clazz, method, codeAttribute, offset, producerMarker);
// Also mark any reverse dependencies.
markReverseDependencies(offset);
}
// 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 (maxMarkedOffset > offset)
{
logger.debug(" -> {}", maxMarkedOffset);
}
}
// 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.
logger.debug("Initialization marking: ");
codeAttribute.instructionsAccept(clazz, method,
necessaryInstructionFilter(
variableInitializationMarker));
// Mark produced stack entries, in order to keep the stack consistent.
logger.debug("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, stackConsistencyMarker);
// 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);
}
}
// Mark unnecessary popping instructions, in order to keep the stack
// consistent.
logger.debug("Extra pop marking:");
maxMarkedOffset = codeLength - 1;
while (maxMarkedOffset >= 0)
{
int offset = maxMarkedOffset;
maxMarkedOffset = offset - 1;
if (partialEvaluator.isTraced(offset) &&
!isInstructionNecessary(offset))
{
Instruction instruction = InstructionFactory.create(codeAttribute.code,
offset);
instruction.accept(clazz, method, codeAttribute, offset, extraPushPopInstructionMarker);
// 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);
}
}
logger.debug("Instruction usage results:");
if (logger.getLevel().isLessSpecificThan(Level.DEBUG))
{
int offset = 0;
do {
Instruction instruction = InstructionFactory.create(codeAttribute.code, offset);
logger.debug("{}{}",
(isInstructionNecessary(offset) ? " + " :
isExtraPushPopInstructionNecessary(offset) ? " ~ " :
" - "),
instruction.toString(offset));
offset += instruction.length(offset);
}
while (offset < codeLength);
}
}
/**
* This MemberVisitor marks stack entries that aren't necessary because
* parameters aren't used in the methods that are visited.
*/
private class MyParameterUsageMarker
implements InstructionVisitor,
ConstantVisitor,
MemberVisitor
{
private int parameterSize;
private long usedParameters;
// 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 Instruction.OP_INVOKEVIRTUAL:
case Instruction.OP_INVOKESPECIAL:
case Instruction.OP_INVOKESTATIC:
case Instruction.OP_INVOKEINTERFACE:
{
parameterSize = 0;
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
// Mark unused parameters.
for (int index = 0; index < parameterSize; index++)
{
if (index < 64 &&
(usedParameters & (1L << index)) == 0L)
{
TracedStack stack =
partialEvaluator.getStackBefore(offset);
int stackIndex = stack.size() - parameterSize + index;
logger.debug(" [{}] Ignoring parameter #{} (stack entry #{} [{}])",
offset,
index,
stackIndex,
stack.getBottom(stackIndex));
logger.debug(" Full stack: {}", stack);
markStackEntryUnwantedBefore(offset, stackIndex);
}
}
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 and the mask of the used
// parameters.
parameterSize = ParameterUsageMarker.getParameterSize(programMethod);
usedParameters = ParameterUsageMarker.getUsedParameters(programMethod);
}
}
/**
* This InstructionVisitor marks the instructions that are intrinsically
* necessary, because they have side effects.
*/
private class MyInitialUsageMarker
implements InstructionVisitor,
ConstantVisitor,
ParameterVisitor
{
private final MemberVisitor reverseDependencyCreator = new AllParameterVisitor(true, this);
// Parameters and values for visitor methods.
private int referencingOffset;
private int referencingPopCount;
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
{
if (sideEffectInstructionChecker.hasSideEffects(clazz,
method,
codeAttribute,
offset,
instruction))
{
markInstruction(offset);
}
}
public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
{
switch (simpleInstruction.opcode)
{
case Instruction.OP_IASTORE:
case Instruction.OP_LASTORE:
case Instruction.OP_FASTORE:
case Instruction.OP_DASTORE:
case Instruction.OP_AASTORE:
case Instruction.OP_BASTORE:
case Instruction.OP_CASTORE:
case Instruction.OP_SASTORE:
createReverseDependencies(clazz, offset, simpleInstruction);
// Also check for side-effects of the instruction itself.
visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
break;
default:
visitAnyInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
break;
}
}
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
switch (constantInstruction.opcode)
{
case Instruction.OP_ANEWARRAY:
case Instruction.OP_MULTIANEWARRAY:
// We may have to mark the instruction due to initializers.
referencingOffset = offset;
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
// Also check for side-effects of the instruction itself.
visitAnyInstruction(clazz, method, codeAttribute, offset, constantInstruction);
break;
case Instruction.OP_LDC:
case Instruction.OP_LDC_W:
case Instruction.OP_NEW:
case Instruction.OP_GETSTATIC:
// We may have to mark the instruction due to initializers.
referencingOffset = offset;
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
break;
case Instruction.OP_PUTFIELD:
// We generally have to mark the putfield instruction,
// unless it's never read. We can reverse the dependencies
// if it's a field of a recently created instance.
if (sideEffectInstructionChecker.hasSideEffects(clazz,
method,
codeAttribute,
offset,
constantInstruction))
{
createReverseDependencies(clazz, offset, constantInstruction);
}
break;
case Instruction.OP_INVOKEVIRTUAL:
case Instruction.OP_INVOKESPECIAL:
case Instruction.OP_INVOKESTATIC:
case Instruction.OP_INVOKEINTERFACE:
referencingOffset = offset;
referencingPopCount = constantInstruction.stackPopCount(clazz);
clazz.constantPoolEntryAccept(constantInstruction.constantIndex, this);
break;
default:
visitAnyInstruction(clazz, method, codeAttribute, offset, constantInstruction);
break;
}
}
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
{
if (branchInstruction.opcode == Instruction.OP_GOTO &&
branchInstruction.branchOffset == 0)
{
logger.debug("(infinite loop)");
markInstruction(offset);
}
else
{
visitAnyInstruction(clazz, method, codeAttribute, offset, branchInstruction);
}
}
// Implementations for ConstantVisitor.
public void visitAnyConstant(Clazz clazz, Constant constant) {}
public void visitStringConstant(Clazz clazz, StringConstant stringConstant)
{
Clazz referencedClass = stringConstant.referencedClass;
// If a static initializer may have side effects, the instruction
// has to be marked.
if (referencedClass != null &&
SideEffectClassChecker.mayHaveSideEffects(clazz,
referencedClass))
{
// Mark the invocation.
markInstruction(referencingOffset);
}
}
public void visitClassConstant(Clazz clazz, ClassConstant classConstant)
{
Clazz referencedClass = classConstant.referencedClass;
// If a static initializer may have side effects, the instruction
// has to be marked.
if (referencedClass == null ||
SideEffectClassChecker.mayHaveSideEffects(clazz,
referencedClass))
{
// Mark the invocation.
markInstruction(referencingOffset);
}
}
public void visitFieldrefConstant(Clazz clazz, FieldrefConstant fieldrefConstant)
{
clazz.constantPoolEntryAccept(fieldrefConstant.u2classIndex, this);
}
public void visitAnyMethodrefConstant(Clazz clazz, AnyMethodrefConstant anyMethodrefConstant)
{
Method referencedMethod = (Method)anyMethodrefConstant.referencedMethod;
// if (referencedMethod != null)
// {
// System.out.println("InstructionUsageMarker$MyInitialUsageMarker.visitAnyMethodrefConstant [" + anyMethodrefConstant.getClassName(clazz) + "." + anyMethodrefConstant.getName(clazz) +
// "]: mark! esc = " + ParameterEscapeMarker.getEscapingParameters(referencedMethod) +
// ", mod = " + ParameterEscapeMarker.modifiesAnything(referencedMethod) +
// ", side = " + SideEffectClassChecker.mayHaveSideEffects(clazz,
// anyMethodrefConstant.referencedClass,
// referencedMethod));
// }
// Is the method invocation really necessary?
if (markExternalSideEffects &&
referencedMethod != null &&
SideEffectMethodMarker.hasSideEffects(referencedMethod) &&
// Skip if the method was explicitly marked as having no external side-effects.
!NoExternalSideEffectMethodMarker.hasNoExternalSideEffects(referencedMethod))
{
// In case we shall optimize conservatively, always mark the method
// call if the referenced method has side effects.
markInstruction(referencingOffset);
}
else if (referencedMethod == null ||
ParameterEscapeMarker.getEscapingParameters(referencedMethod) != 0L ||
ParameterEscapeMarker.modifiesAnything(referencedMethod) ||
SideEffectClassChecker.mayHaveSideEffects(clazz,
anyMethodrefConstant.referencedClass,
referencedMethod))
{
// System.out.println(" -> mark ["+referencingOffset+"]");
// Mark the invocation.
markInstruction(referencingOffset);
}
else
{
logger.debug(" [{}] Checking parameters of [{}.{}{}] (pop count = {})",
referencingOffset,
anyMethodrefConstant.getClassName(clazz),
anyMethodrefConstant.getName(clazz),
anyMethodrefConstant.getType(clazz),
referencingPopCount
);
// Create reverse dependencies for reference parameters that
// are modified.
anyMethodrefConstant.referencedMethodAccept(reverseDependencyCreator);
}
}
// Implementations for ParameterVisitor.
public void visitParameter(Clazz clazz, Member member, int parameterIndex, int parameterCount, int parameterOffset, int parameterSize, String parameterType, Clazz referencedClass)
{
Method method = (Method)member;
logger.debug(" P{}: escaping = {}, modified = {}, returned = {}",
parameterIndex,
ParameterEscapeMarker.isParameterEscaping(method, parameterIndex),
ParameterEscapeMarker.isParameterModified(method, parameterIndex),
ParameterEscapeMarker.isParameterReturned(method, parameterIndex)
);
// Create a reverse dependency if the reference parameter is
// modified.
if (ParameterEscapeMarker.isParameterModified(method, parameterIndex))
{
createReverseDependencies(referencingOffset,
parameterSize - parameterOffset - 1);
}
}
/**
* Marks the specified instruction offset or creates reverse
* dependencies to the producers of its bottom popped stack entry.
*/
private void createReverseDependencies(Clazz clazz,
int offset,
Instruction instruction)
{
createReverseDependencies(offset,
instruction.stackPopCount(clazz) - 1);
}
/**
* Marks the specified instruction offset or creates reverse
* dependencies to the producers of the specified stack entry, if it
* is a reference value.
*/
private void createReverseDependencies(int offset,
int stackEntryIndex)
{
TracedStack stackBefore = partialEvaluator.getStackBefore(offset);
Value stackEntry = stackBefore.getTop(stackEntryIndex);
// System.out.println(" ["+offset+"] s"+stackEntryIndex+": ["+stackEntry+"]");
if (stackEntry.computationalType() == Value.TYPE_REFERENCE)
{
ReferenceValue referenceValue = stackEntry.referenceValue();
// System.out.println("EvaluationShrinker$MyInitialUsageMarker.createReverseDependencies: ["+offset+"] ["+referenceValue+"]?");
// The null reference value may not have a trace value.
if (referenceValue.isNull() != Value.ALWAYS)
{
if (referenceValue instanceof TracedReferenceValue)
{
TracedReferenceValue tracedReferenceValue =
(TracedReferenceValue)referenceValue;
createReverseDependencies(offset,
tracedReferenceValue.getTraceValue().instructionOffsetValue());
}
else
{
// System.out.println("InstructionUsageMarker$MyInitialUsageMarker.createReverseDependencies: not a TracedReferenceValue");
markInstruction(offset);
}
}
}
}
/**
* Marks the specified instruction offset or creates reverse
* dependencies to the producers of the given reference value.
*/
private void createReverseDependencies(int offset,
InstructionOffsetValue producerOffsets)
{
InstructionOffsetValue consumerOffset =
new InstructionOffsetValue(offset);
int offsetCount = producerOffsets.instructionOffsetCount();
for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
if (producerOffsets.isNewinstance(offsetIndex))
{
// Create a reverse dependency. If the creating instruction
// is necessary, then so is this one.
int producerOffset = producerOffsets.instructionOffset(offsetIndex);
// Avoid circular dependencies in code that loops with
// instances on the stack (like the string encryption code).
if (producerOffset != offset)
{
logger.debug(" Inserting reverse dependency from instance producers [{}] to [{}]", producerOffset, offset);
InstructionOffsetValue reverseDependency =
reverseDependencies[producerOffset];
reverseDependencies[producerOffset] =
reverseDependency == null ?
consumerOffset :
reverseDependency.generalize(consumerOffset);
}
}
else
{
// Just mark the instruction.
markInstruction(offset);
}
}
}
}
/**
* 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
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 Instruction.OP_DUP:
conditionallyMarkStackEntryProducers(offset, 0, 0);
conditionallyMarkStackEntryProducers(offset, 1, 0);
break;
case Instruction.OP_DUP_X1:
conditionallyMarkStackEntryProducers(offset, 0, 0);
conditionallyMarkStackEntryProducers(offset, 1, 1);
conditionallyMarkStackEntryProducers(offset, 2, 0);
break;
case Instruction.OP_DUP_X2:
conditionallyMarkStackEntryProducers(offset, 0, 0);
conditionallyMarkStackEntryProducers(offset, 1, 1);
conditionallyMarkStackEntryProducers(offset, 2, 2);
conditionallyMarkStackEntryProducers(offset, 3, 0);
break;
case Instruction.OP_DUP2:
conditionallyMarkStackEntryProducers(offset, 0, 0);
conditionallyMarkStackEntryProducers(offset, 1, 1);
conditionallyMarkStackEntryProducers(offset, 2, 0);
conditionallyMarkStackEntryProducers(offset, 3, 1);
break;
case Instruction.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 Instruction.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 Instruction.OP_SWAP:
conditionallyMarkStackEntryProducers(offset, 0, 1);
conditionallyMarkStackEntryProducers(offset, 1, 0);
break;
default:
markStackProducers(clazz, offset, simpleInstruction);
break;
}
}
public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
{
// Is the variable being loaded or incremented?
if (variableInstruction.isLoad())
{
markVariableProducers(offset, variableInstruction.variableIndex);
}
else
{
markStackProducers(clazz, offset, variableInstruction);
}
}
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
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 == Instruction.OP_JSR ||
branchInstruction.opcode == Instruction.OP_JSR_W)
{
markStackEntryAfter(offset, 0);
}
else
{
markStackProducers(clazz, offset, branchInstruction);
}
}
}
/**
* This InstructionVisitor marks variable initializations that are
* necessary to appease the JVM.
*/
private class MyVariableInitializationMarker
implements InstructionVisitor
{
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction) {}
public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
{
// Is the variable being loaded or incremented?
if (variableInstruction.isLoad())
{
// Mark any variable initializations for this variable load that
// are required according to the JVM.
markVariableInitializersBefore(offset, variableInstruction.variableIndex, null);
}
}
}
/**
* This InstructionVisitor marks stack entries that should be pushed
* (and previously unnecessary pushing instructions) to keep the stack
* consistent at later points in the execution.
*/
private class MyStackConsistencyMarker
implements InstructionVisitor
{
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
{
// We check all entries to make sure the stack is also consistent
// at method exit points, where some stack entries might be
// discarded.
int stackSize = partialEvaluator.getStackBefore(offset).size();
for (int stackIndex = 0; stackIndex < stackSize; stackIndex++)
{
// Is this stack entry pushed by any producer
// (because it is required by other consumers)?
if (!isStackEntryUnwantedBefore(offset, stackIndex) &&
isStackEntryPresentBefore(offset, stackIndex))
{
// Mark all produced stack entries.
markStackEntryProducers(offset, stackIndex, false);
}
}
}
}
/**
* This InstructionVisitor marks instructions that should still push or
* pop some values to keep the stack consistent.
*/
private class MyExtraPushPopInstructionMarker
implements InstructionVisitor
{
// Implementations for InstructionVisitor.
public void visitAnyInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, Instruction instruction)
{
// Check all stack entries that are popped.
//
// Typical case: a stack value that is required elsewhere or a
// pushed exception type that still has to be popped.
int stackSize = partialEvaluator.getStackBefore(offset).size();
int firstStackIndex =
stackSize - instruction.stackPopCount(clazz);
for (int stackIndex = firstStackIndex; stackIndex < stackSize; stackIndex++)
{
// Is this stack entry pushed by any producer
// (because it is required by other consumers)?
if (!isStackEntryUnwantedBefore(offset, stackIndex) &&
isStackEntryPresentBefore(offset, stackIndex))
{
// Mark that we'll need an extra pop instruction.
markExtraPushPopInstruction(offset);
// [DGD-481][DGD-504] Mark the stack entries and
// their producers again for a push/pop. In Kotlin
// code, it can happen that we have missed a producer
// during stack consistency marking.
markStackEntryProducers(offset, stackIndex, false);
}
}
}
}
// Small utility methods.
/**
* Marks the producing instructions of the variable consumer at the given
* offset.
* @param consumerOffset the offset of the variable consumer.
* @param variableIndex the index of the variable that is loaded.
*/
private void markVariableProducers(int consumerOffset,
int variableIndex)
{
InstructionOffsetValue producerOffsets =
partialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue();
if (producerOffsets != null)
{
int offsetCount = producerOffsets.instructionOffsetCount();
for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
if (!producerOffsets.isMethodParameter(offsetIndex) &&
!producerOffsets.isExceptionHandler(offsetIndex))
{
// Make sure the variable and the instruction are marked
// at the producing offset.
int offset = producerOffsets.instructionOffset(offsetIndex);
markInstruction(offset);
}
}
}
}
/**
* Ensures that the given variable is initialized before the specified
* consumer of that variable, in the JVM's view.
* @param consumerOffset the instruction offset before which the variable
* needs to be initialized.
* @param variableIndex the index of the variable.
* @param visitedOffsets the already visited consumer offsets, needed to
* prevent infinite loops.
* @return the updated visited consumer offsets.
*/
private InstructionOffsetValue markVariableInitializersBefore(int consumerOffset,
int variableIndex,
InstructionOffsetValue visitedOffsets)
{
// Avoid infinite loops by stopping recursion if we encounter
// an already visited offset.
if (visitedOffsets == null ||
!visitedOffsets.contains(consumerOffset))
{
visitedOffsets = visitedOffsets == null ?
new InstructionOffsetValue(consumerOffset) :
visitedOffsets.add(consumerOffset);
// Make sure the variable is initialized after all producers.
// Use the simple evaluator, to get the JVM's view of what is
// initialized.
InstructionOffsetValue producerOffsets =
simplePartialEvaluator.getVariablesBefore(consumerOffset).getProducerValue(variableIndex).instructionOffsetValue();
int offsetCount = producerOffsets.instructionOffsetCount();
for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
if (!producerOffsets.isMethodParameter(offsetIndex) &&
!producerOffsets.isExceptionHandler(offsetIndex))
{
int producerOffset =
producerOffsets.instructionOffset(offsetIndex);
visitedOffsets =
markVariableInitializersAfter(producerOffset,
variableIndex,
visitedOffsets);
}
}
}
return visitedOffsets;
}
/**
* Ensures that the given variable is initialized after the specified
* producer of that variable, in the JVM's view.
* @param producerOffset the instruction offset after which the variable
* needs to be initialized.
* @param variableIndex the index of the variable.
* @param visitedOffsets the already visited consumer offsets, needed to
* prevent infinite loops.
* @return the updated visited consumer offsets.
*/
private InstructionOffsetValue markVariableInitializersAfter(int producerOffset,
int variableIndex,
InstructionOffsetValue visitedOffsets)
{
// No problem if the producer has already been marked.
if (!isInstructionNecessary(producerOffset))
{
// Is the unmarked producer a variable initialization?
if (isVariableInitialization(producerOffset, variableIndex))
{
// Mark the producer.
logger.debug(" Marking initialization of v{} at ", variableIndex);
markInstruction(producerOffset);
}
else
{
// Don't mark the producer, but recursively look at the
// preceding producers of the same variable. Their values
// will fall through, replacing this producer.
visitedOffsets =
markVariableInitializersBefore(producerOffset,
variableIndex,
visitedOffsets);
}
}
return visitedOffsets;
}
/**
* Marks the stack entries and their producing instructions of the
* consumer at the given offset.
* @param clazz the containing class.
* @param consumerOffset the offset of the consumer.
* @param consumer the consumer of the stack entries.
*/
private void markStackProducers(Clazz clazz,
int consumerOffset,
Instruction consumer)
{
TracedStack tracedStack =
partialEvaluator.getStackBefore(consumerOffset);
int stackSize = tracedStack.size();
// Mark the producers of the popped values.
int popCount = consumer.stackPopCount(clazz);
for (int stackIndex = stackSize - popCount; stackIndex < stackSize; stackIndex++)
{
markStackEntryProducers(consumerOffset, stackIndex, true);
}
}
/**
* Marks the stack entry and the corresponding producing instructions
* of the consumer at the given offset, if the stack entry of the
* consumer is marked.
* @param consumerOffset the offset of the consumer.
* @param consumerTopStackIndex the index of the stack entry to be checked
* (counting from the top).
* @param producerTopStackIndex the index of the stack entry to be marked
* (counting from the top).
*/
private void conditionallyMarkStackEntryProducers(int consumerOffset,
int consumerTopStackIndex,
int producerTopStackIndex)
{
int consumerBottomStackIndex = partialEvaluator.getStackAfter(consumerOffset).size() - consumerTopStackIndex - 1;
if (isStackEntryNecessaryAfter(consumerOffset, consumerBottomStackIndex))
{
int producerBottomStackIndex = partialEvaluator.getStackBefore(consumerOffset).size() - producerTopStackIndex - 1;
markStackEntryProducers(consumerOffset, producerBottomStackIndex, true);
}
}
/**
* Marks the stack entry and optionally the corresponding producing
* instructions of the consumer at the given offset.
* @param consumerOffset the offset of the consumer.
* @param stackIndex the index of the stack entry to be marked
* (counting from the bottom).
* @param markInstructions specifies whether the producing instructions
* should be marked.
*/
private void markStackEntryProducers(int consumerOffset,
int stackIndex,
boolean markInstructions)
{
if (!isStackEntryUnwantedBefore(consumerOffset, stackIndex))
{
markStackEntryProducers(partialEvaluator.getStackBefore(consumerOffset).getBottomProducerValue(stackIndex).instructionOffsetValue(),
stackIndex,
markInstructions);
}
}
/**
* Marks the stack entry and optionally 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).
* @param markInstructions specifies whether the producing instructions
* should be marked.
*/
private void markStackEntryProducers(InstructionOffsetValue producerOffsets,
int stackIndex,
boolean markInstructions)
{
if (producerOffsets != null)
{
int offsetCount = producerOffsets.instructionOffsetCount();
for (int offsetIndex = 0; offsetIndex < offsetCount; offsetIndex++)
{
if (!producerOffsets.isExceptionHandler(offsetIndex))
{
// Make sure the stack entry and the instruction are marked
// at the producing offset.
int offset = producerOffsets.instructionOffset(offsetIndex);
markStackEntryAfter(offset, stackIndex);
if (markInstructions)
{
// We can mark the producer.
markInstruction(offset);
}
else
{
// We'll need to push a stack entry at that point
// instead.
markExtraPushPopInstruction(offset);
}
}
}
}
}
/**
* Marks any modification instructions that are required by the specified
* creation instruction (new, newarray, method returning new
* instance,...), so this new instance is properly initialized.
* @param instructionOffset the offset of the creation instruction.
*/
private void markReverseDependencies(int instructionOffset)
{
InstructionOffsetValue reverseDependency =
reverseDependencies[instructionOffset];
if (reverseDependency != null)
{
markInstructions(reverseDependency);
}
}
/**
* 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 true
if the above offsets are
* branch targets, false
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))
{
logger.debug("[{}->{}]", branchOrigin, branchTarget);
// Mark the branch instruction.
markInstruction(branchOrigin);
}
}
/**
* 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.
reverseDependencies =
ArrayUtil.ensureArraySize(reverseDependencies, codeLength, null);
if (stacksNecessaryAfter.length < codeLength ||
stacksNecessaryAfter[0].length < maxStack)
{
stacksNecessaryAfter = new boolean[codeLength][maxStack];
}
else
{
for (int offset = 0; offset < codeLength; offset++)
{
Arrays.fill(stacksNecessaryAfter[offset], 0, maxStack, false);
}
}
if (stacksUnwantedBefore.length < codeLength ||
stacksUnwantedBefore[0].length < maxStack)
{
stacksUnwantedBefore = new boolean[codeLength][maxStack];
}
else
{
for (int offset = 0; offset < codeLength; offset++)
{
Arrays.fill(stacksUnwantedBefore[offset], 0, maxStack, false);
}
}
instructionsNecessary =
ArrayUtil.ensureArraySize(instructionsNecessary,
codeLength,
false);
extraPushPopInstructionsNecessary =
ArrayUtil.ensureArraySize(extraPushPopInstructionsNecessary,
codeLength,
false);
}
/**
* Returns whether the specified variable is initialized at the specified
* offset.
*/
private boolean isVariableInitialization(int instructionOffset,
int variableIndex)
{
// Wasn't the variable set yet?
Value valueBefore = simplePartialEvaluator.getVariablesBefore(instructionOffset).getValue(variableIndex);
if (valueBefore == null)
{
return true;
}
// Is the computational type different now?
Value valueAfter = simplePartialEvaluator.getVariablesAfter(instructionOffset).getValue(variableIndex);
if (valueAfter.computationalType() != valueBefore.computationalType())
{
return true;
}
// Is the reference type different now?
if (valueAfter.computationalType() == Value.TYPE_REFERENCE &&
(valueAfter.referenceValue().isNull() == Value.ALWAYS ||
!valueAfter.referenceValue().getType().equals(valueBefore.referenceValue().getType())))
{
return true;
}
// Was the producer an argument (which may be removed)?
InstructionOffsetValue producersBefore = simplePartialEvaluator.getVariablesBefore(instructionOffset).getProducerValue(variableIndex).instructionOffsetValue();
return producersBefore.instructionOffsetCount() == 1 &&
producersBefore.isMethodParameter(0);
}
/**
* Marks the stack entry after the given offset.
* @param instructionOffset the offset of the stack entry to be marked.
* @param stackIndex the index of the stack entry to be marked
* (counting from the bottom).
*/
private void markStackEntryAfter(int instructionOffset,
int stackIndex)
{
if (!isStackEntryNecessaryAfter(instructionOffset, stackIndex))
{
logger.debug("[{}.s{}],", instructionOffset, stackIndex);
stacksNecessaryAfter[instructionOffset][stackIndex] = true;
if (maxMarkedOffset < instructionOffset)
{
maxMarkedOffset = instructionOffset;
}
}
}
/**
* Marks the specified stack entry as unwanted, typically because it is
* an unused parameter of a method invocation.
* @param instructionOffset the offset of the consumer.
* @param stackIndex the index of the stack entry to be marked
* (counting from the bottom).
*/
private void markStackEntryUnwantedBefore(int instructionOffset,
int stackIndex)
{
stacksUnwantedBefore[instructionOffset][stackIndex] = true;
}
/**
* Marks the specified instructions as used.
* @param instructionOffsets the offsets of the instructions.
*/
private void markInstructions(InstructionOffsetValue instructionOffsets)
{
int count = instructionOffsets.instructionOffsetCount();
for (int index = 0; index < count; index++)
{
markInstruction(instructionOffsets.instructionOffset(index));
}
}
/**
* Marks the specified instruction as used.
* @param instructionOffset the offset of the instruction.
*/
private void markInstruction(int instructionOffset)
{
if (!isInstructionNecessary(instructionOffset))
{
logger.debug("{},", instructionOffset);
instructionsNecessary[instructionOffset] = true;
if (maxMarkedOffset < instructionOffset)
{
maxMarkedOffset = instructionOffset;
}
}
}
/**
* Marks that an extra push/pop instruction is required at the given
* offset, if the current instruction at that offset is unused.
* @param instructionOffset the offset of the instruction.
*/
private void markExtraPushPopInstruction(int instructionOffset)
{
if (!isInstructionNecessary(instructionOffset) &&
!isExtraPushPopInstructionNecessary(instructionOffset))
{
logger.debug("{},", instructionOffset);
extraPushPopInstructionsNecessary[instructionOffset] = true;
if (maxMarkedOffset < instructionOffset)
{
maxMarkedOffset = instructionOffset;
}
}
}
/**
* Returns whether any instruction in the specified sequence of
* instructions is necessary.
* @param startInstructionOffset the start offset of the instruction
* sequence (inclusive).
* @param endInstructionOffset the end offset of the instruction
* sequence (exclusive).
* @return whether any instruction is necessary.
*/
private boolean isAnyInstructionNecessary(int startInstructionOffset,
int endInstructionOffset)
{
for (int instructionOffset = startInstructionOffset;
instructionOffset < endInstructionOffset;
instructionOffset++)
{
if (isInstructionNecessary(instructionOffset) ||
isExtraPushPopInstructionNecessary(instructionOffset))
{
return true;
}
}
return false;
}
/**
* This InstructionVisitor delegates its visits to a given
* InstructionVisitor, but only if the instruction has been marked as
* necessary (or not).
*/
private class MyNecessaryInstructionFilter implements InstructionVisitor
{
private final boolean necessary;
private final InstructionVisitor instructionVisitor;
public MyNecessaryInstructionFilter(boolean necessary,
InstructionVisitor instructionVisitor)
{
this.necessary = necessary;
this.instructionVisitor = instructionVisitor;
}
// Implementations for InstructionVisitor.
public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
{
if (shouldVisit(offset))
{
instructionVisitor.visitSimpleInstruction(clazz, method, codeAttribute, offset, simpleInstruction);
}
}
public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
{
if (shouldVisit(offset))
{
instructionVisitor.visitVariableInstruction(clazz, method, codeAttribute, offset, variableInstruction);
}
}
public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
{
if (shouldVisit(offset))
{
instructionVisitor.visitConstantInstruction(clazz, method, codeAttribute, offset, constantInstruction);
}
}
public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
{
if (shouldVisit(offset))
{
instructionVisitor.visitBranchInstruction(clazz, method, codeAttribute, offset, branchInstruction);
}
}
public void visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
{
if (shouldVisit(offset))
{
instructionVisitor.visitTableSwitchInstruction(clazz, method, codeAttribute, offset, tableSwitchInstruction);
}
}
public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
{
if (shouldVisit(offset))
{
instructionVisitor.visitLookUpSwitchInstruction(clazz, method, codeAttribute, offset, lookUpSwitchInstruction);
}
}
// Small utility methods.
/**
* Returns whether the instruction at the given offset should be
* visited, depending on whether it is necessary or not.
*/
private boolean shouldVisit(int offset)
{
return isInstructionNecessary(offset) == necessary;
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy