summaryrefslogtreecommitdiff
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.java1360
1 files changed, 0 insertions, 1360 deletions
diff --git a/src/proguard/optimize/evaluation/EvaluationSimplifier.java b/src/proguard/optimize/evaluation/EvaluationSimplifier.java
deleted file mode 100644
index 8187342..0000000
--- a/src/proguard/optimize/evaluation/EvaluationSimplifier.java
+++ /dev/null
@@ -1,1360 +0,0 @@
-/*
- * ProGuard -- shrinking, optimization, obfuscation, and preverification
- * of Java bytecode.
- *
- * Copyright (c) 2002-2014 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.ClassPrinter;
-import proguard.evaluation.TracedVariables;
-import proguard.evaluation.value.*;
-import proguard.optimize.info.SideEffectInstructionChecker;
-
-import java.util.Arrays;
-
-/**
- * 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 int POS_ZERO_FLOAT_BITS = Float.floatToIntBits(0.0f);
- private static final long POS_ZERO_DOUBLE_BITS = Double.doubleToLongBits(0.0);
-
- //*
- private static final boolean DEBUG = false;
- /*/
- private static boolean DEBUG = System.getProperty("es") != null;
- //*/
-
- private final InstructionVisitor extraInstructionVisitor;
-
- private final PartialEvaluator partialEvaluator;
- private final SideEffectInstructionChecker sideEffectInstructionChecker = new SideEffectInstructionChecker(true, true);
- private final CodeAttributeEditor codeAttributeEditor = new CodeAttributeEditor(false, true);
-
-
- /**
- * 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("EvaluationSimplifier ["+clazz.getName()+"."+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:
- case InstructionConstants.OP_ARRAYLENGTH:
- 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 visitTableSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, TableSwitchInstruction tableSwitchInstruction)
- {
- // First try to simplify it to a simple branch.
- replaceBranchInstruction(clazz, offset, tableSwitchInstruction);
-
- // Otherwise try to simplify simple enum switches.
- if (!codeAttributeEditor.isModified(offset))
- {
- replaceSimpleEnumSwitchInstruction(clazz,
- codeAttribute,
- offset,
- tableSwitchInstruction);
-
- // Otherwise make sure all branch targets are valid.
- if (!codeAttributeEditor.isModified(offset))
- {
- cleanUpSwitchInstruction(clazz, offset, tableSwitchInstruction);
-
- trimSwitchInstruction(clazz, offset, tableSwitchInstruction);
- }
- }
- }
-
-
- public void visitLookUpSwitchInstruction(Clazz clazz, Method method, CodeAttribute codeAttribute, int offset, LookUpSwitchInstruction lookUpSwitchInstruction)
- {
- // First try to simplify it to a simple branch.
- replaceBranchInstruction(clazz, offset, lookUpSwitchInstruction);
-
- // Otherwise try to simplify simple enum switches.
- if (!codeAttributeEditor.isModified(offset))
- {
- replaceSimpleEnumSwitchInstruction(clazz,
- codeAttribute,
- offset,
- lookUpSwitchInstruction);
-
- // Otherwise make sure all branch targets are valid.
- if (!codeAttributeEditor.isModified(offset))
- {
- cleanUpSwitchInstruction(clazz, offset, lookUpSwitchInstruction);
-
- trimSwitchInstruction(clazz, offset, lookUpSwitchInstruction);
- }
- }
- }
-
-
- // 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())
- {
- // Push a constant instead.
- int value = pushedValue.integerValue().value();
- if ((short)value == 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));
-
- replaceInstruction(clazz, offset, instruction, replacementInstruction);
- }
- }
- else if (pushedValue.isSpecific())
- {
- // Load an equivalent lower-numbered variable instead, if any.
- 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);
- break;
- }
- }
- }
- }
-
-
- /**
- * 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())
- {
- // Push a constant instead.
- 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));
-
- replaceInstruction(clazz, offset, instruction, replacementInstruction);
- }
- }
- else if (pushedValue.isSpecific())
- {
- // Load an equivalent lower-numbered variable instead, if any.
- TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
- for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
- {
- // Note that we have to check the second part as well.
- if (pushedValue.equals(variables.load(variableIndex)) &&
- variables.load(variableIndex + 1) != null &&
- variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP)
- {
- 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())
- {
- // Push a constant instead.
- // Make sure to distinguish between +0.0 and -0.0.
- float value = pushedValue.floatValue().value();
- if (value == 0.0f && Float.floatToIntBits(value) == POS_ZERO_FLOAT_BITS ||
- value == 1.0f ||
- value == 2.0f)
- {
- 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));
-
- replaceInstruction(clazz, offset, instruction, replacementInstruction);
- }
- }
- else if (pushedValue.isSpecific())
- {
- // Load an equivalent lower-numbered variable instead, if any.
- 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())
- {
- // Push a constant instead.
- // Make sure to distinguish between +0.0 and -0.0.
- double value = pushedValue.doubleValue().value();
- if (value == 0.0 && Double.doubleToLongBits(value) == POS_ZERO_DOUBLE_BITS ||
- 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));
-
- replaceInstruction(clazz, offset, instruction, replacementInstruction);
- }
- }
- else if (pushedValue.isSpecific())
- {
- // Load an equivalent lower-numbered variable instead, if any.
- TracedVariables variables = partialEvaluator.getVariablesBefore(offset);
- for (int variableIndex = 0; variableIndex < maxVariableIndex; variableIndex++)
- {
- // Note that we have to check the second part as well.
- if (pushedValue.equals(variables.load(variableIndex)) &&
- variables.load(variableIndex + 1) != null &&
- variables.load(variableIndex + 1).computationalType() == Value.TYPE_TOP)
- {
- 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);
-
- 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);
-
- 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,
- branchOffset);
-
- replaceInstruction(clazz, offset, instruction, replacementInstruction);
- }
- }
- }
-
-
- /**
- * Replaces the given table switch instruction, if it is based on the value
- * of a fixed array. This is typical for switches on simple enums.
- */
- private void replaceSimpleEnumSwitchInstruction(Clazz clazz,
- CodeAttribute codeAttribute,
- int offset,
- TableSwitchInstruction tableSwitchInstruction)
- {
- // Check if the switch instruction is consuming a single value loaded
- // from a fully specified array.
- InstructionOffsetValue producerOffsets =
- partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue();
-
- if (producerOffsets.instructionOffsetCount() == 1)
- {
- int producerOffset = producerOffsets.instructionOffset(0);
-
- if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD &&
- !codeAttributeEditor.isModified(producerOffset))
- {
- ReferenceValue referenceValue =
- partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue();
-
- if (referenceValue.isParticular())
- {
- // Simplify the entire construct.
- replaceSimpleEnumSwitchInstruction(clazz,
- codeAttribute,
- producerOffset,
- offset,
- tableSwitchInstruction,
- referenceValue);
- }
- }
- }
- }
-
-
- /**
- * Replaces the given table switch instruction that is based on a value of
- * the given fixed array.
- */
- private void replaceSimpleEnumSwitchInstruction(Clazz clazz,
- CodeAttribute codeAttribute,
- int loadOffset,
- int switchOffset,
- TableSwitchInstruction tableSwitchInstruction,
- ReferenceValue mappingValue)
- {
- ValueFactory valueFactory = new ParticularValueFactory();
-
- // Transform the jump offsets.
- int[] jumpOffsets = tableSwitchInstruction.jumpOffsets;
- int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()];
-
- for (int index = 0; index < newJumpOffsets.length; index++)
- {
- int switchCase =
- mappingValue.integerArrayLoad(valueFactory.createIntegerValue(
- index),
- valueFactory).value();
-
- newJumpOffsets[index] =
- switchCase >= tableSwitchInstruction.lowCase &&
- switchCase <= tableSwitchInstruction.highCase ?
- jumpOffsets[switchCase - tableSwitchInstruction.lowCase] :
- tableSwitchInstruction.defaultOffset;
- }
-
- // Update the instruction.
- tableSwitchInstruction.lowCase = 0;
- tableSwitchInstruction.highCase = newJumpOffsets.length - 1;
- tableSwitchInstruction.jumpOffsets = newJumpOffsets;
-
- // Replace the original one with the new version.
- replaceSimpleEnumSwitchInstruction(clazz,
- loadOffset,
- switchOffset,
- tableSwitchInstruction);
-
- cleanUpSwitchInstruction(clazz, switchOffset, tableSwitchInstruction);
-
- trimSwitchInstruction(clazz, switchOffset, tableSwitchInstruction);
- }
-
-
- /**
- * Replaces the given look up switch instruction, if it is based on the
- * value of a fixed array. This is typical for switches on simple enums.
- */
- private void replaceSimpleEnumSwitchInstruction(Clazz clazz,
- CodeAttribute codeAttribute,
- int offset,
- LookUpSwitchInstruction lookupSwitchInstruction)
- {
- // Check if the switch instruction is consuming a single value loaded
- // from a fully specified array.
- InstructionOffsetValue producerOffsets =
- partialEvaluator.getStackBefore(offset).getTopProducerValue(0).instructionOffsetValue();
-
- if (producerOffsets.instructionOffsetCount() == 1)
- {
- int producerOffset = producerOffsets.instructionOffset(0);
-
- if (codeAttribute.code[producerOffset] == InstructionConstants.OP_IALOAD &&
- !codeAttributeEditor.isModified(producerOffset))
- {
- ReferenceValue referenceValue =
- partialEvaluator.getStackBefore(producerOffset).getTop(1).referenceValue();
-
- if (referenceValue.isParticular())
- {
- // Simplify the entire construct.
- replaceSimpleEnumSwitchInstruction(clazz,
- codeAttribute,
- producerOffset,
- offset,
- lookupSwitchInstruction,
- referenceValue);
- }
- }
- }
- }
-
-
- /**
- * Replaces the given look up switch instruction that is based on a value of
- * the given fixed array. This is typical for switches on simple enums.
- */
- private void replaceSimpleEnumSwitchInstruction(Clazz clazz,
- CodeAttribute codeAttribute,
- int loadOffset,
- int switchOffset,
- LookUpSwitchInstruction lookupSwitchInstruction,
- ReferenceValue mappingValue)
- {
- ValueFactory valueFactory = new ParticularValueFactory();
-
- // Transform the jump offsets.
- int[] cases = lookupSwitchInstruction.cases;
- int[] jumpOffsets = lookupSwitchInstruction.jumpOffsets;
- int[] newJumpOffsets = new int[mappingValue.arrayLength(valueFactory).value()];
-
- for (int index = 0; index < newJumpOffsets.length; index++)
- {
- int switchCase =
- mappingValue.integerArrayLoad(valueFactory.createIntegerValue(index),
- valueFactory).value();
-
- int caseIndex = Arrays.binarySearch(cases, switchCase);
-
- newJumpOffsets[index] = caseIndex >= 0 ?
- jumpOffsets[caseIndex] :
- lookupSwitchInstruction.defaultOffset;
- }
-
- // Replace the original lookup switch with a table switch.
- TableSwitchInstruction replacementSwitchInstruction =
- new TableSwitchInstruction(InstructionConstants.OP_TABLESWITCH,
- lookupSwitchInstruction.defaultOffset,
- 0,
- newJumpOffsets.length - 1,
- newJumpOffsets);
-
- replaceSimpleEnumSwitchInstruction(clazz,
- loadOffset,
- switchOffset,
- replacementSwitchInstruction);
-
- cleanUpSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction);
-
- trimSwitchInstruction(clazz, switchOffset, replacementSwitchInstruction);
- }
-
-
- /**
- * Makes sure all branch targets of the given switch instruction are valid.
- */
- private void cleanUpSwitchInstruction(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);
- }
- }
-
-
- /**
- * Trims redundant offsets from the given switch instruction.
- */
- private void trimSwitchInstruction(Clazz clazz,
- int offset,
- TableSwitchInstruction tableSwitchInstruction)
- {
- // Get an offset that can serve as a valid default offset.
- int defaultOffset = tableSwitchInstruction.defaultOffset;
- int[] jumpOffsets = tableSwitchInstruction.jumpOffsets;
- int length = jumpOffsets.length;
-
- // Find the lowest index with a non-default jump offset.
- int lowIndex = 0;
- while (lowIndex < length &&
- jumpOffsets[lowIndex] == defaultOffset)
- {
- lowIndex++;
- }
-
- // Find the highest index with a non-default jump offset.
- int highIndex = length - 1;
- while (highIndex >= 0 &&
- jumpOffsets[highIndex] == defaultOffset)
- {
- highIndex--;
- }
-
- // Can we use a shorter array?
- int newLength = highIndex - lowIndex + 1;
- if (newLength < length)
- {
- if (newLength <= 0)
- {
- // Replace the switch instruction by a simple branch instruction.
- Instruction replacementInstruction =
- new BranchInstruction(InstructionConstants.OP_GOTO,
- defaultOffset);
-
- replaceInstruction(clazz, offset, tableSwitchInstruction,
- replacementInstruction);
- }
- else
- {
- // Trim the array.
- int[] newJumpOffsets = new int[newLength];
-
- System.arraycopy(jumpOffsets, lowIndex, newJumpOffsets, 0, newLength);
-
- tableSwitchInstruction.jumpOffsets = newJumpOffsets;
- tableSwitchInstruction.lowCase += lowIndex;
- tableSwitchInstruction.highCase -= length - newLength - lowIndex;
-
- replaceInstruction(clazz, offset, tableSwitchInstruction,
- tableSwitchInstruction);
- }
- }
- }
-
-
- /**
- * Trims redundant offsets from the given switch instruction.
- */
- private void trimSwitchInstruction(Clazz clazz,
- int offset,
- LookUpSwitchInstruction lookUpSwitchInstruction)
- {
- // Get an offset that can serve as a valid default offset.
- int defaultOffset = lookUpSwitchInstruction.defaultOffset;
- int[] jumpOffsets = lookUpSwitchInstruction.jumpOffsets;
- int length = jumpOffsets.length;
- int newLength = length;
-
- // Count the default jump offsets.
- for (int index = 0; index < length; index++)
- {
- if (jumpOffsets[index] == defaultOffset)
- {
- newLength--;
- }
- }
-
- // Can we use shorter arrays?
- if (newLength < length)
- {
- if (newLength <= 0)
- {
- // Replace the switch instruction by a simple branch instruction.
- Instruction replacementInstruction =
- new BranchInstruction(InstructionConstants.OP_GOTO,
- defaultOffset);
-
- replaceInstruction(clazz, offset, lookUpSwitchInstruction,
- replacementInstruction);
- }
- else
- {
- // Remove redundant entries from the arrays.
- int[] cases = lookUpSwitchInstruction.cases;
- int[] newJumpOffsets = new int[newLength];
- int[] newCases = new int[newLength];
-
- int newIndex = 0;
-
- for (int index = 0; index < length; index++)
- {
- if (jumpOffsets[index] != defaultOffset)
- {
- newJumpOffsets[newIndex] = jumpOffsets[index];
- newCases[newIndex++] = cases[index];
- }
- }
-
- lookUpSwitchInstruction.jumpOffsets = newJumpOffsets;
- lookUpSwitchInstruction.cases = newCases;
-
- replaceInstruction(clazz, offset, lookUpSwitchInstruction,
- lookUpSwitchInstruction);
- }
- }
- }
-
-
- /**
- * 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;
- }
- }
- }
-
-
- /**
- * Replaces the simple enum switch instructions at a given offsets by a
- * given replacement instruction.
- */
- private void replaceSimpleEnumSwitchInstruction(Clazz clazz,
- int loadOffset,
- int switchOffset,
- SwitchInstruction replacementSwitchInstruction)
- {
- if (DEBUG) System.out.println(" Replacing switch instruction at ["+switchOffset+"] -> ["+loadOffset+"] swap + pop, "+replacementSwitchInstruction.toString(switchOffset)+")");
-
- // Remove the array load instruction.
- codeAttributeEditor.replaceInstruction(loadOffset, new Instruction[]
- {
- new SimpleInstruction(InstructionConstants.OP_SWAP),
- new SimpleInstruction(InstructionConstants.OP_POP),
- });
-
- // Replace the switch instruction.
- codeAttributeEditor.replaceInstruction(switchOffset, replacementSwitchInstruction);
-
- // 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.
- replacementSwitchInstruction.accept(clazz,
- null,
- null,
- switchOffset,
- extraInstructionVisitor);
- }
- }
-}