diff options
Diffstat (limited to 'src/proguard/optimize/evaluation/EvaluationSimplifier.java')
-rw-r--r-- | src/proguard/optimize/evaluation/EvaluationSimplifier.java | 1360 |
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); - } - } -} |