summaryrefslogtreecommitdiffstats
path: root/src/proguard/optimize/evaluation/EvaluationSimplifier.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/proguard/optimize/evaluation/EvaluationSimplifier.java')
-rw-r--r--src/proguard/optimize/evaluation/EvaluationSimplifier.java968
1 files changed, 968 insertions, 0 deletions
diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
new file mode 100644
index 0000000..0c3a9c7
--- /dev/null
+++ b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
@@ -0,0 +1,968 @@
+/*
+ * ProGuard -- shrinking, optimization, obfuscation, and preverification
+ * of Java bytecode.
+ *
+ * Copyright (c) 2002-2009 Eric Lafortune (eric@graphics.cornell.edu)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+package proguard.optimize.evaluation;
+
+import proguard.classfile.*;
+import proguard.classfile.attribute.*;
+import proguard.classfile.attribute.visitor.AttributeVisitor;
+import proguard.classfile.editor.*;
+import proguard.classfile.instruction.*;
+import proguard.classfile.instruction.visitor.InstructionVisitor;
+import proguard.classfile.util.*;
+import proguard.classfile.visitor.*;
+import proguard.evaluation.*;
+import proguard.evaluation.value.*;
+import proguard.optimize.info.*;
+
+/**
+ * This AttributeVisitor simplifies the code attributes that it visits, based
+ * on partial evaluation.
+ *
+ * @author Eric Lafortune
+ */
+public class EvaluationSimplifier
+extends SimplifiedVisitor
+implements AttributeVisitor,
+ InstructionVisitor
+{
+ //*
+ private static final boolean DEBUG = false;
+ /*/
+ private static boolean DEBUG = true;
+ //*/
+
+ private final InstructionVisitor extraInstructionVisitor;
+
+ private final PartialEvaluator partialEvaluator;
+ private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true);
+ private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false);
+
+
+ /**
+ * Creates a new EvaluationSimplifier.
+ */
+ public EvaluationSimplifier()
+ {
+ this(new PartialEvaluator(), null);
+ }
+
+
+ /**
+ * Creates a new EvaluationSimplifier.
+ * @param partialEvaluator the partial evaluator that will
+ * execute the code and provide
+ * information about the results.
+ * @param extraInstructionVisitor an optional extra visitor for all
+ * simplified instructions.
+ */
+ public EvaluationSimplifier(PartialEvaluator partialEvaluator,
+ InstructionVisitor extraInstructionVisitor)
+ {
+ this.partialEvaluator = partialEvaluator;
+ this.extraInstructionVisitor = extraInstructionVisitor;
+ }
+
+
+ // Implementations for AttributeVisitor.
+
+ public void visitAnyAttribute(Clazz clazz, Attribute attribute) {}
+
+
+ public void visitCodeAttribute(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+// DEBUG =
+// clazz.getName().equals("abc/Def") &&
+// method.getName(clazz).equals("abc");
+
+ // TODO: Remove this when the evaluation simplifier has stabilized.
+ // Catch any unexpected exceptions from the actual visiting method.
+ try
+ {
+ // Process the code.
+ visitCodeAttribute0(clazz, method, codeAttribute);
+ }
+ catch (RuntimeException ex)
+ {
+ System.err.println("Unexpected error while simplifying instructions after partial evaluation:");
+ System.err.println(" Class = ["+clazz.getName()+"]");
+ System.err.println(" Method = ["+method.getName(clazz)+method.getDescriptor(clazz)+"]");
+ System.err.println(" Exception = ["+ex.getClass().getName()+"] ("+ex.getMessage()+")");
+ System.err.println("Not optimizing this method");
+
+ if (DEBUG)
+ {
+ method.accept(clazz, new ClassPrinter());
+
+ throw ex;
+ }
+ }
+ }
+
+
+ public void visitCodeAttribute0(Clazz clazz, Method method, CodeAttribute codeAttribute)
+ {
+ if (DEBUG)
+ {
+ System.out.println();
+ System.out.println("Class "+ClassUtil.externalClassName(clazz.getName()));
+ System.out.println("Method "+ClassUtil.externalFullMethodDescription(clazz.getName(),
+ 0,
+ method.getName(clazz),
+ method.getDescriptor(clazz)));
+ }
+
+ // Evaluate the method.
+ partialEvaluator.visitCodeAttribute(clazz, method, codeAttribute);
+
+ int codeLength = codeAttribute.u4codeLength;
+
+ // Reset the code changes.
+ codeAttributeEditor.reset(codeLength);
+
+ // Replace any instructions that can be simplified.
+ for (int offset = 0; offset < codeLength; offset++)
+ {
+ if (partialEvaluator.isTraced(offset))
+ {
+ Instruction instruction = InstructionFactory.create(codeAttribute.code,
+ offset);
+
+ instruction.accept(clazz, method, codeAttribute, offset, this);
+ }
+ }
+
+ // Apply all accumulated changes to the code.
+ codeAttributeEditor.visitCodeAttribute(clazz, method, codeAttribute);
+ }
+
+
+ // Implementations for InstructionVisitor.
+
+ public void visitSimpleInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SimpleInstruction simpleInstruction)
+ {
+ switch (simpleInstruction.opcode)
+ {
+ case InstructionConstants.OP_IALOAD:
+ case InstructionConstants.OP_BALOAD:
+ case InstructionConstants.OP_CALOAD:
+ case InstructionConstants.OP_SALOAD:
+ case InstructionConstants.OP_IADD:
+ case InstructionConstants.OP_ISUB:
+ case InstructionConstants.OP_IMUL:
+ case InstructionConstants.OP_IDIV:
+ case InstructionConstants.OP_IREM:
+ case InstructionConstants.OP_INEG:
+ case InstructionConstants.OP_ISHL:
+ case InstructionConstants.OP_ISHR:
+ case InstructionConstants.OP_IUSHR:
+ case InstructionConstants.OP_IAND:
+ case InstructionConstants.OP_IOR:
+ case InstructionConstants.OP_IXOR:
+ case InstructionConstants.OP_L2I:
+ case InstructionConstants.OP_F2I:
+ case InstructionConstants.OP_D2I:
+ case InstructionConstants.OP_I2B:
+ case InstructionConstants.OP_I2C:
+ case InstructionConstants.OP_I2S:
+ replaceIntegerPushInstruction(clazz, offset, simpleInstruction);
+ break;
+
+ case InstructionConstants.OP_LALOAD:
+ case InstructionConstants.OP_LADD:
+ case InstructionConstants.OP_LSUB:
+ case InstructionConstants.OP_LMUL:
+ case InstructionConstants.OP_LDIV:
+ case InstructionConstants.OP_LREM:
+ case InstructionConstants.OP_LNEG:
+ case InstructionConstants.OP_LSHL:
+ case InstructionConstants.OP_LSHR:
+ case InstructionConstants.OP_LUSHR:
+ case InstructionConstants.OP_LAND:
+ case InstructionConstants.OP_LOR:
+ case InstructionConstants.OP_LXOR:
+ case InstructionConstants.OP_I2L:
+ case InstructionConstants.OP_F2L:
+ case InstructionConstants.OP_D2L:
+ replaceLongPushInstruction(clazz, offset, simpleInstruction);
+ break;
+
+ case InstructionConstants.OP_FALOAD:
+ case InstructionConstants.OP_FADD:
+ case InstructionConstants.OP_FSUB:
+ case InstructionConstants.OP_FMUL:
+ case InstructionConstants.OP_FDIV:
+ case InstructionConstants.OP_FREM:
+ case InstructionConstants.OP_FNEG:
+ case InstructionConstants.OP_I2F:
+ case InstructionConstants.OP_L2F:
+ case InstructionConstants.OP_D2F:
+ replaceFloatPushInstruction(clazz, offset, simpleInstruction);
+ break;
+
+ case InstructionConstants.OP_DALOAD:
+ case InstructionConstants.OP_DADD:
+ case InstructionConstants.OP_DSUB:
+ case InstructionConstants.OP_DMUL:
+ case InstructionConstants.OP_DDIV:
+ case InstructionConstants.OP_DREM:
+ case InstructionConstants.OP_DNEG:
+ case InstructionConstants.OP_I2D:
+ case InstructionConstants.OP_L2D:
+ case InstructionConstants.OP_F2D:
+ replaceDoublePushInstruction(clazz, offset, simpleInstruction);
+ break;
+
+ case InstructionConstants.OP_AALOAD:
+ replaceReferencePushInstruction(clazz, offset, simpleInstruction);
+ break;
+ }
+ }
+
+
+ public void visitVariableInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, VariableInstruction variableInstruction)
+ {
+ int variableIndex = variableInstruction.variableIndex;
+
+ switch (variableInstruction.opcode)
+ {
+ case InstructionConstants.OP_ILOAD:
+ case InstructionConstants.OP_ILOAD_0:
+ case InstructionConstants.OP_ILOAD_1:
+ case InstructionConstants.OP_ILOAD_2:
+ case InstructionConstants.OP_ILOAD_3:
+ replaceIntegerPushInstruction(clazz, offset, variableInstruction, variableIndex);
+ break;
+
+ case InstructionConstants.OP_LLOAD:
+ case InstructionConstants.OP_LLOAD_0:
+ case InstructionConstants.OP_LLOAD_1:
+ case InstructionConstants.OP_LLOAD_2:
+ case InstructionConstants.OP_LLOAD_3:
+ replaceLongPushInstruction(clazz, offset, variableInstruction, variableIndex);
+ break;
+
+ case InstructionConstants.OP_FLOAD:
+ case InstructionConstants.OP_FLOAD_0:
+ case InstructionConstants.OP_FLOAD_1:
+ case InstructionConstants.OP_FLOAD_2:
+ case InstructionConstants.OP_FLOAD_3:
+ replaceFloatPushInstruction(clazz, offset, variableInstruction, variableIndex);
+ break;
+
+ case InstructionConstants.OP_DLOAD:
+ case InstructionConstants.OP_DLOAD_0:
+ case InstructionConstants.OP_DLOAD_1:
+ case InstructionConstants.OP_DLOAD_2:
+ case InstructionConstants.OP_DLOAD_3:
+ replaceDoublePushInstruction(clazz, offset, variableInstruction, variableIndex);
+ break;
+
+ case InstructionConstants.OP_ALOAD:
+ case InstructionConstants.OP_ALOAD_0:
+ case InstructionConstants.OP_ALOAD_1:
+ case InstructionConstants.OP_ALOAD_2:
+ case InstructionConstants.OP_ALOAD_3:
+ replaceReferencePushInstruction(clazz, offset, variableInstruction);
+ break;
+
+ case InstructionConstants.OP_ASTORE:
+ case InstructionConstants.OP_ASTORE_0:
+ case InstructionConstants.OP_ASTORE_1:
+ case InstructionConstants.OP_ASTORE_2:
+ case InstructionConstants.OP_ASTORE_3:
+ deleteReferencePopInstruction(clazz, offset, variableInstruction);
+ break;
+
+ case InstructionConstants.OP_RET:
+ replaceBranchInstruction(clazz, offset, variableInstruction);
+ break;
+ }
+ }
+
+
+ public void visitConstantInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, ConstantInstruction constantInstruction)
+ {
+ switch (constantInstruction.opcode)
+ {
+ case InstructionConstants.OP_GETSTATIC:
+ case InstructionConstants.OP_GETFIELD:
+ replaceAnyPushInstruction(clazz, offset, constantInstruction);
+ break;
+
+ case InstructionConstants.OP_INVOKEVIRTUAL:
+ case InstructionConstants.OP_INVOKESPECIAL:
+ case InstructionConstants.OP_INVOKESTATIC:
+ case InstructionConstants.OP_INVOKEINTERFACE:
+ if (constantInstruction.stackPushCount(clazz) > 0 &&
+ !sideEffectInstructionChecker.hasSideEffects(clazz,
+ method,
+ codeAttribute,
+ offset,
+ constantInstruction))
+ {
+ replaceAnyPushInstruction(clazz, offset, constantInstruction);
+ }
+
+ break;
+
+ case InstructionConstants.OP_CHECKCAST:
+ replaceReferencePushInstruction(clazz, offset, constantInstruction);
+ break;
+ }
+ }
+
+
+ public void visitBranchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, BranchInstruction branchInstruction)
+ {
+ switch (branchInstruction.opcode)
+ {
+ case InstructionConstants.OP_GOTO:
+ case InstructionConstants.OP_GOTO_W:
+ // Don't replace unconditional branches.
+ break;
+
+ case InstructionConstants.OP_JSR:
+ case InstructionConstants.OP_JSR_W:
+ replaceJsrInstruction(clazz, offset, branchInstruction);
+ break;
+
+ default:
+ replaceBranchInstruction(clazz, offset, branchInstruction);
+ break;
+ }
+ }
+
+
+ public void visitAnySwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, SwitchInstruction switchInstruction)
+ {
+ // First try to simplify it to a simple branch.
+ replaceBranchInstruction(clazz, offset, switchInstruction);
+
+ // Otherwise make sure all branch targets are valid.
+ if (!codeAttributeEditor.isModified(offset))
+ {
+ replaceSwitchInstruction(clazz, offset, switchInstruction);
+ }
+ }
+
+
+ // Small utility methods.
+
+ /**
+ * Replaces the push instruction at the given offset by a simpler push
+ * instruction, if possible.
+ */
+ private void replaceAnyPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+ if (pushedValue.isParticular())
+ {
+ switch (pushedValue.computationalType())
+ {
+ case Value.TYPE_INTEGER:
+ replaceIntegerPushInstruction(clazz, offset, instruction);
+ break;
+ case Value.TYPE_LONG:
+ replaceLongPushInstruction(clazz, offset, instruction);
+ break;
+ case Value.TYPE_FLOAT:
+ replaceFloatPushInstruction(clazz, offset, instruction);
+ break;
+ case Value.TYPE_DOUBLE:
+ replaceDoublePushInstruction(clazz, offset, instruction);
+ break;
+ case Value.TYPE_REFERENCE:
+ replaceReferencePushInstruction(clazz, offset, instruction);
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * Replaces the integer pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceIntegerPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ replaceIntegerPushInstruction(clazz,
+ offset,
+ instruction,
+ partialEvaluator.getVariablesBefore(offset).size());
+ }
+
+
+ /**
+ * Replaces the integer pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceIntegerPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ int maxVariableIndex)
+ {
+ Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+ if (pushedValue.isParticular())
+ {
+ int value = pushedValue.integerValue().value();
+ if (value << 16 >> 16 == value)
+ {
+ replaceConstantPushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_SIPUSH,
+ value);
+ }
+ else
+ {
+ ConstantPoolEditor constantPoolEditor =
+ new ConstantPoolEditor((ProgramClass)clazz);
+
+ Instruction replacementInstruction =
+ new ConstantInstruction(InstructionConstants.OP_LDC,
+ constantPoolEditor.addIntegerConstant(value)).shrink();
+
+ replaceInstruction(clazz, offset, instruction, replacementInstruction);
+ }
+ }
+ else if (pushedValue.isSpecific())
+ {
+ TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
+ for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
+ {
+ if (pushedValue.equals(variables.load(variableIndex)))
+ {
+ replaceVariablePushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_ILOAD,
+ variableIndex);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Replaces the long pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceLongPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ replaceLongPushInstruction(clazz,
+ offset,
+ instruction,
+ partialEvaluator.getVariablesBefore(offset).size());
+ }
+
+
+ /**
+ * Replaces the long pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceLongPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ int maxVariableIndex)
+ {
+ Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+ if (pushedValue.isParticular())
+ {
+ long value = pushedValue.longValue().value();
+ if (value == 0L ||
+ value == 1L)
+ {
+ replaceConstantPushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_LCONST_0,
+ (int)value);
+ }
+ else
+ {
+ ConstantPoolEditor constantPoolEditor =
+ new ConstantPoolEditor((ProgramClass)clazz);
+
+ Instruction replacementInstruction =
+ new ConstantInstruction(InstructionConstants.OP_LDC2_W,
+ constantPoolEditor.addLongConstant(value)).shrink();
+
+ replaceInstruction(clazz, offset, instruction, replacementInstruction);
+ }
+ }
+ else if (pushedValue.isSpecific())
+ {
+ TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
+ for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
+ {
+ if (pushedValue.equals(variables.load(variableIndex)))
+ {
+ replaceVariablePushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_LLOAD,
+ variableIndex);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Replaces the float pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceFloatPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ replaceFloatPushInstruction(clazz,
+ offset,
+ instruction,
+ partialEvaluator.getVariablesBefore(offset).size());
+ }
+
+
+ /**
+ * Replaces the float pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceFloatPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ int maxVariableIndex)
+ {
+ Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+ if (pushedValue.isParticular())
+ {
+ float value = pushedValue.floatValue().value();
+ if (value == 0f ||
+ value == 1f ||
+ value == 2f)
+ {
+ replaceConstantPushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_FCONST_0,
+ (int)value);
+ }
+ else
+ {
+ ConstantPoolEditor constantPoolEditor =
+ new ConstantPoolEditor((ProgramClass)clazz);
+
+ Instruction replacementInstruction =
+ new ConstantInstruction(InstructionConstants.OP_LDC,
+ constantPoolEditor.addFloatConstant(value)).shrink();
+
+ replaceInstruction(clazz, offset, instruction, replacementInstruction);
+ }
+ }
+ else if (pushedValue.isSpecific())
+ {
+ TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
+ for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
+ {
+ if (pushedValue.equals(variables.load(variableIndex)))
+ {
+ replaceVariablePushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_FLOAD,
+ variableIndex);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Replaces the double pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceDoublePushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ replaceDoublePushInstruction(clazz,
+ offset,
+ instruction,
+ partialEvaluator.getVariablesBefore(offset).size());
+ }
+
+
+ /**
+ * Replaces the double pushing instruction at the given offset by a simpler
+ * push instruction, if possible.
+ */
+ private void replaceDoublePushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ int maxVariableIndex)
+ {
+ Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+ if (pushedValue.isParticular())
+ {
+ double value = pushedValue.doubleValue().value();
+ if (value == 0.0 ||
+ value == 1.0)
+ {
+ replaceConstantPushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_DCONST_0,
+ (int)value);
+ }
+ else
+ {
+ ConstantPoolEditor constantPoolEditor =
+ new ConstantPoolEditor((ProgramClass)clazz);
+
+ Instruction replacementInstruction =
+ new ConstantInstruction(InstructionConstants.OP_LDC2_W,
+ constantPoolEditor.addDoubleConstant(value)).shrink();
+
+ replaceInstruction(clazz, offset, instruction, replacementInstruction);
+ }
+ }
+ else if (pushedValue.isSpecific())
+ {
+ TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
+ for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
+ {
+ if (pushedValue.equals(variables.load(variableIndex)))
+ {
+ replaceVariablePushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_DLOAD,
+ variableIndex);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Replaces the reference pushing instruction at the given offset by a
+ * simpler push instruction, if possible.
+ */
+ private void replaceReferencePushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ Value pushedValue = partialEvaluator.getStackAfter(offset).getTop(0);
+ if (pushedValue.isParticular())
+ {
+ // A reference value can only be specific if it is null.
+ replaceConstantPushInstruction(clazz,
+ offset,
+ instruction,
+ InstructionConstants.OP_ACONST_NULL,
+ 0);
+ }
+ }
+
+
+ /**
+ * Replaces the instruction at a given offset by a given push instruction
+ * of a constant.
+ */
+ private void replaceConstantPushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ byte replacementOpcode,
+ int value)
+ {
+ Instruction replacementInstruction =
+ new SimpleInstruction(replacementOpcode, value).shrink();
+
+ replaceInstruction(clazz, offset, instruction, replacementInstruction);
+ }
+
+
+ /**
+ * Replaces the instruction at a given offset by a given push instruction
+ * of a variable.
+ */
+ private void replaceVariablePushInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ byte replacementOpcode,
+ int variableIndex)
+ {
+ Instruction replacementInstruction =
+ new VariableInstruction(replacementOpcode, variableIndex).shrink();
+
+ replaceInstruction(clazz, offset, instruction, replacementInstruction);
+ }
+
+
+ /**
+ * Replaces the given 'jsr' instruction by a simpler branch instruction,
+ * if it jumps to a subroutine that doesn't return or a subroutine that
+ * is only called from one place.
+ */
+ private void replaceJsrInstruction(Clazz clazz,
+ int offset,
+ BranchInstruction branchInstruction)
+ {
+ // Is the subroutine ever returning?
+ int subroutineStart = offset + branchInstruction.branchOffset;
+ if (!partialEvaluator.isSubroutineReturning(subroutineStart) ||
+ partialEvaluator.branchOrigins(subroutineStart).instructionOffsetCount() == 1)
+ {
+ // All 'jsr' instructions to this subroutine can be replaced
+ // by unconditional branch instructions.
+ replaceBranchInstruction(clazz, offset, branchInstruction);
+ }
+ else if (!partialEvaluator.isTraced(offset + branchInstruction.length(offset)))
+ {
+ // We have to make sure the instruction after this 'jsr'
+ // instruction is valid, even if it is never reached.
+ replaceByInfiniteLoop(clazz, offset + branchInstruction.length(offset), branchInstruction);
+ }
+ }
+
+
+ /**
+ * Deletes the reference popping instruction at the given offset, if
+ * it is at the start of a subroutine that doesn't return or a subroutine
+ * that is only called from one place.
+ */
+ private void deleteReferencePopInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ if (partialEvaluator.isSubroutineStart(offset) &&
+ (!partialEvaluator.isSubroutineReturning(offset) ||
+ partialEvaluator.branchOrigins(offset).instructionOffsetCount() == 1))
+ {
+ if (DEBUG) System.out.println(" Deleting store of subroutine return address "+instruction.toString(offset));
+
+ // A reference value can only be specific if it is null.
+ codeAttributeEditor.deleteInstruction(offset);
+ }
+ }
+
+
+ /**
+ * Deletes the given branch instruction, or replaces it by a simpler branch
+ * instruction, if possible.
+ */
+ private void replaceBranchInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+
+ // Is there exactly one branch target (not from a goto or jsr)?
+ if (branchTargets != null &&
+ branchTargets.instructionOffsetCount() == 1)
+ {
+ // Is it branching to the next instruction?
+ int branchOffset = branchTargets.instructionOffset(0) - offset;
+ if (branchOffset == instruction.length(offset))
+ {
+ if (DEBUG) System.out.println(" Ignoring zero branch instruction at ["+offset+"]");
+ }
+ else
+ {
+ // Replace the branch instruction by a simple branch instruction.
+ Instruction replacementInstruction =
+ new BranchInstruction(InstructionConstants.OP_GOTO_W,
+ branchOffset).shrink();
+
+ replaceInstruction(clazz, offset, instruction, replacementInstruction);
+ }
+ }
+ }
+
+
+ /**
+ * Makes sure all branch targets of the given switch instruction are valid.
+ */
+ private void replaceSwitchInstruction(Clazz clazz,
+ int offset,
+ SwitchInstruction switchInstruction)
+ {
+ // Get the actual branch targets.
+ InstructionOffsetValue branchTargets = partialEvaluator.branchTargets(offset);
+
+ // Get an offset that can serve as a valid default offset.
+ int defaultOffset =
+ branchTargets.instructionOffset(branchTargets.instructionOffsetCount()-1) -
+ offset;
+
+ Instruction replacementInstruction = null;
+
+ // Check the jump offsets.
+ int[] jumpOffsets = switchInstruction.jumpOffsets;
+ for (int index = 0; index < jumpOffsets.length; index++)
+ {
+ if (!branchTargets.contains(offset + jumpOffsets[index]))
+ {
+ // Replace the unused offset.
+ jumpOffsets[index] = defaultOffset;
+
+ // Remember to replace the instruction.
+ replacementInstruction = switchInstruction;
+ }
+ }
+
+ // Check the default offset.
+ if (!branchTargets.contains(offset + switchInstruction.defaultOffset))
+ {
+ // Replace the unused offset.
+ switchInstruction.defaultOffset = defaultOffset;
+
+ // Remember to replace the instruction.
+ replacementInstruction = switchInstruction;
+ }
+
+ if (replacementInstruction != null)
+ {
+ replaceInstruction(clazz, offset, switchInstruction, replacementInstruction);
+ }
+ }
+
+
+ /**
+ * Replaces the given instruction by an infinite loop.
+ */
+ private void replaceByInfiniteLoop(Clazz clazz,
+ int offset,
+ Instruction instruction)
+ {
+ // Replace the instruction by an infinite loop.
+ Instruction replacementInstruction =
+ new BranchInstruction(InstructionConstants.OP_GOTO, 0);
+
+ if (DEBUG) System.out.println(" Replacing unreachable instruction by infinite loop "+replacementInstruction.toString(offset));
+
+ codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ // Note: we're not passing the right arguments for now, knowing that
+ // they aren't used anyway.
+ instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
+ }
+ }
+
+
+ /**
+ * Replaces the instruction at a given offset by a given push instruction.
+ */
+ private void replaceInstruction(Clazz clazz,
+ int offset,
+ Instruction instruction,
+ Instruction replacementInstruction)
+ {
+ // Pop unneeded stack entries if necessary.
+ int popCount =
+ instruction.stackPopCount(clazz) -
+ replacementInstruction.stackPopCount(clazz);
+
+ insertPopInstructions(offset, popCount);
+
+ if (DEBUG) System.out.println(" Replacing instruction "+instruction.toString(offset)+" -> "+replacementInstruction.toString()+(popCount == 0 ? "" : " ("+popCount+" pops)"));
+
+ codeAttributeEditor.replaceInstruction(offset, replacementInstruction);
+
+ // Visit the instruction, if required.
+ if (extraInstructionVisitor != null)
+ {
+ // Note: we're not passing the right arguments for now, knowing that
+ // they aren't used anyway.
+ instruction.accept(clazz, null, null, offset, extraInstructionVisitor);
+ }
+ }
+
+
+ /**
+ * Pops the given number of stack entries before the instruction at the
+ * given offset.
+ */
+ private void insertPopInstructions(int offset, int popCount)
+ {
+ switch (popCount)
+ {
+ case 0:
+ {
+ break;
+ }
+ case 1:
+ {
+ // Insert a single pop instruction.
+ Instruction popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP);
+
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ popInstruction);
+ break;
+ }
+ case 2:
+ {
+ // Insert a single pop2 instruction.
+ Instruction popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP2);
+
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ popInstruction);
+ break;
+ }
+ default:
+ {
+ // Insert the specified number of pop instructions.
+ Instruction[] popInstructions =
+ new Instruction[popCount / 2 + popCount % 2];
+
+ Instruction popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP2);
+
+ for (int index = 0; index < popCount / 2; index++)
+ {
+ popInstructions[index] = popInstruction;
+ }
+
+ if (popCount % 2 == 1)
+ {
+ popInstruction =
+ new SimpleInstruction(InstructionConstants.OP_POP);
+
+ popInstructions[popCount / 2] = popInstruction;
+ }
+
+ codeAttributeEditor.insertBeforeInstruction(offset,
+ popInstructions);
+ break;
+ }
+ }
+ }
+}